@samitouri / QOS-React-2 / commits / 89e36e3976

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

Brian Vaughn committed Jun 7, 2019 at 10:51 UTC 89e36e3976b062584f83c22d43f043cf464c3403
8 files changed +88 -33
shells/browser/shared/src/injectGlobalHook.js
+2 -1
@@ -3,6 +3,7 @@
3 import nullthrows from 'nullthrows';
4 import { installHook } from 'src/hook';
5 import { LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY } from 'src/constants';
6 +import { localStorageGetItem } from 'src/storage';
7
8 function injectCode(code) {
9 const script = document.createElement('script');
@@ -63,7 +64,7 @@ window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set;
64 `;
65
66 // If we have just reloaded to profile, we need to inject the renderer interface before the app loads.
66 -if (localStorage.getItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
67 +if (localStorageGetItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
68 const rendererURL = chrome.runtime.getURL('build/renderer.js');
69 let rendererCode;
70
shells/browser/shared/src/main.js
+8 -3
@@ -11,6 +11,11 @@ import {
11 getBrowserTheme,
12 } from './utils';
13 import { getSavedComponentFilters } from 'src/utils';
14 +import {
15 + localStorageGetItem,
16 + localStorageRemoveItem,
17 + localStorageSetItem,
18 +} from 'src/storage';
19 import DevTools from 'src/devtools/views/DevTools';
20
21 const LOCAL_STORAGE_SUPPORTS_PROFILING_KEY =
@@ -86,7 +91,7 @@ function createPanelIfReactLoaded() {
91 },
92 });
93 bridge.addListener('reloadAppForProfiling', () => {
89 - localStorage.setItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY, 'true');
94 + localStorageSetItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY, 'true');
95 chrome.devtools.inspectedWindow.eval('window.location.reload();');
96 });
97 bridge.addListener('captureScreenshot', ({ commitIndex, rootID }) => {
@@ -109,11 +114,11 @@ function createPanelIfReactLoaded() {
114 let isProfiling = false;
115 let supportsProfiling = false;
116 if (
112 - localStorage.getItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY) === 'true'
117 + localStorageGetItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY) === 'true'
118 ) {
119 supportsProfiling = true;
120 isProfiling = true;
116 - localStorage.removeItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY);
121 + localStorageRemoveItem(LOCAL_STORAGE_SUPPORTS_PROFILING_KEY);
122 }
123
124 const browserName = getBrowserName();
src/backend/agent.js
+23 -19
@@ -8,6 +8,14 @@ import {
8 SESSION_STORAGE_LAST_SELECTION_KEY,
9 __DEBUG__,
10 } from '../constants';
11 +import {
12 + localStorageGetItem,
13 + localStorageRemoveItem,
14 + localStorageSetItem,
15 + sessionStorageGetItem,
16 + sessionStorageRemoveItem,
17 + sessionStorageSetItem,
18 +} from 'src/storage';
19 import { hideOverlay, showOverlay } from './views/Highlighter';
20
21 import type {
@@ -71,19 +79,17 @@ export default class Agent extends EventEmitter {
79 constructor(bridge: Bridge) {
80 super();
81
74 - if (localStorage.getItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
82 + if (localStorageGetItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
83 this._isProfiling = true;
84
77 - localStorage.removeItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY);
85 + localStorageRemoveItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY);
86 }
87
80 - if (typeof sessionStorage !== 'undefined') {
81 - const persistedSelectionString = sessionStorage.getItem(
82 - SESSION_STORAGE_LAST_SELECTION_KEY
83 - );
84 - if (persistedSelectionString != null) {
85 - this._persistedSelection = JSON.parse(persistedSelectionString);
86 - }
88 + const persistedSelectionString = sessionStorageGetItem(
89 + SESSION_STORAGE_LAST_SELECTION_KEY
90 + );
91 + if (persistedSelectionString != null) {
92 + this._persistedSelection = JSON.parse(persistedSelectionString);
93 }
94
95 this._bridge = bridge;
@@ -237,7 +243,7 @@ export default class Agent extends EventEmitter {
243 };
244
245 reloadAndProfile = () => {
240 - localStorage.setItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
246 + localStorageSetItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
247
248 // This code path should only be hit if the shell has explicitly told the Store that it supports profiling.
249 // 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 {
553 // This is why we need the defensive checks here.
554 const renderer = this._rendererInterfaces[rendererID];
555 const path = renderer != null ? renderer.getPathForElement(id) : null;
550 - if (typeof sessionStorage !== 'undefined') {
551 - if (path !== null) {
552 - sessionStorage.setItem(
553 - SESSION_STORAGE_LAST_SELECTION_KEY,
554 - JSON.stringify(({ rendererID, path }: PersistedSelection))
555 - );
556 - } else {
557 - sessionStorage.removeItem(SESSION_STORAGE_LAST_SELECTION_KEY);
558 - }
556 + if (path !== null) {
557 + sessionStorageSetItem(
558 + SESSION_STORAGE_LAST_SELECTION_KEY,
559 + JSON.stringify(({ rendererID, path }: PersistedSelection))
560 + );
561 + } else {
562 + sessionStorageRemoveItem(SESSION_STORAGE_LAST_SELECTION_KEY);
563 }
564 }, 1000);
565 }
src/backend/renderer.js
+3 -1
@@ -25,6 +25,7 @@ import {
25 getUID,
26 utfEncodeString,
27 } from 'src/utils';
28 +import { localStorageGetItem } from 'src/storage';
29 import { cleanForBridge, copyWithSet, setInObject } from './utils';
30 import {
31 __DEBUG__,
@@ -2200,7 +2201,8 @@ export function attach(
2201 }
2202
2203 // Automatically start profiling so that we don't miss timing info from initial "mount".
2203 - if (localStorage.getItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
2204 + // TODO This doens't seem right
2205 + if (localStorageGetItem(LOCAL_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
2206 startProfiling();
2207 }
2208
src/devtools/store.js
+5 -5
@@ -15,6 +15,7 @@ import {
15 separateDisplayNameAndHOCs,
16 utfDecodeString,
17 } from '../utils';
18 +import { localStorageGetItem, localStorageSetItem } from '../storage';
19 import { __DEBUG__ } from '../constants';
20 import { printStore } from 'src/__tests__/storeSerializer';
21 import ProfilerStore from './ProfilerStore';
@@ -110,7 +111,7 @@ export default class Store extends EventEmitter {
111
112 // Default this setting to true unless otherwise specified.
113 this._collapseNodesByDefault =
113 - localStorage.getItem(LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY) !==
114 + localStorageGetItem(LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY) !==
115 'false';
116
117 this._componentFilters = getSavedComponentFilters();
@@ -127,8 +128,7 @@ export default class Store extends EventEmitter {
128 if (supportsCaptureScreenshots) {
129 this._supportsCaptureScreenshots = true;
130 this._captureScreenshots =
130 - localStorage.getItem(LOCAL_STORAGE_CAPTURE_SCREENSHOTS_KEY) ===
131 - 'true';
131 + localStorageGetItem(LOCAL_STORAGE_CAPTURE_SCREENSHOTS_KEY) === 'true';
132 }
133 if (supportsProfiling) {
134 this._supportsProfiling = true;
@@ -184,7 +184,7 @@ export default class Store extends EventEmitter {
184 set captureScreenshots(value: boolean): void {
185 this._captureScreenshots = value;
186
187 - localStorage.setItem(
187 + localStorageSetItem(
188 LOCAL_STORAGE_CAPTURE_SCREENSHOTS_KEY,
189 value ? 'true' : 'false'
190 );
@@ -198,7 +198,7 @@ export default class Store extends EventEmitter {
198 set collapseNodesByDefault(value: boolean): void {
199 this._collapseNodesByDefault = value;
200
201 - localStorage.setItem(
201 + localStorageSetItem(
202 LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY,
203 value ? 'true' : 'false'
204 );
src/devtools/views/hooks.js
+3 -2
@@ -2,6 +2,7 @@
2
3 import throttle from 'lodash.throttle';
4 import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
5 +import { localStorageGetItem, localStorageSetItem } from 'src/storage';
6
7 export function useIsOverflowing(
8 containerRef: { current: HTMLDivElement | null },
@@ -42,7 +43,7 @@ export function useLocalStorage<T>(
43 ): [T, (value: T | (() => T)) => void] {
44 const getValueFromLocalStorage = useCallback(() => {
45 try {
45 - const item = window.localStorage.getItem(key);
46 + const item = localStorageGetItem(key);
47 if (item != null) {
48 return JSON.parse(item);
49 }
@@ -64,7 +65,7 @@ export function useLocalStorage<T>(
65 const valueToStore =
66 value instanceof Function ? (value: any)(storedValue) : value;
67 setStoredValue(valueToStore);
67 - window.localStorage.setItem(key, JSON.stringify(valueToStore));
68 + localStorageSetItem(key, JSON.stringify(valueToStore));
69 } catch (error) {
70 console.log(error);
71 }
src/storage.js new
+41
@@ -0,0 +1,41 @@
1 +// @flow
2 +
3 +export function localStorageGetItem(key: string): any {
4 + try {
5 + return localStorage.getItem(key);
6 + } catch (error) {
7 + return null;
8 + }
9 +}
10 +
11 +export function localStorageRemoveItem(key: string): void {
12 + try {
13 + localStorage.removeItem(key);
14 + } catch (error) {}
15 +}
16 +
17 +export function localStorageSetItem(key: string, value: any): void {
18 + try {
19 + return localStorage.setItem(key, value);
20 + } catch (error) {}
21 +}
22 +
23 +export function sessionStorageGetItem(key: string): any {
24 + try {
25 + return sessionStorage.getItem(key);
26 + } catch (error) {
27 + return null;
28 + }
29 +}
30 +
31 +export function sessionStorageRemoveItem(key: string): void {
32 + try {
33 + sessionStorage.removeItem(key);
34 + } catch (error) {}
35 +}
36 +
37 +export function sessionStorageSetItem(key: string, value: any): void {
38 + try {
39 + return sessionStorage.setItem(key, value);
40 + } catch (error) {}
41 +}
src/utils.js
+3 -2
@@ -9,6 +9,7 @@ import {
9 ElementTypeFunction,
10 ElementTypeMemo,
11 } from 'src/types';
12 +import { localStorageGetItem, localStorageSetItem } from './storage';
13
14 import type { ComponentFilter, ElementType } from './types';
15
@@ -82,7 +83,7 @@ export function getDefaultComponentFilters(): Array<ComponentFilter> {
83
84 export function getSavedComponentFilters(): Array<ComponentFilter> {
85 try {
85 - const raw = localStorage.getItem(LOCAL_STORAGE_FILTER_PREFERENCES_KEY);
86 + const raw = localStorageGetItem(LOCAL_STORAGE_FILTER_PREFERENCES_KEY);
87 if (raw != null) {
88 return JSON.parse(raw);
89 }
@@ -93,7 +94,7 @@ export function getSavedComponentFilters(): Array<ComponentFilter> {
94 export function saveComponentFilters(
95 componentFilters: Array<ComponentFilter>
96 ): void {
96 - localStorage.setItem(
97 + localStorageSetItem(
98 LOCAL_STORAGE_FILTER_PREFERENCES_KEY,
99 JSON.stringify(componentFilters)
100 );