Skip to content

Commit

Permalink
[react devtools] device storage
Browse files Browse the repository at this point in the history
  • Loading branch information
rbalicki2 committed Oct 8, 2022
1 parent 66a8ed0 commit b66ca95
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 32 deletions.
23 changes: 23 additions & 0 deletions packages/react-devtools-core/src/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ 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 {
initializeUsingCachedSettings,
cacheSettings,
type CachedSettingsStore,
} from './cachedSettings';

import type {BackendBridge} from 'react-devtools-shared/src/bridge';
import type {ComponentFilter} from 'react-devtools-shared/src/types';
Expand All @@ -29,6 +34,7 @@ type ConnectOptions = {
retryConnectionDelay?: number,
isAppActive?: () => boolean,
websocket?: ?WebSocket,
cachedSettingsStore: ?CachedSettingsStore,
...
};

Expand Down Expand Up @@ -63,6 +69,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
resolveRNStyle = null,
retryConnectionDelay = 2000,
isAppActive = () => true,
cachedSettingsStore,
} = options || {};

const protocol = useHttps ? 'wss' : 'ws';
Expand All @@ -78,6 +85,16 @@ export function connectToDevTools(options: ?ConnectOptions) {
}
}

if (cachedSettingsStore != null) {
try {
initializeUsingCachedSettings(cachedSettingsStore);
} catch (e) {
// If we're passed a cachedSettingsStore.getValue that throws, or there
// is invalid data read out, don't throw and don't interrupt initialization
console.error(e);
}
}

if (!isAppActive()) {
// If the app is in background, maybe retry later.
// Don't actually attempt to connect until we're in foreground.
Expand Down Expand Up @@ -157,6 +174,12 @@ export function connectToDevTools(options: ?ConnectOptions) {
},
);

if (cachedSettingsStore != null) {
bridge.addListener('updateConsolePatchSettings', consolePatchSettings =>
cacheSettings(cachedSettingsStore, {consolePatchSettings}),
);
}

// The renderer interface doesn't read saved component filters directly,
// because they are generally stored in localStorage within the context of the extension.
// Because of this it relies on the extension to pass filters.
Expand Down
80 changes: 80 additions & 0 deletions packages/react-devtools-core/src/cachedSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {
type ConsolePatchSettings,
writeConsolePatchSettingsToWindow,
} from 'react-devtools-shared/src/backend/console';
import {castBool, castBrowserTheme} from 'react-devtools-shared/src/utils';

export type CachedSettingsStore = {
setValue: (key: string, value: string) => void,
getValue: (key: string) => ?string,
};

export type CachedSettings = {
consolePatchSettings: ?ConsolePatchSettings,
};

const CONSOLE_PATCH_SETTINGS_KEY = 'ConsolePatchSettings';

export function initializeUsingCachedSettings(
cachedSettingsStore: CachedSettingsStore,
) {
const cachedSettingsString = cachedSettingsStore.getValue(
CONSOLE_PATCH_SETTINGS_KEY,
);
if (cachedSettingsString == null) {
return;
}
const cachedSettings = parseCachedSettings(cachedSettingsString);
if (cachedSettings != null && cachedSettings.consolePatchSettings != null) {
writeConsolePatchSettingsToWindow(cachedSettings.consolePatchSettings);
}
}

function parseCachedSettings(cachedSettingsString: string): ?CachedSettings {
const parsedValue = JSON.parse(cachedSettingsString);

if (typeof parsedValue === 'object' && parsedValue != null) {
const consolePatchSettings = parseConsolePatchSettings(
parsedValue.consolePatchSettings,
);
return {
consolePatchSettings,
};
}
}

function parseConsolePatchSettings(value: any): ?ConsolePatchSettings {
if (typeof value !== 'object' || value === null) {
return;
}
const {
appendComponentStack,
breakOnConsoleErrors,
showInlineWarningsAndErrors,
hideConsoleLogsInStrictMode,
browserTheme,
} = value;
return {
appendComponentStack: castBool(appendComponentStack) ?? true,
breakOnConsoleErrors: castBool(breakOnConsoleErrors) ?? false,
showInlineWarningsAndErrors: castBool(showInlineWarningsAndErrors) ?? true,
hideConsoleLogsInStrictMode: castBool(hideConsoleLogsInStrictMode) ?? false,
browserTheme: castBrowserTheme(browserTheme) ?? 'dark',
};
}

export function cacheSettings(
cachedSettings: CachedSettingsStore,
value: CachedSettings,
): void {
cachedSettings.setValue(CONSOLE_PATCH_SETTINGS_KEY, JSON.stringify(value));
}
10 changes: 5 additions & 5 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
initialize as setupTraceUpdates,
toggleEnabled as setTraceUpdatesEnabled,
} from './views/TraceUpdates';
import {patch as patchConsole} from './console';
import {patch as patchConsole, type ConsolePatchSettings} from './console';
import {currentBridgeProtocol} from 'react-devtools-shared/src/bridge';

