promisify DB access

This commit is contained in:
tophf 2017-04-26 00:48:27 +03:00
parent 02fd4f1abe
commit acc4d83b9d
4 changed files with 149 additions and 149 deletions

View File

@ -1,4 +1,4 @@
/* global getDatabase, getStyles, saveStyle */ /* global dbExec, getStyles, saveStyle */
'use strict'; 'use strict';
// eslint-disable-next-line no-var // eslint-disable-next-line no-var
@ -6,7 +6,7 @@ var browserCommands, contextMenus;
// ************************************************************************* // *************************************************************************
// preload the DB and report errors // preload the DB and report errors
getDatabase(() => {}, (...args) => { dbExec().catch((...args) => {
args.forEach(arg => 'message' in arg && console.error(arg.message)); args.forEach(arg => 'message' in arg && console.error(arg.message));
}); });
@ -183,7 +183,7 @@ prefs.subscribe((id, checked) => {
// ************************************************************************* // *************************************************************************
function webNavigationListener(method, {url, tabId, frameId}) { function webNavigationListener(method, {url, tabId, frameId}) {
getStyles({matchUrl: url, enabled: true, asHash: true}, styles => { getStyles({matchUrl: url, enabled: true, asHash: true}).then(styles => {
if (method && !url.startsWith('chrome:') && tabId >= 0) { if (method && !url.startsWith('chrome:') && tabId >= 0) {
chrome.tabs.sendMessage(tabId, { chrome.tabs.sendMessage(tabId, {
method, method,
@ -209,9 +209,9 @@ function updateIcon(tab, styles) {
stylesReceived(styles); stylesReceived(styles);
return; return;
} }
getTabRealURL(tab).then(url => getTabRealURL(tab)
getStyles({matchUrl: url, enabled: true, asHash: true}, .then(url => getStyles({matchUrl: url, enabled: true, asHash: true}))
stylesReceived)); .then(stylesReceived);
function stylesReceived(styles) { function stylesReceived(styles) {
let numStyles = styles.length; let numStyles = styles.length;
@ -255,7 +255,7 @@ function onRuntimeMessage(request, sender, sendResponse) {
switch (request.method) { switch (request.method) {
case 'getStyles': case 'getStyles':
getStyles(request, sendResponse); getStyles(request).then(sendResponse);
return KEEP_CHANNEL_OPEN; return KEEP_CHANNEL_OPEN;
case 'saveStyle': case 'saveStyle':
@ -263,9 +263,9 @@ function onRuntimeMessage(request, sender, sendResponse) {
return KEEP_CHANNEL_OPEN; return KEEP_CHANNEL_OPEN;
case 'healthCheck': case 'healthCheck':
getDatabase( dbExec()
() => sendResponse(true), .then(() => sendResponse(true))
() => sendResponse(false)); .catch(() => sendResponse(false));
return KEEP_CHANNEL_OPEN; return KEEP_CHANNEL_OPEN;
case 'download': case 'download':

View File

@ -268,13 +268,13 @@ function sessionStorageHash(name) {
} }
function onBackgroundReady() { function onBackgroundReady(...dataPassthru) {
return BG ? Promise.resolve() : new Promise(ping); return BG ? Promise.resolve(...dataPassthru) : new Promise(ping);
function ping(resolve) { function ping(resolve) {
chrome.runtime.sendMessage({method: 'healthCheck'}, health => { chrome.runtime.sendMessage({method: 'healthCheck'}, health => {
if (health !== undefined) { if (health !== undefined) {
BG = chrome.extension.getBackgroundPage(); BG = chrome.extension.getBackgroundPage();
resolve(); resolve(...dataPassthru);
} else { } else {
ping(resolve); ping(resolve);
} }
@ -285,20 +285,13 @@ function onBackgroundReady() {
// in case Chrome haven't yet loaded the bg page and displays our page like edit/manage // in case Chrome haven't yet loaded the bg page and displays our page like edit/manage
function getStylesSafe(options) { function getStylesSafe(options) {
return new Promise(resolve => { return onBackgroundReady(options).then(BG.getStyles);
if (BG) {
BG.getStyles(options, resolve);
} else {
onBackgroundReady().then(() =>
BG.getStyles(options, resolve));
}
});
} }
function saveStyleSafe(style) { function saveStyleSafe(style) {
return onBackgroundReady() return onBackgroundReady(BG.deepCopy(style))
.then(() => BG.saveStyle(BG.deepCopy(style))) .then(BG.saveStyle)
.then(savedStyle => { .then(savedStyle => {
if (style.notify === false) { if (style.notify === false) {
handleUpdate(savedStyle, style); handleUpdate(savedStyle, style);
@ -309,8 +302,8 @@ function saveStyleSafe(style) {
function deleteStyleSafe({id, notify = true} = {}) { function deleteStyleSafe({id, notify = true} = {}) {
return onBackgroundReady() return onBackgroundReady({id, notify})
.then(() => BG.deleteStyle({id, notify})) .then(BG.deleteStyle)
.then(() => { .then(() => {
if (!notify) { if (!notify) {
handleDelete(id); handleDelete(id);

View File

@ -43,58 +43,69 @@ var chromeLocal = {
}; };
function getDatabase(ready, error) { function dbExec(method, data) {
const dbOpenRequest = window.indexedDB.open('stylish', 2); return new Promise((resolve, reject) => {
dbOpenRequest.onsuccess = event => { Object.assign(indexedDB.open('stylish', 2), {
ready(event.target.result); onsuccess(event) {
}; const database = event.target.result;
dbOpenRequest.onerror = event => { if (!method) {
console.warn(event.target.errorCode); resolve(database);
if (error) { } else {
error(event); const transaction = database.transaction(['styles'], 'readwrite');
const store = transaction.objectStore('styles');
Object.assign(store[method](data), {
onsuccess: event => resolve(event, store, transaction, database),
onerror: reject,
});
} }
}; },
dbOpenRequest.onupgradeneeded = event => { onerror(event) {
console.warn(event.target.errorCode);
reject(event);
},
onupgradeneeded(event) {
if (event.oldVersion == 0) { if (event.oldVersion == 0) {
event.target.result.createObjectStore('styles', { event.target.result.createObjectStore('styles', {
keyPath: 'id', keyPath: 'id',
autoIncrement: true, autoIncrement: true,
}); });
} }
}; },
});
});
} }
function getStyles(options, callback) { function getStyles(options) {
if (cachedStyles.list) { if (cachedStyles.list) {
callback(filterStyles(options)); return Promise.resolve(filterStyles(options));
return;
} }
if (cachedStyles.mutex.inProgress) { if (cachedStyles.mutex.inProgress) {
cachedStyles.mutex.onDone.push({options, callback}); return new Promise(resolve => {
return; cachedStyles.mutex.onDone.push({options, resolve});
});
} }
cachedStyles.mutex.inProgress = true; cachedStyles.mutex.inProgress = true;
getDatabase(db => { return dbExec('getAll').then(event => {
const tx = db.transaction(['styles'], 'readonly');
const os = tx.objectStore('styles');
os.getAll().onsuccess = event => {
cachedStyles.list = event.target.result || []; cachedStyles.list = event.target.result || [];
cachedStyles.byId.clear(); cachedStyles.byId.clear();
const t0 = performance.now();
let hasTimeToCompile = true;
for (const style of cachedStyles.list) { for (const style of cachedStyles.list) {
cachedStyles.byId.set(style.id, style); cachedStyles.byId.set(style.id, style);
compileStyleRegExps({style}); if (hasTimeToCompile) {
hasTimeToCompile = !compileStyleRegExps({style}) || performance.now() - t0 > 100;
}
} }
callback(filterStyles(options));
cachedStyles.mutex.inProgress = false; cachedStyles.mutex.inProgress = false;
for (const {options, callback} of cachedStyles.mutex.onDone) { for (const {options, resolve} of cachedStyles.mutex.onDone) {
callback(filterStyles(options)); resolve(filterStyles(options));
} }
cachedStyles.mutex.onDone = []; cachedStyles.mutex.onDone = [];
}; return filterStyles(options);
}, null); });
} }
@ -213,12 +224,7 @@ function filterStylesInternal({
function saveStyle(style) { function saveStyle(style) {
return new Promise(resolve => { const id = Number(style.id) >= 0 ? Number(style.id) : null;
getDatabase(db => {
const tx = db.transaction(['styles'], 'readwrite');
const os = tx.objectStore('styles');
const id = style.id == '0' ? 0 : Number(style.id) || null;
const reason = style.reason; const reason = style.reason;
const notify = style.notify !== false; const notify = style.notify !== false;
delete style.method; delete style.method;
@ -228,32 +234,42 @@ function saveStyle(style) {
delete style.name; delete style.name;
} }
let existed, codeIsUpdated; let existed, codeIsUpdated;
if (id !== null) { if (id !== null) {
// Update or create // Update or create
style.id = id; style.id = id;
os.get(id).onsuccess = eventGet => { return dbExec('get', id).then((event, store) => {
const oldStyle = eventGet.target.result; const oldStyle = event.target.result;
existed = Boolean(oldStyle); existed = Boolean(oldStyle);
codeIsUpdated = !existed || style.sections && !styleSectionsEqual(style, oldStyle); codeIsUpdated = !existed || style.sections && !styleSectionsEqual(style, oldStyle);
write(Object.assign({}, oldStyle, style)); style = Object.assign({}, oldStyle, style);
}; return write(style, store);
});
} else { } else {
// Create // Create
delete style.id; delete style.id;
write(Object.assign({ style = Object.assign({
// Set optional things if they're undefined // Set optional things if they're undefined
enabled: true, enabled: true,
updateUrl: null, updateUrl: null,
md5Url: null, md5Url: null,
url: null, url: null,
originalMd5: null, originalMd5: null,
}, style)); }, style);
return write(style);
} }
function write(style) { function write(style, store) {
style.sections = normalizeStyleSections(style); style.sections = normalizeStyleSections(style);
os.put(style).onsuccess = event => { if (store) {
return new Promise(resolve => {
store.put(style).onsuccess = event => resolve(done(event));
});
} else {
return dbExec('put', style).then(done);
}
}
function done(event) {
style.id = style.id || event.target.result; style.id = style.id || event.target.result;
invalidateCache(existed ? {updated: style} : {added: style}); invalidateCache(existed ? {updated: style} : {added: style});
compileStyleRegExps({style}); compileStyleRegExps({style});
@ -268,28 +284,21 @@ function saveStyle(style) {
} else if (reason == 'import') { } else if (reason == 'import') {
chrome.storage.local.remove(DIGEST_KEY_PREFIX + style.id, ignoreChromeError); chrome.storage.local.remove(DIGEST_KEY_PREFIX + style.id, ignoreChromeError);
} }
resolve(style); return style;
};
} }
});
});
} }
function deleteStyle({id, notify = true}) { function deleteStyle({id, notify = true}) {
id = Number(id);
chrome.storage.local.remove(DIGEST_KEY_PREFIX + id, ignoreChromeError); chrome.storage.local.remove(DIGEST_KEY_PREFIX + id, ignoreChromeError);
return new Promise(resolve => return dbExec('delete', id).then(() => {
getDatabase(db => {
const tx = db.transaction(['styles'], 'readwrite');
const os = tx.objectStore('styles');
os.delete(Number(id)).onsuccess = () => {
invalidateCache({deletedId: id}); invalidateCache({deletedId: id});
if (notify) { if (notify) {
notifyAllTabs({method: 'styleDeleted', id}); notifyAllTabs({method: 'styleDeleted', id});
} }
resolve(id); return id;
}; });
}));
} }
@ -448,11 +457,12 @@ function compileStyleRegExps({style, compileAll}) {
const rx = tryRegExp(anchored); const rx = tryRegExp(anchored);
cachedStyles.regexps.set(cacheKey, rx || false); cachedStyles.regexps.set(cacheKey, rx || false);
if (!compileAll && performance.now() - t0 > 100) { if (!compileAll && performance.now() - t0 > 100) {
return; return false;
} }
} }
} }
} }
return true;
} }

View File

@ -22,17 +22,14 @@ var updater = {
checkAllStyles({observer = () => {}, save = true, ignoreDigest} = {}) { checkAllStyles({observer = () => {}, save = true, ignoreDigest} = {}) {
updater.resetInterval(); updater.resetInterval();
return new Promise(resolve => { return getStyles({}).then(styles => {
getStyles({}, styles => {
styles = styles.filter(style => style.updateUrl); styles = styles.filter(style => style.updateUrl);
observer(updater.COUNT, styles.length); observer(updater.COUNT, styles.length);
Promise.all(styles.map(style => return Promise.all(
updater.checkStyle({style, observer, save, ignoreDigest}) styles.map(style =>
)).then(() => { updater.checkStyle({style, observer, save, ignoreDigest})));
}).then(() => {
observer(updater.DONE); observer(updater.DONE);
resolve();
});
});
}); });
}, },