Bug 1378882 - Support is_default for built-in engines only. r=mixedpuppy

MozReview-Commit-ID: C4iM2boQhK3

--HG--
extra : rebase_source : d1d0b629184c28e6ddd9d0e46ed783d5b767e77a
This commit is contained in:
Michael Kaply 2017-07-18 11:28:33 -05:00
parent 9883d98ef9
commit daf2df6528
4 changed files with 590 additions and 1 deletions

View File

@ -4,8 +4,15 @@
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPreferencesManager",
"resource://gre/modules/ExtensionPreferencesManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm");
const DEFAULT_SEARCH_STORE_TYPE = "default_search";
const DEFAULT_SEARCH_SETTING_NAME = "defaultSearch";
const searchInitialized = () => {
return new Promise(resolve => {
@ -25,10 +32,39 @@ const searchInitialized = () => {
};
this.chrome_settings_overrides = class extends ExtensionAPI {
processDefaultSearchSetting(action) {
let {extension} = this;
let {manifest} = extension;
let item = ExtensionSettingsStore.getSetting(DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
if (!item) {
return;
}
if (Services.search.currentEngine.name != item.value &&
Services.search.currentEngine.name != item.initialValue) {
// The current engine is not the same as the value that the ExtensionSettingsStore has.
// This means that the user changed the engine, so we shouldn't control it anymore.
// Do nothing and remove our entry from the ExtensionSettingsStore.
ExtensionSettingsStore.removeSetting(extension, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
return;
}
item = ExtensionSettingsStore[action](extension, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
if (item) {
try {
let engine = Services.search.getEngineByName(item.value || item.initialValue);
if (engine) {
Services.search.currentEngine = engine;
}
} catch (e) {
Components.utils.reportError(e);
}
}
}
async onManifestEntry(entryName) {
let {extension} = this;
let {manifest} = extension;
await ExtensionSettingsStore.initialize();
if (manifest.chrome_settings_overrides.homepage) {
ExtensionPreferencesManager.setSetting(extension, "homepage_override",
manifest.chrome_settings_overrides.homepage);
@ -36,6 +72,40 @@ this.chrome_settings_overrides = class extends ExtensionAPI {
if (manifest.chrome_settings_overrides.search_provider) {
await searchInitialized();
let searchProvider = manifest.chrome_settings_overrides.search_provider;
if (searchProvider.is_default) {
let engineName = searchProvider.name.trim();
let engine = Services.search.getEngineByName(engineName);
if (engine && Services.search.getDefaultEngines().includes(engine)) {
// Only add onclose handlers if we would definitely
// be setting the default engine.
extension.callOnClose({
close: () => {
switch (extension.shutdownReason) {
case "ADDON_DISABLE":
this.processDefaultSearchSetting("disable");
break;
case "ADDON_UNINSTALL":
this.processDefaultSearchSetting("removeSetting");
break;
}
},
});
if (extension.startupReason === "ADDON_INSTALL") {
let item = await ExtensionSettingsStore.addSetting(
extension, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME, engineName, () => {
return Services.search.currentEngine.name;
});
Services.search.currentEngine = Services.search.getEngineByName(item.value);
} else if (extension.startupReason === "ADDON_ENABLE") {
this.processDefaultSearchSetting("enable");
}
// If we would have set the default engine,
// we don't allow a search provider to be added.
return;
}
Components.utils.reportError("is_default can only be used for built-in engines.");
}
let isCurrent = false;
let index = -1;
if (extension.startupReason === "ADDON_UPGRADE") {
@ -70,6 +140,17 @@ this.chrome_settings_overrides = class extends ExtensionAPI {
Components.utils.reportError(e);
}
}
// If the setting exists for the extension, but is missing from the manifest,
// remove it. This can happen if the extension removes is_default.
// There's really no good place to put this, because the entire search section
// could be removed.
// We'll never get here in the normal case because we always return early
// if we have an is_default value that we use.
if (ExtensionSettingsStore.hasSetting(
extension, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME)) {
await searchInitialized();
this.processDefaultSearchSetting("removeSetting");
}
}
async onShutdown(reason) {
let {extension} = this;

View File

@ -98,7 +98,7 @@
"is_default": {
"type": "boolean",
"optional": true,
"deprecated": "Unsupported on Firefox at this time."
"description": "Sets the default engine to a built-in engine only."
}
}
}

View File

@ -106,6 +106,7 @@ skip-if = debug || asan # Bug 1354681
[browser_ext_sessions_getRecentlyClosed_private.js]
[browser_ext_sessions_getRecentlyClosed_tabs.js]
[browser_ext_sessions_restore.js]
[browser_ext_settings_overrides_default_search.js]
[browser_ext_settings_overrides_search.js]
[browser_ext_sidebarAction.js]
[browser_ext_sidebarAction_browser_style.js]

View File

@ -0,0 +1,507 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyGetter(this, "Management", () => {
const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
return Management;
});
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
const EXTENSION1_ID = "extension1@mozilla.com";
const EXTENSION2_ID = "extension2@mozilla.com";
function awaitEvent(eventName, id) {
return new Promise(resolve => {
let listener = (_eventName, ...args) => {
let extension = args[0];
if (_eventName === eventName &&
extension.id == id) {
Management.off(eventName, listener);
resolve(...args);
}
};
Management.on(eventName, listener);
});
}
/* This tests setting a default engine. */
add_task(async function test_extension_setting_default_engine() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "DuckDuckGo",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext1.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
});
/* This tests that using an invalid engine does nothing. */
add_task(async function test_extension_setting_invalid_name_default_engine() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "InvalidName",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
await ext1.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
});
/* This tests that uninstalling add-ons maintains the proper
* search default. */
add_task(async function test_extension_setting_multiple_default_engine() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "DuckDuckGo",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
let ext2 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "Twitter",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext2.startup();
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
await ext2.unload();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext1.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
});
/* This tests that uninstalling add-ons in reverse order maintains the proper
* search default. */
add_task(async function test_extension_setting_multiple_default_engine_reversed() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "DuckDuckGo",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
let ext2 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "Twitter",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext2.startup();
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
await ext1.unload();
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
await ext2.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
});
/* This tests adding an engine with one add-on and trying to make it the
*default with anoth. */
add_task(async function test_extension_setting_invalid_default_engine() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "MozSearch",
"keyword": "MozSearch",
"search_url": "https://example.com/?q={searchTerms}",
},
},
},
useAddonManager: "temporary",
});
let ext2 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "MozSearch",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
let engine = Services.search.getEngineByName("MozSearch");
ok(engine, "Engine should exist.");
await ext2.startup();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
await ext2.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
await ext1.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
});
/* This tests that when the user changes the search engine and the add-on
* is unistalled, search stays with the user's choice. */
add_task(async function test_user_changing_default_engine() {
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_settings_overrides": {
"search_provider": {
"name": "DuckDuckGo",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
let engine = Services.search.getEngineByName("Twitter");
Services.search.currentEngine = engine;
await ext1.unload();
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
});
/* This tests that when the user changes the search engine while it is
* disabled, user choice is maintained when the add-on is reenabled. */
add_task(async function test_user_change_with_disabling() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: {
id: EXTENSION1_ID,
},
},
"chrome_settings_overrides": {
"search_provider": {
"name": "DuckDuckGo",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
let engine = Services.search.getEngineByName("Twitter");
Services.search.currentEngine = engine;
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
let disabledPromise = awaitEvent("shutdown", EXTENSION1_ID);
let addon = await AddonManager.getAddonByID(EXTENSION1_ID);
addon.userDisabled = true;
await disabledPromise;
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
addon.userDisabled = false;
await enabledPromise;
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
await ext1.unload();
});
/* This tests that when two add-ons are installed that change default
* search and the first one is disabled, before the second one is installed,
* when the first one is reenabled, the second add-on keeps the search. */
add_task(async function test_two_addons_with_first_disabled_before_second() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: {
id: EXTENSION1_ID,
},
},
"chrome_settings_overrides": {
"search_provider": {
"name": "DuckDuckGo",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
let ext2 = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: {
id: EXTENSION2_ID,
},
},
"chrome_settings_overrides": {
"search_provider": {
"name": "Twitter",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
let disabledPromise = awaitEvent("shutdown", EXTENSION1_ID);
let addon1 = await AddonManager.getAddonByID(EXTENSION1_ID);
addon1.userDisabled = true;
await disabledPromise;
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
await ext2.startup();
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
addon1.userDisabled = false;
await enabledPromise;
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
await ext2.unload();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext1.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
});
/* This tests that when two add-ons are installed that change default
* search and the first one is disabled, the second one maintains
* the search. */
add_task(async function test_two_addons_with_first_disabled() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: {
id: EXTENSION1_ID,
},
},
"chrome_settings_overrides": {
"search_provider": {
"name": "DuckDuckGo",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
let ext2 = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: {
id: EXTENSION2_ID,
},
},
"chrome_settings_overrides": {
"search_provider": {
"name": "Twitter",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext2.startup();
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
let disabledPromise = awaitEvent("shutdown", EXTENSION1_ID);
let addon1 = await AddonManager.getAddonByID(EXTENSION1_ID);
addon1.userDisabled = true;
await disabledPromise;
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
addon1.userDisabled = false;
await enabledPromise;
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
await ext2.unload();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext1.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
});
/* This tests that when two add-ons are installed that change default
* search and the second one is disabled, the first one properly
* gets the search. */
add_task(async function test_two_addons_with_second_disabled() {
let defaultEngineName = Services.search.currentEngine.name;
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: {
id: EXTENSION1_ID,
},
},
"chrome_settings_overrides": {
"search_provider": {
"name": "DuckDuckGo",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
let ext2 = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: {
id: EXTENSION2_ID,
},
},
"chrome_settings_overrides": {
"search_provider": {
"name": "Twitter",
"search_url": "https://example.com/?q={searchTerms}",
"is_default": true,
},
},
},
useAddonManager: "temporary",
});
await ext1.startup();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext2.startup();
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
let disabledPromise = awaitEvent("shutdown", EXTENSION2_ID);
let addon2 = await AddonManager.getAddonByID(EXTENSION2_ID);
addon2.userDisabled = true;
await disabledPromise;
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
let enabledPromise = awaitEvent("ready", EXTENSION2_ID);
addon2.userDisabled = false;
await enabledPromise;
is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
await ext2.unload();
is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
await ext1.unload();
is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
});