From c95f74e089ee6cf63043e1a4519ec73bd111244f Mon Sep 17 00:00:00 2001 From: tophf Date: Sun, 23 Jan 2022 14:07:55 +0300 Subject: [PATCH] extract sync and cache from styleMan --- background/common.js | 63 +++++++++++++- background/style-manager.js | 166 +++++++----------------------------- background/sync-manager.js | 58 ++++++++++--- 3 files changed, 137 insertions(+), 150 deletions(-) diff --git a/background/common.js b/background/common.js index a08174fa..7fbdd53f 100644 --- a/background/common.js +++ b/background/common.js @@ -8,7 +8,7 @@ /* exported addAPI bgReady - compareRevision + createCache */ const bgReady = {}; @@ -26,6 +26,63 @@ function addAPI(methods) { } } -function compareRevision(rev1, rev2) { - return rev1 - rev2; +/** Creates a FIFO limit-size map. */ +function createCache({size = 1000, onDeleted} = {}) { + const map = new Map(); + const buffer = Array(size); + let index = 0; + let lastIndex = 0; + return { + get(id) { + const item = map.get(id); + return item && item.data; + }, + set(id, data) { + if (map.size === size) { + // full + map.delete(buffer[lastIndex].id); + if (onDeleted) { + onDeleted(buffer[lastIndex].id, buffer[lastIndex].data); + } + lastIndex = (lastIndex + 1) % size; + } + const item = {id, data, index}; + map.set(id, item); + buffer[index] = item; + index = (index + 1) % size; + }, + delete(id) { + const item = map.get(id); + if (!item) { + return false; + } + map.delete(item.id); + const lastItem = buffer[lastIndex]; + lastItem.index = item.index; + buffer[item.index] = lastItem; + lastIndex = (lastIndex + 1) % size; + if (onDeleted) { + onDeleted(item.id, item.data); + } + return true; + }, + clear() { + map.clear(); + index = lastIndex = 0; + }, + has: id => map.has(id), + *entries() { + for (const [id, item] of map) { + yield [id, item.data]; + } + }, + *values() { + for (const item of map.values()) { + yield item.data; + } + }, + get size() { + return map.size; + }, + }; } diff --git a/background/style-manager.js b/background/style-manager.js index 52e5b080..bece32ad 100644 --- a/background/style-manager.js +++ b/background/style-manager.js @@ -1,6 +1,6 @@ /* global API msg */// msg.js /* global CHROME URLS isEmptyObj stringAsRegExp tryRegExp tryURL */// toolbox.js -/* global bgReady compareRevision */// common.js +/* global bgReady createCache */// common.js /* global calcStyleDigest styleCodeEmpty styleSectionGlobal */// sections-util.js /* global db */ /* global prefs */ @@ -18,8 +18,15 @@ The live preview feature relies on `runtime.connect` and `port.onDisconnect` to cleanup the temporary code. See livePreview in /edit. */ +const styleUtil = {}; + const styleMan = (() => { + Object.assign(styleUtil, { + id2style, + handleSave, + }); + //#region Declarations /** @typedef {{ @@ -29,7 +36,6 @@ const styleMan = (() => { }} StyleMapData */ /** @type {Map} */ const dataMap = new Map(); - const uuidIndex = new Map(); /** @typedef {Object} StyleSectionsToApply */ /** @type {Map, sections: StyleSectionsToApply}>} */ const cachedStyleForUrl = createCache({ @@ -77,16 +83,17 @@ const styleMan = (() => { } }); - prefs.subscribe(['injectionOrder'], (key, value) => { - order = {}; - value.forEach((uid, i) => { - const id = uuidIndex.get(uid); - if (id) { - order[id] = i; - } - }); - msg.broadcast({method: 'styleSort', order}); - }); + // TODO: will fix in subsequent commit + // prefs.subscribe(['injectionOrder'], (key, value) => { + // order = {}; + // value.forEach((uid, i) => { + // const id = uuidIndex.get(uid); + // if (id) { + // order[id] = i; + // } + // }); + // msg.broadcast({method: 'styleSort', order}); + // }); //#endregion //#region Exports @@ -107,7 +114,6 @@ const styleMan = (() => { if (cache) delete cache.sections[id]; } dataMap.delete(id); - uuidIndex.delete(style._id); if (style._usw && style._usw.token) { // Must be called after the style is deleted from dataMap API.usw.revoke(id); @@ -120,17 +126,6 @@ const styleMan = (() => { return id; }, - /** @returns {Promise} style id */ - async deleteByUUID(_id, rev) { - if (ready.then) await ready; - const id = uuidIndex.get(_id); - const oldDoc = id && id2style(id); - if (oldDoc && compareRevision(oldDoc._rev, rev) <= 0) { - // FIXME: does it make sense to set reason to 'sync' in deleteByUUID? - return styleMan.delete(id, 'sync'); - } - }, - /** @returns {Promise} */ async editSave(style) { if (ready.then) await ready; @@ -157,12 +152,6 @@ const styleMan = (() => { return Array.from(dataMap.values(), data2style); }, - /** @returns {Promise} */ - async getByUUID(uuid) { - if (ready.then) await ready; - return id2style(uuidIndex.get(uuid)); - }, - /** @returns {Promise} */ async getSectionsByUrl(url, id, isInitialApply) { if (ready.then) await ready; @@ -263,10 +252,9 @@ const styleMan = (() => { } } const events = await db.exec('putMany', items); - return Promise.all(items.map((item, i) => { - afterSave(item, events[i]); - return handleSave(item, {reason: 'import'}); - })); + return Promise.all(items.map((item, i) => + handleSave(item, {reason: 'import'}, events[i]) + )); }, /** @returns {Promise} */ @@ -279,31 +267,6 @@ const styleMan = (() => { return saveStyle(style, {reason}); }, - /** @returns {Promise} */ - async putByUUID(doc) { - if (ready.then) await ready; - const id = uuidIndex.get(doc._id); - if (id) { - doc.id = id; - } else { - delete doc.id; - } - const oldDoc = id && id2style(id); - let diff = -1; - if (oldDoc) { - diff = compareRevision(oldDoc._rev, doc._rev); - if (diff > 0) { - API.sync.put(oldDoc._id, oldDoc._rev); - return; - } - } - if (diff < 0) { - doc.id = await db.exec('put', doc); - uuidIndex.set(doc._id, doc.id); - return handleSave(doc, {reason: 'sync'}); - } - }, - save: saveStyle, /** @returns {Promise} style id */ @@ -472,29 +435,24 @@ const styleMan = (() => { fixKnownProblems(style); } - function afterSave(style, newId) { - if (style.id == null) { - style.id = newId; - } - uuidIndex.set(style._id, style.id); - API.sync.put(style._id, style._rev); - } - async function saveStyle(style, handlingOptions) { beforeSave(style); const newId = await db.exec('put', style); - afterSave(style, newId); - return handleSave(style, handlingOptions); + return handleSave(style, handlingOptions, newId); } - function handleSave(style, {reason, broadcast = true}) { - const data = id2data(style.id); + function handleSave(style, {reason, broadcast = true}, id = style.id) { + if (style.id == null) style.id = id; + const data = id2data(id); const method = data ? 'styleUpdated' : 'styleAdded'; if (!data) { storeInMap(style); } else { data.style = style; } + if (reason !== 'sync') { + API.sync.putStyle(style); + } if (broadcast) broadcastStyleUpdated(style, reason, method); return style; } @@ -524,10 +482,7 @@ const styleMan = (() => { if (updated.length) { await db.exec('putMany', updated); } - for (const style of styles) { - storeInMap(style); - uuidIndex.set(style._id, style.id); - } + styles.forEach(storeInMap); ready = true; bgReady._resolveStyles(); } @@ -732,64 +687,3 @@ const styleMan = (() => { //#endregion })(); - -/** Creates a FIFO limit-size map. */ -function createCache({size = 1000, onDeleted} = {}) { - const map = new Map(); - const buffer = Array(size); - let index = 0; - let lastIndex = 0; - return { - get(id) { - const item = map.get(id); - return item && item.data; - }, - set(id, data) { - if (map.size === size) { - // full - map.delete(buffer[lastIndex].id); - if (onDeleted) { - onDeleted(buffer[lastIndex].id, buffer[lastIndex].data); - } - lastIndex = (lastIndex + 1) % size; - } - const item = {id, data, index}; - map.set(id, item); - buffer[index] = item; - index = (index + 1) % size; - }, - delete(id) { - const item = map.get(id); - if (!item) { - return false; - } - map.delete(item.id); - const lastItem = buffer[lastIndex]; - lastItem.index = item.index; - buffer[item.index] = lastItem; - lastIndex = (lastIndex + 1) % size; - if (onDeleted) { - onDeleted(item.id, item.data); - } - return true; - }, - clear() { - map.clear(); - index = lastIndex = 0; - }, - has: id => map.has(id), - *entries() { - for (const [id, item] of map) { - yield [id, item.data]; - } - }, - *values() { - for (const item of map.values()) { - yield item.data; - } - }, - get size() { - return map.size; - }, - }; -} diff --git a/background/sync-manager.js b/background/sync-manager.js index e1aafd4e..5ce5be4c 100644 --- a/background/sync-manager.js +++ b/background/sync-manager.js @@ -1,8 +1,9 @@ /* global API msg */// msg.js /* global chromeLocal chromeSync */// storage-util.js -/* global compareRevision */// common.js +/* global db */ /* global iconMan */ /* global prefs */ +/* global styleUtil */ /* global tokenMan */ 'use strict'; @@ -28,11 +29,17 @@ const syncMan = (() => { errorMessage: null, login: false, }; + const uuidIndex = new Map(); + const uuid2style = uuid => styleUtil.id2style(uuidIndex.get(uuid)); + const compareRevision = (rev1, rev2) => rev1 - rev2; let lastError = null; let ctrl; let currentDrive; /** @type {Promise|boolean} will be `true` to avoid wasting a microtask tick on each `await` */ - let ready = prefs.ready.then(() => { + let ready = prefs.ready.then(async () => { + for (const {id, _id} of await API.styles.getAll()) { + uuidIndex.set(_id, id); + } ready = true; prefs.subscribe('sync.enabled', (_, val) => val === 'none' @@ -52,11 +59,12 @@ const syncMan = (() => { return { - async delete(...args) { + async delete(_id, rev) { if (ready.then) await ready; if (!currentDrive) return; schedule(); - return ctrl.delete(...args); + uuidIndex.delete(_id); + return ctrl.delete(_id, rev); }, /** @returns {Promise} */ @@ -86,6 +94,11 @@ const syncMan = (() => { return ctrl.put(...args); }, + putStyle({id, _id, _rev}) { + uuidIndex.set(_id, id); + return syncMan.put(_id, _rev); + }, + async setDriveOptions(driveName, options) { const key = `secure/sync/driveOptions/${driveName}`; await chromeSync.setValue(key, options); @@ -178,14 +191,37 @@ const syncMan = (() => { async function initController() { await require(['/vendor/db-to-cloud/db-to-cloud.min']); /* global dbToCloud */ ctrl = dbToCloud.dbToCloud({ - onGet(id) { - return API.styles.getByUUID(id); + onGet: uuid2style, + async onPut(doc) { + const id = uuidIndex.get(doc._id); + const oldDoc = uuid2style(doc._id); + if (id) { + doc.id = id; + } else { + delete doc.id; + } + let diff = -1; + if (oldDoc) { + diff = compareRevision(oldDoc._rev, doc._rev); + if (diff > 0) { + syncMan.put(oldDoc); + return; + } + } + if (diff < 0) { + doc.id = await db.exec('put', doc); + uuidIndex.set(doc._id, doc.id); + return styleUtil.handleSave(doc, {reason: 'sync'}); + } }, - onPut(doc) { - return API.styles.putByUUID(doc); - }, - onDelete(id, rev) { - return API.styles.deleteByUUID(id, rev); + onDelete(_id, rev) { + const id = uuidIndex.get(_id); + const oldDoc = uuid2style(_id); + if (oldDoc && compareRevision(oldDoc._rev, rev) <= 0) { + // FIXME: does it make sense to set reason to 'sync' in deleteByUUID? + uuidIndex.delete(id); + return API.styles.delete(id, 'sync'); + } }, async onFirstSync() { for (const i of await API.styles.getAll()) {