diff --git a/src/Lifecycle.js b/src/Lifecycle.js index f7579cf3c0e..fbb68481ad7 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -31,7 +31,8 @@ import Modal from './Modal'; import sdk from './index'; import ActiveWidgetStore from './stores/ActiveWidgetStore'; import PlatformPeg from "./PlatformPeg"; -import {sendLoginRequest} from "./Login"; +import { sendLoginRequest } from "./Login"; +import * as StorageManager from './utils/StorageManager'; /** * Called at startup, to attempt to build a logged-in Matrix session. It tries @@ -353,6 +354,8 @@ async function _doSetLoggedIn(credentials, clearStorage) { await _clearStorage(); } + await StorageManager.checkConsistency(); + Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl, credentials.identityServerUrl); if (localStorage) { diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index ad20988d9eb..f5994921de4 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -171,7 +171,7 @@ class MatrixClientPeg { return matches[1]; } - _createClient(creds: MatrixClientCreds, useIndexedDb) { + _createClient(creds: MatrixClientCreds) { const opts = { baseUrl: creds.homeserverUrl, idBaseUrl: creds.identityServerUrl, @@ -183,7 +183,7 @@ class MatrixClientPeg { verificationMethods: [verificationMethods.SAS] }; - this.matrixClient = createMatrixClient(opts, useIndexedDb); + this.matrixClient = createMatrixClient(opts); // we're going to add eventlisteners for each matrix event tile, so the // potential number of event listeners is quite high. diff --git a/src/utils/StorageManager.js b/src/utils/StorageManager.js new file mode 100644 index 00000000000..4774d3b7e1e --- /dev/null +++ b/src/utils/StorageManager.js @@ -0,0 +1,90 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import Matrix from 'matrix-js-sdk'; + +const localStorage = window.localStorage; + +// just *accessing* indexedDB throws an exception in firefox with +// indexeddb disabled. +let indexedDB; +try { + indexedDB = window.indexedDB; +} catch (e) {} + +// The JS SDK will add a prefix of "matrix-js-sdk:" to the sync store name. +const SYNC_STORE_NAME = "riot-web-sync"; +const CRYPTO_STORE_NAME = "matrix-js-sdk:crypto"; + +function log(msg) { + console.log(`StorageManager: ${msg}`); +} + +function error(msg) { + console.error(`StorageManager: ${msg}`); +} + +export async function checkConsistency() { + log("Checking storage consistency"); + log(`Local storage supported? ${!!localStorage}`); + log(`IndexedDB supported? ${!!indexedDB}`); + + let dataInLocalStorage = false; + let dataInCryptoStore = false; + let healthy = true; + + if (localStorage) { + dataInLocalStorage = localStorage.length > 0; + log(`Local storage contains data? ${dataInLocalStorage}`); + } else { + healthy = false; + error("Local storage cannot be used on this browser"); + } + + if (indexedDB && localStorage) { + const dataInSyncStore = await Matrix.IndexedDBStore.exists( + indexedDB, SYNC_STORE_NAME, + ); + log(`Sync store contains data? ${dataInSyncStore}`); + } else { + healthy = false; + error("Sync store cannot be used on this browser"); + } + + if (indexedDB) { + dataInCryptoStore = await Matrix.IndexedDBCryptoStore.exists( + indexedDB, CRYPTO_STORE_NAME, + ); + log(`Crypto store contains data? ${dataInCryptoStore}`); + } else { + healthy = false; + error("Crypto store cannot be used on this browser"); + } + + if (dataInLocalStorage && !dataInCryptoStore) { + healthy = false; + error( + "Data exists in local storage but not in crypto store. " + + "IndexedDB storage has likely been evicted by the browser!", + ); + } + + if (healthy) { + log("Storage consistency checks passed"); + } else { + error("Storage consistency checks failed"); + } +} diff --git a/src/utils/createMatrixClient.js b/src/utils/createMatrixClient.js index 040f1e96cbc..dee93244606 100644 --- a/src/utils/createMatrixClient.js +++ b/src/utils/createMatrixClient.js @@ -32,23 +32,18 @@ try { * @param {Object} opts options to pass to Matrix.createClient. This will be * extended with `sessionStore` and `store` members. * - * @param {bool} useIndexedDb True to attempt to use indexeddb, or false to force - * use of the memory store. Default: true. - * * @property {string} indexedDbWorkerScript Optional URL for a web worker script * for IndexedDB store operations. By default, indexeddb ops are done on * the main thread. * * @returns {MatrixClient} the newly-created MatrixClient */ -export default function createMatrixClient(opts, useIndexedDb) { - if (useIndexedDb === undefined) useIndexedDb = true; - +export default function createMatrixClient(opts) { const storeOpts = { useAuthorizationHeader: true, }; - if (indexedDB && localStorage && useIndexedDb) { + if (indexedDB && localStorage) { storeOpts.store = new Matrix.IndexedDBStore({ indexedDB: indexedDB, dbName: "riot-web-sync", @@ -61,7 +56,7 @@ export default function createMatrixClient(opts, useIndexedDb) { storeOpts.sessionStore = new Matrix.WebStorageSessionStore(localStorage); } - if (indexedDB && useIndexedDb) { + if (indexedDB) { storeOpts.cryptoStore = new Matrix.IndexedDBCryptoStore( indexedDB, "matrix-js-sdk:crypto", );