Commit 4f68045c authored by Kirk Steuber's avatar Kirk Steuber
Browse files

Bug 1428948 - Add policies to modify the available search engines r=Felipe,florian

This adds a policy with the capability of adding search engines, choosing the default search engine, and blocking the installation of new search engines.

Additionally, fixes the messages for errors reported by MainProcessSingleton.addSearchEngine so that the offending URL is printed rather than "[xpconnect wrapped nsIURI]".

MozReview-Commit-ID: HuLT15Rnq0r

--HG--
rename : browser/components/search/test/testEngine.xml => browser/components/enterprisepolicies/tests/browser/opensearchEngine.xml
extra : rebase_source : 7815f5d03395decc39ee2657ba05c071dd332abb
parent 3ccc401f
......@@ -347,6 +347,63 @@ var Policies = {
setAndLockPref("signon.rememberSignons", param);
}
},
"SearchEngines": {
onAllWindowsRestored(manager, param) {
Services.search.init(() => {
if (param.Add) {
// Only rerun if the list of engine names has changed.
let engineNameList = param.Add.map(engine => engine.Name);
runOncePerModification("addSearchEngines",
JSON.stringify(engineNameList),
() => {
for (let newEngine of param.Add) {
let newEngineParameters = {
template: newEngine.URLTemplate,
iconURL: newEngine.IconURL,
alias: newEngine.Alias,
description: newEngine.Description,
method: newEngine.Method,
suggestURL: newEngine.SuggestURLTemplate,
extensionID: "set-via-policy"
};
try {
Services.search.addEngineWithDetails(newEngine.Name,
newEngineParameters);
} catch (ex) {
log.error("Unable to add search engine", ex);
}
}
});
}
if (param.Default) {
runOnce("setDefaultSearchEngine", () => {
let defaultEngine;
try {
defaultEngine = Services.search.getEngineByName(param.Default);
if (!defaultEngine) {
throw "No engine by that name could be found";
}
} catch (ex) {
log.error(`Search engine lookup failed when attempting to set ` +
`the default engine. Requested engine was ` +
`"${param.Default}".`, ex);
}
if (defaultEngine) {
try {
Services.search.currentEngine = defaultEngine;
} catch (ex) {
log.error("Unable to set the default search engine", ex);
}
}
});
}
if (param.PreventInstalls) {
manager.disallowFeature("installSearchEngine");
}
});
}
}
};
/*
......
......@@ -367,6 +367,54 @@
"first_available": "60.0",
"type": "boolean"
},
"SearchEngines": {
"description": "Modifies the list of search engines built into Firefox",
"first_available": "60.0",
"enterprise_only": true,
"type": "object",
"properties": {
"Add": {
"type": "array",
"items": {
"type": "object",
"required": ["Name", "URLTemplate"],
"properties": {
"Name": {
"type": "string"
},
"IconURL": {
"type": "URLorEmpty"
},
"Alias": {
"type": "string"
},
"Description": {
"type": "string"
},
"Method": {
"type": "string",
"enum": ["GET", "POST"]
},
"URLTemplate": {
"type": "string"
},
"SuggestURLTemplate": {
"type": "string"
}
}
}
},
"Default": {
"type": "string"
},
"PreventInstalls": {
"type": "boolean"
}
}
}
}
}
......@@ -3,6 +3,8 @@ support-files =
head.js
config_popups_cookies_addons_flash.json
config_broken_json.json
opensearch.html
opensearchEngine.xml
[browser_policies_basic_tests.js]
[browser_policies_broken_json.js]
......@@ -34,4 +36,5 @@ support-files =
[browser_policy_display_bookmarks.js]
[browser_policy_display_menu.js]
[browser_policy_proxy.js]
[browser_policy_search_engine.js]
[browser_policy_set_homepage.js]
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.policies.runonce.setDefaultSearchEngine");
Services.prefs.clearUserPref("browser.policies.runOncePerModification.addSearchEngines");
});
// |shouldWork| should be true if opensearch is expected to work and false if
// it is not.
async function test_opensearch(shouldWork) {
await SpecialPowers.pushPrefEnv({ set: [
["browser.search.widget.inNavBar", true],
]});
let rootDir = getRootDirectory(gTestPath);
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, rootDir + "opensearch.html");
let searchPopup = document.getElementById("PopupSearchAutoComplete");
let searchBar = document.getElementById("searchbar");
let promiseSearchPopupShown = BrowserTestUtils.waitForEvent(searchPopup, "popupshown");
let searchBarButton = document.getAnonymousElementByAttribute(searchBar,
"anonid",
"searchbar-search-button");
searchBarButton.click();
await promiseSearchPopupShown;
let oneOffsContainer = document.getAnonymousElementByAttribute(searchPopup,
"anonid",
"search-one-off-buttons");
let engineListElement = document.getAnonymousElementByAttribute(oneOffsContainer,
"anonid",
"add-engines");
if (shouldWork) {
ok(engineListElement.firstChild,
"There should be search engines available to add");
ok(searchBar.getAttribute("addengines"),
"Search bar should have addengines attribute");
} else {
is(engineListElement.firstChild, null,
"There should be no search engines available to add");
ok(!searchBar.getAttribute("addengines"),
"Search bar should not have addengines attribute");
}
await BrowserTestUtils.removeTab(tab);
}
add_task(async function test_install_and_set_default() {
// Make sure we are starting in an expected state to avoid false positive
// test results.
isnot(Services.search.currentEngine.name, "MozSearch",
"Default search engine should not be MozSearch when test starts");
is(Services.search.getEngineByName("Foo"), null,
"Engine \"Foo\" should not be present when test starts");
await setupPolicyEngineWithJson({
"policies": {
"SearchEngines": {
"Add": [
{
"Name": "MozSearch",
"URLTemplate": "http://example.com/?q={searchTerms}"
}
],
"Default": "MozSearch"
}
}
});
// If this passes, it means that the new search engine was properly installed
// *and* was properly set as the default.
is(Services.search.currentEngine.name, "MozSearch",
"Specified search engine should be the default");
// Clean up
Services.search.removeEngine(Services.search.currentEngine);
});
add_task(async function test_opensearch_works() {
// Ensure that opensearch works before we make sure that it can be properly
// disabled
await test_opensearch(true);
});
add_task(async function setup_prevent_installs() {
await setupPolicyEngineWithJson({
"policies": {
"SearchEngines": {
"PreventInstalls": true
}
}
});
});
add_task(async function test_prevent_install_ui() {
// Check that about:preferences does not prompt user to install search engines
// if that feature is disabled
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
let linkContainer = content.document.getElementById("addEnginesBox");
if (!linkContainer.hidden) {
await new Promise(resolve => {
let mut = new linkContainer.ownerGlobal.MutationObserver(mutations => {
mut.disconnect();
resolve();
});
mut.observe(linkContainer, {attributeFilter: ["hidden"]});
});
}
is(linkContainer.hidden, true,
"\"Find more search engines\" link should be hidden");
});
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_opensearch_disabled() {
// Check that search engines cannot be added via opensearch
await test_opensearch(false);
});
add_task(async function test_AddSearchProvider() {
// Mock the modal error dialog
let mockPrompter = {
promptCount: 0,
alert() {
this.promptCount++;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
};
let windowWatcher = {
getNewPrompter: () => mockPrompter,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher]),
};
let origWindowWatcher = Services.ww;
Services.ww = windowWatcher;
registerCleanupFunction(() => {
Services.ww = origWindowWatcher;
});
let engineURL = getRootDirectory(gTestPath) + "opensearchEngine.xml";
// AddSearchProvider will refuse to take URLs with a "chrome:" scheme
engineURL = engineURL.replace("chrome://mochitests/content", "http://example.com");
await ContentTask.spawn(gBrowser.selectedBrowser, {engineURL}, async function(args) {
content.window.external.AddSearchProvider(args.engineURL);
});
is(Services.search.getEngineByName("Foo"), null,
"Engine should not have been added successfully.");
is(mockPrompter.promptCount, 1,
"Should have alerted the user of an error when installing new search engine");
});
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="search" type="application/opensearchdescription+xml" title="newEngine" href="http://mochi.test:8888/browser/browser/components/enterprisepolicies/tests/browser/opensearchEngine.xml">
</head>
<body></body>
</html>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>Foo</ShortName>
<Description>Foo Search</Description>
<InputEncoding>utf-8</InputEncoding>
<Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC</Image>
<Url type="text/html" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/?search">
<Param name="test" value="{searchTerms}"/>
</Url>
<moz:SearchForm>http://mochi.test:8888/browser/browser/components/search/test/</moz:SearchForm>
<moz:Alias>fooalias</moz:Alias>
</OpenSearchDescription>
......@@ -40,10 +40,15 @@ var gSearchPane = {
document.getElementById("engineList").view = gEngineView;
this.buildDefaultEngineDropDown();
let addEnginesLink = document.getElementById("addEngines");
let searchEnginesURL = Services.wm.getMostRecentWindow("navigator:browser")
.BrowserSearch.searchEnginesURL;
addEnginesLink.setAttribute("href", searchEnginesURL);
if (Services.policies &&
!Services.policies.isAllowed("installSearchEngine")) {
document.getElementById("addEnginesBox").hidden = true;
} else {
let addEnginesLink = document.getElementById("addEngines");
let searchEnginesURL = Services.wm.getMostRecentWindow("navigator:browser")
.BrowserSearch.searchEnginesURL;
addEnginesLink.setAttribute("href", searchEnginesURL);
}
window.addEventListener("click", this);
window.addEventListener("command", this);
......
......@@ -336,6 +336,10 @@ var ContentLinkHandler = {
iconAdded = handleFaviconLink(link, isRichIcon, chromeGlobal, faviconLoads);
break;
case "search":
if (Services.policies &&
!Services.policies.isAllowed("installSearchEngine")) {
break;
}
if (!searchAdded && event.type == "DOMLinkAdded") {
var type = link.type && link.type.toLowerCase();
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
......
......@@ -31,10 +31,15 @@ MainProcessSingleton.prototype = {
let isWeb = ["https", "http", "ftp"];
if (!isWeb.includes(engineURL.scheme))
throw "Unsupported search engine URL: " + engineURL;
throw "Unsupported search engine URL: " + engineURL.spec;
if (iconURL && !isWeb.includes(iconURL.scheme))
throw "Unsupported search icon URL: " + iconURL;
throw "Unsupported search icon URL: " + iconURL.spec;
if (Services.policies &&
!Services.policies.isAllowed("installSearchEngine")) {
throw "Search Engine installation blocked by the Enterprise Policy Manager.";
}
} catch (ex) {
Cu.reportError("Invalid argument passed to window.external.AddSearchProvider: " + ex);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment