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

Ensure sync-xhr is allowed before reload and profile #20879

Merged
merged 13 commits into from
Mar 11, 2021
Merged
4 changes: 4 additions & 0 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import type {
RendererInterface,
} from './types';
import type {ComponentFilter} from '../types';
import {
} from './utils';

const debug = (methodName, ...args) => {
if (__DEBUG__) {
Expand Down Expand Up @@ -215,12 +217,14 @@ export default class Agent extends EventEmitter<{|

// 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.
// The same goes for sync XHR requests; it's how DevTools injects before ReactDOM starts rendering.
let isBackendStorageAPISupported = false;
try {
localStorage.getItem('test');
isBackendStorageAPISupported = true;
} catch (error) {}
bridge.send('isBackendStorageAPISupported', isBackendStorageAPISupported);
bridge.send('isSynchronousXHRSupported', isSynchronousXHRSupported());

setupHighlighter(bridge, this);
setupTraceUpdates(this);
Expand Down
8 changes: 8 additions & 0 deletions packages/react-devtools-shared/src/backend/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,11 @@ export function format(

return '' + formatted;
}

export function isSynchronousXHRSupported(): boolean {
return (
window.document &&
window.document.featurePolicy &&
bvaughn marked this conversation as resolved.
Show resolved Hide resolved
window.document.featurePolicy.allowsFeature('sync-xhr')
);
}
bvaughn marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions packages/react-devtools-shared/src/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export type BackendEvents = {|
syncSelectionFromNativeElementsPanel: [],
syncSelectionToNativeElementsPanel: [],
unsupportedRendererVersion: [RendererID],
isSynchronousXHRSupported: [boolean],

// React Native style editor plug-in.
isNativeStyleEditorSupported: [
Expand Down
17 changes: 17 additions & 0 deletions packages/react-devtools-shared/src/devtools/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default class Store extends EventEmitter<{|
supportsProfiling: [],
supportsReloadAndProfile: [],
unsupportedRendererVersionDetected: [],
supportsSynchronousXHR: [],
|}> {
_bridge: FrontendBridge;

Expand Down Expand Up @@ -141,6 +142,9 @@ export default class Store extends EventEmitter<{|
_supportsReloadAndProfile: boolean = false;
_supportsTraceUpdates: boolean = false;

// Needed for reload and profile
_supportsSynchronousXHR: boolean = false;
bvaughn marked this conversation as resolved.
Show resolved Hide resolved

_unsupportedRendererVersionDetected: boolean = false;

// Total number of visible elements (within all roots).
Expand Down Expand Up @@ -205,6 +209,10 @@ export default class Store extends EventEmitter<{|
'unsupportedRendererVersion',
this.onBridgeUnsupportedRendererVersion,
);
bridge.addListener(
'isSynchronousXHRSupported',
this.onBridgeSynchronousXHRSupported,
);

this._profilerStore = new ProfilerStore(bridge, this, isProfiling);
}
Expand Down Expand Up @@ -365,6 +373,9 @@ export default class Store extends EventEmitter<{|
// Both of these are required for the reload-and-profile feature to work.
return this._supportsReloadAndProfile && this._isBackendStorageAPISupported;
}
get supportsSynchronousXHR(): boolean {
return this._supportsSynchronousXHR;
}

get supportsTraceUpdates(): boolean {
return this._supportsTraceUpdates;
Expand Down Expand Up @@ -1149,4 +1160,10 @@ export default class Store extends EventEmitter<{|

this.emit('unsupportedRendererVersionDetected');
};

onBridgeSynchronousXHRSupported = (isSynchronousXHRSupported: boolean) => {
this._supportsSynchronousXHR = isSynchronousXHRSupported;

this.emit('supportsSynchronousXHR');
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {useSubscription} from '../hooks';
type SubscriptionData = {|
recordChangeDescriptions: boolean,
supportsReloadAndProfile: boolean,
supportsSynchronousXHR: boolean,
|};

export default function ReloadAndProfileButton() {
Expand All @@ -28,13 +29,16 @@ export default function ReloadAndProfileButton() {
getCurrentValue: () => ({
recordChangeDescriptions: store.recordChangeDescriptions,
supportsReloadAndProfile: store.supportsReloadAndProfile,
supportsSynchronousXHR: store.supportsSynchronousXHR,
}),
subscribe: (callback: Function) => {
store.addListener('recordChangeDescriptions', callback);
store.addListener('supportsReloadAndProfile', callback);
store.addListener('supportsSynchronousXHR', callback);
return () => {
store.removeListener('recordChangeDescriptions', callback);
store.removeListener('supportsReloadAndProfile', callback);
store.removeListener('supportsSynchronousXHR', callback);
};
},
}),
Expand All @@ -43,6 +47,7 @@ export default function ReloadAndProfileButton() {
const {
recordChangeDescriptions,
supportsReloadAndProfile,
supportsSynchronousXHR,
} = useSubscription<SubscriptionData>(subscription);

const reloadAndProfile = useCallback(() => {
Expand All @@ -59,11 +64,15 @@ export default function ReloadAndProfileButton() {
return null;
}

const buttonTitle = supportsSynchronousXHR
? 'Reload and start profiling'
: 'Reload and start profiling has been disabled because synchronous XHR is not supported on this site';

return (
<Button
disabled={!store.supportsProfiling}
disabled={!store.supportsProfiling || !supportsSynchronousXHR}
onClick={reloadAndProfile}
title="Reload and start profiling">
title={buttonTitle}>
<ButtonIcon type="reload" />
</Button>
);
Expand Down