mirror of
https://github.com/facebook/react.git
synced 2026-02-24 20:53:03 +00:00
## Summary - #26234 is reverted and replaced with a better approach - introduce a new global devtools variable to decouple the global hook's dependency on backend/console.js, and add it to react-devtools-inline and react-devtools-standalone With this PR, I want to introduce a new principle to hook.js: we should always be alert when editing this file and avoid importing from other files. In the past, we try to inline a lot of the implementation because we use `.toString()` to inject this function from the extension (we still have some old comments left). Although it is no longer inlined that way, it has became now more important to keep it clean as it is a de facto global API people are using (9.9K files contains it on Github search as of today). **File size change for extension:** Before: 379K installHook.js After: 21K installHook.js 363K renderer.js
128 lines
4.5 KiB
JavaScript
128 lines
4.5 KiB
JavaScript
/** @flow */
|
|
|
|
import Agent from 'react-devtools-shared/src/backend/agent';
|
|
import Bridge from 'react-devtools-shared/src/bridge';
|
|
import {initBackend} from 'react-devtools-shared/src/backend';
|
|
import {installConsoleFunctionsToWindow} from 'react-devtools-shared/src/backend/console';
|
|
import {installHook} from 'react-devtools-shared/src/hook';
|
|
import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
|
|
|
|
import type {BackendBridge} from 'react-devtools-shared/src/bridge';
|
|
import type {Wall} from 'react-devtools-shared/src/types';
|
|
|
|
function startActivation(contentWindow: any, bridge: BackendBridge) {
|
|
const onSavedPreferences = (data: $FlowFixMe) => {
|
|
// This is the only message we're listening for,
|
|
// so it's safe to cleanup after we've received it.
|
|
bridge.removeListener('savedPreferences', onSavedPreferences);
|
|
|
|
const {
|
|
appendComponentStack,
|
|
breakOnConsoleErrors,
|
|
componentFilters,
|
|
showInlineWarningsAndErrors,
|
|
hideConsoleLogsInStrictMode,
|
|
} = data;
|
|
|
|
contentWindow.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ =
|
|
appendComponentStack;
|
|
contentWindow.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ =
|
|
breakOnConsoleErrors;
|
|
contentWindow.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters;
|
|
contentWindow.__REACT_DEVTOOLS_SHOW_INLINE_WARNINGS_AND_ERRORS__ =
|
|
showInlineWarningsAndErrors;
|
|
contentWindow.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ =
|
|
hideConsoleLogsInStrictMode;
|
|
|
|
// TRICKY
|
|
// The backend entry point may be required in the context of an iframe or the parent window.
|
|
// If it's required within the parent window, store the saved values on it as well,
|
|
// since the injected renderer interface will read from window.
|
|
// Technically we don't need to store them on the contentWindow in this case,
|
|
// but it doesn't really hurt anything to store them there too.
|
|
if (contentWindow !== window) {
|
|
window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = appendComponentStack;
|
|
window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ = breakOnConsoleErrors;
|
|
window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters;
|
|
window.__REACT_DEVTOOLS_SHOW_INLINE_WARNINGS_AND_ERRORS__ =
|
|
showInlineWarningsAndErrors;
|
|
window.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ =
|
|
hideConsoleLogsInStrictMode;
|
|
}
|
|
|
|
finishActivation(contentWindow, bridge);
|
|
};
|
|
|
|
bridge.addListener('savedPreferences', onSavedPreferences);
|
|
|
|
// The backend may be unable to read saved preferences directly,
|
|
// because they are stored in localStorage within the context of the extension (on the frontend).
|
|
// Instead it relies on the extension to pass preferences through.
|
|
// Because we might be in a sandboxed iframe, we have to ask for them by way of postMessage().
|
|
bridge.send('getSavedPreferences');
|
|
}
|
|
|
|
function finishActivation(contentWindow: any, bridge: BackendBridge) {
|
|
const agent = new Agent(bridge);
|
|
|
|
const hook = contentWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
if (hook) {
|
|
initBackend(hook, agent, contentWindow);
|
|
|
|
// Setup React Native style editor if a renderer like react-native-web has injected it.
|
|
if (hook.resolveRNStyle) {
|
|
setupNativeStyleEditor(
|
|
bridge,
|
|
agent,
|
|
hook.resolveRNStyle,
|
|
hook.nativeStyleEditorValidAttributes,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function activate(
|
|
contentWindow: any,
|
|
{
|
|
bridge,
|
|
}: {
|
|
bridge?: BackendBridge,
|
|
} = {},
|
|
): void {
|
|
if (bridge == null) {
|
|
bridge = createBridge(contentWindow);
|
|
}
|
|
|
|
startActivation(contentWindow, bridge);
|
|
}
|
|
|
|
export function createBridge(contentWindow: any, wall?: Wall): BackendBridge {
|
|
const {parent} = contentWindow;
|
|
|
|
if (wall == null) {
|
|
wall = {
|
|
listen(fn) {
|
|
const onMessage = ({data}: $FlowFixMe) => {
|
|
fn(data);
|
|
};
|
|
contentWindow.addEventListener('message', onMessage);
|
|
return () => {
|
|
contentWindow.removeEventListener('message', onMessage);
|
|
};
|
|
},
|
|
send(event: string, payload: any, transferable?: Array<any>) {
|
|
parent.postMessage({event, payload}, '*', transferable);
|
|
},
|
|
};
|
|
}
|
|
|
|
return (new Bridge(wall): BackendBridge);
|
|
}
|
|
|
|
export function initialize(contentWindow: any): void {
|
|
// Install a global variable to allow patching console early (during injection).
|
|
// This provides React Native developers with components stacks even if they don't run DevTools.
|
|
installConsoleFunctionsToWindow();
|
|
installHook(contentWindow);
|
|
}
|