simplify saveStyle, invalidateCache
This commit is contained in:
parent
4bc27db3fc
commit
8f784a19d4
|
@ -1,4 +1,4 @@
|
||||||
/* global getDatabase, getStyles, saveStyle, reportError, invalidateCache */
|
/* global getDatabase, getStyles, saveStyle, reportError */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
chrome.webNavigation.onBeforeNavigate.addListener(data => {
|
chrome.webNavigation.onBeforeNavigate.addListener(data => {
|
||||||
|
@ -60,10 +60,6 @@ function onRuntimeMessage(request, sender, sendResponse) {
|
||||||
saveStyle(request).then(sendResponse);
|
saveStyle(request).then(sendResponse);
|
||||||
return KEEP_CHANNEL_OPEN;
|
return KEEP_CHANNEL_OPEN;
|
||||||
|
|
||||||
case 'invalidateCache':
|
|
||||||
invalidateCache(false, request);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'healthCheck':
|
case 'healthCheck':
|
||||||
getDatabase(
|
getDatabase(
|
||||||
() => sendResponse(true),
|
() => sendResponse(true),
|
||||||
|
|
128
storage.js
128
storage.js
|
@ -10,15 +10,15 @@ const SLOPPY_REGEXP_PREFIX = '\0';
|
||||||
// Note, only 'var'-declared variables are visible from another extension page
|
// Note, only 'var'-declared variables are visible from another extension page
|
||||||
// eslint-disable-next-line no-var
|
// eslint-disable-next-line no-var
|
||||||
var cachedStyles = {
|
var cachedStyles = {
|
||||||
list: null,
|
list: null, // array of all styles
|
||||||
byId: new Map(),
|
byId: new Map(), // all styles indexed by id
|
||||||
filters: new Map(),
|
filters: new Map(), // filterStyles() parameters mapped to the returned results, 10k max
|
||||||
regexps: new Map(),
|
regexps: new Map(), // compiled style regexps
|
||||||
urlDomains: new Map(),
|
urlDomains: new Map(), // getDomain() results for 100 last checked urls
|
||||||
emptyCode: new Map(), // entire code is comments/whitespace/@namespace
|
emptyCode: new Map(), // entire code is comments/whitespace/@namespace
|
||||||
mutex: {
|
mutex: {
|
||||||
inProgress: false,
|
inProgress: false, // while getStyles() is reading IndexedDB all subsequent calls
|
||||||
onDone: [],
|
onDone: [], // to getStyles() are queued and resolved when the first one finishes
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +56,6 @@ function getStyles(options, callback) {
|
||||||
}
|
}
|
||||||
cachedStyles.mutex.inProgress = true;
|
cachedStyles.mutex.inProgress = true;
|
||||||
|
|
||||||
//const t0 = performance.now();
|
|
||||||
getDatabase(db => {
|
getDatabase(db => {
|
||||||
const tx = db.transaction(['styles'], 'readonly');
|
const tx = db.transaction(['styles'], 'readonly');
|
||||||
const os = tx.objectStore('styles');
|
const os = tx.objectStore('styles');
|
||||||
|
@ -67,7 +66,6 @@ function getStyles(options, callback) {
|
||||||
cachedStyles.byId.set(style.id, style);
|
cachedStyles.byId.set(style.id, style);
|
||||||
compileStyleRegExps({style});
|
compileStyleRegExps({style});
|
||||||
}
|
}
|
||||||
//console.debug('%s getStyles %s, invoking cached callbacks: %o', (performance.now() - t0).toFixed(1), JSON.stringify(options), cachedStyles.mutex.onDone.map(e => JSON.stringify(e.options))); // eslint-disable-line max-len
|
|
||||||
callback(filterStyles(options));
|
callback(filterStyles(options));
|
||||||
|
|
||||||
cachedStyles.mutex.inProgress = false;
|
cachedStyles.mutex.inProgress = false;
|
||||||
|
@ -88,7 +86,6 @@ function filterStyles({
|
||||||
asHash = null,
|
asHash = null,
|
||||||
strictRegexp = true, // used by the popup to detect bad regexps
|
strictRegexp = true, // used by the popup to detect bad regexps
|
||||||
} = {}) {
|
} = {}) {
|
||||||
//const t0 = performance.now();
|
|
||||||
enabled = fixBoolean(enabled);
|
enabled = fixBoolean(enabled);
|
||||||
id = id === null ? null : Number(id);
|
id = id === null ? null : Number(id);
|
||||||
|
|
||||||
|
@ -97,11 +94,8 @@ function filterStyles({
|
||||||
&& id === null
|
&& id === null
|
||||||
&& matchUrl === null
|
&& matchUrl === null
|
||||||
&& asHash != true) {
|
&& asHash != true) {
|
||||||
//console.debug('%c%s filterStyles SKIPPED LOOP %s', 'color:gray', (performance.now() - t0).toFixed(1), enabled, id, asHash, strictRegexp, matchUrl); // eslint-disable-line max-len
|
|
||||||
return cachedStyles.list;
|
return cachedStyles.list;
|
||||||
}
|
}
|
||||||
// silence the inapplicable warning for async code
|
|
||||||
// eslint-disable-next-line no-use-before-define
|
|
||||||
const disableAll = asHash && prefs.get('disableAll', false);
|
const disableAll = asHash && prefs.get('disableAll', false);
|
||||||
|
|
||||||
if (matchUrl && matchUrl.startsWith(URLS.chromeWebStore)) {
|
if (matchUrl && matchUrl.startsWith(URLS.chromeWebStore)) {
|
||||||
|
@ -114,7 +108,6 @@ function filterStyles({
|
||||||
const cacheKey = ' ' + enabled + url + '\t' + id + matchUrl + '\t' + asHash + strictRegexp;
|
const cacheKey = ' ' + enabled + url + '\t' + id + matchUrl + '\t' + asHash + strictRegexp;
|
||||||
const cached = cachedStyles.filters.get(cacheKey);
|
const cached = cachedStyles.filters.get(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
//console.debug('%c%s filterStyles REUSED RESPONSE %s', 'color:gray', (performance.now() - t0).toFixed(1), enabled, id, asHash, strictRegexp, matchUrl); // eslint-disable-line max-len
|
|
||||||
cached.hits++;
|
cached.hits++;
|
||||||
cached.lastHit = Date.now();
|
cached.lastHit = Date.now();
|
||||||
|
|
||||||
|
@ -144,8 +137,8 @@ function filterStyles({
|
||||||
|
|
||||||
for (let i = 0, style; (style = styles[i]); i++) {
|
for (let i = 0, style; (style = styles[i]); i++) {
|
||||||
if ((enabled === null || style.enabled == enabled)
|
if ((enabled === null || style.enabled == enabled)
|
||||||
&& (url === null || style.url == url)
|
&& (url === null || style.url == url)
|
||||||
&& (id === null || style.id == id)) {
|
&& (id === null || style.id == id)) {
|
||||||
const sections = needSections &&
|
const sections = needSections &&
|
||||||
getApplicableSections({style, matchUrl, strictRegexp, stopOnFirst: !asHash});
|
getApplicableSections({style, matchUrl, strictRegexp, stopOnFirst: !asHash});
|
||||||
if (asHash) {
|
if (asHash) {
|
||||||
|
@ -157,7 +150,6 @@ function filterStyles({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//console.debug('%s filterStyles %s', (performance.now() - t0).toFixed(1), enabled, id, asHash, strictRegexp, matchUrl); // eslint-disable-line max-len
|
|
||||||
cachedStyles.filters.set(cacheKey, {
|
cachedStyles.filters.set(cacheKey, {
|
||||||
styles: filtered,
|
styles: filtered,
|
||||||
lastHit: Date.now(),
|
lastHit: Date.now(),
|
||||||
|
@ -188,52 +180,50 @@ function saveStyle(style) {
|
||||||
delete style.name;
|
delete style.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update
|
|
||||||
if (id !== null) {
|
if (id !== null) {
|
||||||
|
// Update or create
|
||||||
style.id = id;
|
style.id = id;
|
||||||
os.get(id).onsuccess = eventGet => {
|
os.get(id).onsuccess = eventGet => {
|
||||||
const existed = Boolean(eventGet.target.result);
|
const existed = Boolean(eventGet.target.result);
|
||||||
const oldStyle = Object.assign({}, eventGet.target.result);
|
const oldStyle = Object.assign({}, eventGet.target.result);
|
||||||
const codeIsUpdated = 'sections' in style && !styleSectionsEqual(style, oldStyle);
|
const codeIsUpdated = 'sections' in style && !styleSectionsEqual(style, oldStyle);
|
||||||
style = Object.assign(oldStyle, style);
|
write(Object.assign(oldStyle, style), {existed, codeIsUpdated});
|
||||||
addMissingStyleTargets(style);
|
|
||||||
os.put(style).onsuccess = eventPut => {
|
|
||||||
style.id = style.id || eventPut.target.result;
|
|
||||||
invalidateCache(notify, existed ? {updated: style} : {added: style});
|
|
||||||
compileStyleRegExps({style});
|
|
||||||
if (notify) {
|
|
||||||
notifyAllTabs({
|
|
||||||
method: existed ? 'styleUpdated' : 'styleAdded',
|
|
||||||
style, codeIsUpdated, reason,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
resolve(style);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
return;
|
} else {
|
||||||
|
// Create
|
||||||
|
delete style.id;
|
||||||
|
write(Object.assign({
|
||||||
|
// Set optional things if they're undefined
|
||||||
|
enabled: true,
|
||||||
|
updateUrl: null,
|
||||||
|
md5Url: null,
|
||||||
|
url: null,
|
||||||
|
originalMd5: null,
|
||||||
|
}, style));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
function write(style, {existed, codeIsUpdated} = {}) {
|
||||||
delete style.id;
|
style.sections = (style.sections || []).map(section =>
|
||||||
style = Object.assign({
|
Object.assign({
|
||||||
// Set optional things if they're undefined
|
urls: [],
|
||||||
enabled: true,
|
urlPrefixes: [],
|
||||||
updateUrl: null,
|
domains: [],
|
||||||
md5Url: null,
|
regexps: [],
|
||||||
url: null,
|
}, section)
|
||||||
originalMd5: null,
|
);
|
||||||
}, style);
|
os.put(style).onsuccess = event => {
|
||||||
addMissingStyleTargets(style);
|
style.id = style.id || event.target.result;
|
||||||
os.add(style).onsuccess = event => {
|
invalidateCache(existed ? {updated: style} : {added: style});
|
||||||
// Give it the ID that was generated
|
compileStyleRegExps({style});
|
||||||
style.id = event.target.result;
|
if (notify) {
|
||||||
invalidateCache(notify, {added: style});
|
notifyAllTabs({
|
||||||
compileStyleRegExps({style});
|
method: existed ? 'styleUpdated' : 'styleAdded',
|
||||||
if (notify) {
|
style, codeIsUpdated, reason,
|
||||||
notifyAllTabs({method: 'styleAdded', style, reason});
|
});
|
||||||
}
|
}
|
||||||
resolve(style);
|
resolve(style);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -245,7 +235,7 @@ function deleteStyle({id, notify = true}) {
|
||||||
const tx = db.transaction(['styles'], 'readwrite');
|
const tx = db.transaction(['styles'], 'readwrite');
|
||||||
const os = tx.objectStore('styles');
|
const os = tx.objectStore('styles');
|
||||||
os.delete(Number(id)).onsuccess = () => {
|
os.delete(Number(id)).onsuccess = () => {
|
||||||
invalidateCache(notify, {deletedId: id});
|
invalidateCache({deletedId: id});
|
||||||
if (notify) {
|
if (notify) {
|
||||||
notifyAllTabs({method: 'styleDeleted', id});
|
notifyAllTabs({method: 'styleDeleted', id});
|
||||||
}
|
}
|
||||||
|
@ -429,15 +419,12 @@ function compileStyleRegExps({style, compileAll}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function invalidateCache(andNotify, {added, updated, deletedId} = {}) {
|
function invalidateCache({added, updated, deletedId} = {}) {
|
||||||
// prevent double-add on echoed invalidation
|
// prevent double-add on echoed invalidation
|
||||||
const cached = added && cachedStyles.byId.get(added.id);
|
const cached = added && cachedStyles.byId.get(added.id);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (andNotify) {
|
|
||||||
chrome.runtime.sendMessage({method: 'invalidateCache', added, updated, deletedId});
|
|
||||||
}
|
|
||||||
if (!cachedStyles.list) {
|
if (!cachedStyles.list) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -445,7 +432,6 @@ function invalidateCache(andNotify, {added, updated, deletedId} = {}) {
|
||||||
const cached = cachedStyles.byId.get(updated.id);
|
const cached = cachedStyles.byId.get(updated.id);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
Object.assign(cached, updated);
|
Object.assign(cached, updated);
|
||||||
//console.debug('cache: updated', updated);
|
|
||||||
}
|
}
|
||||||
cachedStyles.filters.clear();
|
cachedStyles.filters.clear();
|
||||||
return;
|
return;
|
||||||
|
@ -453,7 +439,6 @@ function invalidateCache(andNotify, {added, updated, deletedId} = {}) {
|
||||||
if (added) {
|
if (added) {
|
||||||
cachedStyles.list.push(added);
|
cachedStyles.list.push(added);
|
||||||
cachedStyles.byId.set(added.id, added);
|
cachedStyles.byId.set(added.id, added);
|
||||||
//console.debug('cache: added', added);
|
|
||||||
cachedStyles.filters.clear();
|
cachedStyles.filters.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -463,22 +448,18 @@ function invalidateCache(andNotify, {added, updated, deletedId} = {}) {
|
||||||
const cachedIndex = cachedStyles.list.indexOf(deletedStyle);
|
const cachedIndex = cachedStyles.list.indexOf(deletedStyle);
|
||||||
cachedStyles.list.splice(cachedIndex, 1);
|
cachedStyles.list.splice(cachedIndex, 1);
|
||||||
cachedStyles.byId.delete(deletedId);
|
cachedStyles.byId.delete(deletedId);
|
||||||
//console.debug('cache: deleted', deletedStyle);
|
|
||||||
cachedStyles.filters.clear();
|
cachedStyles.filters.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cachedStyles.list = null;
|
cachedStyles.list = null;
|
||||||
//console.debug('cache cleared');
|
|
||||||
cachedStyles.filters.clear();
|
cachedStyles.filters.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function cleanupCachedFilters({force = false} = {}) {
|
function cleanupCachedFilters({force = false} = {}) {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
// sliding timer for 1 second
|
debounce(cleanupCachedFilters, 1000, {force: true});
|
||||||
clearTimeout(cleanupCachedFilters.timeout);
|
|
||||||
cleanupCachedFilters.timeout = setTimeout(cleanupCachedFilters, 1000, {force: true});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const size = cachedStyles.filters.size;
|
const size = cachedStyles.filters.size;
|
||||||
|
@ -500,19 +481,6 @@ function cleanupCachedFilters({force = false} = {}) {
|
||||||
.sort((a, b) => a.weight - b.weight)
|
.sort((a, b) => a.weight - b.weight)
|
||||||
.slice(0, size / 10 + 1)
|
.slice(0, size / 10 + 1)
|
||||||
.forEach(({id}) => cachedStyles.filters.delete(id));
|
.forEach(({id}) => cachedStyles.filters.delete(id));
|
||||||
cleanupCachedFilters.timeout = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function addMissingStyleTargets(style) {
|
|
||||||
style.sections = (style.sections || []).map(section =>
|
|
||||||
Object.assign({
|
|
||||||
urls: [],
|
|
||||||
urlPrefixes: [],
|
|
||||||
domains: [],
|
|
||||||
regexps: [],
|
|
||||||
}, section)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user