main
js 121 lines 3.46 KB
Raw
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 {installHook} from 'react-devtools-shared/src/hook';
7 import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
8
9 import type {
10 BackendBridge,
11 SavedPreferencesParams,
12 } from 'react-devtools-shared/src/bridge';
13 import type {
14 ComponentFilter,
15 Wall,
16 } from 'react-devtools-shared/src/frontend/types';
17 import {
18 getIfReloadedAndProfiling,
19 getIsReloadAndProfileSupported,
20 onReloadAndProfile,
21 onReloadAndProfileFlagsReset,
22 } from 'react-devtools-shared/src/utils';
23
24 let resolveComponentFiltersInjection: (filters: Array<ComponentFilter>) => void;
25 const componentFiltersPromise = new Promise<Array<ComponentFilter>>(resolve => {
26 resolveComponentFiltersInjection = resolve;
27 });
28
29 function startActivation(contentWindow: any, bridge: BackendBridge) {
30 const onSavedPreferences = (data: SavedPreferencesParams) => {
31 // This is the only message we're listening for,
32 // so it's safe to cleanup after we've received it.
33 bridge.removeListener('savedPreferences', onSavedPreferences);
34
35 const {componentFilters} = data;
36
37 resolveComponentFiltersInjection(componentFilters);
38 };
39
40 componentFiltersPromise.then(
41 finishActivation.bind(null, contentWindow, bridge),
42 );
43
44 bridge.addListener('savedPreferences', onSavedPreferences);
45
46 // The backend may be unable to read saved preferences directly,
47 // because they are stored in localStorage within the context of the extension (on the frontend).
48 // Instead it relies on the extension to pass preferences through.
49 // Because we might be in a sandboxed iframe, we have to ask for them by way of postMessage().
50 bridge.send('getSavedPreferences');
51 }
52
53 function finishActivation(contentWindow: any, bridge: BackendBridge) {
54 const agent = new Agent(
55 bridge,
56 getIfReloadedAndProfiling(),
57 onReloadAndProfile,
58 );
59 onReloadAndProfileFlagsReset();
60
61 const hook = contentWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__;
62 if (hook) {
63 initBackend(hook, agent, contentWindow, getIsReloadAndProfileSupported());
64
65 // Setup React Native style editor if a renderer like react-native-web has injected it.
66 if (hook.resolveRNStyle) {
67 setupNativeStyleEditor(
68 bridge,
69 agent,
70 hook.resolveRNStyle,
71 hook.nativeStyleEditorValidAttributes,
72 );
73 }
74 }
75 }
76
77 export function activate(
78 contentWindow: any,
79 {
80 bridge,
81 }: {
82 bridge?: BackendBridge,
83 } = {},
84 ): void {
85 if (bridge == null) {
86 bridge = createBridge(contentWindow);
87 }
88
89 startActivation(contentWindow, bridge);
90 }
91
92 export function createBridge(contentWindow: any, wall?: Wall): BackendBridge {
93 const {parent} = contentWindow;
94
95 if (wall == null) {
96 wall = {
97 listen(fn) {
98 const onMessage = ({data}: $FlowFixMe) => {
99 fn(data);
100 };
101 contentWindow.addEventListener('message', onMessage);
102 return () => {
103 contentWindow.removeEventListener('message', onMessage);
104 };
105 },
106 send(
107 event: string,
108 payload: mixed,
109 transferable?: $ReadOnlyArray<mixed>,
110 ) {
111 parent.postMessage({event, payload}, '*', transferable);
112 },
113 };
114 }
115
116 return new Bridge(wall) as BackendBridge;
117 }
118
119 export function initialize(contentWindow: any): void {
120 installHook(contentWindow, componentFiltersPromise);
121 }