Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: allow custom impl of backend realod-to-profile support check #31048

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions packages/react-devtools-core/src/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {installHook} from 'react-devtools-shared/src/hook';
import {initBackend} from 'react-devtools-shared/src/backend';
import {__DEBUG__} from 'react-devtools-shared/src/constants';
import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
import {getDefaultComponentFilters} from 'react-devtools-shared/src/utils';
import {
getDefaultComponentFilters,
getIsReloadAndProfileSupported,
} from 'react-devtools-shared/src/utils';

import type {BackendBridge} from 'react-devtools-shared/src/bridge';
import type {
Expand All @@ -36,6 +39,7 @@ type ConnectOptions = {
isAppActive?: () => boolean,
websocket?: ?WebSocket,
onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
isReloadAndProfileSupported?: boolean,
};

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

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

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

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

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

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

const nativeStyleResolver: ResolveNativeStyle | void =
resolveRNStyle || hook.resolveRNStyle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
} from 'react-devtools-shared/src/backend/types';
import {hasAssignedBackend} from 'react-devtools-shared/src/backend/utils';
import {COMPACT_VERSION_NAME} from 'react-devtools-extensions/src/utils';
import {getIsReloadAndProfileSupported} from 'react-devtools-shared/src/utils';

let welcomeHasInitialized = false;

Expand Down Expand Up @@ -140,7 +141,7 @@ function activateBackend(version: string, hook: DevToolsHook) {
hook.emit('shutdown');
});

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

// Setup React Native style editor if a renderer like react-native-web has injected it.
if (typeof setupNativeStyleEditor === 'function' && hook.resolveRNStyle) {
Expand Down
3 changes: 2 additions & 1 deletion packages/react-devtools-inline/src/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyl

import type {BackendBridge} from 'react-devtools-shared/src/bridge';
import type {Wall} from 'react-devtools-shared/src/frontend/types';
import {getIsReloadAndProfileSupported} from 'react-devtools-shared/src/utils';

function startActivation(contentWindow: any, bridge: BackendBridge) {
const onSavedPreferences = (data: $FlowFixMe) => {
Expand Down Expand Up @@ -66,7 +67,7 @@ function finishActivation(contentWindow: any, bridge: BackendBridge) {

const hook = contentWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__;
if (hook) {
initBackend(hook, agent, contentWindow);
initBackend(hook, agent, contentWindow, getIsReloadAndProfileSupported());

// Setup React Native style editor if a renderer like react-native-web has injected it.
if (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
5 changes: 5 additions & 0 deletions packages/react-devtools-shared/src/backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function initBackend(
hook: DevToolsHook,
agent: Agent,
global: Object,
isReloadAndProfileSupported: boolean,
): () => void {
if (hook == null) {
// DevTools didn't get injected into this page (maybe b'c of the contentType).
Expand Down Expand Up @@ -94,6 +95,10 @@ export function initBackend(
}
});

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

return () => {
subs.forEach(fn => fn());
};
Expand Down
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 @@ -234,7 +226,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 @@ -255,17 +247,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 @@ -469,13 +457,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 @@ -1433,17 +1417,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 @@ -1458,18 +1438,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
13 changes: 13 additions & 0 deletions packages/react-devtools-shared/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import type {
LRUCache,
} from 'react-devtools-shared/src/frontend/types';
import type {SerializedElement as SerializedElementBackend} from 'react-devtools-shared/src/backend/types';
import {isSynchronousXHRSupported} from './backend/utils';

// $FlowFixMe[method-unbinding]
const hasOwnProperty = Object.prototype.hasOwnProperty;
Expand Down Expand Up @@ -965,3 +966,15 @@ export function backendToFrontendSerializedElementMapper(
export function normalizeUrl(url: string): string {
return url.replace('/./', '/');
}

export function getIsReloadAndProfileSupported(): 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();
}
Loading