import type {BackendBridge} from 'react-devtools-shared/src/bridge';
Expand Down Expand Up @@ -712,11 +712,11 @@ export default class Agent extends EventEmitter<{
showInlineWarningsAndErrors,
hideConsoleLogsInStrictMode,
browserTheme,
}) => {
// If the frontend preference has change,
// or in the case of React Native- if the backend is just finding out the preference-
}: ConsolePatchSettings) => {
// If the frontend preferences have changed,
// or in the case of React Native- if the backend is just finding out the preferences-
// then reinstall the console overrides.
// It's safe to call these methods multiple times, so we don't need to worry about that.
// It's safe to call `patchConsole` multiple times.
patchConsole({
appendComponentStack,
breakOnConsoleErrors,
Expand Down
42 changes: 25 additions & 17 deletions packages/react-devtools-shared/src/backend/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {format, formatWithStyles} from './utils';
import {getInternalReactConstants} from './renderer';
import {getStackByFiberInDevAndProd} from './DevToolsFiberComponentStack';
import {consoleManagedByDevToolsDuringStrictMode} from 'react-devtools-feature-flags';
import {castBool, castBrowserTheme} from '../utils';

const OVERRIDE_CONSOLE_METHODS = ['error', 'trace', 'warn'];
const DIMMED_NODE_CONSOLE_COLOR = '\x1b[2m%s\x1b[0m';
Expand Down Expand Up @@ -143,6 +144,14 @@ const consoleSettingsRef = {
browserTheme: 'dark',
};

export type ConsolePatchSettings = {
appendComponentStack: boolean,
breakOnConsoleErrors: boolean,
showInlineWarningsAndErrors: boolean,
hideConsoleLogsInStrictMode: boolean,
browserTheme: BrowserTheme,
};

// Patches console methods to append component stack for the current fiber.
// Call unpatch() to remove the injected behavior.
export function patch({
Expand All @@ -151,13 +160,7 @@ export function patch({
showInlineWarningsAndErrors,
hideConsoleLogsInStrictMode,
browserTheme,
}: {
appendComponentStack: boolean,
breakOnConsoleErrors: boolean,
showInlineWarningsAndErrors: boolean,
hideConsoleLogsInStrictMode: boolean,
browserTheme: BrowserTheme,
}): void {
}: ConsolePatchSettings): void {
// Settings may change after we've patched the console.
// Using a shared ref allows the patch function to read the latest values.
consoleSettingsRef.appendComponentStack = appendComponentStack;
Expand Down Expand Up @@ -390,14 +393,19 @@ export function patchConsoleUsingWindowValues() {
});
}

function castBool(v: any): ?boolean {
if (v === true || v === false) {
return v;
}
}

function castBrowserTheme(v: any): ?BrowserTheme {
if (v === 'light' || v === 'dark' || v === 'auto') {
return v;
}
// After receiving cached console patch settings from React Native, we set them on window.
// When the console is initially patched (in renderer.js and hook.js), these values are read.
// The browser extension (etc.) sets these values on window, but through another method.
export function writeConsolePatchSettingsToWindow(
settings: ConsolePatchSettings,
): void {
window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ =
settings.appendComponentStack;
window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ =
settings.breakOnConsoleErrors;
window.__REACT_DEVTOOLS_SHOW_INLINE_WARNINGS_AND_ERRORS__ =
settings.showInlineWarningsAndErrors;
window.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ =
settings.hideConsoleLogsInStrictMode;
window.__REACT_DEVTOOLS_BROWSER_THEME__ = settings.browserTheme;
}
12 changes: 2 additions & 10 deletions packages/react-devtools-shared/src/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type {
RendererID,
} from 'react-devtools-shared/src/backend/types';
import type {StyleAndLayout as StyleAndLayoutPayload} from 'react-devtools-shared/src/backend/NativeStyleEditor/types';
import type {BrowserTheme} from 'react-devtools-shared/src/devtools/views/DevTools';
import type {ConsolePatchSettings} from 'react-devtools-shared/src/backend/console';

const BATCH_DURATION = 100;

Expand Down Expand Up @@ -171,14 +171,6 @@ type NativeStyleEditor_SetValueParams = {
value: string,
};

type UpdateConsolePatchSettingsParams = {
appendComponentStack: boolean,
breakOnConsoleErrors: boolean,
showInlineWarningsAndErrors: boolean,
hideConsoleLogsInStrictMode: boolean,
browserTheme: BrowserTheme,
};

type SavedPreferencesParams = {
appendComponentStack: boolean,
breakOnConsoleErrors: boolean,
Expand Down Expand Up @@ -247,7 +239,7 @@ type FrontendEvents = {
stopProfiling: [],
storeAsGlobal: [StoreAsGlobalParams],
updateComponentFilters: [Array<ComponentFilter>],
updateConsolePatchSettings: [UpdateConsolePatchSettingsParams],
updateConsolePatchSettings: [ConsolePatchSettings],
viewAttributeSource: [ViewAttributeSourceParams],
viewElementSource: [ElementAndRendererID],

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 @@ -55,6 +55,7 @@ import isArray from './isArray';

import type {ComponentFilter, ElementType} from './types';
import type {LRUCache} from 'react-devtools-shared/src/types';
import type {BrowserTheme} from 'react-devtools-shared/src/devtools/views/DevTools';

// $FlowFixMe[method-unbinding]
const hasOwnProperty = Object.prototype.hasOwnProperty;
Expand Down Expand Up @@ -353,6 +354,18 @@ function parseBool(s: ?string): ?boolean {
}
}

export function castBool(v: any): ?boolean {
if (v === true || v === false) {
return v;
}
}

export function castBrowserTheme(v: any): ?BrowserTheme {
if (v === 'light' || v === 'dark' || v === 'auto') {
return v;
}
}

export function getAppendComponentStack(): boolean {
const raw = localStorageGetItem(
LOCAL_STORAGE_SHOULD_APPEND_COMPONENT_STACK_KEY,
Expand Down

0 comments on commit b66ca95

Please sign in to comment.