Merge pull request #6529 from jitsi/local-storage

fix(localStorage): exception when disabled.
This commit is contained in:
Emil Ivov 2020-05-02 19:39:07 -05:00 committed by GitHub
commit 0fd0897531
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 81 additions and 165 deletions

View File

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

View File

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

10
package-lock.json generated
View File

@ -10594,8 +10594,8 @@
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
}, },
"js-utils": { "js-utils": {
"version": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1", "version": "github:jitsi/js-utils#0c0c4142bd559fffffaebaded80bdaba8414d6b8",
"from": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1", "from": "github:jitsi/js-utils#0c0c4142bd559fffffaebaded80bdaba8414d6b8",
"requires": { "requires": {
"bowser": "2.7.0", "bowser": "2.7.0",
"js-md5": "0.7.3", "js-md5": "0.7.3",
@ -10794,15 +10794,15 @@
} }
}, },
"lib-jitsi-meet": { "lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#dffe94f270f692c7421412bbbc597279e4a2d805", "version": "github:jitsi/lib-jitsi-meet#c6e8d596b0817a6584915457b2cb9ef726e94181",
"from": "github:jitsi/lib-jitsi-meet#dffe94f270f692c7421412bbbc597279e4a2d805", "from": "github:jitsi/lib-jitsi-meet#c6e8d596b0817a6584915457b2cb9ef726e94181",
"requires": { "requires": {
"@jitsi/sdp-interop": "1.0.2", "@jitsi/sdp-interop": "1.0.2",
"@jitsi/sdp-simulcast": "0.3.0", "@jitsi/sdp-simulcast": "0.3.0",
"async": "0.9.0", "async": "0.9.0",
"current-executing-script": "0.1.3", "current-executing-script": "0.1.3",
"jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#5ec92357570dc8f0b7ffc1528820721c84c6af8b", "jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#5ec92357570dc8f0b7ffc1528820721c84c6af8b",
"js-utils": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1", "js-utils": "github:jitsi/js-utils#0c0c4142bd559fffffaebaded80bdaba8414d6b8",
"lodash.isequal": "4.5.0", "lodash.isequal": "4.5.0",
"sdp-transform": "2.3.0", "sdp-transform": "2.3.0",
"strophe.js": "1.3.4", "strophe.js": "1.3.4",

View File

@ -53,10 +53,10 @@
"jquery-contextmenu": "2.4.5", "jquery-contextmenu": "2.4.5",
"jquery-i18next": "1.2.1", "jquery-i18next": "1.2.1",
"js-md5": "0.6.1", "js-md5": "0.6.1",
"js-utils": "github:jitsi/js-utils#df68966e3c65b5c57fcd2670da1326a2c77518d1", "js-utils": "github:jitsi/js-utils#0c0c4142bd559fffffaebaded80bdaba8414d6b8",
"jsrsasign": "8.0.12", "jsrsasign": "8.0.12",
"jwt-decode": "2.2.0", "jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#dffe94f270f692c7421412bbbc597279e4a2d805", "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#c6e8d596b0817a6584915457b2cb9ef726e94181",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.13", "lodash": "4.17.13",
"moment": "2.19.4", "moment": "2.19.4",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,19 +1,24 @@
// @flow // @flow
import { jitsiLocalStorage } from 'js-utils';
import React, { PureComponent } from 'react'; 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 { import {
createChromeExtensionBannerEvent, createChromeExtensionBannerEvent,
sendAnalytics sendAnalytics
} from '../../analytics'; } 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; declare var interfaceConfig: Object;
@ -182,7 +187,7 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
return true; 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 return !this.props.bannerCfg.url
|| dontShowAgain || dontShowAgain
@ -212,7 +217,7 @@ class ChromeExtensionBanner extends PureComponent<Props, State> {
render() { render() {
if (this._shouldNotRender()) { if (this._shouldNotRender()) {
if (this.state.dontShowAgainChecked) { if (this.state.dontShowAgainChecked) {
localStorage.setItem(DONT_SHOW_AGAIN_CHECKED, 'true'); jitsiLocalStorage.setItem(DONT_SHOW_AGAIN_CHECKED, 'true');
} }
return null; return null;

View File

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

View File

@ -1,4 +1,7 @@
// @flow // @flow
import { jitsiLocalStorage } from 'js-utils';
import { APP_WILL_MOUNT } from '../base/app'; import { APP_WILL_MOUNT } from '../base/app';
import { getURLWithoutParamsNormalized } from '../base/connection'; import { getURLWithoutParamsNormalized } from '../base/connection';
import { ReducerRegistry } from '../base/redux'; import { ReducerRegistry } from '../base/redux';
@ -119,16 +122,17 @@ function _appWillMount(state) {
* @returns {Array<Object>} * @returns {Array<Object>}
*/ */
function _getLegacyRecentRoomList(): Array<Object> { function _getLegacyRecentRoomList(): Array<Object> {
try { const str = jitsiLocalStorage.getItem(LEGACY_STORAGE_KEY);
const str = window.localStorage.getItem(LEGACY_STORAGE_KEY);
if (str) { if (str) {
try {
return JSON.parse(str); 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 []; return [];
} }