Bug 1511636 toggle private browsing via about:addons r=rpl,mstriemer,flod,kmag

Differential Revision: https://phabricator.services.mozilla.com/D13621

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Shane Caraveo 2019-01-28 18:12:07 +00:00
Родитель 050ca2e7d4
Коммит db03082008
14 изменённых файлов: 327 добавлений и 8 удалений

Просмотреть файл

@ -39,6 +39,7 @@
to the add-on name for extensions that are not webextensions, which
will stop working in Firefox 57. -->
<!ENTITY addon.legacy.label "LEGACY">
<!ENTITY addon.privateBrowsing.label "ALLOWED IN PRIVATE WINDOWS">
<!-- LOCALIZATION NOTE (addon.disabled.postfix): This is used in a normal list
to signify that an add-on is disabled, in the form
"<Addon name> <1.0> (disabled)" -->

Просмотреть файл

@ -126,6 +126,20 @@ detail-update-manual =
.label = Off
.tooltiptext = Dont 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. <label data-l10n-name="detail-private-browsing-learn-more">Learn more</label>
detail-private-browsing-on =
.label = Allow
.tooltiptext = Enable in Private Browsing
detail-private-browsing-off =
.label = Dont Allow
.tooltiptext = Disable in Private Browsing
detail-home =
.label = Homepage

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;

Просмотреть файл

@ -686,6 +686,9 @@
</xul:hbox>
</xul:vbox>
</xul:hbox>
<xul:hbox class="description-container privateBrowsing-notice-container">
<xul:label anonid="privateBrowsing" class="description privateBrowsing-notice" value="&addon.privateBrowsing.label;"/>
</xul:hbox>
</content>
<implementation>
@ -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 &&

Просмотреть файл

@ -536,6 +536,22 @@
<column flex="2"/>
</columns>
<rows id="detail-rows">
<row class="detail-row-complex" id="detail-privateBrowsing-row">
<label class="detail-row-label" data-l10n-id="detail-private-browsing"/>
<hbox align="center">
<radiogroup id="detail-privateBrowsing" orient="horizontal">
<radio data-l10n-id="detail-private-browsing-on"
value="1"/>
<radio data-l10n-id="detail-private-browsing-off"
value="0"/>
</radiogroup>
</hbox>
</row>
<hbox class="detail-row-footer" id="detail-privateBrowsing-row-footer">
<description class="indent preferences-description" data-l10n-id="detail-private-browsing-description">
<label id="detail-private-browsing-learnmore-link" class="learnMore text-link" data-l10n-name="detail-private-browsing-learn-more"/>
</description>
</hbox>
<row class="detail-row-complex" id="detail-updates-row">
<label class="detail-row-label" data-l10n-id="detail-update-type"/>
<hbox align="center">

Просмотреть файл

@ -108,7 +108,7 @@ const PROP_JSON_FIELDS = ["id", "syncGUID", "version", "type",
"softDisabled", "foreignInstall",
"strictCompatibility", "locales", "targetApplications",
"targetPlatforms", "signedState",
"seen", "dependencies",
"seen", "dependencies", "incognito",
"userPermissions", "icons", "iconURL",
"blocklistState", "blocklistURL", "startupData",
"previewImage", "hidden", "installTelemetryInfo"];
@ -732,6 +732,10 @@ AddonWrapper = class {
return addon.optionsBrowserStyle;
}
get incognito() {
return addonFor(this).incognito;
}
async getBlocklistURL() {
return addonFor(this).blocklistURL;
}

Просмотреть файл

