ditch localStorage, use on-demand SessionStore proxy
This commit is contained in:
parent
0b3e027bfd
commit
e5ad9fce46
|
@ -135,24 +135,13 @@ if (chrome.commands) {
|
|||
|
||||
// *************************************************************************
|
||||
chrome.runtime.onInstalled.addListener(({reason, previousVersion}) => {
|
||||
// save install type: "admin", "development", "normal", "sideload" or "other"
|
||||
// "normal" = addon installed from webstore
|
||||
chrome.management.getSelf(info => {
|
||||
localStorage.installType = info.installType;
|
||||
if (reason === 'install' && info.installType === 'development' && chrome.contextMenus) {
|
||||
createContextMenus(['reload']);
|
||||
}
|
||||
});
|
||||
|
||||
if (reason !== 'update') return;
|
||||
// translations may change
|
||||
localStorage.L10N = JSON.stringify({
|
||||
browserUIlanguage: chrome.i18n.getUILanguage(),
|
||||
});
|
||||
// themes may change
|
||||
delete localStorage.codeMirrorThemes;
|
||||
// inline search cache for USO is not needed anymore, TODO: remove this by the middle of 2021
|
||||
if (semverCompare(previousVersion, '1.5.13') <= 0) {
|
||||
// Removing unused stuff
|
||||
// TODO: delete this entire block by the middle of 2021
|
||||
try {
|
||||
localStorage.clear();
|
||||
} catch (e) {}
|
||||
setTimeout(async () => {
|
||||
const del = Object.keys(await chromeLocal.get())
|
||||
.filter(key => key.startsWith('usoSearchCache'));
|
||||
|
@ -181,7 +170,7 @@ contextMenus = {
|
|||
click: browserCommands.openOptions,
|
||||
},
|
||||
'reload': {
|
||||
presentIf: () => localStorage.installType === 'development',
|
||||
presentIf: async () => (await browser.management.getSelf()).installType === 'development',
|
||||
title: 'reload',
|
||||
click: browserCommands.reload,
|
||||
},
|
||||
|
@ -198,10 +187,10 @@ contextMenus = {
|
|||
}
|
||||
};
|
||||
|
||||
function createContextMenus(ids) {
|
||||
async function createContextMenus(ids) {
|
||||
for (const id of ids) {
|
||||
let item = contextMenus[id];
|
||||
if (item.presentIf && !item.presentIf()) {
|
||||
if (item.presentIf && !await item.presentIf()) {
|
||||
continue;
|
||||
}
|
||||
item = Object.assign({id}, item);
|
||||
|
|
|
@ -24,14 +24,11 @@ const db = (() => {
|
|||
async function tryUsingIndexedDB() {
|
||||
// we use chrome.storage.local fallback if IndexedDB doesn't save data,
|
||||
// which, once detected on the first run, is remembered in chrome.storage.local
|
||||
// for reliablility and in localStorage for fast synchronous access
|
||||
// (FF may block localStorage depending on its privacy options)
|
||||
// note that it may throw when accessing the variable
|
||||
// https://github.com/openstyles/stylus/issues/615
|
||||
// note that accessing indexedDB may throw, https://github.com/openstyles/stylus/issues/615
|
||||
if (typeof indexedDB === 'undefined') {
|
||||
throw new Error('indexedDB is undefined');
|
||||
}
|
||||
switch (await getFallback()) {
|
||||
switch (await chromeLocal.getValue(FALLBACK)) {
|
||||
case true: throw null;
|
||||
case false: break;
|
||||
default: await testDB();
|
||||
|
@ -39,12 +36,6 @@ const db = (() => {
|
|||
return useIndexedDB();
|
||||
}
|
||||
|
||||
async function getFallback() {
|
||||
return localStorage[FALLBACK] === 'true' ? true :
|
||||
localStorage[FALLBACK] === 'false' ? false :
|
||||
chromeLocal.getValue(FALLBACK);
|
||||
}
|
||||
|
||||
async function testDB() {
|
||||
let e = await dbExecIndexedDB('getAllKeys', IDBKeyRange.lowerBound(1), 1);
|
||||
// throws if result is null
|
||||
|
@ -62,13 +53,11 @@ const db = (() => {
|
|||
chromeLocal.setValue(FALLBACK + 'Reason', workerUtil.cloneError(err));
|
||||
console.warn('Failed to access indexedDB. Switched to storage API.', err);
|
||||
}
|
||||
localStorage[FALLBACK] = 'true';
|
||||
return createChromeStorageDB().exec;
|
||||
}
|
||||
|
||||
function useIndexedDB() {
|
||||
chromeLocal.setValue(FALLBACK, false);
|
||||
localStorage[FALLBACK] = 'false';
|
||||
return dbExecIndexedDB;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
const ALARM_NAME = 'scheduledUpdate';
|
||||
const MIN_INTERVAL_MS = 60e3;
|
||||
|
||||
let lastUpdateTime = parseInt(localStorage.lastUpdateTime) || Date.now();
|
||||
let lastUpdateTime;
|
||||
let checkingAll = false;
|
||||
let logQueue = [];
|
||||
let logLastWriteTime = 0;
|
||||
|
@ -46,9 +46,11 @@
|
|||
API_METHODS.updateCheck = checkStyle;
|
||||
API_METHODS.getUpdaterStates = () => STATES;
|
||||
|
||||
prefs.subscribe(['updateInterval'], schedule);
|
||||
schedule();
|
||||
chrome.alarms.onAlarm.addListener(onAlarm);
|
||||
chromeLocal.getValue('lastUpdateTime').then(val => {
|
||||
lastUpdateTime = val || Date.now();
|
||||
prefs.subscribe('updateInterval', schedule, {now: true});
|
||||
chrome.alarms.onAlarm.addListener(onAlarm);
|
||||
});
|
||||
|
||||
return {checkAllStyles, checkStyle, STATES};
|
||||
|
||||
|
@ -255,7 +257,7 @@
|
|||
}
|
||||
|
||||
function resetInterval() {
|
||||
localStorage.lastUpdateTime = lastUpdateTime = Date.now();
|
||||
chromeLocal.setValue('lastUpdateTime', lastUpdateTime = Date.now());
|
||||
schedule();
|
||||
}
|
||||
|
||||
|
|
12
edit/edit.js
12
edit/edit.js
|
@ -22,7 +22,7 @@
|
|||
prefs
|
||||
rerouteHotkeys
|
||||
SectionsEditor
|
||||
sessionStorageHash
|
||||
sessionStore
|
||||
setupLivePrefs
|
||||
SourceEditor
|
||||
t
|
||||
|
@ -100,7 +100,7 @@ lazyInit();
|
|||
// switching the mode here to show the correct page ASAP, usually before DOMContentLoaded
|
||||
editor.isUsercss = Boolean(style.usercssData || !style.id && prefs.get('newStyleAsUsercss'));
|
||||
document.documentElement.classList.toggle('usercss', editor.isUsercss);
|
||||
sessionStorage.justEditedStyleId = style.id || '';
|
||||
sessionStore.justEditedStyleId = style.id || '';
|
||||
// no such style so let's clear the invalid URL parameters
|
||||
if (!style.id) history.replaceState({}, '', location.pathname);
|
||||
updateTitle(false);
|
||||
|
@ -335,7 +335,7 @@ function lazyInit() {
|
|||
async function patchHistoryBack(tab) {
|
||||
ownTabId = tab.id;
|
||||
// use browser history back when 'back to manage' is clicked
|
||||
if (sessionStorageHash('manageStylesHistory').value[ownTabId] === location.href) {
|
||||
if (sessionStore['manageStylesHistory' + ownTabId] === location.href) {
|
||||
await onDOMready();
|
||||
$('#cancel-button').onclick = event => {
|
||||
event.stopPropagation();
|
||||
|
@ -346,8 +346,8 @@ function lazyInit() {
|
|||
}
|
||||
/** resize on 'undo close' */
|
||||
function restoreWindowSize() {
|
||||
const pos = tryJSONparse(sessionStorage.windowPos);
|
||||
delete sessionStorage.windowPos;
|
||||
const pos = tryJSONparse(sessionStore.windowPos);
|
||||
delete sessionStore.windowPos;
|
||||
if (pos && pos.left != null && chrome.windows) {
|
||||
chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, pos);
|
||||
}
|
||||
|
@ -408,7 +408,7 @@ function onRuntimeMessage(request) {
|
|||
}
|
||||
|
||||
function beforeUnload(e) {
|
||||
sessionStorage.windowPos = JSON.stringify(canSaveWindowPos() && prefs.get('windowPosition'));
|
||||
sessionStore.windowPos = JSON.stringify(canSaveWindowPos() && prefs.get('windowPosition'));
|
||||
const activeElement = document.activeElement;
|
||||
if (activeElement) {
|
||||
// blurring triggers 'change' or 'input' event if needed
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
messageBox
|
||||
prefs
|
||||
sectionsToMozFormat
|
||||
sessionStore
|
||||
showCodeMirrorPopup
|
||||
showHelp
|
||||
t
|
||||
|
@ -117,7 +118,7 @@ function SectionsEditor() {
|
|||
}
|
||||
newStyle = await API.editSave(newStyle);
|
||||
destroyRemovedSections();
|
||||
sessionStorage.justEditedStyleId = newStyle.id;
|
||||
sessionStore.justEditedStyleId = newStyle.id;
|
||||
editor.replaceStyle(newStyle, false);
|
||||
},
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
MozSectionWidget
|
||||
prefs
|
||||
sectionsToMozFormat
|
||||
sessionStore
|
||||
t
|
||||
*/
|
||||
|
||||
|
@ -217,7 +218,7 @@ function SourceEditor() {
|
|||
if (style.id !== newStyle.id) {
|
||||
history.replaceState({}, '', `?id=${newStyle.id}`);
|
||||
}
|
||||
sessionStorage.justEditedStyleId = newStyle.id;
|
||||
sessionStore.justEditedStyleId = newStyle.id;
|
||||
Object.assign(style, newStyle);
|
||||
$('#preview-label').classList.remove('hidden');
|
||||
updateMeta();
|
||||
|
|
|
@ -7,14 +7,8 @@ tDocLoader();
|
|||
|
||||
|
||||
function t(key, params) {
|
||||
const cache = !params && t.cache[key];
|
||||
const s = cache || chrome.i18n.getMessage(key, params);
|
||||
if (s === '') {
|
||||
throw `Missing string "${key}"`;
|
||||
}
|
||||
if (!params && !cache) {
|
||||
t.cache[key] = s;
|
||||
}
|
||||
const s = chrome.i18n.getMessage(key, params);
|
||||
if (!s) throw `Missing string "${key}"`;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -138,11 +132,6 @@ function tNodeList(nodes) {
|
|||
|
||||
function tDocLoader() {
|
||||
t.DOMParser = new DOMParser();
|
||||
t.cache = (() => {
|
||||
try {
|
||||
return JSON.parse(localStorage.L10N);
|
||||
} catch (e) {}
|
||||
})() || {};
|
||||
t.RX_WORD_BREAK = new RegExp([
|
||||
'(',
|
||||
/[\d\w\u007B-\uFFFF]{10}/,
|
||||
|
@ -154,14 +143,6 @@ function tDocLoader() {
|
|||
/(?!\b|\s|$)/,
|
||||
].map(rx => rx.source || rx).join(''), 'g');
|
||||
|
||||
// reset L10N cache on UI language change
|
||||
const UIlang = chrome.i18n.getUILanguage();
|
||||
if (t.cache.browserUIlanguage !== UIlang) {
|
||||
t.cache = {browserUIlanguage: UIlang};
|
||||
localStorage.L10N = JSON.stringify(t.cache);
|
||||
}
|
||||
const cacheLength = Object.keys(t.cache).length;
|
||||
|
||||
Object.assign(tDocLoader, {
|
||||
observer: new MutationObserver(process),
|
||||
start() {
|
||||
|
@ -197,9 +178,6 @@ function tDocLoader() {
|
|||
document.removeEventListener('DOMContentLoaded', onLoad);
|
||||
process(tDocLoader.observer.takeRecords());
|
||||
tDocLoader.stop();
|
||||
if (cacheLength !== Object.keys(t.cache).length) {
|
||||
localStorage.L10N = JSON.stringify(t.cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,20 @@
|
|||
/* exported getTab getActiveTab onTabReady stringAsRegExp openURL ignoreChromeError
|
||||
getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual
|
||||
closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */
|
||||
/* exported
|
||||
capitalize
|
||||
CHROME_HAS_BORDER_BUG
|
||||
closeCurrentTab
|
||||
deepEqual
|
||||
download
|
||||
getActiveTab
|
||||
getStyleWithNoCode
|
||||
getTab
|
||||
ignoreChromeError
|
||||
onTabReady
|
||||
openURL
|
||||
sessionStore
|
||||
stringAsRegExp
|
||||
tryCatch
|
||||
tryRegExp
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(\d+)|$/)[1]);
|
||||
|
@ -316,24 +330,28 @@ function deepEqual(a, b, ignoredKeys) {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
function sessionStorageHash(name) {
|
||||
return {
|
||||
name,
|
||||
value: tryCatch(JSON.parse, sessionStorage[name]) || {},
|
||||
set(k, v) {
|
||||
this.value[k] = v;
|
||||
this.updateStorage();
|
||||
},
|
||||
unset(k) {
|
||||
delete this.value[k];
|
||||
this.updateStorage();
|
||||
},
|
||||
updateStorage() {
|
||||
sessionStorage[this.name] = JSON.stringify(this.value);
|
||||
/* A simple polyfill in case DOM storage is disabled in the browser */
|
||||
const sessionStore = new Proxy({}, {
|
||||
get(target, name) {
|
||||
try {
|
||||
return sessionStorage[name];
|
||||
} catch (e) {
|
||||
Object.defineProperty(window, 'sessionStorage', {value: target});
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
set(target, name, value, proxy) {
|
||||
try {
|
||||
sessionStorage[name] = `${value}`;
|
||||
} catch (e) {
|
||||
proxy[name]; // eslint-disable-line no-unused-expressions
|
||||
target[name] = `${value}`;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
deleteProperty(target, name) {
|
||||
return delete target[name];
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {String} url
|
||||
|
|
|
@ -66,15 +66,6 @@ self.INJECTED !== 1 && (() => {
|
|||
|
||||
//#region for our extension pages
|
||||
|
||||
for (const storage of ['localStorage', 'sessionStorage']) {
|
||||
try {
|
||||
window[storage]._access_check = 1;
|
||||
delete window[storage]._access_check;
|
||||
} catch (err) {
|
||||
Object.defineProperty(window, storage, {value: {}});
|
||||
}
|
||||
}
|
||||
|
||||
if (!(new URLSearchParams({foo: 1})).get('foo')) {
|
||||
// TODO: remove when minimum_chrome_version >= 61
|
||||
window.URLSearchParams = class extends URLSearchParams {
|
||||
|
|
|
@ -6,7 +6,7 @@ global messageBox getStyleWithNoCode
|
|||
configDialog
|
||||
sorter msg prefs API $ $$ $create template setupLivePrefs
|
||||
t tWordBreak formatDate
|
||||
getOwnTab getActiveTab openURL animateElement sessionStorageHash debounce
|
||||
getOwnTab getActiveTab openURL animateElement sessionStore debounce
|
||||
scrollElementIntoView CHROME VIVALDI router
|
||||
*/
|
||||
'use strict';
|
||||
|
@ -129,7 +129,7 @@ function showStyles(styles = [], matchUrlIds) {
|
|||
let firstRun = true;
|
||||
installed.dataset.total = styles.length;
|
||||
const scrollY = (history.state || {}).scrollY;
|
||||
const shouldRenderAll = scrollY > window.innerHeight || sessionStorage.justEditedStyleId;
|
||||
const shouldRenderAll = scrollY > window.innerHeight || sessionStore.justEditedStyleId;
|
||||
const renderBin = document.createDocumentFragment();
|
||||
if (scrollY) {
|
||||
renderStyles();
|
||||
|
@ -155,7 +155,7 @@ function showStyles(styles = [], matchUrlIds) {
|
|||
return;
|
||||
}
|
||||
setTimeout(getFaviconImgSrc);
|
||||
if (sessionStorage.justEditedStyleId) {
|
||||
if (sessionStore.justEditedStyleId) {
|
||||
highlightEditedStyle();
|
||||
} else if ('scrollY' in (history.state || {})) {
|
||||
setTimeout(window.scrollTo, 0, 0, history.state.scrollY);
|
||||
|
@ -400,7 +400,7 @@ Object.assign(handleEvent, {
|
|||
} else {
|
||||
onVisibilityChange();
|
||||
getActiveTab().then(tab => {
|
||||
sessionStorageHash('manageStylesHistory').set(tab.id, url);
|
||||
sessionStore['manageStylesHistory' + tab.id] = url;
|
||||
location.href = url;
|
||||
});
|
||||
}
|
||||
|
@ -691,10 +691,10 @@ function onVisibilityChange() {
|
|||
// the catch here is that DOM may be outdated so we'll at least refresh the just edited style
|
||||
// assuming other changes aren't important enough to justify making a complicated DOM sync
|
||||
case 'visible': {
|
||||
const id = sessionStorage.justEditedStyleId;
|
||||
const id = sessionStore.justEditedStyleId;
|
||||
if (id) {
|
||||
handleUpdateForId(Number(id), {method: 'styleUpdated'});
|
||||
delete sessionStorage.justEditedStyleId;
|
||||
delete sessionStore.justEditedStyleId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -707,9 +707,9 @@ function onVisibilityChange() {
|
|||
|
||||
|
||||
function highlightEditedStyle() {
|
||||
if (!sessionStorage.justEditedStyleId) return;
|
||||
const entry = $(ENTRY_ID_PREFIX + sessionStorage.justEditedStyleId);
|
||||
delete sessionStorage.justEditedStyleId;
|
||||
if (!sessionStore.justEditedStyleId) return;
|
||||
const entry = $(ENTRY_ID_PREFIX + sessionStore.justEditedStyleId);
|
||||
delete sessionStore.justEditedStyleId;
|
||||
if (entry) {
|
||||
animateElement(entry);
|
||||
requestAnimationFrame(() => scrollElementIntoView(entry));
|
||||
|
|
Loading…
Reference in New Issue
Block a user