Bug 1563021 - Add preferences UI to allow enabling and selection of a separate default private engine. r=fluent-reviewers,jaws,flod

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Mark Banner 2019-09-30 19:18:26 +00:00
parent f31f9e1083
commit f9c236531f
11 changed files with 505 additions and 20 deletions

View File

@ -386,6 +386,10 @@ pref("browser.search.hiddenOneOffs", "");
// Mirrors whether the search-container widget is in the navigation toolbar.
pref("browser.search.widget.inNavBar", false);
// Enables display of the options for the user using a separate default search
// engine in private browsing mode.
pref("browser.search.separatePrivateDefault.ui.enabled", false);
pref("browser.sessionhistory.max_entries", 50);
// Built-in default permissions.

View File

@ -621,7 +621,8 @@
<!-- Address Bar -->
<groupbox id="locationBarGroup"
data-category="panePrivacy"
hidden="true">
hidden="true"
data-subcategory="locationBar">
<label><html:h2 data-l10n-id="addressbar-header"/></label>
<label id="locationBarSuggestionLabel" data-l10n-id="addressbar-suggest"/>
<checkbox id="historySuggestion" data-l10n-id="addressbar-locbar-history-option"

View File

@ -39,6 +39,25 @@
</menulist>
</hbox>
</hbox>
<checkbox id="browserSeparateDefaultEngine"
data-l10n-id="search-separate-default-engine"
hidden="true"/>
<vbox id="browserPrivateEngineSelection" class="indent" hidden="true">
<description data-l10n-id="search-engine-default-private-desc" />
<hbox>
<!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
<hbox>
<menulist id="defaultPrivateEngine">
<menupopup/>
</menulist>
</hbox>
</hbox>
</vbox>
</groupbox>
<groupbox id="searchSuggestionsGroup" data-category="paneSearch" hidden="true">
<label><html:h2 data-l10n-id="search-suggestions-header" /></label>
<description data-l10n-id="search-suggestions-desc" />
<checkbox id="suggestionsInSearchFieldsCheckbox"
data-l10n-id="search-suggestions-option"
@ -52,6 +71,8 @@
<label flex="1" data-l10n-id="search-suggestions-cant-show" />
</hbox>
</vbox>
<label id="openLocationBarPrivacyPreferences" is="text-link"
data-l10n-id="suggestions-addressbar-settings"/>
</groupbox>
<groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch" hidden="true">

View File

