diff --git a/src/background.ts b/src/background.ts index 77004395..6561ae12 100644 --- a/src/background.ts +++ b/src/background.ts @@ -231,6 +231,12 @@ browser.tabs.onCreated.addListener(aucon.tabCreatedListener) // An object to collect all of our statistics in one place. const statsLogger: perf.StatsLogger = new perf.StatsLogger() const messages = { + config_background: { + clear: config.clear, + pull: config.pull, + set: config.set, + unset: config.unset, + }, excmd_background: excmds_background, controller_background: controller, performance_background: statsLogger, diff --git a/src/background/config_rc.ts b/src/background/config_rc.ts index 41b6fcf2..9857445a 100644 --- a/src/background/config_rc.ts +++ b/src/background/config_rc.ts @@ -53,7 +53,6 @@ export async function writeRc(conf: string, force = false, filename = "auto") { export async function runRc(rc: string) { for (const cmd of rcFileToExCmds(rc)) { - await new Promise(resolve => setTimeout(resolve, 100)) await controller.acceptExCmd(cmd) } } diff --git a/src/excmds.ts b/src/excmds.ts index 5c65765b..36041443 100644 --- a/src/excmds.ts +++ b/src/excmds.ts @@ -5118,7 +5118,7 @@ export async function sanitise(...args: string[]) { // Tridactyl-specific items if (dts.commandline === true) state.cmdHistory = [] delete dts.commandline - if (dts.tridactyllocal === true) await browser.storage.local.clear() + if (dts.tridactyllocal === true) await config.clear() delete dts.tridactyllocal if (dts.tridactylsync === true) await browser.storage.sync.clear() delete dts.tridactylsync diff --git a/src/lib/config.ts b/src/lib/config.ts index ba98f737..4fbd2921 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -35,6 +35,8 @@ const removeNull = R.when( /** @hidden */ const CONFIGNAME = "userconfig" +const BACKGROUND_URL = browser.runtime.getURL("_generated_background_page.html") +const IN_BACKGROUND = !BACKGROUND_URL || BACKGROUND_URL === window.location.href /** @hidden */ const WAITERS = [] /** @hidden */ @@ -54,6 +56,17 @@ function schlepp(settings) { /** @hidden */ export let USERCONFIG = o({}) +let STORE_QUEUE = Promise.resolve() + +function store(operation: () => Promise) { + const pending = STORE_QUEUE.then(operation) + STORE_QUEUE = pending.catch(() => undefined) + return pending +} + +function mutateInBackground(command, args) { + return browser.runtime.sendMessage({ type: "config_background", command, args }) +} /** @hidden * Ideally, LoggingLevel should be in logging.ts and imported from there. However this would cause a circular dependency, which webpack can't deal with @@ -2080,6 +2093,7 @@ export async function getAsync( ...target: string[] ) { if (INITIALISED) { + if (IN_BACKGROUND) return get(target_typed, ...target) // TODO: consider storing keys directly const browserconfig = await browser.storage.local.get(CONFIGNAME) USERCONFIG = browserconfig[CONFIGNAME] || o({}) @@ -2108,7 +2122,11 @@ export async function push() { * Replaces the local configuration with the configuration from your sync storage. Does not merge: it overwrites. */ export async function pull() { - return browser.storage.local.set(await browser.storage.sync.get(CONFIGNAME)) + if (!IN_BACKGROUND) return mutateInBackground("pull", []) + if (!INITIALISED) await getAsync() + const synced = await browser.storage.sync.get(CONFIGNAME) + USERCONFIG = synced[CONFIGNAME] || o({}) + return save() } /** @hidden @@ -2141,14 +2159,14 @@ export async function set(...args) { const target = args.slice(0, args.length - 1) const value = args[args.length - 1] - if (INITIALISED) { - // wait for storage to settle, otherwise we could clobber a previous incomplete set() - setDeepProperty(USERCONFIG, value, target) - - return save() - } else { + if (!IN_BACKGROUND) { setDeepProperty(USERCONFIG, value, target) + return mutateInBackground("set", args) } + + if (!INITIALISED) await getAsync() + setDeepProperty(USERCONFIG, value, target) + return save() } /** @hidden @@ -2160,12 +2178,24 @@ export function unsetURL(pattern, ...target) { /** Delete the key at target in USERCONFIG if it exists * @hidden */ -export function unset(...target) { +export async function unset(...target) { + if (IN_BACKGROUND && !INITIALISED) await getAsync() const parent = getDeepProperty(USERCONFIG, target.slice(0, -1)) if (parent !== undefined) delete parent[target[target.length - 1]] + if (!IN_BACKGROUND) return mutateInBackground("unset", target) return save() } +export async function clear() { + if (!IN_BACKGROUND) { + USERCONFIG = o({}) + return mutateInBackground("clear", []) + } + if (!INITIALISED) await getAsync() + USERCONFIG = o({}) + return store(() => browser.storage.local.clear()) +} + /** Save the config back to storage API. Config is not synchronised between different instances of this module until @@ -2175,8 +2205,8 @@ export function unset(...target) { */ export async function save() { const settingsobj = o({}) - settingsobj[CONFIGNAME] = USERCONFIG - return browser.storage.local.set(settingsobj) + settingsobj[CONFIGNAME] = JSON.parse(JSON.stringify(USERCONFIG)) + return store(() => browser.storage.local.set(settingsobj)) } /** Updates the config to the latest version. @@ -2588,6 +2618,7 @@ const parseConfigHelper = (pconf, parseobj, prefix = []) => { // Listen for changes to the storage and update the USERCONFIG if appropriate. // TODO: BUG! Sync and local storage are merged at startup, but not by this thing. browser.storage.onChanged.addListener((changes, areaname) => { + if (IN_BACKGROUND) return if (CONFIGNAME in changes) { const { newValue, oldValue } = changes[CONFIGNAME] const old = oldValue || {} @@ -2633,7 +2664,7 @@ browser.storage.onChanged.addListener((changes, areaname) => { Object.keys(old) .filter(key => old[key] !== DEFAULTS[key]) - .forEach(key => triggerChangeListeners(key)) + .forEach(key => triggerChangeListeners(key, DEFAULTS[key])) } } })