From 78721bde4cd5a277b168df4673d38cf78d40a5f0 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 7 Jun 2019 13:08:49 -0700 Subject: [PATCH] Backend notifies the frontend of Storage API support. Frontend disables e.g. reload-and-profile based on this. --- src/backend/agent.js | 9 ++++++++ src/devtools/store.js | 23 ++++++++++++++++++- .../views/Profiler/ReloadAndProfileButton.js | 20 ++++++++++++++-- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/backend/agent.js b/src/backend/agent.js index 105cb9a312..4c1bf1b4c9 100644 --- a/src/backend/agent.js +++ b/src/backend/agent.js @@ -127,6 +127,15 @@ 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); } captureScreenshot = ({ diff --git a/src/devtools/store.js b/src/devtools/store.js index 6ad59c848a..6ad34d7e7b 100644 --- a/src/devtools/store.js +++ b/src/devtools/store.js @@ -73,6 +73,10 @@ export default class Store extends EventEmitter { // The InspectedElementContext also relies on this mutability for its WeakMap usage. _idToElement: Map = new Map(); + // 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; + // Map of element (id) to the set of elements (ids) it owns. // This map enables getOwnersListForElement() to avoid traversing the entire tree. _ownersMap: Map> = new Map(); @@ -141,6 +145,10 @@ export default class Store extends EventEmitter { this._bridge = bridge; bridge.addListener('operations', this.onBridgeOperations); bridge.addListener('shutdown', this.onBridgeShutdown); + bridge.addListener( + 'isBackendStorageAPISupported', + this.onBridgeStorageSupported + ); this._profilerStore = new ProfilerStore(bridge, this, isProfiling); } @@ -261,7 +269,10 @@ export default class Store extends EventEmitter { } get supportsReloadAndProfile(): boolean { - return this._supportsReloadAndProfile; + // Does the DevTools shell support reloading and eagerly injecting the renderer interface? + // And if so, can the backend use the localStorage API? + // Both of these are required for the reload-and-profile feature to work. + return this._supportsReloadAndProfile && this._isBackendStorageAPISupported; } containsElement(id: number): boolean { @@ -904,5 +915,15 @@ export default class Store extends EventEmitter { this._bridge.removeListener('operations', this.onBridgeOperations); this._bridge.removeListener('shutdown', this.onBridgeShutdown); + this._bridge.removeListener( + 'isBackendStorageAPISupported', + this.onBridgeStorageSupported + ); + }; + + onBridgeStorageSupported = (isBackendStorageAPISupported: boolean) => { + this._isBackendStorageAPISupported = isBackendStorageAPISupported; + + this.emit('supportsReloadAndProfile'); }; } diff --git a/src/devtools/views/Profiler/ReloadAndProfileButton.js b/src/devtools/views/Profiler/ReloadAndProfileButton.js index ac33e1feec..110a1b61a4 100644 --- a/src/devtools/views/Profiler/ReloadAndProfileButton.js +++ b/src/devtools/views/Profiler/ReloadAndProfileButton.js @@ -1,19 +1,35 @@ // @flow -import React, { useCallback, useContext } from 'react'; +import React, { useCallback, useContext, useMemo } from 'react'; import Button from '../Button'; import ButtonIcon from '../ButtonIcon'; import { BridgeContext, StoreContext } from '../context'; +import { useSubscription } from '../hooks'; +import Store from 'src/devtools/store'; export default function ReloadAndProfileButton() { const bridge = useContext(BridgeContext); const store = useContext(StoreContext); + const supportsReloadAndProfileSubscription = useMemo( + () => ({ + getCurrentValue: () => store.supportsReloadAndProfile, + subscribe: (callback: Function) => { + store.addListener('supportsReloadAndProfile', callback); + return () => store.removeListener('supportsReloadAndProfile', callback); + }, + }), + [store] + ); + const supportsReloadAndProfile = useSubscription( + supportsReloadAndProfileSubscription + ); + const reloadAndProfile = useCallback(() => bridge.send('reloadAndProfile'), [ bridge, ]); - if (!store.supportsReloadAndProfile) { + if (!supportsReloadAndProfile) { return null; }