fix(localStorage): exception when disabled.

This commit is contained in:
Hristo Terezov 2020-05-01 14:48:08 -05:00
parent 77d38731e9
commit 1ffd75c0a6
14 changed files with 93 additions and 161 deletions

View File

@ -1,7 +1,8 @@
/* global APP, JitsiMeetJS, config */
import { jitsiLocalStorage } from 'js-utils';
import AuthHandler from './modules/UI/authentication/AuthHandler';
import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
import {
connectionEstablished,

View File

@ -1,7 +1,7 @@
/* global $, APP */
const logger = require('jitsi-meet-logger').getLogger(__filename);
import jitsiLocalStorage from '../../util/JitsiLocalStorage';
import { jitsiLocalStorage } from 'js-utils';
import {
NOTIFICATION_TIMEOUT,

View File

@ -1,77 +0,0 @@
import Logger from 'jitsi-meet-logger';
const logger = Logger.getLogger(__filename);
/**
* Dummy implementation of Storage interface with empty methods.
*/
class DummyLocalStorage {
/* eslint-disable no-empty-function */
/**
* Empty function
*/
getItem() { }
/**
* Empty function
*/
setItem() { }
/**
* Empty function
*/
removeItem() { }
/* eslint-enable no-empty-function */
}
/**
* Wrapper class for browser's local storage object.
*/
class JitsiLocalStorage extends DummyLocalStorage {
/**
* @constructor
* @param {Storage} storage browser's local storage object.
*/
constructor() {
super();
let storage;
try {
storage = window.localStorage;
} catch (error) {
logger.error(error);
}
this.storage = storage || new DummyLocalStorage();
}
/**
* Returns that passed key's value.
* @param {string} keyName the name of the key you want to retrieve
* the value of.
* @returns {String|null} the value of the key. If the key does not exist,
* null is returned.
*/
getItem(keyName) {
return this.storage.getItem(keyName);
}
/**
* Adds a key to the storage, or update key's value if it already exists.
* @param {string} keyName the name of the key you want to create/update.
* @param {string} keyValue the value you want to give the key you are
* creating/updating.
*/
setItem(keyName, keyValue) {
return this.storage.setItem(keyName, keyValue);
}
/**
* Remove a key from the storage.
* @param {string} keyName the name of the key you want to remove.
*/
removeItem(keyName) {
return this.storage.removeItem(keyName);
}
}
export default new JitsiLocalStorage();

20
package-lock.json generated
View File

@ -10594,8 +10594,8 @@
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
},
"js-utils": {
"version": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1",
"from": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1",
"version": "github:jitsi/js-utils#efb02828785b2b532097d96f4b32a101847ba58b",
"from": "github:jitsi/js-utils#efb02828785b2b532097d96f4b32a101847ba58b",
"requires": {
"bowser": "2.7.0",
"js-md5": "0.7.3",
@ -10809,6 +10809,22 @@
"strophejs-plugin-disco": "0.0.2",
"strophejs-plugin-stream-management": "github:jitsi/strophejs-plugin-stream-management#e719a02b4f83856c1530882084a4b048ee622d45",
"webrtc-adapter": "7.5.0"
},
"dependencies": {
"js-md5": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz",
"integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ=="
},
"js-utils": {
"version": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1",
"from": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1",
"requires": {
"bowser": "2.7.0",
"js-md5": "0.7.3",
"postis": "2.2.0"
}
}
}
},
"libflacjs": {

View File

@ -53,7 +53,7 @@
"jquery-contextmenu": "2.4.5",
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"js-utils": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1",
"js-utils": "github:jitsi/js-utils#efb02828785b2b532097d96f4b32a101847ba58b",
"jsrsasign": "8.0.12",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#dffe94f270f692c7421412bbbc597279e4a2d805",

View File

@ -1,5 +1,6 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import _ from 'lodash';
import React, { Component, Fragment } from 'react';
import { I18nextProvider } from 'react-i18next';
@ -15,7 +16,6 @@ import {
} from '../../redux';
import { SoundCollection } from '../../sounds';
import { PersistenceRegistry } from '../../storage';
import { appWillMount, appWillUnmount } from '../actions';
import logger from '../logger';
@ -110,7 +110,7 @@ export default class BaseApp extends Component<*, State> {
* @returns {Promise}
*/
_initStorage(): Promise<*> {
const { _initializing } = window.localStorage;
const _initializing = jitsiLocalStorage.getItem('_initializing');
return _initializing || Promise.resolve();
}

View File

@ -1,5 +1,6 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import type { Dispatch } from 'redux';
import { addKnownDomains } from '../known-domains';
@ -109,11 +110,8 @@ export function storeConfig(baseURL: string, config: Object) {
let b = false;
try {
if (typeof window.config === 'undefined'
|| window.config !== config) {
window.localStorage.setItem(
`${_CONFIG_STORE_PREFIX}/${baseURL}`,
JSON.stringify(config));
if (typeof window.config === 'undefined' || window.config !== config) {
jitsiLocalStorage.setItem(`${_CONFIG_STORE_PREFIX}/${baseURL}`, JSON.stringify(config));
b = true;
}
} catch (e) {

View File

@ -1,5 +1,6 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import _ from 'lodash';
import CONFIG_WHITELIST from './configWhitelist';
@ -136,22 +137,16 @@ function _getWhitelistedJSON(configName, configJSON) {
* otherwise, {@code undefined}.
*/
export function restoreConfig(baseURL: string): ?Object {
let storage;
const key = `${_CONFIG_STORE_PREFIX}/${baseURL}`;
const config = jitsiLocalStorage.getItem(key);
try {
// XXX Even reading the property localStorage of window may throw an
// error (which is user agent-specific behavior).
storage = window.localStorage;
const config = storage.getItem(key);
if (config) {
if (config) {
try {
return JSON.parse(config) || undefined;
} catch (e) {
// Somehow incorrect data ended up in the storage. Clean it up.
jitsiLocalStorage.removeItem(key);
}
} catch (e) {
// Somehow incorrect data ended up in the storage. Clean it up.
storage && storage.removeItem(key);
}
return undefined;

View File

@ -1,5 +1,7 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { APP_WILL_MOUNT } from '../app';
import { addKnownDomains } from '../known-domains';
import { MiddlewareRegistry } from '../redux';
@ -51,31 +53,28 @@ function _appWillMount(store, next, action) {
// consequently, the feature known-domains, it's possible for the feature
// base/config to know of domains which the feature known-domains is yet to
// discover.
const { localStorage } = window;
if (localStorage) {
const prefix = `${_CONFIG_STORE_PREFIX}/`;
const knownDomains = [];
const prefix = `${_CONFIG_STORE_PREFIX}/`;
const knownDomains = [];
for (let i = 0; /* localStorage.key(i) */; ++i) {
const key = localStorage.key(i);
for (let i = 0; /* localStorage.key(i) */; ++i) {
const key = jitsiLocalStorage.key(i);
if (key) {
let baseURL;
if (key) {
let baseURL;
if (key.startsWith(prefix)
&& (baseURL = key.substring(prefix.length))) {
const uri = parseURIString(baseURL);
let host;
if (key.startsWith(prefix)
&& (baseURL = key.substring(prefix.length))) {
const uri = parseURIString(baseURL);
let host;
uri && (host = uri.host) && knownDomains.push(host);
}
} else {
break;
uri && (host = uri.host) && knownDomains.push(host);
}
} else {
break;
}
knownDomains.length && store.dispatch(addKnownDomains(knownDomains));
}
knownDomains.length && store.dispatch(addKnownDomains(knownDomains));
return result;
}

View File

@ -1,5 +1,6 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { randomHexString } from 'js-utils/random';
import _ from 'lodash';
@ -86,8 +87,7 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
* @returns {Object}
*/
function _getLegacyProfile() {
let persistedProfile
= window.localStorage.getItem('features/base/profile');
let persistedProfile = jitsiLocalStorage.getItem('features/base/profile');
if (persistedProfile) {
try {
@ -123,9 +123,9 @@ function _initSettings(featureState) {
// FIXME: jibri uses old settings.js local storage values to set its display
// name and email. Provide another way for jibri to set these values, update
// jibri, and remove the old settings.js values.
const savedDisplayName = window.localStorage.getItem('displayname');
const savedEmail = window.localStorage.getItem('email');
let avatarID = _.escape(window.localStorage.getItem('avatarId'));
const savedDisplayName = jitsiLocalStorage.getItem('displayname');
const savedEmail = jitsiLocalStorage.getItem('email');
let avatarID = _.escape(jitsiLocalStorage.getItem('avatarId'));
// The helper _.escape will convert null to an empty strings. The empty
// string will be saved in settings. On app re-load, because an empty string
@ -149,16 +149,13 @@ function _initSettings(featureState) {
if (!browser.isReactNative()) {
// Browser only
const localFlipX
= JSON.parse(window.localStorage.getItem('localFlipX') || 'true');
const cameraDeviceId
= window.localStorage.getItem('cameraDeviceId') || '';
const micDeviceId = window.localStorage.getItem('micDeviceId') || '';
const localFlipX = JSON.parse(jitsiLocalStorage.getItem('localFlipX') || 'true');
const cameraDeviceId = jitsiLocalStorage.getItem('cameraDeviceId') || '';
const micDeviceId = jitsiLocalStorage.getItem('micDeviceId') || '';
// Currently audio output device change is supported only in Chrome and
// default output always has 'default' device ID
const audioOutputDeviceId
= window.localStorage.getItem('audioOutputDeviceId') || 'default';
const audioOutputDeviceId = jitsiLocalStorage.getItem('audioOutputDeviceId') || 'default';
settings = assignIfDefined({
audioOutputDeviceId,

View File

@ -1,6 +1,7 @@
// @flow
import md5 from 'js-md5';
import { jitsiLocalStorage } from 'js-utils';
import logger from './logger';
@ -63,8 +64,7 @@ class PersistenceRegistry {
// legacy
if (Object.keys(filteredPersistedState).length === 0) {
const { localStorage } = window;
let persistedState = localStorage.getItem(PERSISTED_STATE_NAME);
let persistedState = jitsiLocalStorage.getItem(PERSISTED_STATE_NAME);
if (persistedState) {
try {
@ -82,7 +82,7 @@ class PersistenceRegistry {
// Store into the new format and delete the old format so that
// it's not used again.
this.persistState(filteredPersistedState);
localStorage.removeItem(PERSISTED_STATE_NAME);
jitsiLocalStorage.removeItem(PERSISTED_STATE_NAME);
}
}
@ -110,18 +110,12 @@ class PersistenceRegistry {
if (checksum !== this._checksum) {
for (const subtreeName of Object.keys(filteredState)) {
try {
window.localStorage.setItem(
subtreeName,
JSON.stringify(filteredState[subtreeName]));
jitsiLocalStorage.setItem(subtreeName, JSON.stringify(filteredState[subtreeName]));
} catch (error) {
logger.error(
'Error persisting redux subtree',
subtreeName,
error);
logger.error('Error persisting redux subtree', subtreeName, error);
}
}
logger.info(
`redux state persisted. ${this._checksum} -> ${checksum}`);
logger.info(`redux state persisted. ${this._checksum} -> ${checksum}`);
this._checksum = checksum;
}
}
@ -225,7 +219,7 @@ class PersistenceRegistry {
* @returns {Object}
*/
_getPersistedSubtree(subtreeName, subtreeConfig, subtreeDefaults) {
let persistedSubtree = window.localStorage.getItem(subtreeName);
let persistedSubtree = jitsiLocalStorage.getItem(subtreeName);
if (persistedSubtree) {
try {

View File

@ -1,19 +1,24 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import React, { PureComponent } from 'react';
import { connect } from '../../base/redux';
import { Icon, IconClose } from '../../base/icons';
import { translate } from '../../base/i18n';
import { getCurrentConference } from '../../base/conference/functions';
import { browser } from '../../base/lib-jitsi-meet';
import {
checkChromeExtensionsInstalled,
isMobileBrowser
} from '../../base/environment/utils';
import logger from '../logger';
import {
createChromeExtensionBannerEvent,
sendAnalytics
} from '../../analytics';
import { getCurrentConference } from '../../base/conference/functions';
import { Icon, IconClose } from '../../base/icons';
import { translate } from '../../base/i18n';
import { browser } from '../../base/lib-jitsi-meet';
import { connect } from '../../base/redux';
import {
checkChromeExtensionsInstalled,
isMobileBrowser
} from '../../base/environment/utils';
import logger from '../logger';
declare var interfaceConfig: Object;
@ -182,7 +187,7 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
return true;
}
const dontShowAgain = localStorage.getItem(DONT_SHOW_AGAIN_CHECKED) === 'true';
const dontShowAgain = jitsiLocalStorage.getItem(DONT_SHOW_AGAIN_CHECKED) === 'true';
return !this.props.bannerCfg.url
|| dontShowAgain
@ -212,7 +217,7 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
render() {
if (this._shouldNotRender()) {
if (this.state.dontShowAgainChecked) {
localStorage.setItem(DONT_SHOW_AGAIN_CHECKED, 'true');
jitsiLocalStorage.setItem(DONT_SHOW_AGAIN_CHECKED, 'true');
}
return null;

View File

@ -1,6 +1,6 @@
/* @flow */
import jitsiLocalStorage from '../../../../modules/util/JitsiLocalStorage';
import { jitsiLocalStorage } from 'js-utils';
import logger from '../logger';

View File

@ -1,4 +1,7 @@
// @flow
import { jitsiLocalStorage } from 'js-utils';
import { APP_WILL_MOUNT } from '../base/app';
import { getURLWithoutParamsNormalized } from '../base/connection';
import { ReducerRegistry } from '../base/redux';
@ -119,16 +122,17 @@ function _appWillMount(state) {
* @returns {Array<Object>}
*/
function _getLegacyRecentRoomList(): Array<Object> {
try {
const str = window.localStorage.getItem(LEGACY_STORAGE_KEY);
const str = jitsiLocalStorage.getItem(LEGACY_STORAGE_KEY);
if (str) {
if (str) {
try {
return JSON.parse(str);
} catch (error) {
logger.warn('Failed to parse legacy recent-room list!');
}
} catch (error) {
logger.warn('Failed to parse legacy recent-room list!');
}
return [];
}