quodolores
Version:
Monorepo for the Firebase JavaScript SDK
177 lines (161 loc) • 5.55 kB
JavaScript
/**
* @license
* Copyright 2018 Google Inc.
*
* 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.
*/
goog.provide('fireauth.storage.HybridIndexedDB');
goog.require('fireauth.storage.IndexedDB');
goog.require('fireauth.storage.Storage');
goog.require('fireauth.util');
goog.require('goog.Promise');
goog.require('goog.array');
/**
* HybridStorage provides an interface to indexedDB, the persistent Web
* Storage API for browsers that support it. This will fallback to the provided
* fallback storage when indexedDB is not supported which is determined
* asynchronously.
* @param {!fireauth.storage.Storage} fallbackStorage The storage to fallback to
* when indexedDB is not available.
* @constructor
* @implements {fireauth.storage.Storage}
*/
fireauth.storage.HybridIndexedDB = function(fallbackStorage) {
var self = this;
var storage = null;
/**
* @const @private {!Array<function((!goog.events.BrowserEvent|
* !Array<string>))>} The storage listeners.
*/
this.storageListeners_ = [];
// This type may change if the fallback is used.
/** @public {string} The storage type identifier. */
this.type = fireauth.storage.Storage.Type.INDEXEDDB;
/**
* @const @private {!fireauth.storage.Storage} The fallback storage when
* indexedDB is unavailable.
*/
this.fallbackStorage_ = fallbackStorage;
/**
* @const @private {!goog.Promise<!fireauth.storage.Storage>} A promise that
* resolves with the underlying indexedDB storage or a fallback when not
* supported.
*/
this.underlyingStoragePromise_ = goog.Promise.resolve().then(function() {
// Initial check shows indexedDB is available. This is not enough.
// Try to write/read from indexedDB. If it fails, switch to fallback.
if (fireauth.storage.IndexedDB.isAvailable()) {
// Test write/read using a random key. This is important for the following
// reasons:
// 1. Double inclusion of the firebase-auth.js library.
// 2. Multiple windows opened at the same time.
// The above may cause collision if multiple instances try to
// write/read/delete from the same entry.
var randomId = fireauth.util.generateEventId();
var randomKey = fireauth.storage.HybridIndexedDB.KEY_ + randomId;
storage = fireauth.storage.IndexedDB.getFireauthManager();
return storage.set(randomKey, randomId)
.then(function() {
return storage.get(randomKey);
})
.then(function(value) {
if (value !== randomId) {
throw new Error('indexedDB not supported!');
}
return storage.remove(randomKey);
})
.then(function() {
return storage;
})
.thenCatch(function(error) {
return self.fallbackStorage_;
});
} else {
// indexedDB not available, use fallback.
return self.fallbackStorage_;
}
}).then(function(storage) {
// Update type.
self.type = storage.type;
// Listen to all storage changes.
storage.addStorageListener(function(key) {
// Trigger all attached storage listeners.
goog.array.forEach(self.storageListeners_, function(listener) {
listener(key);
});
});
return storage;
});
};
/**
* The key used to check if the storage instance is available.
* @private {string}
* @const
*/
fireauth.storage.HybridIndexedDB.KEY_ = '__sak';
/**
* Retrieves the value stored at the key.
* @param {string} key
* @return {!goog.Promise<*>}
* @override
*/
fireauth.storage.HybridIndexedDB.prototype.get = function(key) {
return this.underlyingStoragePromise_.then(function(storage) {
return storage.get(key);
});
};
/**
* Stores the value at the specified key.
* @param {string} key
* @param {*} value
* @return {!goog.Promise<void>}
* @override
*/
fireauth.storage.HybridIndexedDB.prototype.set = function(key, value) {
return this.underlyingStoragePromise_.then(function(storage) {
return storage.set(key, value);
});
};
/**
* Removes the value at the specified key.
* @param {string} key
* @return {!goog.Promise<void>}
* @override
*/
fireauth.storage.HybridIndexedDB.prototype.remove = function(key) {
return this.underlyingStoragePromise_.then(function(storage) {
return storage.remove(key);
});
};
/**
* Adds a listener to storage event change.
* @param {function((!goog.events.BrowserEvent|!Array<string>))} listener The
* storage event listener.
* @override
*/
fireauth.storage.HybridIndexedDB.prototype.addStorageListener =
function(listener) {
this.storageListeners_.push(listener);
};
/**
* Removes a listener to storage event change.
* @param {function(!goog.events.BrowserEvent)} listener The storage event
* listener.
* @override
*/
fireauth.storage.HybridIndexedDB.prototype.removeStorageListener =
function(listener) {
goog.array.removeAllIf(this.storageListeners_, function(ele) {
return ele == listener;
});
};