Skip to content

Commit

Permalink
refactor: allow custom impl of backend realod-to-profile support check
Browse files Browse the repository at this point in the history
  • Loading branch information
EdmondChuiHW committed Sep 25, 2024
1 parent 04bd67a commit 9a7cec7
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 57 deletions.
13 changes: 11 additions & 2 deletions packages/react-devtools-core/src/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type ConnectOptions = {
isAppActive?: () => boolean,
websocket?: ?WebSocket,
onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
getIsReloadAndProfileSupported?: () => boolean,
};

let savedComponentFilters: Array<ComponentFilter> =
Expand Down Expand Up @@ -77,6 +78,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
retryConnectionDelay = 2000,
isAppActive = () => true,
onSettingsUpdated,
getIsReloadAndProfileSupported,
} = options || {};

const protocol = useHttps ? 'wss' : 'ws';
Expand Down Expand Up @@ -184,7 +186,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
hook.emit('shutdown');
});

initBackend(hook, agent, window);
initBackend(hook, agent, window, getIsReloadAndProfileSupported);

// Setup React Native style editor if the environment supports it.
if (resolveRNStyle != null || hook.resolveRNStyle != null) {
Expand Down Expand Up @@ -309,6 +311,7 @@ type ConnectWithCustomMessagingOptions = {
nativeStyleEditorValidAttributes?: $ReadOnlyArray<string>,
resolveRNStyle?: ResolveNativeStyle,
onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
getIsReloadAndProfileSupported?: () => boolean,
};

export function connectWithCustomMessagingProtocol({
Expand All @@ -318,6 +321,7 @@ export function connectWithCustomMessagingProtocol({
nativeStyleEditorValidAttributes,
resolveRNStyle,
onSettingsUpdated,
getIsReloadAndProfileSupported,
}: ConnectWithCustomMessagingOptions): Function {
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
if (hook == null) {
Expand Down Expand Up @@ -368,7 +372,12 @@ export function connectWithCustomMessagingProtocol({
hook.emit('shutdown');
});

const unsubscribeBackend = initBackend(hook, agent, window);
const unsubscribeBackend = initBackend(
hook,
agent,
window,
getIsReloadAndProfileSupported,
);

const nativeStyleResolver: ResolveNativeStyle | void =
resolveRNStyle || hook.resolveRNStyle;
Expand Down
16 changes: 5 additions & 11 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import type {
DevToolsHookSettings,
} from './types';
import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types';
import {isSynchronousXHRSupported, isReactNativeEnvironment} from './utils';
import {isReactNativeEnvironment} from './utils';

const debug = (methodName: string, ...args: Array<string>) => {
if (__DEBUG__) {
Expand Down Expand Up @@ -242,16 +242,6 @@ export default class Agent extends EventEmitter<{
if (this._isProfiling) {
bridge.send('profilingStatus', true);
}

// Notify the frontend if the backend supports the Storage API (e.g. localStorage).
// If not, features like reload-and-profile will not work correctly and must be disabled.
let isBackendStorageAPISupported = false;
try {
localStorage.getItem('test');
isBackendStorageAPISupported = true;
} catch (error) {}
bridge.send('isBackendStorageAPISupported', isBackendStorageAPISupported);
bridge.send('isSynchronousXHRSupported', isSynchronousXHRSupported());
}

get rendererInterfaces(): {[key: RendererID]: RendererInterface, ...} {
Expand Down Expand Up @@ -675,6 +665,10 @@ export default class Agent extends EventEmitter<{
}
};

onReloadAndProfileSupportedByHost: () => void = () => {
this._bridge.send('isReloadAndProfileSupportedByBackend', true);
};

reloadAndProfile: (recordChangeDescriptions: boolean) => void =
recordChangeDescriptions => {
sessionStorageSetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
Expand Down
18 changes: 18 additions & 0 deletions packages/react-devtools-shared/src/backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
import Agent from './agent';

import type {DevToolsHook, RendererID, RendererInterface} from './types';
import {isSynchronousXHRSupported} from './utils';

export type InitBackend = typeof initBackend;

export function initBackend(
hook: DevToolsHook,
agent: Agent,
global: Object,
getIsReloadAndProfileSupported: () => boolean = defaultGetIsReloadAndProfileSupported,
): () => void {
if (hook == null) {
// DevTools didn't get injected into this page (maybe b'c of the contentType).
Expand Down Expand Up @@ -94,7 +96,23 @@ export function initBackend(
}
});

if (getIsReloadAndProfileSupported()) {
agent.onReloadAndProfileSupportedByHost();
}

return () => {
subs.forEach(fn => fn());
};
}

function defaultGetIsReloadAndProfileSupported(): boolean {
// Notify the frontend if the backend supports the Storage API (e.g. localStorage).
// If not, features like reload-and-profile will not work correctly and must be disabled.
let isBackendStorageAPISupported = false;
try {
localStorage.getItem('test');
isBackendStorageAPISupported = true;
} catch (error) {}

return isBackendStorageAPISupported && isSynchronousXHRSupported();
}
3 changes: 1 addition & 2 deletions packages/react-devtools-shared/src/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,7 @@ export type BackendEvents = {
fastRefreshScheduled: [],
getSavedPreferences: [],
inspectedElement: [InspectedElementPayload],
isBackendStorageAPISupported: [boolean],
isSynchronousXHRSupported: [boolean],
isReloadAndProfileSupportedByBackend: [boolean],
operations: [Array<number>],
ownersList: [OwnersList],
overrideComponentFilters: [Array<ComponentFilter>],
Expand Down
56 changes: 14 additions & 42 deletions packages/react-devtools-shared/src/devtools/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,6 @@ export default class Store extends EventEmitter<{
// Should the React Native style editor panel be shown?
_isNativeStyleEditorSupported: boolean = false;

// Can the backend use the Storage API (e.g. localStorage)?
// If not, features like reload-and-profile will not work correctly and must be disabled.
_isBackendStorageAPISupported: boolean = false;

// Can DevTools use sync XHR requests?
// If not, features like reload-and-profile will not work correctly and must be disabled.
// This current limitation applies only to web extension builds
// and will need to be reconsidered in the future if we add support for reload to React Native.
_isSynchronousXHRSupported: boolean = false;

_nativeStyleEditorValidAttributes: $ReadOnlyArray<string> | null = null;

// Older backends don't support an explicit bridge protocol,
Expand Down Expand Up @@ -178,10 +168,12 @@ export default class Store extends EventEmitter<{
// These options may be initially set by a configuration option when constructing the Store.
_supportsInspectMatchingDOMElement: boolean = false;
_supportsClickToInspect: boolean = false;
_supportsReloadAndProfile: boolean = false;
_supportsTimeline: boolean = false;
_supportsTraceUpdates: boolean = false;

_isReloadAndProfileFrontendSupported: boolean = false;
_isReloadAndProfileBackendSupported: boolean = false;

// These options default to false but may be updated as roots are added and removed.
_rootSupportsBasicProfiling: boolean = false;
_rootSupportsTimelineProfiling: boolean = false;
Expand Down Expand Up @@ -233,7 +225,7 @@ export default class Store extends EventEmitter<{
this._supportsClickToInspect = true;
}
if (supportsReloadAndProfile) {
this._supportsReloadAndProfile = true;
this._isReloadAndProfileFrontendSupported = true;
}
if (supportsTimeline) {
this._supportsTimeline = true;
Expand All @@ -254,17 +246,13 @@ export default class Store extends EventEmitter<{
);
bridge.addListener('shutdown', this.onBridgeShutdown);
bridge.addListener(
'isBackendStorageAPISupported',
this.onBackendStorageAPISupported,
'isReloadAndProfileSupportedByBackend',
this.onBackendReloadAndProfileSupported,
);
bridge.addListener(
'isNativeStyleEditorSupported',
this.onBridgeNativeStyleEditorSupported,
);
bridge.addListener(
'isSynchronousXHRSupported',
this.onBridgeSynchronousXHRSupported,
);
bridge.addListener(
'unsupportedRendererVersion',
this.onBridgeUnsupportedRendererVersion,
Expand Down Expand Up @@ -452,13 +440,9 @@ export default class Store extends EventEmitter<{
}

get supportsReloadAndProfile(): boolean {
// Does the DevTools shell support reloading and eagerly injecting the renderer interface?
// And if so, can the backend use the localStorage API and sync XHR?
// All of these are currently required for the reload-and-profile feature to work.
return (
this._supportsReloadAndProfile &&
this._isBackendStorageAPISupported &&
this._isSynchronousXHRSupported
this._isReloadAndProfileFrontendSupported &&
this._isReloadAndProfileBackendSupported
);
}

Expand Down Expand Up @@ -1407,17 +1391,13 @@ export default class Store extends EventEmitter<{
);
bridge.removeListener('shutdown', this.onBridgeShutdown);
bridge.removeListener(
'isBackendStorageAPISupported',
this.onBackendStorageAPISupported,
'isReloadAndProfileSupportedByBackend',
this.onBackendReloadAndProfileSupported,
);
bridge.removeListener(
'isNativeStyleEditorSupported',
this.onBridgeNativeStyleEditorSupported,
);
bridge.removeListener(
'isSynchronousXHRSupported',
this.onBridgeSynchronousXHRSupported,
);
bridge.removeListener(
'unsupportedRendererVersion',
this.onBridgeUnsupportedRendererVersion,
Expand All @@ -1432,18 +1412,10 @@ export default class Store extends EventEmitter<{
}
};

onBackendStorageAPISupported: (
isBackendStorageAPISupported: boolean,
) => void = isBackendStorageAPISupported => {
this._isBackendStorageAPISupported = isBackendStorageAPISupported;

this.emit('supportsReloadAndProfile');
};

onBridgeSynchronousXHRSupported: (
isSynchronousXHRSupported: boolean,
) => void = isSynchronousXHRSupported => {
this._isSynchronousXHRSupported = isSynchronousXHRSupported;
onBackendReloadAndProfileSupported: (
isReloadAndProfileSupported: boolean,
) => void = isReloadAndProfileSupported => {
this._isReloadAndProfileBackendSupported = isReloadAndProfileSupported;

this.emit('supportsReloadAndProfile');
};
Expand Down

0 comments on commit 9a7cec7

Please sign in to comment.