Backed out changeset 607922c730a1 (bug 1386018) for eslint bustage

MozReview-Commit-ID: JKQnKm7Nf4K
This commit is contained in:
Phil Ringnalda 2017-10-31 19:12:01 -07:00
parent e6dd599a0d
commit 7bf1119c24
9 changed files with 115 additions and 344 deletions

View File

@ -6,6 +6,11 @@
/* import-globals-from ../../../../toolkit/mozapps/preferences/fontbuilder.js */
/* import-globals-from ../../../base/content/aboutDialog-appUpdater.js */
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/Downloads.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
@ -36,13 +41,6 @@ const PREF_SHOW_PLUGINS_IN_LIST = "browser.download.show_plugins_in_list";
const PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS =
"browser.download.hide_plugins_without_extensions";
// Strings to identify ExtensionSettingsStore overrides
const PREF_SETTING_TYPE = "prefs";
const CONTAINERS_KEY = "privacy.containers";
const HOMEPAGE_OVERRIDE_KEY = "homepage_override";
const URL_OVERRIDES_TYPE = "url_overrides";
const NEW_TAB_KEY = "newTabURL";
/*
* Preferences where we store handling information about the feed type.
*
@ -220,10 +218,10 @@ var gMainPane = {
this.updateBrowserStartupLastSession();
handleControllingExtension(URL_OVERRIDES_TYPE, NEW_TAB_KEY);
handleControllingExtension("url_overrides", "newTabURL");
let newTabObserver = {
observe(subject, topic, data) {
handleControllingExtension(URL_OVERRIDES_TYPE, NEW_TAB_KEY);
handleControllingExtension("url_overrides", "newTabURL");
},
};
Services.obs.addObserver(newTabObserver, "newtab-url-changed");
@ -262,11 +260,11 @@ var gMainPane = {
setEventListener("restoreDefaultHomePage", "command",
gMainPane.restoreDefaultHomePage);
setEventListener("disableHomePageExtension", "command",
makeDisableControllingExtension(PREF_SETTING_TYPE, HOMEPAGE_OVERRIDE_KEY));
gMainPane.makeDisableControllingExtension("prefs", "homepage_override"));
setEventListener("disableContainersExtension", "command",
makeDisableControllingExtension(PREF_SETTING_TYPE, CONTAINERS_KEY));
gMainPane.makeDisableControllingExtension("prefs", "privacy.containers"));
setEventListener("disableNewTabExtension", "command",
makeDisableControllingExtension(URL_OVERRIDES_TYPE, NEW_TAB_KEY));
gMainPane.makeDisableControllingExtension("url_overrides", "newTabURL"));
setEventListener("chooseLanguage", "command",
gMainPane.showLanguages);
setEventListener("translationAttributionImage", "click",
@ -486,7 +484,7 @@ var gMainPane = {
const containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
const containersCheckbox = document.getElementById("browserContainersCheckbox");
containersCheckbox.checked = containersEnabled;
handleControllingExtension(PREF_SETTING_TYPE, CONTAINERS_KEY)
handleControllingExtension("prefs", "privacy.containers")
.then((isControlled) => {
containersCheckbox.disabled = isControlled;
});
@ -618,11 +616,11 @@ var gMainPane = {
if (homePref.locked) {
// An extension can't control these settings if they're locked.
hideControllingExtension(HOMEPAGE_OVERRIDE_KEY);
hideControllingExtension("homepage_override");
setInputDisabledStates(false);
} else {
// Asynchronously update the extension controlled UI.
handleControllingExtension(PREF_SETTING_TYPE, HOMEPAGE_OVERRIDE_KEY)
handleControllingExtension("prefs", "homepage_override")
.then(setInputDisabledStates);
}
@ -731,7 +729,7 @@ var gMainPane = {
useCurrent.label = useCurrent.getAttribute("label1");
// If the homepage is controlled by an extension then you can't use this.
if (await getControllingExtensionInfo(PREF_SETTING_TYPE, HOMEPAGE_OVERRIDE_KEY)) {
if (await getControllingExtensionId("prefs", "homepage_override")) {
useCurrent.disabled = true;
return;
}
@ -777,6 +775,14 @@ var gMainPane = {
homePage.value = homePage.defaultValue;
},
makeDisableControllingExtension(type, settingName) {
return async function disableExtension() {
let id = await getControllingExtensionId(type, settingName);
let addon = await AddonManager.getAddonByID(id);
addon.userDisabled = true;
};
},
/**
* Utility function to enable/disable the button specified by aButtonID based
* on the value of the Boolean preference specified by aPreferenceID.
@ -2583,6 +2589,75 @@ function getLocalHandlerApp(aFile) {
return localHandlerApp;
}
let extensionControlledContentIds = {
"privacy.containers": "browserContainersExtensionContent",
"homepage_override": "browserHomePageExtensionContent",
"newTabURL": "browserNewTabExtensionContent",
};
/**
* Check if a pref is being managed by an extension.
*/
async function getControllingExtensionId(type, settingName) {
await ExtensionSettingsStore.initialize();
return ExtensionSettingsStore.getTopExtensionId(type, settingName);
}
function getControllingExtensionEl(settingName) {
return document.getElementById(extensionControlledContentIds[settingName]);
}
async function handleControllingExtension(type, settingName) {
let controllingExtensionId = await getControllingExtensionId(type, settingName);
let addon = controllingExtensionId
&& await AddonManager.getAddonByID(controllingExtensionId);
// Sometimes the ExtensionSettingsStore gets in a bad state where it thinks
// an extension is controlling a setting but the extension has been uninstalled
// outside of the regular lifecycle. If the extension isn't currently installed
// then we should treat the setting as not being controlled.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1411046 for an example.
if (addon) {
showControllingExtension(settingName, addon);
} else {
hideControllingExtension(settingName);
}
return !!addon;
}
async function showControllingExtension(settingName, addon) {
// Tell the user what extension is controlling the setting.
let extensionControlledContent = getControllingExtensionEl(settingName);
const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
let stringParts = document
.getElementById("bundlePreferences")
.getString(`extensionControlled.${settingName}`)
.split("%S");
let description = extensionControlledContent.querySelector("description");
// Remove the old content from the description.
while (description.firstChild) {
description.firstChild.remove();
}
// Populate the description.
description.appendChild(document.createTextNode(stringParts[0]));
let image = document.createElement("image");
image.setAttribute("src", addon.iconURL || defaultIcon);
image.classList.add("extension-controlled-icon");
description.appendChild(image);
description.appendChild(document.createTextNode(` ${addon.name}`));
description.appendChild(document.createTextNode(stringParts[1]));
// Show the controlling extension row and hide the old label.
extensionControlledContent.hidden = false;
}
function hideControllingExtension(settingName) {
getControllingExtensionEl(settingName).hidden = true;
}
/**
* An enumeration of items in a JS array.
*

View File

@ -23,11 +23,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
var gLastHash = "";
var gCategoryInits = new Map();
@ -327,88 +322,3 @@ function appendSearchKeywords(aId, keywords) {
}
element.setAttribute("searchkeywords", keywords.join(" "));
}
let extensionControlledContentIds = {
"privacy.containers": "browserContainersExtensionContent",
"homepage_override": "browserHomePageExtensionContent",
"newTabURL": "browserNewTabExtensionContent",
"defaultSearch": "browserDefaultSearchExtensionContent",
};
/**
* Check if a pref is being managed by an extension.
*/
async function getControllingExtensionInfo(type, settingName) {
await ExtensionSettingsStore.initialize();
return ExtensionSettingsStore.getSetting(type, settingName);
}
function getControllingExtensionEl(settingName) {
return document.getElementById(extensionControlledContentIds[settingName]);
}
async function handleControllingExtension(type, settingName) {
let info = await getControllingExtensionInfo(type, settingName);
let addon = info && info.id
&& await AddonManager.getAddonByID(info.id);
// Sometimes the ExtensionSettingsStore gets in a bad state where it thinks
// an extension is controlling a setting but the extension has been uninstalled
// outside of the regular lifecycle. If the extension isn't currently installed
// then we should treat the setting as not being controlled.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1411046 for an example.
if (addon) {
showControllingExtension(settingName, addon);
} else {
hideControllingExtension(settingName);
}
return !!addon;
}
async function showControllingExtension(settingName, addon) {
// Tell the user what extension is controlling the setting.
let extensionControlledContent = getControllingExtensionEl(settingName);
extensionControlledContent.classList.remove("extension-controlled-disabled");
const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
let stringParts = document
.getElementById("bundlePreferences")
.getString(`extensionControlled.${settingName}`)
.split("%S");
let description = extensionControlledContent.querySelector("description");
// Remove the old content from the description.
while (description.firstChild) {
description.firstChild.remove();
}
// Populate the description.
description.appendChild(document.createTextNode(stringParts[0]));
let image = document.createElement("image");
image.setAttribute("src", addon.iconURL || defaultIcon);
image.classList.add("extension-controlled-icon");
description.appendChild(image);
description.appendChild(document.createTextNode(` ${addon.name}`));
description.appendChild(document.createTextNode(stringParts[1]));
let disableButton = extensionControlledContent.querySelector("button");
if (disableButton) {
disableButton.hidden = false;
}
// Show the controlling extension row and hide the old label.
extensionControlledContent.hidden = false;
}
function hideControllingExtension(settingName) {
getControllingExtensionEl(settingName).hidden = true;
}
function makeDisableControllingExtension(type, settingName) {
return async function disableExtension() {
let {id} = await getControllingExtensionInfo(type, settingName);
let addon = await AddonManager.getAddonByID(id);
addon.userDisabled = true;
};
}

