diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 3c67eb31..4154d9e8 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -823,6 +823,30 @@ "TabGuard_optAllow": { "message": "Load normally" }, + "DefaultContainerName": { + "message": "Default" + }, + "enable_container_tabs_label": { + "message": "Enable support for container tabs" + }, + "select_container_label": { + "message": "Choose a container:" + }, + "copy_container_label": { + "message": "Copy permissions from container:" + }, + "clear_container_label": { + "message": "Clear permissions for this container" + }, + "forbid_replace_default_policy": { + "message": "Cannot replace the default policy." + }, + "container_copy_warning": { + "message": "Copying permissions from \"$1\".\nAll site permissions for this container will be removed.\nThis action cannot be reverted.\nDo you want to continue?" + }, + "container_clear_warning": { + "message": "All site permissions for this container will be removed.\nThis action cannot be reverted.\nDo you want to continue?" + }, "LearnMoreLink": { "message": "Learn more…" }, diff --git a/src/bg/LifeCycle.js b/src/bg/LifeCycle.js index 1ce2ac07..3887d804 100644 --- a/src/bg/LifeCycle.js +++ b/src/bg/LifeCycle.js @@ -70,6 +70,7 @@ var LifeCycle = (() => { )); const policy = ns.policy.dry(true); + const contextStore = ns.contextStore.dry(true); const unrestrictedTabs = [...ns.unrestrictedTabs]; if (policy.sites.temp.length == 0 && @@ -118,6 +119,7 @@ var LifeCycle = (() => { let {url} = tab; let {cypherText, key, iv} = await encrypt(JSON.stringify({ policy, + contextStore, allSeen, unrestrictedTabs, })); @@ -225,7 +227,7 @@ var LifeCycle = (() => { iv }, key, cypherText ); - let {policy, allSeen, unrestrictedTabs} = JSON.parse(new TextDecoder().decode(encoded)); + let {policy, contextStore, allSeen, unrestrictedTabs} = JSON.parse(new TextDecoder().decode(encoded)); if (!policy) { throw new error("Ephemeral policy not found in survival tab %s!", tabId); } @@ -233,6 +235,7 @@ var LifeCycle = (() => { destroyIfNeeded(); if (ns.initializing) await ns.initializing; ns.policy = new Policy(policy); + ns.contextStore = new ContextStore(contextStore); await Promise.allSettled( Object.entries(allSeen).map( async ([tabId, seen]) => { @@ -336,6 +339,17 @@ var LifeCycle = (() => { if (changed) { await ns.savePolicy(); } + if (ns.contextStore) { + changed = false; + for (let k of Object.keys(ns.contextStore.policies)){ + for (let p of ns.contextStore.policies[k].getPresets(presetNames)) { + if (callback(p)) changed = true; + } + } + if (changed) { + await ns.saveContextStore(); + } + } }; const configureNewCap = async (cap, presetNames, capsFilter) => { diff --git a/src/bg/RequestGuard.js b/src/bg/RequestGuard.js index e4c9a0d5..02dc8d51 100644 --- a/src/bg/RequestGuard.js +++ b/src/bg/RequestGuard.js @@ -381,17 +381,19 @@ const wantsContext = checked.includes("ctx"); - let { siteMatch, contextMatch, perms } = ns.policy.get(key, contextUrl); + let cookieStoreId = sender.tab && sender.tab.cookieStoreId; + let policy = ns.getPolicy(cookieStoreId); + let { contextMatch, perms } = policy.get(key, contextUrl); if (!perms.capabilities.has(policyType) || !contextMatch && wantsContext && ctxKey) { const wantsTemp = forcedTemp || checked.includes("temp"); if (!contextMatch) { - const isDefault = perms === ns.policy.DEFAULT; + const isDefault = perms === policy.DEFAULT; perms = perms.clone(); if (isDefault) perms.temp = wantsTemp; - ns.policy.set(key, perms); + policy.set(key, perms); if (ctxKey && wantsContext) { perms.contextual.set(ctxKey, perms = perms.clone(/* noContext = */ true)); } @@ -399,6 +401,7 @@ perms.temp = wantsTemp; perms.capabilities.add(policyType); await ns.savePolicy(); + await ns.saveContextStore(); await RequestGuard.DNRPolicy?.update(); } return {enable: key}; @@ -645,13 +648,14 @@ function intersectCapabilities(policyMatch, request) { const {cascadePermissions, cascadeRestrictions} = ns.sync; if (request.frameId !== 0 && cascadeRestrictions || request.type != "main_frame" && cascadePermissions) { - const {tabUrl, frameAncestors} = request; + const {tabUrl, frameAncestors, cookieStoreId} = request; const topUrl = tabUrl || cascadePermissions && request.frameId == 0 && request.documentUrl || frameAncestors && frameAncestors[frameAncestors?.length - 1]?.url || TabCache.get(request.tabId)?.url; if (topUrl) { - return ns.policy.cascade(policyMatch, topUrl, { + const policy = ns.getPolicy(cookieStoreId); + return policy.cascade(policyMatch, topUrl, { permissions: cascadePermissions, restrictions: cascadeRestrictions, }).capabilities; @@ -719,9 +723,10 @@ function checkLANRequest(request) { if (!ns.isEnforced(request.tabId)) return ALLOW; - let {originUrl, url} = request; + let {originUrl, url, cookieStoreId} = request; + let policy = ns.getPolicy(cookieStoreId); if (originUrl && !Sites.isInternal(originUrl) && url.startsWith("http") && - !ns.policy.can(originUrl, "lan", ns.policyContext(request))) { + !policy.can(originUrl, "lan", ns.policyContext(request))) { // we want to block any request whose origin resolves to at least one external WAN IP // and whose destination resolves to at least one LAN IP const {proxyInfo} = request; // see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo @@ -756,9 +761,9 @@ normalizeRequest(request); - let {tabId, type, url, originUrl} = request; + let {tabId, type, cookieStoreId, url, originUrl} = request; - const { policy } = ns; + const policy = ns.getPolicy(cookieStoreId); let previous = recent.find(request); if (previous) { @@ -917,12 +922,12 @@ let result = ALLOW; pending.headersProcessed = true; - let {url, tabId, responseHeaders, type} = request; + let {url, tabId, cookieStoreId, responseHeaders, type} = request; let isMainFrame = type === "main_frame"; try { let capabilities; if (ns.isEnforced(tabId)) { - const { policy } = ns; + const policy = ns.getPolicy(cookieStoreId); const policyMatch = policy.get(url, ns.policyContext(request)); let { perms } = policyMatch; if (isMainFrame) { @@ -1013,13 +1018,14 @@ async function injectPolicyScript(details) { await ns.initializing; if (ns.local.debug?.disablePolicyInjection) return ''; // DEV_ONLY - const {url, tabId, frameId, type} = details; + const {url, tabId, frameId, cookieStoreId, type} = details; const isTop = type == "main_frame"; const domPolicy = await ns.computeChildPolicy( { url }, { tab: { id: tabId, url: isTop ? url : null }, frameId: isTop ? 0 : frameId, + cookieStoreId, } ); domPolicy.navigationURL = url; diff --git a/src/bg/Settings.js b/src/bg/Settings.js index bccf7f01..c7d358c0 100644 --- a/src/bg/Settings.js +++ b/src/bg/Settings.js @@ -100,6 +100,7 @@ var Settings = { async update(settings) { let { policy, + contextStore, xssUserChoices, tabId, unrestrictedTab, @@ -179,6 +180,7 @@ var Settings = { // User is resetting options: // pick either current Tor Browser Security Level or default NoScript policy policy = ns.local.torBrowserPolicy || this.createDefaultDryPolicy(); + contextStore = new ContextStore().dry(); reloadOptionsUI = true; } @@ -198,6 +200,18 @@ var Settings = { await ns.savePolicy(); } + if (contextStore) { + ns.contextStore = new ContextStore(contextStore); + } + + if (policy && ns.contextStore) { + ns.contextStore.updatePresets(ns.policy); + } + + if (contextStore || (policy && ns.contextStore)) { + await ns.saveContextStore(); + } + if (typeof unrestrictedTab === "boolean") { await ns.toggleTabRestrictions(tabId, !unrestrictedTab); } @@ -245,6 +259,7 @@ var Settings = { knownCapabilities: Permissions.ALL, }, policy: ns.policy.dry(), + contextStore: ns.contextStore.dry(), local: ns.local, sync: ns.sync, xssUserChoices: XSS.getUserChoices(), diff --git a/src/bg/TabGuard.js b/src/bg/TabGuard.js index cf8f8dc6..8c514e03 100644 --- a/src/bg/TabGuard.js +++ b/src/bg/TabGuard.js @@ -184,8 +184,10 @@ var TabGuard = (() => { // we suspect tabs which 1) have not been removed/discarded, 2) are restricted by policy, 3) can run JavaScript let suspiciousTabs = [...ties].map(TabCache.get).filter( - tab => tab && !tab.discarded && ns.isEnforced(tab.id) && - (!(tab._isExplicitOrigin = tab._isExplicitOrigin || /^(?:https?|ftps?|file):/.test(tab.url)) || ns.policy.can(tab.url, "script")) + tab => tab && !tab.discarded && ns.isEnforced(tab.id) && ( + !(tab._isExplicitOrigin = tab._isExplicitOrigin || /^(?:https?|ftps?|file):/.test(tab.url)) || + ns.getPolicy(tab.cookieStoreId).can(tab.url, "script") + ) ); return suspiciousTabs.length > 0 && (async () => { @@ -222,7 +224,7 @@ var TabGuard = (() => { } if (tab.url !== "about:blank") { debug(`Real origin for ${tab._externalUrl} (tab ${tab.id}) is ${tab.url}.`); - if (!ns.policy.can(tab.url, "script")) return; + if (!ns.getPolicy(tab.cookieStoreId).can(tab.url, "script")) return; } } if (!tab._contentType) { diff --git a/src/bg/main.js b/src/bg/main.js index 003f5b80..7dbd70d0 100644 --- a/src/bg/main.js +++ b/src/bg/main.js @@ -105,6 +105,18 @@ } } + if (!ns.contextStore) { // it could have been already retrieved by LifeCycle + const contextStoreData = (await Storage.get("sync", "contextStore")).contextStore; + if (contextStoreData) { + ns.contextStore = new ContextStore(contextStoreData); + await ns.contextStore.updateContainers(ns.policy); + } else { + log("No container data found. Initializing new policies.") + ns.contextStore = new ContextStore(); + await ns.contextStore.updateContainers(ns.policy); + await ns.saveContextStore(); + } + } const {isTorBrowser} = ns.local; Sites.onionSecure = isTorBrowser; @@ -177,10 +189,12 @@ tabId = -1 }) { const policy = ns.policy.dry(true); + const contextStore = ns.contextStore.dry(true); const seen = tabId !== -1 ? await ns.collectSeen(tabId) : null; const xssUserChoices = await XSS.getUserChoices(); await Messages.send("settings", { policy, + contextStore, seen, xssUserChoices, local: ns.local, @@ -281,6 +295,7 @@ } let _policy = null; + let _contextStore = null; globalThis.ns = { running: false, @@ -289,6 +304,11 @@ RequestGuard.DNRPolicy?.update(); }, get policy() { return _policy; }, + set contextStore(c) { + _contextStore = c; + RequestGuard.DNRPolicy?.update(); + }, + get contextStore() { return _contextStore; }, local: null, sync: null, initializing: null, @@ -319,12 +339,29 @@ return tab?.url || documentUrl || url; }, requestCan(request, capability) { - return !this.isEnforced(request.tabId) || this.policy.can(request.url, capability, this.policyContext(request)); + return ( + !this.isEnforced(request.tabId) || + ns.getPolicy(request.cookieStoreId).can(request.url, capability, this.policyContext(request)) + ); + }, + + getPolicy(cookieStoreId){ + if ( + ns.contextStore && + ns.contextStore.enabled && + ns.contextStore.policies.hasOwnProperty(cookieStoreId) + ) { + let currentPolicy = ns.contextStore.policies[cookieStoreId]; + debug("id", cookieStoreId, "has cookiestore", currentPolicy); + if (currentPolicy) return currentPolicy; + } + debug("default cookiestore", cookieStoreId); + return ns.policy; }, async computeChildPolicy({url, contextUrl}, sender) { await ns.initializing; - let { tab, origin, frameId, documentLifecycle } = sender; + let { tab, origin, frameId, cookieStoreId, documentLifecycle } = sender; const tabId = tab ? tab.id : -1; if (url == sender.url) { @@ -341,7 +378,8 @@ } } } - let policy = ns.policy; + if (!cookieStoreId && tab) cookieStoreId = tab.cookieStoreId; + let policy = ns.getPolicy(cookieStoreId); const {isTorBrowser} = ns.local; if (!policy) { console.log("Policy is null, initializing: %o, sending fallback.", ns.initializing); @@ -449,6 +487,19 @@ return this.policy; }, + async saveContextStore() { + if (this.contextStore) { + await Promise.allSettled([ + Storage.set("sync", { + contextStore: this.contextStore.dry() + }), + session.save(), + browser.webRequest.handlerBehaviorChanged() + ]); + } + return this.contextStore; + }, + openOptionsPage({tab, focus, hilite}) { const url = new URL(browser.runtime.getManifest().options_ui.page); if (tab !== undefined) { @@ -510,5 +561,7 @@ if (!browser.windows) { log("All tabs closed: revoking temporary permissions."); ns.policy.revokeTemp(); ns.savePolicy(); + ns.contextStore.revokeTemp(); + ns.saveContextStore(); }); } diff --git a/src/manifest.json b/src/manifest.json index 964d921d..69c3aa25 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -36,7 +36,8 @@ "webRequestFilterResponse", "webRequestFilterResponse.serviceWorkerScript", "dns", - "" + "", + "contextualIdentities" ], "host_permissions": [ "" @@ -62,6 +63,7 @@ "/nscl/common/Sites.js", "/nscl/common/Permissions.js", "/nscl/common/Policy.js", + "/nscl/common/ContextStore.js", "/nscl/common/locale.js", "/nscl/common/Storage.js", "/nscl/common/include.js", diff --git a/src/nscl b/src/nscl index 9c9b0273..c9baa6c7 160000 --- a/src/nscl +++ b/src/nscl @@ -1 +1 @@ -Subproject commit 9c9b02739678b7aab8d0c87f62ab2cc08235779c +Subproject commit c9baa6c73b19e598f37343c212cc994e683710a4 diff --git a/src/ui/options.css b/src/ui/options.css index d21861c7..9bbd2b0f 100644 --- a/src/ui/options.css +++ b/src/ui/options.css @@ -114,6 +114,24 @@ fieldset:disabled { flex: 2 2; } +.per-site-buttons { + display: flex; + flex-flow: row wrap; + justify-content: flex-end; + width: 100%; + text-align: right; + margin: .5em 0 0 0; +} +#btn-clear-container { + margin-inline-start: .5em; +} +#copy-container { + margin-inline: .5em; +} +#copy-container-label { + margin-block: auto; +} + #policy { display: block; margin-top: .5em; @@ -123,6 +141,12 @@ fieldset:disabled { .hide, body:not(.debug) div.debug { display: none; } +#context-store { + display: block; + margin-top: .5em; + min-height: 20em; + width: 90%; +} #debug-tools { padding-left: 2.5em; @@ -142,6 +166,14 @@ fieldset:disabled { font-weight: bold; } +#context-store-error { + background: red; + color: #ff8; + padding: 0; + margin: 0; + font-weight: bold; +} + input, button { font-size: 1em; } diff --git a/src/ui/options.html b/src/ui/options.html index 7e649f07..606c3d3f 100644 --- a/src/ui/options.html +++ b/src/ui/options.html @@ -55,6 +55,9 @@

@@ -85,6 +88,10 @@

+ +
+
+ + +
+ +
@@ -226,6 +242,12 @@

diff --git a/src/ui/popup.js b/src/ui/popup.js index 39564f9e..c01b9833 100644 --- a/src/ui/popup.js +++ b/src/ui/popup.js @@ -106,6 +106,7 @@ addEventListener("unload", e => { } else { tabId = tab.id; } + let cookieStoreId = pageTab.cookieStoreId; if (!browser.windows) { // emulate popup over page by using blurred content as background @@ -186,6 +187,19 @@ addEventListener("unload", e => { }); } + if (UI.contextStore && UI.contextStore.enabled && browser.contextualIdentities) { + try { + let containerName = (await browser.contextualIdentities.get(cookieStoreId)).name; + document.querySelector("#container-id").textContent = containerName; + debug("found container name", containerName, "for cookieStoreId", cookieStoreId); + } catch(err) { + document.querySelector("#container-id").textContent = _("DefaultContainerName"); + debug("no container for cookieStoreId", cookieStoreId, "error:", err.message); + } + } else { + document.querySelector("#container-id").style.visibility = 'hidden'; + } + await include("/ui/toolbar.js"); UI.toolbarInit(); { @@ -386,7 +400,9 @@ addEventListener("unload", e => { let justDomains = !UI.local.showFullAddresses; - sitesUI = new UI.Sites(document.getElementById("sites")); + let policy = await UI.getPolicy(cookieStoreId); + debug("popup policy", policy); + sitesUI = new UI.Sites(document.getElementById("sites"), UI.DEF_PRESETS, policy); sitesUI.onChange = (row) => { const reload = sitesUI.anyPermissionsChanged() @@ -423,7 +439,7 @@ addEventListener("unload", e => { typesMap } = sitesUI; typesMap.clear(); - let policySites = UI.policy.sites; + let policySites = policy.sites; let domains = new Map(); let protocols = new Set(); diff --git a/src/ui/ui.js b/src/ui/ui.js index 8118f35b..cf63bc4f 100644 --- a/src/ui/ui.js +++ b/src/ui/ui.js @@ -58,6 +58,7 @@ var UI = (() => { "/nscl/common/Sites.js", "/nscl/common/Permissions.js", "/nscl/common/Policy.js", + "/nscl/common/ContextStore.js" ]; this.mobile = UA.mobile; @@ -69,7 +70,8 @@ var UI = (() => { async settings(m) { if (UI.tabId !== m.tabId) return; UI.policy = new Policy(m.policy); - UI.snapshot = UI.policy.snapshot; + UI.contextStore = new ContextStore(m.contextStore); + UI.snapshot = UI.policy.snapshot+UI.contextStore.snapshot; UI.seen = m.seen; UI.tabLess = m.tabLess; UI.unrestrictedTab = m.unrestrictedTab; @@ -115,7 +117,7 @@ var UI = (() => { window.reload(); } this.initialized = true; - debug("Imported", Policy); + debug("Imported", Policy, ContextStore); }, async pullSettings() { try { @@ -125,10 +127,12 @@ var UI = (() => { browser.runtime.reload(); } }, - async updateSettings({policy, xssUserChoices, unrestrictedTab, local, sync, reloadAffected, command}) { + async updateSettings({policy, contextStore, xssUserChoices, unrestrictedTab, local, sync, reloadAffected, command}) { if (policy) policy = policy.dry(true); + if (contextStore) contextStore = contextStore.dry(true); return await Messages.send("updateSettings", { policy, + contextStore, xssUserChoices, unrestrictedTab, local, @@ -148,13 +152,41 @@ var UI = (() => { async revokeTemp(reloadAffected = false) { this.policy.revokeTemp(); + this.contextStore.revokeTemp(); if (this.isDirty(true)) { - await this.updateSettings({policy: this.policy, reloadAffected}); + await this.updateSettings({policy: this.policy, contextStore: this.contextStore, reloadAffected}); + await this.updateSettings({policy, contextStore, reloadAffected}); + } + }, + + async getPolicy(cookieStoreId) { + await this.contextStore.updateContainers(this.policy); + if (this.contextStore.enabled && this.contextStore.policies.hasOwnProperty(cookieStoreId)) { + let currentPolicy = this.contextStore.policies[cookieStoreId]; + debug("id", cookieStoreId, "has cookiestore", currentPolicy); + return currentPolicy; + } else { + debug("default cookiestore", cookieStoreId); + return this.policy; + } + }, + + async replacePolicy(cookieStoreId, policy) { + await this.contextStore.updateContainers(this.policy); + if (this.contextStore.policies.hasOwnProperty(cookieStoreId)) { + this.contextStore.policies[cookieStoreId] = policy; + debug("replaced id", cookieStoreId, "with policy", policy); + let currentPolicy = this.contextStore.policies[cookieStoreId]; + return currentPolicy; + } else { + this.policy = policy; + debug("replaced default cookiestore", cookieStoreId, "with policy", policy); + return this.policy; } }, isDirty(reset = false) { - let currentSnapshot = this.policy.snapshot; + let currentSnapshot = this.policy.snapshot+this.contextStore.snapshot; let dirty = currentSnapshot != this.snapshot; if (reset) this.snapshot = currentSnapshot; return dirty; @@ -340,7 +372,7 @@ var UI = (() => { function fireOnChange(sitesUI, data) { if (UI.isDirty(true)) { - UI.updateSettings({policy: UI.policy}); + UI.updateSettings({policy: UI.policy, contextStore: UI.contextStore}); if (sitesUI.onChange) sitesUI.onChange(data, this); } } @@ -419,10 +451,11 @@ var UI = (() => { const EXTRA_CAPS = ["x-load"]; UI.Sites = class { - constructor(parentNode, presets = DEF_PRESETS) { + constructor(parentNode, presets = DEF_PRESETS, policy = null) { this.parentNode = parentNode; + this.policy = (policy)? policy : UI.policy; this.uiCount = UI.Sites.count = (UI.Sites.count || 0) + 1; - this.sites = UI.policy.sites; + this.sites = this.policy.sites; this.presets = presets; this.customizing = null; this.typesMap = new Map(); @@ -571,6 +604,11 @@ var UI = (() => { this.customize(null); this.sitesCount = 0; + this.sites = ({ + trusted: [], + untrusted: [], + custom: {}, + }) } siteNeeds(site, type) { @@ -617,7 +655,6 @@ var UI = (() => { let tempToggle = preset.parentNode.querySelector("input.temp"); if (ev.type === "change") { - let { policy } = UI; if (!row._originalPerms) { row._originalPerms = row.perms.clone(); Object.defineProperty(row, "permissionsChanged", { @@ -631,7 +668,7 @@ var UI = (() => { if (!opt) return; let context = opt.value; if (context === "*") context = null; - ({ siteMatch, perms, contextMatch } = policy.get(siteMatch, context)); + ({ siteMatch, perms, contextMatch } = this.policy.get(siteMatch, context)); if (!context) { row._customPerms = perms; } else if (contextMatch !== context) { @@ -650,8 +687,8 @@ var UI = (() => { let presetValue = preset.value; let policyPreset = presetValue.startsWith("T_") - ? policy[presetValue.substring(2)].tempTwin - : policy[presetValue]; + ? this.policy[presetValue.substring(2)].tempTwin + : this.policy[presetValue]; if (policyPreset && row.perms !== policyPreset) { row.perms = policyPreset; @@ -669,7 +706,7 @@ var UI = (() => { row.perms = policyPreset; delete row._customPerms; if (siteMatch) { - policy.set(siteMatch, policyPreset); + this.policy.set(siteMatch, policyPreset); } else { this.customize(policyPreset, preset, row); } @@ -686,7 +723,7 @@ var UI = (() => { temp )); row.perms = perms; - policy.set(siteMatch, perms); + this.policy.set(siteMatch, perms); this.customize(perms, preset, row); } } @@ -799,7 +836,7 @@ var UI = (() => { let selected = ctxSelect.querySelector("option:checked"); ctxReset.disabled = !(selected?.value !== "*"); ctxReset.onclick = () => { - let perms = UI.policy.get(row.siteMatch).perms; + let perms = this.policy.get(row.siteMatch).perms; perms.contextual.delete(row.contextMatch); fireOnChange(this, row); selected.previousElementSibling.selected = true; @@ -1032,7 +1069,7 @@ var UI = (() => { site = site.site; context = site.context; } - let { siteMatch, perms, contextMatch } = UI.policy.get(site, context); + let { siteMatch, perms, contextMatch } = this.policy.get(site, context); this.append(site, siteMatch, perms, contextMatch); if (!hasTemp) hasTemp = perms.temp; } @@ -1084,22 +1121,25 @@ var UI = (() => { } async tempTrustAll() { - let { policy } = UI; let changed = 0; for (let row of this.allSiteRows()) { if (row._preset === "DEFAULT") { - policy.set(row._site, policy.TRUSTED.tempTwin); + this.policy.set(row._site, this.policy.TRUSTED.tempTwin); changed++; } } if (changed && UI.isDirty(true)) { - await UI.updateSettings({ policy, reloadAffected: true }); + await UI.updateSettings({ + policy: UI.policy, + contextStore: UI.contextStore, + reloadAffected: true + }); } return changed; } _customOrAuto(row) { - const { policy } = UI; + const policy = this.policy; const { perms, contextMatch, siteMatch } = row; const isAuto = policy.autoAllowTop && perms.temp && contextMatch == siteMatch && @@ -1132,7 +1172,6 @@ var UI = (() => { contextMatch, perms ); - const { policy } = UI; let row = this.rowTemplate.cloneNode(true); row.sitesCount = sitesCount; @@ -1140,7 +1179,7 @@ var UI = (() => { try { url = new URL(site); if (siteMatch !== site && siteMatch === url.protocol) { - perms = policy.DEFAULT; + perms = this.policy.DEFAULT; } } catch (e) { if (/^(\w+:)\/*$/.test(site)) { @@ -1163,7 +1202,7 @@ var UI = (() => { let { hostname } = url; let overrideDefault = site && url.protocol && site !== url.protocol - ? policy.get(url.protocol, contextMatch) + ? this.policy.get(url.protocol, contextMatch) : null; if (!overrideDefault?.siteMatch) overrideDefault = null; @@ -1243,7 +1282,7 @@ var UI = (() => { let getPresetName = (perms) => { let presetName = "CUSTOM"; for (let p of ["TRUSTED", "UNTRUSTED", "DEFAULT"]) { - let preset = policy[p]; + let preset = this.policy[p]; switch (perms) { case preset: presetName = p; @@ -1296,8 +1335,8 @@ var UI = (() => { row .querySelector(`.presets input[value="${p}"]`) .parentNode.querySelector("input.temp").checked = true; - if (perms == policy.TRUSTED) { - perms = policy.TRUSTED.tempTwin; + if (perms == this.policy.TRUSTED) { + perms = this.policy.TRUSTED.tempTwin; } } } @@ -1324,7 +1363,7 @@ var UI = (() => { w.title = title; w.textContent &&= label; } - row._customPerms = perms = UI.policy.cascade(policy.DEFAULT, this.mainUrl, {permissions: true}); + row._customPerms = perms = this.policy.cascade(this.policy.DEFAULT, this.mainUrl, {permissions: true}); } preset.classList.toggle("canScript", perms.capabilities.has("script")); } @@ -1348,9 +1387,8 @@ var UI = (() => { if (site !== row.siteMatch) { this.customize(null); let focused = document.activeElement; - let { policy } = UI; - policy.set(row.siteMatch, policy.DEFAULT); - policy.set(site, row.perms); + this.policy.set(row.siteMatch, this.policy.DEFAULT); + this.policy.set(site, row.perms); for (let r of this.allSiteRows()) { if ( r !== row &&