@ -22,6 +22,8 @@ Preferences.addAll([
{ id: "browser.search.hiddenOneOffs", type: "unichar" },
{ id: "browser.search.widget.inNavBar", type: "bool" },
{ id: "browser.urlbar.matchBuckets", type: "string" },
{ id: "browser.search.separatePrivateDefault", type: "bool" },
{ id: "browser.search.separatePrivateDefault.ui.enabled", type: "bool" },
]);
const ENGINE_FLAVOR = "text/x-moz-search-engine";
@ -43,7 +45,7 @@ var gSearchPane = {
init() {
gEngineView = new EngineView(new EngineStore());
document.getElementById("engineList").view = gEngineView;
this.buildDefaultEngineDropDown();
this.buildDefaultEngineDropDowns().catch(console.error);
if (
Services.policies &&
@ -84,10 +86,67 @@ var gSearchPane = {
urlbarSuggestsPref.value = urlbarSuggests.checked;
});
setEventListener(
"browserSeparateDefaultEngine",
"command",
this._onBrowserSeparateDefaultEngineChange.bind(this)
);
setEventListener("openLocationBarPrivacyPreferences", "click", function(
event
) {
if (event.button == 0) {
gotoPref("privacy-locationBar");
}
});
this._initDefaultEngines();
this._initShowSearchSuggestionsFirst();
this._updateSuggestionCheckboxes();
},
/**
* Initialize the default engine handling. This will hide the private default
* options if they are not enabled yet.
*/
_initDefaultEngines() {
this._separatePrivateDefaultEnabledPref = Preferences.get(
"browser.search.separatePrivateDefault.ui.enabled"
);
this._separatePrivateDefaultPref = Preferences.get(
"browser.search.separatePrivateDefault"
);
const checkbox = document.getElementById("browserSeparateDefaultEngine");
checkbox.checked = !this._separatePrivateDefaultPref.value;
this._updatePrivateEngineDisplayBoxes();
const listener = () => {
this._updatePrivateEngineDisplayBoxes();
this.buildDefaultEngineDropDowns().catch(console.error);
};
this._separatePrivateDefaultEnabledPref.on("change", listener);
this._separatePrivateDefaultPref.on("change", listener);
},
_updatePrivateEngineDisplayBoxes() {
const separateEnabled = this._separatePrivateDefaultEnabledPref.value;
document.getElementById(
"browserSeparateDefaultEngine"
).hidden = !separateEnabled;
const separateDefault = this._separatePrivateDefaultPref.value;
const vbox = document.getElementById("browserPrivateEngineSelection");
vbox.hidden = !separateEnabled || !separateDefault;
},
_onBrowserSeparateDefaultEngineChange(event) {
this._separatePrivateDefaultPref.value = !event.target.checked;
},
_initShowSearchSuggestionsFirst() {
this._urlbarSuggestionsPosPref = Preferences.get(
"browser.urlbar.matchBuckets"
@ -163,12 +222,37 @@ var gSearchPane = {
permanentPBLabel.hidden = urlbarSuggests.hidden || !permanentPB;
},
async buildDefaultEngineDropDown() {
// This is called each time something affects the list of engines.
let list = document.getElementById("defaultEngine");
// Set selection to the current default engine.
let currentEngine = (await Services.search.getDefault()).name;
/**
* Builds the default and private engines drop down lists. This is called
* each time something affects the list of engines.
*/
async buildDefaultEngineDropDowns() {
await this._buildEngineDropDown(
document.getElementById("defaultEngine"),
(await Services.search.getDefault()).name,
false
);
if (this._separatePrivateDefaultEnabledPref.value) {
await this._buildEngineDropDown(
document.getElementById("defaultPrivateEngine"),
(await Services.search.getDefaultPrivate()).name,
true
);
}
},
/**
* Builds a drop down menu of search engines.
*
* @param {DOMMenuList} list
* The menu list element to attach the list of engines.
* @param {string} currentEngine
* The name of the current default engine.
* @param {boolean} isPrivate
* True if we are dealing with the default engine for private mode.
*/
async _buildEngineDropDown(list, currentEngine, isPrivate) {
// If the current engine isn't in the list any more, select the first item.
let engines = gEngineView._engineStore._engines;
if (!engines.length) {
@ -195,6 +279,12 @@ var gSearchPane = {
}
});
// We don't currently support overriding the engine for private mode with
// extensions.
if (isPrivate) {
return;
}
handleControllingExtension(SEARCH_TYPE, SEARCH_KEY);
let searchEngineListener = {
observe(subject, topic, data) {
@ -241,10 +331,15 @@ var gSearchPane = {
case "":
if (
aEvent.target.parentNode &&
aEvent.target.parentNode.parentNode &&
aEvent.target.parentNode.parentNode.id == "defaultEngine"
aEvent.target.parentNode.parentNode
) {
if (aEvent.target.parentNode.parentNode.id == "defaultEngine") {
gSearchPane.setDefaultEngine();
} else if (
aEvent.target.parentNode.parentNode.id == "defaultPrivateEngine"
) {
gSearchPane.setDefaultPrivateEngine();
}
}
break;
case "restoreDefaultSearchEngines":
@ -291,7 +386,7 @@ var gSearchPane = {
case "engine-added":
gEngineView._engineStore.addEngine(aEngine);
gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
gSearchPane.buildDefaultEngineDropDown();
gSearchPane.buildDefaultEngineDropDowns();
break;
case "engine-changed":
gEngineView._engineStore.reloadIcons();
@ -300,17 +395,29 @@ var gSearchPane = {
case "engine-removed":
gSearchPane.remove(aEngine);
break;
case "engine-default":
case "engine-default": {
// If the user is going through the drop down using up/down keys, the
// dropdown may still be open (eg. on Windows) when engine-default is
// fired, so rebuilding the list unconditionally would get in the way.
let selectedEngine = document.getElementById("defaultEngine")
.selectedItem.engine;
if (selectedEngine.name != aEngine.name) {
gSearchPane.buildDefaultEngineDropDown();
gSearchPane.buildDefaultEngineDropDowns();
}
break;
}
case "engine-default-private": {
// If the user is going through the drop down using up/down keys, the
// dropdown may still be open (eg. on Windows) when engine-default is
// fired, so rebuilding the list unconditionally would get in the way.
const selectedEngine = document.getElementById("defaultPrivateEngine")
.selectedItem.engine;
if (selectedEngine.name != aEngine.name) {
gSearchPane.buildDefaultEngineDropDowns();
}
break;
}
}
}
},
@ -453,6 +560,12 @@ var gSearchPane = {
);
ExtensionSettingsStore.setByUser(SEARCH_TYPE, SEARCH_KEY);
},
async setDefaultPrivateEngine() {
await Services.search.setDefaultPrivate(
document.getElementById("defaultPrivateEngine").selectedItem.engine
);
},
};
function onDragEngineStart(event) {
@ -480,7 +593,7 @@ function EngineStore() {
gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
}
this._defaultEngines = defaultEngines.map(this._cloneEngine, this);
gSearchPane.buildDefaultEngineDropDown();
gSearchPane.buildDefaultEngineDropDowns();
// check if we need to disable the restore defaults button
var someHidden = this._defaultEngines.some(e => e.hidden);
@ -563,7 +676,7 @@ EngineStore.prototype = {
if (this._defaultEngines.some(this._isSameEngine, removedEngine)) {
gSearchPane.showRestoreDefaults(true);
}
gSearchPane.buildDefaultEngineDropDown();
gSearchPane.buildDefaultEngineDropDowns();
return index;
},
@ -592,7 +705,7 @@ EngineStore.prototype = {
}
Services.search.resetToOriginalDefaultEngine();
gSearchPane.showRestoreDefaults(false);
gSearchPane.buildDefaultEngineDropDown();
gSearchPane.buildDefaultEngineDropDowns();
return added;
},
@ -720,7 +833,7 @@ EngineView.prototype = {
await this._engineStore.moveEngine(sourceEngine, dropIndex);
gSearchPane.showRestoreDefaults(true);
gSearchPane.buildDefaultEngineDropDown();
gSearchPane.buildDefaultEngineDropDowns();
// Redraw, and adjust selection
this.invalidate();

View File

@ -81,6 +81,10 @@ skip-if = e10s
[browser_privacypane_3.js]
[browser_privacy_passwordGenerationAndAutofill.js]
[browser_sanitizeOnShutdown_prefLocked.js]
[browser_searchDefaultEngine.js]
support-files =
engine1/manifest.json
engine2/manifest.json
[browser_searchShowSuggestionsFirst.js]
[browser_searchsuggestions.js]
[browser_security-1.js]

View File

@ -0,0 +1,277 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const { SearchTestUtils } = ChromeUtils.import(
"resource://testing-common/SearchTestUtils.jsm"
);
add_task(async function setup() {
const engine1 = await Services.search.addEngineWithDetails("engine1", {
method: "get",
template: "http://example.com/engine1?search={searchTerms}",
});
const engine2 = await Services.search.addEngineWithDetails("engine2", {
method: "get",
template: "http://example.com/engine2?search={searchTerms}",
});
const originalDefault = await Services.search.getDefault();
const originalDefaultPrivate = await Services.search.getDefaultPrivate();
registerCleanupFunction(async () => {
await Services.search.setDefault(originalDefault);
await Services.search.setDefaultPrivate(originalDefaultPrivate);
await Services.search.removeEngine(engine1);
await Services.search.removeEngine(engine2);
});
});
add_task(async function test_openWithPrivateDefaultNotEnabledFirst() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.search.separatePrivateDefault.ui.enabled", false],
["browser.search.separatePrivateDefault", false],
],
});
await openPreferencesViaOpenPreferencesAPI("search", { leaveOpen: true });
const doc = gBrowser.selectedBrowser.contentDocument;
const separateEngineCheckbox = doc.getElementById(
"browserSeparateDefaultEngine"
);
const privateDefaultVbox = doc.getElementById(
"browserPrivateEngineSelection"
);
Assert.ok(
separateEngineCheckbox.hidden,
"Should have hidden the separate search engine checkbox"
);
Assert.ok(
privateDefaultVbox.hidden,
"Should have hidden the private engine selection box"
);
await SpecialPowers.pushPrefEnv({
set: [["browser.search.separatePrivateDefault.ui.enabled", true]],
});
Assert.ok(
!separateEngineCheckbox.hidden,
"Should have displayed the separate search engine checkbox"
);
Assert.ok(
privateDefaultVbox.hidden,
"Should not have displayed the private engine selection box"
);
await SpecialPowers.pushPrefEnv({
set: [["browser.search.separatePrivateDefault", true]],
});
Assert.ok(
!separateEngineCheckbox.hidden,
"Should still be displaying the separate search engine checkbox"
);
Assert.ok(
!privateDefaultVbox.hidden,
"Should have displayed the private engine selection box"
);
gBrowser.removeCurrentTab();
});
add_task(async function test_openWithPrivateDefaultEnabledFirst() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.search.separatePrivateDefault.ui.enabled", true],
["browser.search.separatePrivateDefault", true],
],
});
await openPreferencesViaOpenPreferencesAPI("search", { leaveOpen: true });
const doc = gBrowser.selectedBrowser.contentDocument;
const separateEngineCheckbox = doc.getElementById(
"browserSeparateDefaultEngine"
);
const privateDefaultVbox = doc.getElementById(
"browserPrivateEngineSelection"
);
Assert.ok(
!separateEngineCheckbox.hidden,
"Should not have hidden the separate search engine checkbox"
);
Assert.ok(
!privateDefaultVbox.hidden,
"Should not have hidden the private engine selection box"
);
await SpecialPowers.pushPrefEnv({
set: [["browser.search.separatePrivateDefault", false]],
});
Assert.ok(
!separateEngineCheckbox.hidden,
"Should not have hidden the separate search engine checkbox"
);
Assert.ok(
privateDefaultVbox.hidden,
"Should have hidden the private engine selection box"
);
await SpecialPowers.pushPrefEnv({
set: [["browser.search.separatePrivateDefault.ui.enabled", false]],
});
Assert.ok(
separateEngineCheckbox.hidden,
"Should have hidden the separate private engine checkbox"
);
Assert.ok(
privateDefaultVbox.hidden,
"Should still be hiding the private engine selection box"
);
gBrowser.removeCurrentTab();
});
add_task(async function test_separatePrivateDefault() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.search.separatePrivateDefault.ui.enabled", true],
["browser.search.separatePrivateDefault", false],
],
});
await openPreferencesViaOpenPreferencesAPI("search", { leaveOpen: true });
const doc = gBrowser.selectedBrowser.contentDocument;
const separateEngineCheckbox = doc.getElementById(
"browserSeparateDefaultEngine"
);
const privateDefaultVbox = doc.getElementById(
"browserPrivateEngineSelection"
);
Assert.ok(
privateDefaultVbox.hidden,
"Should not be displaying the private engine selection box"
);
separateEngineCheckbox.checked = false;
separateEngineCheckbox.doCommand();
Assert.ok(
Services.prefs.getBoolPref("browser.search.separatePrivateDefault"),
"Should have correctly set the pref"
);
Assert.ok(
!privateDefaultVbox.hidden,
"Should be displaying the private engine selection box"
);
separateEngineCheckbox.checked = true;
separateEngineCheckbox.doCommand();
Assert.ok(
!Services.prefs.getBoolPref("browser.search.separatePrivateDefault"),
"Should have correctly turned the pref off"
);
Assert.ok(
privateDefaultVbox.hidden,
"Should have hidden the private engine selection box"
);
gBrowser.removeCurrentTab();
});
async function setDefaultEngine(
testPrivate,
currentEngineName,
expectedEngineName
) {
await openPreferencesViaOpenPreferencesAPI("search", { leaveOpen: true });
const doc = gBrowser.selectedBrowser.contentDocument;
const defaultEngineSelector = doc.getElementById(
testPrivate ? "defaultPrivateEngine" : "defaultEngine"
);
Assert.equal(
defaultEngineSelector.selectedItem.engine.name,
currentEngineName,
"Should have the correct engine as default on first open"
);
const popup = defaultEngineSelector.menupopup;
const popupShown = BrowserTestUtils.waitForEvent(popup, "popupshown");
BrowserTestUtils.synthesizeMouseAtCenter(
defaultEngineSelector,
{},
gBrowser.selectedBrowser
);
await popupShown;
const items = Array.from(popup.children);
const engine2Item = items.find(
item => item.engine.name == expectedEngineName
);
const defaultChanged = SearchTestUtils.promiseSearchNotification(
testPrivate ? "engine-default-private" : "engine-default",
"browser-search-engine-modified"
);
const popupHidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
BrowserTestUtils.synthesizeMouseAtCenter(
engine2Item,
{},
gBrowser.selectedBrowser
);
await popupHidden;
await defaultChanged;
const newDefault = testPrivate
? await Services.search.getDefaultPrivate()
: await Services.search.getDefault();
Assert.equal(
newDefault.name,
expectedEngineName,
"Should have changed the default engine to engine2"
);
}
add_task(async function test_setDefaultEngine() {
const engine1 = Services.search.getEngineByName("engine1");
// Set an initial default so we have a known engine.
await Services.search.setDefault(engine1);
await setDefaultEngine(false, "engine1", "engine2");
gBrowser.removeCurrentTab();
});
add_task(async function test_setPrivateDefaultEngine() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.search.separatePrivateDefault.ui.enabled", true],
["browser.search.separatePrivateDefault", true],
],
});
const engine2 = Services.search.getEngineByName("engine2");
// Set an initial default so we have a known engine.
await Services.search.setDefaultPrivate(engine2);
await setDefaultEngine(true, "engine2", "engine1");
gBrowser.removeCurrentTab();
});

