1. /** @flow */
    
  2. 
    
  3. import Agent from 'react-devtools-shared/src/backend/agent';
    
  4. import Bridge from 'react-devtools-shared/src/bridge';
    
  5. import {initBackend} from 'react-devtools-shared/src/backend';
    
  6. import {installConsoleFunctionsToWindow} from 'react-devtools-shared/src/backend/console';
    
  7. import {installHook} from 'react-devtools-shared/src/hook';
    
  8. import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
    
  9. 
    
  10. import type {BackendBridge} from 'react-devtools-shared/src/bridge';
    
  11. import type {Wall} from 'react-devtools-shared/src/frontend/types';
    
  12. 
    
  13. function startActivation(contentWindow: any, bridge: BackendBridge) {
    
  14.   const onSavedPreferences = (data: $FlowFixMe) => {
    
  15.     // This is the only message we're listening for,
    
  16.     // so it's safe to cleanup after we've received it.
    
  17.     bridge.removeListener('savedPreferences', onSavedPreferences);
    
  18. 
    
  19.     const {
    
  20.       appendComponentStack,
    
  21.       breakOnConsoleErrors,
    
  22.       componentFilters,
    
  23.       showInlineWarningsAndErrors,
    
  24.       hideConsoleLogsInStrictMode,
    
  25.     } = data;
    
  26. 
    
  27.     contentWindow.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ =
    
  28.       appendComponentStack;
    
  29.     contentWindow.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ =
    
  30.       breakOnConsoleErrors;
    
  31.     contentWindow.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters;
    
  32.     contentWindow.__REACT_DEVTOOLS_SHOW_INLINE_WARNINGS_AND_ERRORS__ =
    
  33.       showInlineWarningsAndErrors;
    
  34.     contentWindow.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ =
    
  35.       hideConsoleLogsInStrictMode;
    
  36. 
    
  37.     // TRICKY
    
  38.     // The backend entry point may be required in the context of an iframe or the parent window.
    
  39.     // If it's required within the parent window, store the saved values on it as well,
    
  40.     // since the injected renderer interface will read from window.
    
  41.     // Technically we don't need to store them on the contentWindow in this case,
    
  42.     // but it doesn't really hurt anything to store them there too.
    
  43.     if (contentWindow !== window) {
    
  44.       window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = appendComponentStack;
    
  45.       window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ = breakOnConsoleErrors;
    
  46.       window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters;
    
  47.       window.__REACT_DEVTOOLS_SHOW_INLINE_WARNINGS_AND_ERRORS__ =
    
  48.         showInlineWarningsAndErrors;
    
  49.       window.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ =
    
  50.         hideConsoleLogsInStrictMode;
    
  51.     }
    
  52. 
    
  53.     finishActivation(contentWindow, bridge);
    
  54.   };
    
  55. 
    
  56.   bridge.addListener('savedPreferences', onSavedPreferences);
    
  57. 
    
  58.   // The backend may be unable to read saved preferences directly,
    
  59.   // because they are stored in localStorage within the context of the extension (on the frontend).
    
  60.   // Instead it relies on the extension to pass preferences through.
    
  61.   // Because we might be in a sandboxed iframe, we have to ask for them by way of postMessage().
    
  62.   bridge.send('getSavedPreferences');
    
  63. }
    
  64. 
    
  65. function finishActivation(contentWindow: any, bridge: BackendBridge) {
    
  66.   const agent = new Agent(bridge);
    
  67. 
    
  68.   const hook = contentWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__;
    
  69.   if (hook) {
    
  70.     initBackend(hook, agent, contentWindow);
    
  71. 
    
  72.     // Setup React Native style editor if a renderer like react-native-web has injected it.
    
  73.     if (hook.resolveRNStyle) {
    
  74.       setupNativeStyleEditor(
    
  75.         bridge,
    
  76.         agent,
    
  77.         hook.resolveRNStyle,
    
  78.         hook.nativeStyleEditorValidAttributes,
    
  79.       );
    
  80.     }
    
  81.   }
    
  82. }
    
  83. 
    
  84. export function activate(
    
  85.   contentWindow: any,
    
  86.   {
    
  87.     bridge,
    
  88.   }: {
    
  89.     bridge?: BackendBridge,
    
  90.   } = {},
    
  91. ): void {
    
  92.   if (bridge == null) {
    
  93.     bridge = createBridge(contentWindow);
    
  94.   }
    
  95. 
    
  96.   startActivation(contentWindow, bridge);
    
  97. }
    
  98. 
    
  99. export function createBridge(contentWindow: any, wall?: Wall): BackendBridge {
    
  100.   const {parent} = contentWindow;
    
  101. 
    
  102.   if (wall == null) {
    
  103.     wall = {
    
  104.       listen(fn) {
    
  105.         const onMessage = ({data}: $FlowFixMe) => {
    
  106.           fn(data);
    
  107.         };
    
  108.         contentWindow.addEventListener('message', onMessage);
    
  109.         return () => {
    
  110.           contentWindow.removeEventListener('message', onMessage);
    
  111.         };
    
  112.       },
    
  113.       send(event: string, payload: any, transferable?: Array<any>) {
    
  114.         parent.postMessage({event, payload}, '*', transferable);
    
  115.       },
    
  116.     };
    
  117.   }
    
  118. 
    
  119.   return (new Bridge(wall): BackendBridge);
    
  120. }
    
  121. 
    
  122. export function initialize(contentWindow: any): void {
    
  123.   // Install a global variable to allow patching console early (during injection).
    
  124.   // This provides React Native developers with components stacks even if they don't run DevTools.
    
  125.   installConsoleFunctionsToWindow();
    
  126.   installHook(contentWindow);
    
  127. }