Wrap all calls to localStorage/sessionStorage to avoid potential runtime errors

This commit is contained in:
Brian Vaughn
2019-06-07 10:51:01 -07:00
parent a987206a36
commit 89e36e3976
8 changed files with 88 additions and 33 deletions
@@ -3,6 +3,7 @@
import nullthrows from 'nullthrows';
import { installHook } from 'src/hook';
import { LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY } from 'src/constants';
import { localStorageGetItem } from 'src/storage';
function injectCode(code) {
const script = document.createElement('script');
@@ -63,7 +64,7 @@ window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set;
`;
// If we have just reloaded to profile, we need to inject the renderer interface before the app loads.
if (localStorage.getItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
if (localStorageGetItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
const rendererURL = chrome.runtime.getURL('build/renderer.js');
let rendererCode;
+8 -3
View File
@@ -11,6 +11,11 @@ import {
getBrowserTheme,
} from './utils';
import { getSavedComponentFilters } from 'src/utils';
import {
localStorageGetItem,
localStorageRemoveItem,
localStorageSetItem,
} from 'src/storage';
import DevTools from 'src/devtools/views/DevTools';
const LOCAL_STORAGE_SUPPORTS_PROFILING_KEY =
@@ -86,7 +91,7 @@ function createPanelIfReactLoaded() {
},
});
bridge.addListener('reloadAppForProfiling', () => {
localStorage.setItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY, 'true');
localStorageSetItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY, 'true');
chrome.devtools.inspectedWindow.eval('window.location.reload();');
});
bridge.addListener('captureScreenshot', ({ commitIndex, rootID }) => {
@@ -109,11 +114,11 @@ function createPanelIfReactLoaded() {
let isProfiling = false;
let supportsProfiling = false;
if (
localStorage.getItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY) === 'true'
localStorageGetItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY) === 'true'
) {
supportsProfiling = true;
isProfiling = true;
localStorage.removeItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY);
localStorageRemoveItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY);
}
const browserName = getBrowserName();
+23 -19
View File
@@ -8,6 +8,14 @@ import {
SESSION_STORAGE_LAST_SELECTION_KEY,
__DEBUG__,
} from '../constants';
import {
localStorageGetItem,
localStorageRemoveItem,
localStorageSetItem,
sessionStorageGetItem,
sessionStorageRemoveItem,
sessionStorageSetItem,
} from 'src/storage';
import { hideOverlay, showOverlay } from './views/Highlighter';
import type {
@@ -71,19 +79,17 @@ export default class Agent extends EventEmitter {
constructor(bridge: Bridge) {
super();
if (localStorage.getItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
if (localStorageGetItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
this._isProfiling = true;
localStorage.removeItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY);
localStorageRemoveItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY);
}
if (typeof sessionStorage !== 'undefined') {
const persistedSelectionString = sessionStorage.getItem(
SESSION_STORAGE_LAST_SELECTION_KEY
);
if (persistedSelectionString != null) {
this._persistedSelection = JSON.parse(persistedSelectionString);
}
const persistedSelectionString = sessionStorageGetItem(
SESSION_STORAGE_LAST_SELECTION_KEY
);
if (persistedSelectionString != null) {
this._persistedSelection = JSON.parse(persistedSelectionString);
}
this._bridge = bridge;
@@ -237,7 +243,7 @@ export default class Agent extends EventEmitter {
};
reloadAndProfile = () => {
localStorage.setItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
localStorageSetItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
// This code path should only be hit if the shell has explicitly told the Store that it supports profiling.
// In that case, the shell must also listen for this specific message to know when it needs to reload the app.
@@ -547,15 +553,13 @@ export default class Agent extends EventEmitter {
// This is why we need the defensive checks here.
const renderer = this._rendererInterfaces[rendererID];
const path = renderer != null ? renderer.getPathForElement(id) : null;
if (typeof sessionStorage !== 'undefined') {
if (path !== null) {
sessionStorage.setItem(
SESSION_STORAGE_LAST_SELECTION_KEY,
JSON.stringify(({ rendererID, path }: PersistedSelection))
);
} else {
sessionStorage.removeItem(SESSION_STORAGE_LAST_SELECTION_KEY);
}
if (path !== null) {
sessionStorageSetItem(
SESSION_STORAGE_LAST_SELECTION_KEY,
JSON.stringify(({ rendererID, path }: PersistedSelection))
);
} else {
sessionStorageRemoveItem(SESSION_STORAGE_LAST_SELECTION_KEY);
}
}, 1000);
}
+3 -1
View File
@@ -25,6 +25,7 @@ import {
getUID,
utfEncodeString,
} from 'src/utils';
import { localStorageGetItem } from 'src/storage';
import { cleanForBridge, copyWithSet, setInObject } from './utils';
import {
__DEBUG__,
@@ -2200,7 +2201,8 @@ export function attach(
}
// Automatically start profiling so that we don't miss timing info from initial "mount".
if (localStorage.getItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
// TODO This doens't seem right
if (localStorageGetItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
startProfiling();
}
+5 -5
View File
@@ -15,6 +15,7 @@ import {
separateDisplayNameAndHOCs,
utfDecodeString,
} from '../utils';
import { localStorageGetItem, localStorageSetItem } from '../storage';
import { __DEBUG__ } from '../constants';
import { printStore } from 'src/__tests__/storeSerializer';
import ProfilerStore from './ProfilerStore';
@@ -110,7 +111,7 @@ export default class Store extends EventEmitter {
// Default this setting to true unless otherwise specified.
this._collapseNodesByDefault =
localStorage.getItem(LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY) !==
localStorageGetItem(LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY) !==
'false';
this._componentFilters = getSavedComponentFilters();
@@ -127,8 +128,7 @@ export default class Store extends EventEmitter {
if (supportsCaptureScreenshots) {
this._supportsCaptureScreenshots = true;
this._captureScreenshots =
localStorage.getItem(LOCAL_STORAGE_CAPTURE_SCREENSHOTS_KEY) ===
'true';
localStorageGetItem(LOCAL_STORAGE_CAPTURE_SCREENSHOTS_KEY) === 'true';
}
if (supportsProfiling) {
this._supportsProfiling = true;
@@ -184,7 +184,7 @@ export default class Store extends EventEmitter {
set captureScreenshots(value: boolean): void {
this._captureScreenshots = value;
localStorage.setItem(
localStorageSetItem(
LOCAL_STORAGE_CAPTURE_SCREENSHOTS_KEY,
value ? 'true' : 'false'
);
@@ -198,7 +198,7 @@ export default class Store extends EventEmitter {
set collapseNodesByDefault(value: boolean): void {
this._collapseNodesByDefault = value;
localStorage.setItem(
localStorageSetItem(
LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY,
value ? 'true' : 'false'
);
+3 -2
View File
@@ -2,6 +2,7 @@
import throttle from 'lodash.throttle';
import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
import { localStorageGetItem, localStorageSetItem } from 'src/storage';
export function useIsOverflowing(
containerRef: { current: HTMLDivElement | null },
@@ -42,7 +43,7 @@ export function useLocalStorage<T>(
): [T, (value: T | (() => T)) => void] {
const getValueFromLocalStorage = useCallback(() => {
try {
const item = window.localStorage.getItem(key);
const item = localStorageGetItem(key);
if (item != null) {
return JSON.parse(item);
}
@@ -64,7 +65,7 @@ export function useLocalStorage<T>(
const valueToStore =
value instanceof Function ? (value: any)(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
localStorageSetItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.log(error);
}
+41
View File
@@ -0,0 +1,41 @@
// @flow
export function localStorageGetItem(key: string): any {
try {
return localStorage.getItem(key);
} catch (error) {
return null;
}
}
export function localStorageRemoveItem(key: string): void {
try {
localStorage.removeItem(key);
} catch (error) {}
}
export function localStorageSetItem(key: string, value: any): void {
try {
return localStorage.setItem(key, value);
} catch (error) {}
}
export function sessionStorageGetItem(key: string): any {
try {
return sessionStorage.getItem(key);
} catch (error) {
return null;
}
}
export function sessionStorageRemoveItem(key: string): void {
try {
sessionStorage.removeItem(key);
} catch (error) {}
}
export function sessionStorageSetItem(key: string, value: any): void {
try {
return sessionStorage.setItem(key, value);
} catch (error) {}
}
+3 -2
View File
@@ -9,6 +9,7 @@ import {
ElementTypeFunction,
ElementTypeMemo,
} from 'src/types';
import { localStorageGetItem, localStorageSetItem } from './storage';
import type { ComponentFilter, ElementType } from './types';
@@ -82,7 +83,7 @@ export function getDefaultComponentFilters(): Array<ComponentFilter> {
export function getSavedComponentFilters(): Array<ComponentFilter> {
try {
const raw = localStorage.getItem(LOCAL_STORAGE_FILTER_PREFERENCES_KEY);
const raw = localStorageGetItem(LOCAL_STORAGE_FILTER_PREFERENCES_KEY);
if (raw != null) {
return JSON.parse(raw);
}
@@ -93,7 +94,7 @@ export function getSavedComponentFilters(): Array<ComponentFilter> {
export function saveComponentFilters(
componentFilters: Array<ComponentFilter>
): void {
localStorage.setItem(
localStorageSetItem(
LOCAL_STORAGE_FILTER_PREFERENCES_KEY,
JSON.stringify(componentFilters)
);