Bug 1373206 - Create a new dialog for notification settings under Firefox Preferences to match the new spec. r?johannh
MozReview-Commit-ID: 5gk4mr3D0jT
--- a/browser/components/preferences/in-content-new/privacy.js
+++ b/browser/components/preferences/in-content-new/privacy.js
@@ -263,18 +263,18 @@ var gPrivacyPane = {
bundlePrefs.getString("blockliststitle"),
bundlePrefs.getString("blockliststext"),
]);
appendSearchKeywords("popupPolicyButton", [
bundlePrefs.getString("popuppermissionstitle2"),
bundlePrefs.getString("popuppermissionstext"),
]);
appendSearchKeywords("notificationsPolicyButton", [
- bundlePrefs.getString("notificationspermissionstitle"),
- bundlePrefs.getString("notificationspermissionstext4"),
+ bundlePrefs.getString("notificationspermissionstitle2"),
+ bundlePrefs.getString("notificationspermissionstext5"),
]);
appendSearchKeywords("addonExceptions", [
bundlePrefs.getString("addons_permissions_title2"),
bundlePrefs.getString("addonspermissionstext"),
]);
appendSearchKeywords("viewSecurityDevicesButton", [
pkiBundle.getString("enable_fips"),
]);
@@ -801,21 +801,21 @@ var gPrivacyPane = {
// NOTIFICATIONS
/**
* Displays the notifications exceptions dialog where specific site notification
* preferences can be set.
*/
showNotificationExceptions() {
let bundlePreferences = document.getElementById("bundlePreferences");
- let params = { permissionType: "desktop-notification" };
- params.windowTitle = bundlePreferences.getString("notificationspermissionstitle");
- params.introText = bundlePreferences.getString("notificationspermissionstext4");
+ let params = { permissionType: "desktop-notification"};
+ params.windowTitle = bundlePreferences.getString("notificationspermissionstitle2");
+ params.introText = bundlePreferences.getString("notificationspermissionstext5");
- gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
+ gSubDialog.open("chrome://browser/content/preferences/sitePermissions.xul",
"resizable=yes", params);
try {
Services.telemetry
.getHistogramById("WEB_NOTIFICATION_EXCEPTIONS_OPENED").add();
} catch (e) {}
},
--- a/browser/components/preferences/in-content-new/tests/browser.ini
+++ b/browser/components/preferences/in-content-new/tests/browser.ini
@@ -57,16 +57,17 @@ skip-if = e10s
[browser_privacypane_8.js]
[browser_sanitizeOnShutdown_prefLocked.js]
[browser_searchsuggestions.js]
[browser_security-1.js]
[browser_security-2.js]
[browser_siteData.js]
[browser_siteData2.js]
[browser_site_login_exceptions.js]
+[browser_permissions_dialog.js]
[browser_cookies_dialog.js]
[browser_subdialogs.js]
support-files =
subdialog.xul
subdialog2.xul
[browser_telemetry.js]
# Skip this test on Android as FHR and Telemetry are separate systems there.
skip-if = !healthreport || !telemetry || (os == 'linux' && debug) || (os == 'android')
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content-new/tests/browser_permissions_dialog.js
@@ -0,0 +1,215 @@
+"use strict";
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource:///modules/SitePermissions.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const PERMISSIONS_URL = "chrome://browser/content/preferences/sitePermissions.xul";
+const URL = "http://www.example.com";
+const URI = Services.io.newURI(URL);
+var sitePermissionsDialog;
+
+function checkPermissionItem(origin, state) {
+ let doc = sitePermissionsDialog.document;
+
+ let label = doc.getElementsByTagName("label")[0];
+ Assert.equal(label.value, origin);
+
+ let menulist = doc.getElementsByTagName("menulist")[0];
+ Assert.equal(menulist.label, state);
+}
+
+async function openPermissionsDialog() {
+ let dialogOpened = promiseLoadSubDialog(PERMISSIONS_URL);
+
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let doc = content.document;
+ let settingsButton = doc.getElementById("notificationsPolicyButton");
+ settingsButton.click();
+ });
+
+ sitePermissionsDialog = await dialogOpened;
+}
+
+add_task(async function openSitePermissionsDialog() {
+ await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
+ await openPermissionsDialog();
+});
+
+add_task(async function addPermission() {
+ let doc = sitePermissionsDialog.document;
+ let richlistbox = doc.getElementById("permissionsBox");
+
+ // First item in the richlistbox contains column headers.
+ Assert.equal(richlistbox.itemCount - 1, 0,
+ "Number of permission items is 0 initially");
+
+ // Add notification permission for a website.
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+
+ // Observe the added permission changes in the dialog UI.
+ Assert.equal(richlistbox.itemCount - 1, 1);
+ checkPermissionItem(URL, "Allow");
+
+ SitePermissions.remove(URL, "desktop-notification");
+});
+
+add_task(async function observePermissionChange() {
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+
+ // Change the permission.
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.BLOCK);
+
+ checkPermissionItem(URL, "Block");
+
+ SitePermissions.remove(URL, "desktop-notification");
+});
+
+add_task(async function observePermissionDelete() {
+ let doc = sitePermissionsDialog.document;
+ let richlistbox = doc.getElementById("permissionsBox");
+
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+
+ Assert.equal(richlistbox.itemCount - 1, 1,
+ "The box contains one permission item initially");
+
+ SitePermissions.remove(URI, "desktop-notification");
+
+ Assert.equal(richlistbox.itemCount - 1, 0);
+});
+
+add_task(async function onPermissionChange() {
+ let doc = sitePermissionsDialog.document;
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+
+ // Change the permission state in the UI.
+ doc.getElementsByAttribute("value", SitePermissions.BLOCK)[0].click();
+
+ Assert.equal(SitePermissions.get(URI, "desktop-notification").state,
+ SitePermissions.ALLOW,
+ "Permission state does not change before saving changes");
+
+ doc.getElementById("btnApplyChanges").click();
+
+ await waitForCondition(() =>
+ SitePermissions.get(URI, "desktop-notification").state == SitePermissions.BLOCK);
+
+ SitePermissions.remove(URI, "desktop-notification");
+});
+
+add_task(async function onPermissionDelete() {
+ await openPermissionsDialog();
+
+ let doc = sitePermissionsDialog.document;
+ let richlistbox = doc.getElementById("permissionsBox");
+
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+
+ richlistbox.selectItem(richlistbox.getItemAtIndex(1));
+ doc.getElementById("removePermission").click();
+
+ await waitForCondition(() => richlistbox.itemCount - 1 == 0);
+
+ Assert.equal(SitePermissions.get(URI, "desktop-notification").state,
+ SitePermissions.ALLOW,
+ "Permission is not deleted before saving changes");
+
+ doc.getElementById("btnApplyChanges").click();
+
+ await waitForCondition(() =>
+ SitePermissions.get(URI, "desktop-notification").state == SitePermissions.UNKNOWN);
+});
+
+add_task(async function onAllPermissionsDelete() {
+ await openPermissionsDialog();
+
+ let doc = sitePermissionsDialog.document;
+ let richlistbox = doc.getElementById("permissionsBox");
+
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+ let u = Services.io.newURI("http://www.test.com");
+ SitePermissions.set(u, "desktop-notification", SitePermissions.ALLOW);
+
+ doc.getElementById("removeAllPermissions").click();
+ await waitForCondition(() => richlistbox.itemCount - 1 == 0);
+
+ Assert.equal(SitePermissions.get(URI, "desktop-notification").state,
+ SitePermissions.ALLOW);
+ Assert.equal(SitePermissions.get(u, "desktop-notification").state,
+ SitePermissions.ALLOW, "Permissions are not deleted before saving changes");
+
+ doc.getElementById("btnApplyChanges").click();
+
+ await waitForCondition(() =>
+ (SitePermissions.get(URI, "desktop-notification").state == SitePermissions.UNKNOWN) &&
+ (SitePermissions.get(u, "desktop-notification").state == SitePermissions.UNKNOWN));
+});
+
+add_task(async function onPermissionChangeAndDelete() {
+ await openPermissionsDialog();
+
+ let doc = sitePermissionsDialog.document;
+ let richlistbox = doc.getElementById("permissionsBox");
+
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+
+ // Change the permission state in the UI.
+ doc.getElementsByAttribute("value", SitePermissions.BLOCK)[0].click();
+
+ // Remove that permission by clicking the "Remove" button.
+ richlistbox.selectItem(richlistbox.getItemAtIndex(1));
+ doc.getElementById("removePermission").click();
+
+ await waitForCondition(() => richlistbox.itemCount - 1 == 0);
+
+ doc.getElementById("btnApplyChanges").click();
+
+ await waitForCondition(() =>
+ SitePermissions.get(URI, "desktop-notification").state == SitePermissions.UNKNOWN);
+});
+
+add_task(async function onPermissionChangeCancel() {
+ await openPermissionsDialog();
+
+ let doc = sitePermissionsDialog.document;
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+
+ // Change the permission state in the UI.
+ doc.getElementsByAttribute("value", SitePermissions.BLOCK)[0].click();
+
+ doc.getElementById("cancel").click();
+
+ Assert.equal(SitePermissions.get(URI, "desktop-notification").state,
+ SitePermissions.ALLOW,
+ "Permission state does not change on clicking cancel");
+
+ SitePermissions.remove(URI, "desktop-notification");
+});
+
+add_task(async function onPermissionDeleteCancel() {
+ await openPermissionsDialog();
+
+ let doc = sitePermissionsDialog.document;
+ let richlistbox = doc.getElementById("permissionsBox");
+ SitePermissions.set(URI, "desktop-notification", SitePermissions.ALLOW);
+
+ // Remove that permission by clicking the "Remove" button.
+ richlistbox.selectItem(richlistbox.getItemAtIndex(1));
+ doc.getElementById("removePermission").click();
+
+ await waitForCondition(() => richlistbox.itemCount - 1 == 0);
+
+ doc.getElementById("cancel").click();
+
+ Assert.equal(SitePermissions.get(URI, "desktop-notification").state,
+ SitePermissions.ALLOW,
+ "Permission state does not change on clicking cancel");
+});
+
+add_task(async function removeTab() {
+ gBrowser.removeCurrentTab();
+});
--- a/browser/components/preferences/in-content-new/tests/browser_permissions_urlFieldHidden.js
+++ b/browser/components/preferences/in-content-new/tests/browser_permissions_urlFieldHidden.js
@@ -19,27 +19,8 @@ add_task(async function urlFieldVisibleF
let urlLabel = dialog.document.getElementById("urlLabel");
ok(!urlLabel.hidden, "urlLabel should be visible when one of block/session/allow visible");
let url = dialog.document.getElementById("url");
ok(!url.hidden, "url should be visible when one of block/session/allow visible");
popupPolicyCheckbox.click();
gBrowser.removeCurrentTab();
});
-
-add_task(async function urlFieldHiddenForNotificationPermissions() {
- await openPreferencesViaOpenPreferencesAPI("panePrivacy", {leaveOpen: true});
- let win = gBrowser.selectedBrowser.contentWindow;
- let doc = win.document;
- let notificationsPolicyButton = doc.getElementById("notificationsPolicyButton");
- ok(notificationsPolicyButton, "notificationsPolicyButton found");
- let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL);
- notificationsPolicyButton.click();
- let dialog = await dialogPromise;
- ok(dialog, "dialog loaded");
-
- let urlLabel = dialog.document.getElementById("urlLabel");
- ok(urlLabel.hidden, "urlLabel should be hidden as requested");
- let url = dialog.document.getElementById("url");
- ok(url.hidden, "url should be hidden as requested");
-
- gBrowser.removeCurrentTab();
-});
--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_2.js
+++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_2.js
@@ -46,11 +46,11 @@ add_task(async function() {
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for searching for the "Notification Permissions" subdialog.
*/
add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
- evaluateSearchResults("request permission again", "permissionsGroup");
+ evaluateSearchResults("send notifications", "permissionsGroup");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
--- a/browser/components/preferences/jar.mn
+++ b/browser/components/preferences/jar.mn
@@ -15,16 +15,19 @@ browser.jar:
content/browser/preferences/donottrack.xul
* content/browser/preferences/fonts.xul
content/browser/preferences/fonts.js
content/browser/preferences/handlers.xml
content/browser/preferences/handlers.css
* content/browser/preferences/languages.xul
content/browser/preferences/languages.js
content/browser/preferences/permissions.xul
+ content/browser/preferences/sitePermissions.xul
+ content/browser/preferences/sitePermissions.js
+ content/browser/preferences/sitePermissions.css
content/browser/preferences/containers.xul
content/browser/preferences/containers.js
content/browser/preferences/permissions.js
content/browser/preferences/sanitize.xul
content/browser/preferences/sanitize.js
content/browser/preferences/selectBookmark.xul
content/browser/preferences/selectBookmark.js
content/browser/preferences/siteDataSettings.xul
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/sitePermissions.css
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+.website-name {
+ padding: 5px;
+}
+
+#permissionsBox {
+ min-height: 18em;
+}
+
+#permissionsBox > richlistitem {
+ min-height: 35px;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/sitePermissions.js
@@ -0,0 +1,255 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
+Components.utils.import("resource:///modules/SitePermissions.jsm");
+
+function Permission(principal, type, capability, capabilityString) {
+ this.principal = principal;
+ this.origin = principal.origin;
+ this.type = type;
+ this.capability = capability;
+ this.capabilityString = capabilityString;
+}
+
+var gSitePermissionsManager = {
+ _type: "",
+ _isObserving: false,
+ _permissions: new Map(),
+ _permissionsToChange: new Map(),
+ _permissionsToDelete: new Map(),
+ _list: null,
+ _bundle: null,
+ _removeButton: null,
+ _removeAllButton: null,
+
+ onLoad() {
+ let params = window.arguments[0];
+ this.init(params);
+ },
+
+ init(params) {
+ if (!this._isObserving) {
+ Services.obs.addObserver(this, "perm-changed");
+ this._isObserving = true;
+ }
+
+ this._bundle = document.getElementById("bundlePreferences");
+ this._type = params.permissionType;
+ this._list = document.getElementById("permissionsBox");
+ this._removeButton = document.getElementById("removePermission");
+ this._removeAllButton = document.getElementById("removeAllPermissions");
+
+ let permissionsText = document.getElementById("permissionsText");
+ while (permissionsText.hasChildNodes())
+ permissionsText.firstChild.remove();
+ permissionsText.appendChild(document.createTextNode(params.introText));
+
+ document.title = params.windowTitle;
+
+ this._loadPermissions();
+ },
+
+ uninit() {
+ if (this._isObserving) {
+ Services.obs.removeObserver(this, "perm-changed");
+ this._isObserving = false;
+ }
+ },
+
+ observe(subject, topic, data) {
+ if (topic !== "perm-changed")
+ return;
+
+ let permission = subject.QueryInterface(Components.interfaces.nsIPermission);
+
+ // Ignore unrelated permission types.
+ if (permission.type !== this._type)
+ return;
+
+ if (data == "added") {
+ this._addPermissionToList(permission);
+ } else if (data == "changed") {
+ let p = this._permissions.get(permission.principal.origin);
+ p.capability = permission.capability;
+ p.capabilityString = this._getCapabilityString(permission.capability);
+ this._handleCapabilityChange(p);
+ } else if (data == "deleted") {
+ this._removePermissionFromList(permission.principal.origin);
+ }
+ },
+
+ _handleCapabilityChange(perm) {
+ let permissionlistitem = document.getElementsByAttribute("origin", perm.origin)[0];
+ let menulist = permissionlistitem.getElementsByTagName("menulist")[0];
+ menulist.selectedItem =
+ menulist.getElementsByAttribute("value", perm.capability)[0];
+ },
+
+ _getCapabilityString(capability) {
+ let stringKey = null;
+ switch (capability) {
+ case Services.perms.ALLOW_ACTION:
+ stringKey = "can";
+ break;
+ case Services.perms.DENY_ACTION:
+ stringKey = "cannot";
+ break;
+ case Services.perms.PROMPT_ACTION:
+ stringKey = "prompt"
+ break;
+ }
+ return this._bundle.getString(stringKey);
+ },
+
+ _addPermissionToList(perm) {
+ if (perm.type !== this._type)
+ return;
+ let capabilityString = this._getCapabilityString(perm.capability);
+ let p = new Permission(perm.principal, perm.type, perm.capability,
+ capabilityString);
+ this._permissions.set(p.origin, p);
+ this._createPermissionListItem(p);
+ },
+
+ _removePermissionFromList(origin) {
+ this._permissions.delete(origin);
+ let permissionlistitem = document.getElementsByAttribute("origin", origin)[0];
+ this._list.removeItemAt(this._list.getIndexOfItem(permissionlistitem));
+ },
+
+ _loadPermissions() {
+ // load permissions into a table.
+ let enumerator = Services.perms.enumerator;
+ while (enumerator.hasMoreElements()) {
+ let nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
+ this._addPermissionToList(nextPermission);
+ }
+
+ // disable "remove all" button if there are none
+ this._setRemoveButtonState();
+ },
+
+ _createPermissionListItem(permission) {
+ let richlistitem = document.createElement("richlistitem");
+ richlistitem.setAttribute("origin", permission.origin);
+ let row = document.createElement("hbox");
+ row.setAttribute("flex", "1");
+
+ let hbox = document.createElement("hbox");
+ let website = document.createElement("label");
+ website.setAttribute("value", permission.origin);
+ website.setAttribute("width", "50");
+ hbox.setAttribute("class", "website-name");
+ hbox.setAttribute("flex", "3");
+ hbox.appendChild(website);
+
+ let menulist = document.createElement("menulist");
+ let menupopup = document.createElement("menupopup");
+ menulist.setAttribute("flex", "1");
+ menulist.setAttribute("width", "50");
+ menulist.appendChild(menupopup);
+ let states = SitePermissions.getAvailableStates(permission.type);
+ for (let state of states) {
+ if (state == SitePermissions.UNKNOWN)
+ continue;
+ let m = document.createElement("menuitem");
+ m.setAttribute("label", this._getCapabilityString(state));
+ m.setAttribute("value", state);
+ menupopup.appendChild(m);
+ }
+ menulist.value = permission.capability;
+
+ menulist.addEventListener("select", () => {
+ this.onPermissionChange(permission, Number(menulist.selectedItem.value));
+ });
+
+ row.appendChild(hbox);
+ row.appendChild(menulist);
+ richlistitem.appendChild(row);
+ this._list.appendChild(richlistitem)
+ },
+
+ onWindowKeyPress(event) {
+ if (event.keyCode == KeyEvent.DOM_VK_ESCAPE)
+ window.close();
+ },
+
+ onPermissionKeyPress(event) {
+ if (!this._list.selectedItem)
+ return;
+
+ if (event.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ event.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ this.onPermissionDelete();
+ event.preventDefault();
+ }
+ },
+
+ _setRemoveButtonState() {
+ if (!this._list)
+ return;
+
+ let hasSelection = this._list.selectedIndex;
+ let hasRows = this._list.itemCount > 1;
+ this._removeButton.disabled = !hasSelection;
+ this._removeAllButton.disabled = !hasRows;
+ },
+
+ onPermissionDelete() {
+ let richlistitem = this._list.selectedItem;
+ let origin = richlistitem.getAttribute("origin");
+ let permission = this._permissions.get(origin);
+
+ this._removePermissionFromList(origin);
+ this._permissionsToDelete.set(permission.origin, permission);
+
+ this._setRemoveButtonState();
+ },
+
+ onAllPermissionsDelete() {
+ for (let permission of this._permissions.values()) {
+ this._removePermissionFromList(permission.origin);
+ this._permissionsToDelete.set(permission.origin, permission);
+ }
+
+ this._setRemoveButtonState();
+ },
+
+ onPermissionSelect() {
+ this._setRemoveButtonState();
+ },
+
+ onPermissionChange(perm, capability) {
+ let p = this._permissions.get(perm.origin);
+ if (p.capability == capability)
+ return;
+ p.capability = capability;
+ p.capabilityString = this._getCapabilityString(capability);
+ this._permissionsToChange.set(p.origin, p);
+
+ // enable "remove all" button as needed
+ this._setRemoveButtonState();
+ },
+
+ onApplyChanges() {
+ // Stop observing permission changes since we are about
+ // to write out the pending adds/deletes and don't need
+ // to update the UI
+ this.uninit();
+
+ for (let p of this._permissionsToChange.values()) {
+ let uri = Services.io.newURI(p.origin);
+ SitePermissions.set(uri, p.type, p.capability);
+ }
+
+ for (let p of this._permissionsToDelete.values()) {
+ let uri = Services.io.newURI(p.origin);
+ SitePermissions.remove(uri, p.type);
+ }
+ window.close();
+ },
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/sitePermissions.xul
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/preferences/sitePermissions.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/permissions.dtd" >
+
+<window id="SitePermissionsDialog" class="windowDialog"
+ windowtype="Browser:SitePermissions"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: &window.width;;"
+ onload="gSitePermissionsManager.onLoad();"
+ onunload="gSitePermissionsManager.uninit();"
+ persist="screenX screenY width height"
+ onkeypress="gSitePermissionsManager.onWindowKeyPress(event);">
+
+ <script src="chrome://browser/content/preferences/sitePermissions.js"/>
+
+ <stringbundle id="bundlePreferences"
+ src="chrome://browser/locale/preferences/preferences.properties"/>
+
+ <keyset>
+ <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
+ </keyset>
+
+ <vbox class="contentPane largeDialogContainer" flex="1">
+ <description id="permissionsText" control="url"/>
+ <separator class="thin"/>
+ <hbox align="start">
+ <textbox id="url" flex="1" placeholder="Search Website"
+ type="search"/>
+ </hbox>
+ <separator class="thin"/>
+ <richlistbox id="permissionsBox" selected="false"
+ hidecolumnpicker="true" flex="1"
+ onkeypress="gSitePermissionsManager.onPermissionKeyPress(event);"
+ onselect="gSitePermissionsManager.onPermissionSelect();">
+ <richlistitem>
+ <hbox flex="1">
+ <treecol id="siteCol" label="&treehead.sitename2.label;" flex="3"
+ data-field-name="origin" persist="width" width="50"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="statusCol" label="&treehead.status.label;" flex="1"
+ data-field-name="capability" persist="width" width="50"/>
+ </hbox>
+ </richlistitem>
+ </richlistbox>
+ </vbox>
+ <vbox>
+ <hbox class="actionButtons" align="left" flex="1">
+ <button id="removePermission" disabled="true"
+ accesskey="&removepermission2.accesskey;"
+ icon="remove" label="&removepermission2.label;"
+ oncommand="gSitePermissionsManager.onPermissionDelete();"/>
+ <button id="removeAllPermissions"
+ icon="clear" label="&removeallpermissions2.label;"
+ accesskey="&removeallpermissions2.accesskey;"
+ oncommand="gSitePermissionsManager.onAllPermissionsDelete();"/>
+ </hbox>
+ <spacer flex="1"/>
+ <hbox class="actionButtons" align="right" flex="1">
+ <button oncommand="close();" icon="close" id="cancel"
+ label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
+ <button id="btnApplyChanges" oncommand="gSitePermissionsManager.onApplyChanges();" icon="save"
+ label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
+ </hbox>
+ </vbox>
+</window>
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -22,18 +22,18 @@ acceptVeryLargeMinimumFont=Keep my chang
trackingprotectionpermissionstext2=You have disabled Tracking Protection on these websites.
trackingprotectionpermissionstitle=Exceptions - Tracking Protection
cookiepermissionstext=You can specify which websites are always or never allowed to use cookies. Type the exact address of the site you want to manage and then click Block, Allow for Session, or Allow.
cookiepermissionstitle=Exceptions - Cookies
addonspermissionstext=You can specify which websites are allowed to install add-ons. Type the exact address of the site you want to allow and then click Allow.
addons_permissions_title2=Allowed Websites - Add-ons Installation
popuppermissionstext=You can specify which websites are allowed to open pop-up windows. Type the exact address of the site you want to allow and then click Allow.
popuppermissionstitle2=Allowed Websites - Pop-ups
-notificationspermissionstext4=Control which websites are always or never allowed to send you notifications. If you remove a site, it will need to request permission again.
-notificationspermissionstitle=Notification Permissions
+notificationspermissionstext5=The following websites have requested to send you notifications. You can specify which websites are allowed to send you notifications.
+notificationspermissionstitle2=Settings - Notification Permissions
invalidURI=Please enter a valid hostname
invalidURITitle=Invalid Hostname Entered
savedLoginsExceptions_title=Exceptions - Saved Logins
savedLoginsExceptions_desc2=Logins for the following websites will not be saved:
#### Block List Manager
blockliststext=You can choose which list Firefox will use to block Web elements that may track your browsing activity.
@@ -115,16 +115,17 @@ hostColon=Host:
domainColon=Domain:
forSecureOnly=Encrypted connections only
forAnyConnection=Any type of connection
expireAtEndOfSession=At end of session
can=Allow
canAccessFirstParty=Allow first party only
canSession=Allow for Session
cannot=Block
+prompt=Always Ask
noCookieSelected=<no cookie selected>
cookiesAll=The following cookies are stored on your computer:
cookiesFiltered=The following cookies match your search:
# LOCALIZATION NOTE (removeAllCookies, removeAllShownCookies):
# removeAllCookies and removeAllShownCookies are both used on the same one button,
# never displayed together and can share the same accesskey.
# When only partial cookies are shown as a result of keyword search,