@ -434,6 +434,7 @@ async function loadManifestFromWebManifest(aUri, aPackage) {
addon.dependencies = Object.freeze(Array.from(extension.dependencies));
addon.startupData = extension.startupData;
addon.hidden = manifest.hidden;
addon.incognito = manifest.incognito;
if (addon.type === "theme" && await aPackage.hasResource("preview.png")) {
addon.previewImage = "preview.png";

Просмотреть файл

@ -104,7 +104,7 @@ const XPI_PERMISSION = "install";
const XPI_SIGNATURE_CHECK_PERIOD = 24 * 60 * 60;
const DB_SCHEMA = 28;
const DB_SCHEMA = 29;
function encoded(strings, ...values) {
let result = [];

Просмотреть файл

@ -107,6 +107,7 @@ skip-if = verify
[browser_webapi_theme.js]
[browser_webapi_uninstall.js]
[browser_webext_icon.js]
[browser_webext_incognito.js]
[browser_webext_options.js]
tags = webextensions
skip-if = os == 'linux' || (os == 'mac' && debug) # bug 1483347

Просмотреть файл

@ -75,6 +75,18 @@ async function test() {
contributionAmount: null,
updateDate: gDate,
permissions: 0,
}, {
id: "addon-theme@tests.mozilla.org",
name: "Test add-on theme",
version: "2.3",
description: "Short description",
creator: { name: "Mozilla", url: null },
type: "theme",
iconURL: "chrome://foo/skin/icon.png",
contributionURL: "http://foo.com",
contributionAmount: null,
updateDate: gDate,
permissions: 0,
}, {
id: "addon3@tests.mozilla.org",
name: "Test add-on 3",
@ -143,8 +155,9 @@ async function end_test() {
}
// Opens and tests the details view for add-on 2
add_test(function() {
open_details("addon2@tests.mozilla.org", "extension", function() {
add_test(async function() {
await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
open_details("addon2@tests.mozilla.org", "extension", async () => {
is(get("detail-name").textContent, "Test add-on 2", "Name should be correct");
is_element_visible(get("detail-version"), "Version should not be hidden");
is(get("detail-version").value, "2.2", "Version should be correct");
@ -163,6 +176,10 @@ add_test(function() {
is_element_visible(get("detail-dateUpdated"), "Update date should not be hidden");
is(get("detail-dateUpdated").value, formatDate(gDate), "Update date should be correct");
is_element_visible(get("detail-privateBrowsing-row"), "Private browsing should not be hidden");
is_element_visible(get("detail-privateBrowsing-row-footer"), "Private browsing footer should not be hidden");
is(get("detail-privateBrowsing").value, "0", "Private browsing should be off");
is_element_hidden(get("detail-rating-row"), "Rating should be hidden");
is_element_hidden(get("detail-homepage-row"), "Homepage should not be visible");
@ -181,6 +198,28 @@ add_test(function() {
is_element_hidden(get("detail-error-link"), "Error link should be hidden");
is_element_hidden(get("detail-pending"), "Pending message should be hidden");
await SpecialPowers.popPrefEnv();
run_next_test();
});
});
// Opens and tests the details view for add-on theme
add_test(async function() {
// This is a duplicate of addon-2, so we're only testing that private browsing is
// not visible.
await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
open_details("addon-theme@tests.mozilla.org", "theme", async () => {
is(get("detail-name").textContent, "Test add-on theme", "Name should be correct");
is_element_visible(get("detail-version"), "Version should not be hidden");
is(get("detail-version").value, "2.3", "Version should be correct");
is(get("detail-icon").src, "chrome://foo/skin/icon.png", "Icon should be correct");
is_element_hidden(get("detail-privateBrowsing-row"), "Private browsing should be hidden");
is_element_hidden(get("detail-privateBrowsing-row-footer"), "Private browsing footer should be hidden");
is(get("detail-privateBrowsing").value, "0", "Private browsing should be off");
await SpecialPowers.popPrefEnv();
run_next_test();
});
});

Просмотреть файл

@ -0,0 +1,124 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const {ExtensionPermissions} = ChromeUtils.import("resource://gre/modules/ExtensionPermissions.jsm", {});
var gManagerWindow;
function get_test_items() {
var items = {};
for (let item of gManagerWindow.document.getElementById("addon-list").childNodes) {
items[item.mAddon.id] = item;
}
return items;
}
function get(aId) {
return gManagerWindow.document.getElementById(aId);
}
async function hasPrivateAllowed(id) {
let perms = await ExtensionPermissions.get(id);
return perms.permissions.length == 1 &&
perms.permissions[0] == "internal:privateBrowsingAllowed";
}
add_task(async function test_addon() {
await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
let addons = new Map([
["@test-default", {
useAddonManager: "temporary",
manifest: {
applications: {
gecko: {id: "@test-default"},
},
},
}],
["@test-override", {
useAddonManager: "temporary",
manifest: {
applications: {
gecko: {id: "@test-override"},
},
},
incognitoOverride: "spanning",
}],
["@test-override-permanent", {
useAddonManager: "permanent",
manifest: {
applications: {
gecko: {id: "@test-override-permanent"},
},
},
incognitoOverride: "spanning",
}],
["@test-not-allowed", {
useAddonManager: "temporary",
manifest: {
applications: {
gecko: {id: "@test-not-allowed"},
},
incognito: "not_allowed",
},
}],
]);
let extensions = [];
for (let definition of addons.values()) {
let extension = ExtensionTestUtils.loadExtension(definition);
extensions.push(extension);
await extension.startup();
}
gManagerWindow = await open_manager("addons://list/extension");
let doc = gManagerWindow.document;
let items = get_test_items();
for (let [id, definition] of addons.entries()) {
ok(items[id], `${id} listed`);
let badge = doc.getAnonymousElementByAttribute(items[id], "anonid", "privateBrowsing");
if (definition.incognitoOverride == "spanning") {
is_element_visible(badge, `private browsing badge is visible`);
} else {
is_element_hidden(badge, `private browsing badge is hidden`);
}
}
await close_manager(gManagerWindow);
for (let [id, definition] of addons.entries()) {
gManagerWindow = await open_manager("addons://detail/" + encodeURIComponent(id));
ok(true, `==== ${id} detail opened`);
if (definition.manifest.incognito == "not_allowed") {
is_element_hidden(get("detail-privateBrowsing-row"), "Private browsing should be hidden");
is_element_hidden(get("detail-privateBrowsing-row-footer"), "Private browsing footer should be hidden");
ok(!await hasPrivateAllowed(id), "Private browsing permission not set");
} else {
is_element_visible(get("detail-privateBrowsing-row"), "Private browsing should be visible");
is_element_visible(get("detail-privateBrowsing-row-footer"), "Private browsing footer should be visible");
let privateBrowsing = gManagerWindow.document.getElementById("detail-privateBrowsing");
if (definition.incognitoOverride == "spanning") {
is(privateBrowsing.value, "1", "Private browsing should be on");
ok(await hasPrivateAllowed(id), "Private browsing permission set");
EventUtils.synthesizeMouseAtCenter(privateBrowsing.lastChild, { clickCount: 1 }, gManagerWindow);
await TestUtils.waitForCondition(() => privateBrowsing.value == "0");
is(privateBrowsing.value, "0", "Private browsing should be off");
ok(!await hasPrivateAllowed(id), "Private browsing permission removed");
} else {
is(privateBrowsing.value, "0", "Private browsing should be off");
ok(!await hasPrivateAllowed(id), "Private browsing permission not set");
EventUtils.synthesizeMouseAtCenter(privateBrowsing.firstChild, { clickCount: 1 }, gManagerWindow);
await TestUtils.waitForCondition(() => privateBrowsing.value == "1");
is(privateBrowsing.value, "1", "Private browsing should be on");
ok(await hasPrivateAllowed(id), "Private browsing permission set");
}
}
await close_manager(gManagerWindow);
}
for (let extension of extensions) {
await extension.unload();
}
Services.prefs.clearUserPref("extensions.allowPrivateBrowsingByDefault");
});

Просмотреть файл

@ -399,6 +399,19 @@ button.warning {
-moz-box-flex: 1;
}
.privateBrowsing-notice {
background-color: var(--purple-70);
color: #fff;
margin: 4px 0 0;
padding: 4px 5px 3px;
font-size: 0.9rem;
font-weight: 600;
-moz-user-focus: ignore;
transition-property: color, background-color;
transition-timing-function: var(--animation-curve);
transition-duration: 150ms;
}
.legacy-warning {
background-color: #FFE900;
color: #3E2800;
@ -407,7 +420,7 @@ button.warning {
font-weight: 600;
-moz-user-focus: ignore;
transition-property: color, background-color;
transition-timing-function: cubic-bezier(.07,.95,0,1);
transition-timing-function: var(--animation-curve);
transition-duration: 150ms;
}
@ -793,7 +806,7 @@ button.warning {
}
.preferences-description {
font-size: 90.9%;
font-size: 1.1rem;
color: graytext;
margin-top: -2px;
margin-inline-start: 2em;
@ -804,6 +817,18 @@ button.warning {
display: none;
}
.detail-row-footer {
padding-bottom: 6px;
}
.detail-row-footer > .preferences-description {
margin-inline-start: 6px;
margin-top: 0;
margin-bottom: 0;
/* card-width - card-padding - description-margins */
width: calc(664px - 32px - 11px);
color: var(--grey-60);
}
/*** creator ***/

Просмотреть файл

@ -48,6 +48,9 @@
--in-content-table-border-dark-color: #d1d1d1;
--in-content-table-header-background: #0a84ff;
/* The photon animation curve */
--animation-curve: cubic-bezier(.07,.95,0,1);
--blue-40: #45a1ff;
--blue-40-a10: rgb(69, 161, 255, 0.1);
--blue-50: #0a84ff;
@ -64,6 +67,8 @@
--grey-90-a30: rgba(12, 12, 13, 0.3);
--grey-90-a40: rgba(12, 12, 13, 0.4);
--grey-90-a50: rgba(12, 12, 13, 0.5);
--purple-70: #6200a4;
--purple-70-a40: rgba(98, 0, 164, 0.4);
--red-50: #ff0039;
--red-50-a30: rgba(255, 0, 57, 0.3);
--red-60: #d70022;