diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd index 65d1504bbb50..022ac95b7983 100644 --- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd +++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd @@ -39,6 +39,7 @@ to the add-on name for extensions that are not webextensions, which will stop working in Firefox 57. --> + diff --git a/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl b/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl index 148d0ede2dce..c48082f3289e 100644 --- a/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl +++ b/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl @@ -126,6 +126,20 @@ detail-update-manual = .label = Off .tooltiptext = Don’t automatically install updates +# Used as a description for the option to allow or block an add-on in private windows. +detail-private-browsing = + .value = Run in Private Windows + +detail-private-browsing-description = Extension will work in Private Windows, and have access to your online activities. + +detail-private-browsing-on = + .label = Allow + .tooltiptext = Enable in Private Browsing + +detail-private-browsing-off = + .label = Don’t Allow + .tooltiptext = Disable in Private Browsing + detail-home = .label = Homepage diff --git a/toolkit/mozapps/extensions/content/extensions.css b/toolkit/mozapps/extensions/content/extensions.css index 1af940ce874c..ba49d4e46876 100644 --- a/toolkit/mozapps/extensions/content/extensions.css +++ b/toolkit/mozapps/extensions/content/extensions.css @@ -107,6 +107,21 @@ row[unsupported="true"] { display: none; } +.addon .privateBrowsing-notice { + display: none; +} +.addon[privateBrowsing="true"] .privateBrowsing-notice-container { + /* 40px is width and margin of .icon-container */ + margin-inline-start: 40px; +} +.addon[privateBrowsing="true"] .privateBrowsing-notice { + margin: 4px 0 0; + display: inline-block; +} +.addon[active="false"] .privateBrowsing-notice { + background-color: var(--purple-70-a40); +} + #addons-page:not([warning]) #list-view > .global-warning-container { display: none; } diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index fd1785051880..72282d3d9ee7 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -20,6 +20,8 @@ ChromeUtils.defineModuleGetter(this, "Extension", "resource://gre/modules/Extension.jsm"); ChromeUtils.defineModuleGetter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm"); +ChromeUtils.defineModuleGetter(this, "ExtensionPermissions", + "resource://gre/modules/ExtensionPermissions.jsm"); ChromeUtils.defineModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm"); ChromeUtils.defineModuleGetter(this, "Preferences", @@ -34,6 +36,9 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS", XPCOMUtils.defineLazyPreferenceGetter(this, "XPINSTALL_ENABLED", "xpinstall.enabled", true); +XPCOMUtils.defineLazyPreferenceGetter(this, "allowPrivateBrowsingByDefault", + "extensions.allowPrivateBrowsingByDefault", true); + XPCOMUtils.defineLazyPreferenceGetter(this, "SUPPORT_URL", "app.support.baseURL", "", null, val => Services.urlFormatter.formatURL(val)); @@ -2634,13 +2639,13 @@ var gListView = { }, }; - var gDetailView = { node: null, _addon: null, _loadingTimer: null, _autoUpdate: null, isRoot: false, + restartingAddon: false, initialize() { this.node = document.getElementById("detail-view"); @@ -2651,6 +2656,36 @@ var gDetailView = { this._autoUpdate.addEventListener("command", () => { this._addon.applyBackgroundUpdates = this._autoUpdate.value; }, true); + + document.getElementById("detail-private-browsing-learnmore-link") + .setAttribute("href", SUPPORT_URL + "extensions-pb"); + + this._privateBrowsing = document.getElementById("detail-privateBrowsing"); + this._privateBrowsing.addEventListener("command", async () => { + let addon = this._addon; + let policy = WebExtensionPolicy.getByID(addon.id); + let extension = policy && policy.extension; + + let perms = {permissions: ["internal:privateBrowsingAllowed"], origins: []}; + if (this._privateBrowsing.value == "1") { + await ExtensionPermissions.add(addon.id, perms, extension); + } else { + await ExtensionPermissions.remove(addon.id, perms, extension); + } + + // Reload the extension if it is already enabled. This ensures any change + // on the private browsing permission is properly handled. + if (addon.isActive) { + try { + this.restartingAddon = true; + await addon.reload(); + } finally { + this.restartingAddon = false; + this.updateState(); + this._updateView(addon, false); + } + } + }, true); }, shutdown() { @@ -2661,7 +2696,12 @@ var gDetailView = { this.onPropertyChanged(["applyBackgroundUpdates"]); }, - _updateView(aAddon, aIsRemote, aScrollToPreferences) { + async _updateView(aAddon, aIsRemote, aScrollToPreferences) { + // Skip updates to avoid flickering while restarting the addon. + if (this.restartingAddon) { + return; + } + setSearchLabel(aAddon.type); // Set the preview image for themes, if available. @@ -2791,6 +2831,25 @@ var gDetailView = { document.getElementById("detail-findUpdates-btn").hidden = false; } + // Only type = "extension" will ever get privateBrowsingAllowed, other types have + // no code that would be affected by the setting. The permission is read directly + // from ExtensionPermissions so we can get it whether or not the extension is + // currently active. + let privateBrowsingRow = document.getElementById("detail-privateBrowsing-row"); + let privateBrowsingFooterRow = document.getElementById("detail-privateBrowsing-row-footer"); + if (allowPrivateBrowsingByDefault || aAddon.type != "extension" || + aAddon.incognito == "not_allowed") { + this._privateBrowsing.hidden = true; + privateBrowsingRow.hidden = true; + privateBrowsingFooterRow.hidden = true; + } else { + let perms = await ExtensionPermissions.get(aAddon.id); + this._privateBrowsing.hidden = false; + privateBrowsingRow.hidden = false; + privateBrowsingFooterRow.hidden = false; + this._privateBrowsing.value = perms.permissions.includes("internal:privateBrowsingAllowed") ? "1" : "0"; + } + document.getElementById("detail-prefs-btn").hidden = !aIsRemote && !gViewController.commands.cmd_showItemPreferences.isEnabled(aAddon); @@ -2869,6 +2928,11 @@ var gDetailView = { }, updateState() { + // Skip updates to avoid flickering while restarting the addon. + if (this.restartingAddon) { + return; + } + gViewController.updateCommands(); var pending = this._addon.pendingOperations; diff --git a/toolkit/mozapps/extensions/content/extensions.xml b/toolkit/mozapps/extensions/content/extensions.xml index 94b9ad675c5e..6fabe5171d99 100644 --- a/toolkit/mozapps/extensions/content/extensions.xml +++ b/toolkit/mozapps/extensions/content/extensions.xml @@ -686,6 +686,9 @@ + + + @@ -902,6 +905,13 @@ this.setAttribute("legacy", legacyWarning); document.getAnonymousElementByAttribute(this, "anonid", "legacy").href = SUPPORT_URL + "webextensions"; + if (!allowPrivateBrowsingByDefault) { + ExtensionPermissions.get(this.mAddon.id).then((perms) => { + let allowed = perms.permissions.includes("internal:privateBrowsingAllowed"); + this.setAttribute("privateBrowsing", allowed); + }); + } + if (!("applyBackgroundUpdates" in this.mAddon) || (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE || (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DEFAULT && diff --git a/toolkit/mozapps/extensions/content/extensions.xul b/toolkit/mozapps/extensions/content/extensions.xul index d1aef5e1274a..8943ebe4a8b1 100644 --- a/toolkit/mozapps/extensions/content/extensions.xul +++ b/toolkit/mozapps/extensions/content/extensions.xul @@ -536,6 +536,22 @@ + + + + + +