diff --git a/_locales/en/messages.json b/_locales/en/messages.json index c65b08a7..f309accb 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -734,6 +734,9 @@ "message": "Live reload", "description": "The label of live-reload feature" }, + "manageExecutionOrder": { + "message": "Change execution order" + }, "manageFavicons": { "message": "Favicons in applies-to column", "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" diff --git a/background/style-manager.js b/background/style-manager.js index b7c2b8df..a46e1f4c 100644 --- a/background/style-manager.js +++ b/background/style-manager.js @@ -60,6 +60,7 @@ const styleMan = (() => { const DELETE_IF_NULL = ['id', 'customName', 'md5Url', 'originalMd5']; /** @type {Promise|boolean} will be `true` to avoid wasting a microtask tick on each `await` */ let ready = init(); + let order = {}; chrome.runtime.onConnect.addListener(handleLivePreview); // function handleColorScheme() { @@ -71,6 +72,17 @@ const styleMan = (() => { } }); + prefs.subscribe(['styles.order'], (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 @@ -149,7 +161,11 @@ const styleMan = (() => { async getSectionsByUrl(url, id, isInitialApply) { if (ready.then) await ready; if (isInitialApply && prefs.get('disableAll')) { - return {disableAll: true}; + return { + cfg: { + disableAll: true, + }, + }; } const sender = CHROME && this && this.sender || {}; if (sender.frameId === 0) { @@ -171,7 +187,7 @@ const styleMan = (() => { } return id ? cache.sections[id] ? {[id]: cache.sections[id]} : {} - : cache.sections; + : Object.assign({cfg: {order}}, cache.sections); }, /** @returns {Promise} */ diff --git a/background/style-via-api.js b/background/style-via-api.js index ed08dac4..c77d6e24 100644 --- a/background/style-via-api.js +++ b/background/style-via-api.js @@ -56,6 +56,9 @@ return NOP; } return API.styles.getSectionsByUrl(url, id).then(sections => { + if (sections.cfg) { + delete sections.cfg; + } const tasks = []; for (const section of Object.values(sections)) { const styleId = section.id; diff --git a/content/apply.js b/content/apply.js index 0f5dd087..48df0a2d 100644 --- a/content/apply.js +++ b/content/apply.js @@ -13,11 +13,19 @@ let hasStyles = false; let isDisabled = false; let isTab = !chrome.tabs || location.pathname !== '/popup.html'; + let order = {}; const isFrame = window !== parent; const isFrameAboutBlank = isFrame && location.href === 'about:blank'; const isUnstylable = !chrome.app && document instanceof XMLDocument; const styleInjector = StyleInjector({ - compare: (a, b) => a.id - b.id, + compare: (a, b) => { + const ia = order[a.id]; + const ib = order[b.id]; + if (ia === ib) return 0; + if (ia == null) return 1; + if (ib == null) return -1; + return ia - ib; + }, onUpdate: onInjectorUpdate, }); // dynamic iframes don't have a URL yet so we'll use their parent's URL (hash isn't inherited) @@ -100,7 +108,11 @@ parentStyles && await new Promise(onFrameElementInView) && parentStyles || !isFrameAboutBlank && chrome.app && !chrome.tabs && tryCatch(getStylesViaXhr) || await API.styles.getSectionsByUrl(matchUrl, null, true); - isDisabled = styles.disableAll; + if (styles.cfg) { + isDisabled = styles.cfg.disableAll; + order = styles.cfg.order || {}; + delete styles.cfg; + } hasStyles = !isDisabled; if (hasStyles) { window[SYM] = styles; @@ -166,10 +178,18 @@ } break; + case 'styleSort': + order = request.order; + styleInjector.sort(); + break; + case 'urlChanged': if (!hasStyles && isDisabled || matchUrl === request.url) break; matchUrl = request.url; API.styles.getSectionsByUrl(matchUrl).then(sections => { + if (sections.cfg) { + delete sections.cfg; + } hasStyles = true; styleInjector.replace(sections); }); diff --git a/content/style-injector.js b/content/style-injector.js index 675ec135..dc434b38 100644 --- a/content/style-injector.js +++ b/content/style-injector.js @@ -78,6 +78,8 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({ _addRemoveElements(enable); if (enable) _toggleObservers(true); }, + + sort: _sort, }; function _add(style) { diff --git a/js/prefs.js b/js/prefs.js index 7f461173..cfd86acf 100644 --- a/js/prefs.js +++ b/js/prefs.js @@ -126,6 +126,8 @@ 'popupWidth': 246, // popup width in pixels 'updateInterval': 24, // user-style automatic update interval, hours (0 = disable) + + 'styles.order': [], }; const knownKeys = Object.keys(defaults); /** @type {PrefsValues} */ diff --git a/manage.html b/manage.html index c0eddb19..2a3550b2 100644 --- a/manage.html +++ b/manage.html @@ -314,6 +314,8 @@ + +