View File

@ -7,12 +7,8 @@
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm");
const ENGINE_FLAVOR = "text/x-moz-search-engine";
const SEARCH_TYPE = "default_search";
const SEARCH_KEY = "defaultSearch";
var gEngineView = null;
@ -101,17 +97,6 @@ var gSearchPane = {
if (e.name == currentEngine)
list.selectedItem = item;
});
handleControllingExtension(SEARCH_TYPE, SEARCH_KEY);
let searchEngineListener = {
observe(subject, topic, data) {
handleControllingExtension(SEARCH_TYPE, SEARCH_KEY);
},
};
Services.obs.addObserver(searchEngineListener, "browser-search-engine-modified");
window.addEventListener("unload", () => {
Services.obs.removeObserver(searchEngineListener, "browser-search-engine-modified");
});
},
handleEvent(aEvent) {
@ -322,7 +307,6 @@ var gSearchPane = {
setDefaultEngine() {
Services.search.currentEngine =
document.getElementById("defaultEngine").selectedItem.engine;
ExtensionSettingsStore.setByUser(SEARCH_TYPE, SEARCH_KEY);
}
};

View File

@ -44,11 +44,6 @@
<groupbox id="defaultEngineGroup" data-category="paneSearch">
<caption><label>&defaultSearchEngine.label;</label></caption>
<description>&chooseYourDefaultSearchEngine2.label;</description>
<hbox id="browserDefaultSearchExtensionContent" align="center" hidden="true">
<description control="disableDefaultSearchExtension" flex="1"/>
</hbox>
<hbox>
<!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
<hbox>
@ -57,7 +52,6 @@
</menulist>
</hbox>
</hbox>
<checkbox id="suggestionsInSearchFieldsCheckbox"
label="&provideSearchSuggestions.label;"
accesskey="&provideSearchSuggestions.accesskey;"

View File

@ -275,107 +275,13 @@ add_task(async function testExtensionControlledNewTab() {
await waitForMessageHidden("browserNewTabExtensionContent");
ok(!aboutNewTabService.newTabURL.startsWith("moz-extension:"), "new tab page is set back to default");
is(controlledContent.hidden, true, "The extension controlled row is shown");
is(controlledContent.hidden, true, "The extension controlled row is hidden");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
let addon = await AddonManager.getAddonByID("@set_newtab");
addon.uninstall();
});
add_task(async function testExtensionControlledDefaultSearch() {
await openPreferencesViaOpenPreferencesAPI("paneSearch", {leaveOpen: true});
let doc = gBrowser.contentDocument;
let extensionId = "@set_default_search";
let manifest = {
manifest_version: 2,
name: "set_default_search",
applications: {gecko: {id: extensionId}},
description: "set_default_search description",
permissions: [],
chrome_settings_overrides: {
search_provider: {
name: "Yahoo",
search_url: "https://search.yahoo.com/yhs/search?p=%s&ei=UTF-8&hspart=mozilla&hsimp=yhs-002",
is_default: true,
},
}
};
function setEngine(engine) {
doc.querySelector(`#defaultEngine menuitem[label="${engine.name}"]`)
.doCommand();
}
is(gBrowser.currentURI.spec, "about:preferences#search",
"#search should be in the URI for about:preferences");
let controlledContent = doc.getElementById("browserDefaultSearchExtensionContent");
let initialEngine = Services.search.currentEngine;
// Ensure the controlled content is hidden when not controlled.
is(controlledContent.hidden, true, "The extension controlled row is hidden");
// Install an extension that will set the default search engine.
let originalExtension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: Object.assign({}, manifest, {version: "1.0"}),
});
let messageShown = waitForMessageShown("browserDefaultSearchExtensionContent");
await originalExtension.startup();
await messageShown;
let addon = await AddonManager.getAddonByID(extensionId);
is(addon.version, "1.0", "The addon has the expected version.");
// The default search engine has been set by the extension and the user is notified.
let controlledLabel = controlledContent.querySelector("description");
let extensionEngine = Services.search.currentEngine;
ok(initialEngine != extensionEngine, "The default engine has changed.");
// There are two spaces before "set_default_search" because it's " <image /> set_default_search".
is(controlledLabel.textContent,
"An extension, set_default_search, has set your default search engine.",
"The user is notified that an extension is controlling the default search engine");
is(controlledContent.hidden, false, "The extension controlled row is shown");
// Set the engine back to the initial one, ensure the message is hidden.
setEngine(initialEngine);
await waitForMessageHidden(controlledContent.id);
is(initialEngine, Services.search.currentEngine,
"default search engine is set back to default");
is(controlledContent.hidden, true, "The extension controlled row is hidden");
// Setting the engine back to the extension's engine does not show the message.
setEngine(extensionEngine);
is(extensionEngine, Services.search.currentEngine,
"default search engine is set back to extension");
is(controlledContent.hidden, true, "The extension controlled row is still hidden");
// Set the engine to the initial one and verify an upgrade doesn't change it.
setEngine(initialEngine);
await waitForMessageHidden(controlledContent.id);
// Update the extension and wait for "ready".
let updatedExtension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: Object.assign({}, manifest, {version: "2.0"}),
});
await updatedExtension.startup();
addon = await AddonManager.getAddonByID(extensionId);
// Verify the extension is updated and search engine didn't change.
is(addon.version, "2.0", "The updated addon has the expected version");
is(controlledContent.hidden, true, "The extension controlled row is hidden after update");
is(initialEngine, Services.search.currentEngine,
"default search engine is still the initial engine after update");
await originalExtension.unload();
await updatedExtension.unload();
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
add_task(async function testExtensionControlledHomepageUninstalledAddon() {
async function checkHomepageEnabled() {
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});

View File

@ -276,11 +276,6 @@ extensionControlled.homepage_override = An extension, %S, controls your home pag
# This string is shown to notify the user that their new tab page is being controlled by an extension.
extensionControlled.newTabURL = An extension, %S, controls your New Tab page.
# LOCALIZATION NOTE (extensionControlled.defaultSearch):
# This string is shown to notify the user that the default search engine is being controlled
# by an extension. %S is the icon and name of the extension.
extensionControlled.defaultSearch = An extension, %S, has set your default search engine.
# LOCALIZATION NOTE (extensionControlled.privacy.containers):
# This string is shown to notify the user that Container Tabs are being enabled by an extension
# %S is the container addon controlling it

View File

@ -181,6 +181,19 @@ this.ExtensionPreferencesManager = {
return defaultPreferences.get(prefName);
},
/**
* Gets the id of the extension controlling a preference or null if it isn't
* being controlled.
*
* @param {string} prefName The name of the preference.
*
* @returns {Promise} Resolves to the id of the extension, or null.
*/
async getControllingExtensionId(prefName) {
await ExtensionSettingsStore.initialize();
return ExtensionSettingsStore.getTopExtensionId(STORE_TYPE, prefName);
},
/**
* Indicates that an extension would like to change the value of a previously
* defined setting.

View File

@ -358,27 +358,6 @@ this.ExtensionSettingsStore = {
return alterSetting(extension, type, key, "disable");
},
/**
* Mark a setting as being controlled by a user's choice. This will disable all of
* the extension defined values for the extension.
*
* @param {string} type The type of the setting.
* @param {string} key The key of the setting.
*/
setByUser(type, key) {
let {precedenceList} = (_store.data[type] && _store.data[type][key]) || {};
if (!precedenceList) {
// The setting for this key does not exist. Nothing to do.
return;
}
for (let item of precedenceList) {
item.enabled = false;
}
_store.saveSoon();
},
/**
* Retrieves all settings from the store for a given extension.
*
@ -475,6 +454,16 @@ this.ExtensionSettingsStore = {
"controllable_by_this_extension";
},
// Return the id of the controlling extension or null if no extension is
// controlling this setting.
getTopExtensionId(type, key) {
let item = getTopItem(type, key);
if (item) {
return item.id;
}
return null;
},
/**
* Test-only method to force reloading of the JSON file.
*

View File

@ -433,101 +433,6 @@ add_task(async function test_settings_store() {
await promiseShutdownManager();
});
add_task(async function test_settings_store_setByUser() {
await promiseStartupManager();
// Create an array of test framework extension wrappers to install.
let testExtensions = [
ExtensionTestUtils.loadExtension({
useAddonManager: "temporary",
manifest: {
applications: {gecko: {id: "@first"}},
},
}),
ExtensionTestUtils.loadExtension({
useAddonManager: "temporary",
manifest: {
applications: {gecko: {id: "@second"}},
},
}),
ExtensionTestUtils.loadExtension({
useAddonManager: "temporary",
manifest: {
applications: {gecko: {id: "@third"}},
},
}),
];
let type = "some_type";
let key = "some_key";
for (let extension of testExtensions) {
await extension.startup();
}
// Create an array actual Extension objects which correspond to the
// test framework extension wrappers.
let [one, two, three] = testExtensions.map(extension => extension.extension);
let initialCallback = () => "initial";
// Initialize the SettingsStore.
await ExtensionSettingsStore.initialize();
equal(null, ExtensionSettingsStore.getSetting(type, key),
"getSetting is initially null");
let item = await ExtensionSettingsStore.addSetting(
one, type, key, "one", initialCallback);
deepEqual({key, value: "one", id: one.id}, item,
"addSetting returns the first set item");
item = await ExtensionSettingsStore.addSetting(
two, type, key, "two", initialCallback);
deepEqual({key, value: "two", id: two.id}, item,
"addSetting returns the second set item");
item = await ExtensionSettingsStore.addSetting(
three, type, key, "three", initialCallback);
deepEqual({key, value: "three", id: three.id}, item,
"addSetting returns the third set item");
deepEqual(item, ExtensionSettingsStore.getSetting(type, key),
"getSetting returns the third set item");
ExtensionSettingsStore.setByUser(type, key);
deepEqual({key, initialValue: "initial"}, ExtensionSettingsStore.getSetting(type, key),
"getSetting returns the initial value after being set by user");
item = ExtensionSettingsStore.enable(one, type, key);
deepEqual({key, value: "one", id: one.id}, item,
"enable returns the first set item after enable");
item = ExtensionSettingsStore.enable(three, type, key);
deepEqual({key, value: "three", id: three.id}, item,
"enable returns the third set item after enable");
item = ExtensionSettingsStore.enable(two, type, key);
deepEqual(undefined, item,
"enable returns undefined after enabling the second item");
item = ExtensionSettingsStore.getSetting(type, key);
deepEqual({key, value: "three", id: three.id}, item,
"getSetting returns the third set item after enabling the second item");
ExtensionSettingsStore.removeSetting(three, type, key);
ExtensionSettingsStore.removeSetting(two, type, key);
ExtensionSettingsStore.removeSetting(one, type, key);
equal(null, ExtensionSettingsStore.getSetting(type, key),
"getSetting returns null after removing all settings");
for (let extension of testExtensions) {
await extension.unload();
}
await promiseShutdownManager();
});
add_task(async function test_exceptions() {
await ExtensionSettingsStore.initialize();