mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
29a7eadb85
Summary: - Handle the possibility that any access to localStorage causes error. - Move getStorage() and getSessionStorage() safe functions to a separate file. - Use these safe functions in more places. Test Plan: Added a test case, using a webdriver instance that blocks third-party cookies, to enforce third-party restrictions. Added to gristUtil a way to override the webdriver instance. Reviewers: jarek Reviewed By: jarek Differential Revision: https://phab.getgrist.com/D3719
66 lines
2.2 KiB
TypeScript
66 lines
2.2 KiB
TypeScript
/**
|
|
* Expose localStorage and sessionStorage with fallbacks for cases when they don't work (e.g.
|
|
* cross-domain embeds in Firefox and Safari).
|
|
*
|
|
* Usage:
|
|
* import {getStorage, getSessionStorage} from 'app/client/lib/storage';
|
|
* ... use getStorage() in place of localStorage...
|
|
* ... use getSessionStorage() in place of sessionStorage...
|
|
*/
|
|
|
|
/**
|
|
* Returns localStorage if functional, or sessionStorage, or an in-memory storage. The fallbacks
|
|
* help with tests, and when Grist is embedded.
|
|
*/
|
|
export function getStorage(): Storage {
|
|
_storage ??= testStorage('localStorage') || getSessionStorage();
|
|
return _storage;
|
|
}
|
|
|
|
/**
|
|
* Return window.sessionStorage, or when not available, an in-memory storage.
|
|
*/
|
|
export function getSessionStorage(): Storage {
|
|
// If can't use sessionStorage, fall back to a Map-based non-persistent implementation.
|
|
_sessionStorage ??= testStorage('sessionStorage') || createInMemoryStorage();
|
|
return _sessionStorage;
|
|
}
|
|
|
|
|
|
let _storage: Storage|undefined;
|
|
let _sessionStorage: Storage|undefined;
|
|
|
|
/**
|
|
* Returns the result of window[storageName] if storage is functional, or null otherwise. In some
|
|
* cases (e.g. when embedded), using localStorage may throw errors, in which case we return null.
|
|
* This is similar to the approach taken by store.js.
|
|
*/
|
|
function testStorage(storageName: 'localStorage'|'sessionStorage'): Storage|null {
|
|
try {
|
|
const testStr = '__localStorage_test';
|
|
const storage = window[storageName];
|
|
storage.setItem(testStr, testStr);
|
|
const ok = (storage.getItem(testStr) === testStr);
|
|
storage.removeItem(testStr);
|
|
if (ok) {
|
|
return storage;
|
|
}
|
|
} catch (e) {
|
|
// Fall through
|
|
}
|
|
console.warn(`${storageName} is not available; will use fallback`);
|
|
return null;
|
|
}
|
|
|
|
function createInMemoryStorage(): Storage {
|
|
const values = new Map<string, string>();
|
|
return {
|
|
setItem(key: string, val: string) { values.set(key, val); },
|
|
getItem(key: string) { return values.get(key) ?? null; },
|
|
removeItem(key: string) { values.delete(key); },
|
|
clear() { values.clear(); },
|
|
get length() { return values.size; },
|
|
key(index: number): string|null { throw new Error('Not implemented'); },
|
|
};
|
|
}
|