View File

@ -0,0 +1,27 @@
{
"name": "engine1",
"manifest_version": 2,
"version": "1.0",
"applications": {
"gecko": {
"id": "engine1@search.mozilla.org"
}
},
"hidden": true,
"description": "A small test engine",
"icons": {
"16": "favicon.ico"
},
"chrome_settings_overrides": {
"search_provider": {
"name": "engine1",
"search_url": "https://1.example.com/search",
"params": [
{
"name": "q",
"value": "{searchTerms}"
}
]
}
}
}

View File

@ -0,0 +1,27 @@
{
"name": "engine2",
"manifest_version": 2,
"version": "1.0",
"applications": {
"gecko": {
"id": "engine2@search.mozilla.org"
}
},
"hidden": true,
"description": "A small test engine",
"icons": {
"16": "favicon.ico"
},
"chrome_settings_overrides": {
"search_provider": {
"name": "engine2",
"search_url": "https://2.example.com/search",
"params": [
{
"name": "q",
"value": "{searchTerms}"
}
]
}
}
}

View File

@ -629,6 +629,13 @@ search-bar-shown =
search-engine-default-header = Default Search Engine
search-engine-default-desc = Choose the default search engine to use in the address bar and search bar.
search-engine-default-private-desc = Choose the default search engine to use in Private Windows.
search-separate-default-engine =
.label = Use this search engine in Private Windows
.accesskey = U
search-suggestions-header = Search Suggestions
search-suggestions-desc = Choose how suggestions from search engines appear.
search-suggestions-option =
.label = Provide search suggestions
@ -646,6 +653,8 @@ search-show-suggestions-url-bar-option =
search-show-suggestions-above-history-option =
.label = Show search suggestions ahead of browsing history in address bar results
suggestions-addressbar-settings = Change preferences for browsing history, bookmarks, and tab suggestions
search-suggestions-cant-show = Search suggestions will not be shown in location bar results because you have configured { -brand-short-name } to never remember history.
search-one-click-header = One-Click Search Engines

View File

@ -54,7 +54,7 @@ html|h2 {
}
description.indent,
.indent > description {
.indent:not(#browserPrivateEngineSelection) > description {
color: var(--in-content-deemphasized-text);
}

View File

@ -28,11 +28,13 @@
transform: scaleX(-1);
}
#defaultEngine {
#defaultEngine,
#defaultPrivateEngine {
margin-inline-start: 0;
}
#defaultEngine > .menulist-label-box > .menulist-icon {
#defaultEngine > .menulist-label-box > .menulist-icon,
#defaultPrivateEngine > .menulist-label-box > .menulist-icon {
height: 16px;
}