Bug 1647896 - Add local search mode one-offs for history, bookmarks, and tabs. r=harry,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D85237
This commit is contained in:
Drew Willcoxon 2020-07-29 22:11:21 +00:00
Родитель d9488224c3
Коммит 33b61e1ce5
10 изменённых файлов: 661 добавлений и 91 удалений

Просмотреть файл

@ -294,12 +294,6 @@ class SearchOneOffs {
}
if (val) {
val.setAttribute("selected", "true");
if (!val.engine) {
// If the button doesn't have an engine, then clear the popup's
// selection to indicate that pressing Return while the button is
// selected will do the button's command, not search.
this.selectedViewIndex = -1;
}
}
this._selectedButton = val;
@ -512,23 +506,30 @@ class SearchOneOffs {
this.settingsButton.id = origin + "-anon-search-settings";
this.settingsButtonCompact.id = origin + "-anon-search-settings-compact";
this._rebuildEngineList(engines);
this.dispatchEvent(new Event("rebuild"));
}
/**
* Adds one-offs for the given engines to the DOM.
*
* @param {array} engines
* The engines to add.
*/
_rebuildEngineList(engines) {
for (let i = 0; i < engines.length; ++i) {
let engine = engines[i];
let button = this.document.createXULElement("button");
button.engine = engine;
button.id = this._buttonIDForEngine(engine);
let uri = "chrome://browser/skin/search-engine-placeholder.png";
if (engine.iconURI) {
uri = engine.iconURI.spec;
}
button.setAttribute("image", uri);
let iconURI =
engine.iconURI?.spec ||
"chrome://browser/skin/search-engine-placeholder.png";
button.setAttribute("image", iconURI);
button.setAttribute("class", "searchbar-engine-one-off-item");
this.setTooltipForEngineButton(button);
this.buttons.appendChild(button);
}
this.dispatchEvent(new Event("rebuild"));
}
_rebuildAddEngineList() {
@ -880,6 +881,7 @@ class SearchOneOffs {
if (this.textbox && typeof textboxUserValue == "string") {
this.textbox.value = textboxUserValue;
}
this.selectedViewIndex = -1;
this.advanceSelection(false, true, true);
return true;
}

Просмотреть файл

@ -414,20 +414,19 @@ class UrlbarInput {
if (selectedOneOff && isMouseEvent && event.target != selectedOneOff) {
selectedOneOff = null;
}
if (selectedOneOff) {
if (!selectedOneOff.engine) {
// the settings button
this.controller.engagementEvent.discard();
selectedOneOff.doCommand();
return;
}
if (this.view.oneOffsRefresh) {
this.view.oneOffSearchButtons.handleSearchCommand(
event,
selectedOneOff.engine
);
return;
}
if (
selectedOneOff == this.view.oneOffSearchButtons.settingsButtonCompact
) {
this.controller.engagementEvent.discard();
selectedOneOff.doCommand();
return;
}
if (selectedOneOff && this.view.oneOffsRefresh) {
this.view.oneOffSearchButtons.handleSearchCommand(
event,
selectedOneOff.engine || selectedOneOff.source
);
return;
}
}
@ -1163,34 +1162,55 @@ class UrlbarInput {
/**
* Sets search mode and shows the search mode indicator.
*
* @param {nsISearchEngine | string} engineOrMode
* Either the search engine to restrict to or a mode described by a string.
* Exits search mode if null.
* @param {nsISearchEngine|SearchEngine|UrlbarUtils.RESULT_SOURCE} engineOrSource
* Either the search engine to restrict to or a UrlbarUtils.RESULT_SOURCE
* value. Exits search mode if null.
*/
setSearchMode(engineOrMode) {
setSearchMode(engineOrSource) {
if (!UrlbarPrefs.get("update2")) {
return;
}
let indicatorTitle;
if (!engineOrMode) {
this._searchModeIndicatorTitle.textContent = "";
this._searchModeLabel.textContent = "";
this._searchModeIndicatorTitle.removeAttribute("data-l10n-id");
this._searchModeLabel.removeAttribute("data-l10n-id");
if (!engineOrSource) {
this.searchMode = null;
this.removeAttribute("searchmode");
} else if (
engineOrMode instanceof Ci.nsISearchEngine ||
engineOrMode instanceof SearchEngine
engineOrSource instanceof Ci.nsISearchEngine ||
engineOrSource instanceof SearchEngine
) {
this.searchMode = {
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
engineName: engineOrMode.name,
engineName: engineOrSource.name,
};
indicatorTitle = engineOrMode.name;
this._searchModeIndicatorTitle.textContent = engineOrSource.name;
this._searchModeLabel.textContent = engineOrSource.name;
} else if (typeof engineOrSource == "number") {
let sourceName = UrlbarUtils.getResultSourceName(engineOrSource);
if (!sourceName) {
Cu.reportError(`Unrecognized source: ${engineOrSource}`);
this.searchMode = null;
} else {
this.searchMode = { source: engineOrSource };
let l10nID = `urlbar-search-mode-${sourceName}`;
this.document.l10n.setAttributes(
this._searchModeIndicatorTitle,
l10nID
);
this.document.l10n.setAttributes(this._searchModeLabel, l10nID);
}
} else {
Cu.reportError(`Unexpected search mode: ${engineOrSource}`);
}
if (this.searchMode) {
this.toggleAttribute("searchmode", true);
} else {
// TODO: Support non-RESULT_SOURCE.SEARCH search modes (bug 1647896).
this.removeAttribute("searchmode");
}
this._searchModeIndicatorTitle.textContent = indicatorTitle;
this._searchModeLabel.textContent = indicatorTitle;
}
// Getters and Setters below.

Просмотреть файл

@ -233,6 +233,7 @@ class Preferences {
*/
constructor() {
this._map = new Map();
this._observers = new Set();
this.QueryInterface = ChromeUtils.generateQI([
"nsIObserver",
"nsISupportsWeakReference",
@ -278,6 +279,28 @@ class Preferences {
setter(pref, value);
}
/**
* Adds a callback that will be called when a urlbar pref changes. `observer`
* will be passed the name of the pref that changed. For prefs in the
* `browser.urlbar.` branch, the name will be relative to the branch.
*
* @param {function} observer
* A callback function.
*/
addObserver(observer) {
this._observers.add(observer);
}
/**
* Removes an observer callback.
*
* @param {function} observer
* A callback function.
*/
removeObserver(observer) {
this._observers.delete(observer);
}
/**
* Observes preference changes.
*
@ -298,6 +321,10 @@ class Preferences {
if (pref.startsWith("suggest.")) {
this._map.delete("defaultBehavior");
}
for (let observer of this._observers) {
observer(pref);
}
}
/**

Просмотреть файл

@ -11,9 +11,37 @@ const { XPCOMUtils } = ChromeUtils.import(
);
XPCOMUtils.defineLazyModuleGetters(this, {
SearchOneOffs: "resource:///modules/SearchOneOffs.jsm",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.jsm",
UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.jsm",
UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
});
// Maps from RESULT_SOURCE values to { restrict, pref } objects.
const LOCAL_MODES = new Map([
[
UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
{
restrict: UrlbarTokenizer.RESTRICT.BOOKMARK,
pref: "suggest.bookmark",
},
],
[
UrlbarUtils.RESULT_SOURCE.TABS,
{
restrict: UrlbarTokenizer.RESTRICT.OPENPAGE,
pref: "suggest.openpage",
},
],
[
UrlbarUtils.RESULT_SOURCE.HISTORY,
{
restrict: UrlbarTokenizer.RESTRICT.HISTORY,
pref: "suggest.history",
},
],
]);
/**
* The one-off search buttons in the urlbar.
*/
@ -28,6 +56,17 @@ class UrlbarSearchOneOffs extends SearchOneOffs {
super(view.panel.querySelector(".search-one-offs"));
this.view = view;
this.input = view.input;
UrlbarPrefs.addObserver(pref => this._onPrefChanged(pref));
}
/**
* Returns the local search mode one-off buttons.
*
* @returns {array}
* The local one-off buttons.
*/
get localButtons() {
return this.getSelectableButtons(false).filter(b => b.source);
}
/**
@ -108,19 +147,20 @@ class UrlbarSearchOneOffs extends SearchOneOffs {
*
* @param {event} event
* The event that triggered the pick.
* @param {nsISearchEngine|SearchEngine} engine
* The engine that was picked.
* @param {nsISearchEngine|SearchEngine|UrlbarUtils.RESULT_SOURCE} engineOrSource
* The engine that was picked, or for local search mode sources, the source
* that was picked as a UrlbarUtils.RESULT_SOURCE value.
* @param {boolean} forceNewTab
* True if the search results page should be loaded in a new tab.
*/
handleSearchCommand(event, engine, forceNewTab = false) {
handleSearchCommand(event, engineOrSource, forceNewTab = false) {
if (!this.view.oneOffsRefresh) {
let { where, params } = this._whereToOpen(event, forceNewTab);
this.input.handleCommand(event, where, params);
return;
}
this.input.setSearchMode(engine);
this.input.setSearchMode(engineOrSource);
this.selectedButton = null;
this.input.startQuery({
allowAutofill: false,
@ -150,4 +190,79 @@ class UrlbarSearchOneOffs extends SearchOneOffs {
}
);
}
/**
* Overrides _rebuildEngineList to add the local one-offs.
*
* @param {array} engines
* The search engines to add.
*/
_rebuildEngineList(engines) {
super._rebuildEngineList(engines);
if (!this.view.oneOffsRefresh || !UrlbarPrefs.get("update2.localOneOffs")) {
return;
}
for (let [source, { restrict, pref }] of LOCAL_MODES) {
if (!UrlbarPrefs.get(pref)) {
// By design, don't show a local one-off when the user has disabled its
// corresponding pref.
continue;
}
let name = UrlbarUtils.getResultSourceName(source);
let button = this.document.createXULElement("button");
button.id = `urlbar-engine-one-off-item-${name}`;
button.setAttribute("class", "searchbar-engine-one-off-item");
this.document.l10n.setAttributes(button, `search-one-offs-${name}`, {
restrict,
});
button.source = source;
this.buttons.appendChild(button);
}
}
/**
* Overrides the superclass's click listener to handle clicks on local
* one-offs in addition to engine one-offs.
*
* @param {event} event
* The click event.
*/
_on_click(event) {
if (!this.view.oneOffsRefresh) {
super._on_click(event);
return;
}
// Ignore right clicks.
if (event.button == 2) {
return;
}
let button = event.originalTarget;
let engineOrSource = button.engine || button.source;
if (!engineOrSource) {
return;
}
this.handleSearchCommand(event, engineOrSource);
}
/**
* Called when a pref tracked by UrlbarPrefs changes.
*
* @param {string} changedPref
* The name of the pref, relative to `browser.urlbar.` if the pref is in
* that branch.
*/
_onPrefChanged(changedPref) {
// Null out this._engines when the local-one-offs-related prefs change so
// that they rebuild themselves the next time the view opens.
let prefs = [...LOCAL_MODES.values()].map(({ pref }) => pref);
prefs.push("update2", "update2.localOneOffs", "update2.oneOffsRefresh");
if (prefs.includes(changedPref)) {
this._engines = null;
}
}
}

Просмотреть файл

@ -746,6 +746,24 @@ var UrlbarUtils = {
}
return this._logger;
},
/**
* Returns the name of a result source. The name is the lowercase name of the
* corresponding property in the RESULT_SOURCE object.
*
* @param {string} source A UrlbarUtils.RESULT_SOURCE value.
* @returns {string} The token's name, a lowercased name in the RESULT_SOURCE
* object.
*/
getResultSourceName(source) {
if (!this._resultSourceNamesBySource) {
this._resultSourceNamesBySource = new Map();
for (let [name, src] of Object.entries(this.RESULT_SOURCE)) {
this._resultSourceNamesBySource.set(src, name.toLowerCase());
}
}
return this._resultSourceNamesBySource.get(source);
},
};
XPCOMUtils.defineLazyGetter(UrlbarUtils.ICON, "DEFAULT", () => {

Просмотреть файл

@ -33,6 +33,9 @@ var UrlbarTestUtils = {
*/
init(scope) {
this._testScope = scope;
if (scope) {
this.Assert = scope.Assert;
}
},
/**
@ -347,24 +350,72 @@ var UrlbarTestUtils = {
},
/**
* @param {object} win The browser window
* @param {string} [engineName]
* @returns {boolean} True if the UrlbarInput is in search mode. If
* engineName is specified, only returns true if the search mode engine
* matches.
* Asserts that the input is in a given search mode, or no search mode.
*
* @param {Window} window
* The browser window.
* @param {object} expectedSearchMode
* The expected search mode object.
*/
isInSearchMode(win, engineName = null) {
if (!!win.gURLBar.searchMode != win.gURLBar.hasAttribute("searchmode")) {
throw new Error(
"Urlbar should never be in search mode without the corresponding attribute."
assertSearchMode(window, expectedSearchMode) {
this.Assert.equal(
!!window.gURLBar.searchMode,
window.gURLBar.hasAttribute("searchmode"),
"Urlbar should never be in search mode without the corresponding attribute."
);
if (!expectedSearchMode) {
this.Assert.ok(
!window.gURLBar.searchMode,
"gURLBar.searchMode not expected"
);
return;
}
this.Assert.deepEqual(
window.gURLBar.searchMode,
expectedSearchMode,
"Expected searchMode"
);
// Check the textContent and l10n attributes of the indicator and label.
let expectedTextContent = "";
let expectedL10n = {};
if (expectedSearchMode.engineName) {
expectedTextContent = expectedSearchMode.engineName;
} else if (expectedSearchMode.source) {
let name = UrlbarUtils.getResultSourceName(expectedSearchMode.source);
this.Assert.ok(name, "Expected result source should have a name");
expectedL10n = { id: `urlbar-search-mode-${name}` };
} else {
this.Assert.ok(false, "Unexpected searchMode");
}
// document.l10n.getAttributes returns an object with a null value for each
// of these properties when they aren't present.
if (!expectedL10n.id) {
expectedL10n.id = null;
}
if (!expectedL10n.args) {
expectedL10n.args = null;
}
for (let element of [
window.gURLBar._searchModeIndicatorTitle,
window.gURLBar._searchModeLabel,
]) {
this.Assert.equal(
element.textContent,
expectedTextContent,
"Expected textContent"
);
this.Assert.deepEqual(
window.document.l10n.getAttributes(element),
expectedL10n,
"Expected l10n"
);
}
if (engineName) {
return win.gURLBar.searchMode.engineName == engineName;
}
return !!win.gURLBar.searchMode;
},
/**

Просмотреть файл

@ -331,7 +331,7 @@ add_task(async function searchWith() {
await hidePopup();
});
// Clicks a one-off.
// Clicks a one-off with an engine.
add_task(async function oneOffClick() {
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
@ -357,10 +357,10 @@ add_task(async function oneOffClick() {
UrlbarTestUtils.isPopupOpen(window),
"Urlbar view is still open."
);
Assert.ok(
UrlbarTestUtils.isInSearchMode(window, oneOffs[0].engine.name),
"The Urlbar is in search mode."
);
UrlbarTestUtils.assertSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
engineName: oneOffs[0].engine.name,
});
window.gURLBar.setSearchMode(null);
} else {
let resultsPromise = BrowserTestUtils.browserLoaded(
@ -377,7 +377,7 @@ add_task(async function oneOffClick() {
await UrlbarTestUtils.formHistory.clear();
});
// Presses the Return key when a one-off is selected.
// Presses the Return key when a one-off with an engine is selected.
add_task(async function oneOffReturn() {
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
@ -408,10 +408,10 @@ add_task(async function oneOffReturn() {
UrlbarTestUtils.isPopupOpen(window),
"Urlbar view is still open."
);
Assert.ok(
UrlbarTestUtils.isInSearchMode(window, oneOffs[0].engine.name),
"The Urlbar is in search mode."
);
UrlbarTestUtils.assertSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
engineName: oneOffs[0].engine.name,
});
window.gURLBar.setSearchMode(null);
} else {
let resultsPromise = BrowserTestUtils.browserLoaded(
@ -428,6 +428,7 @@ add_task(async function oneOffReturn() {
await UrlbarTestUtils.formHistory.clear();
});
// Hidden engines should not appear in the one-offs.
add_task(async function hiddenOneOffs() {
// Disable all the engines but the current one, check the oneoffs are
// hidden and that moving up selects the last match.
@ -455,6 +456,7 @@ add_task(async function hiddenOneOffs() {
EventUtils.synthesizeKey("KEY_ArrowUp");
assertState(1, -1);
await hidePopup();
await SpecialPowers.popPrefEnv();
});
// The one-offs should be hidden when searching with an "@engine" search engine
@ -489,6 +491,285 @@ add_task(async function hiddenWhenUsingSearchAlias() {
await hidePopup();
});
// Makes sure local search mode one-offs don't exist without update2.
add_task(async function localOneOffsWithoutUpdate2() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.update2", false]],
});
// Null out _engines so that the one-offs rebuild themselves when the view
// opens.
oneOffSearchButtons._engines = null;
let rebuildPromise = BrowserTestUtils.waitForEvent(
oneOffSearchButtons,
"rebuild"
);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "localOneOffsWithoutUpdate2",
});
await rebuildPromise;
Assert.equal(oneOffSearchButtons.localButtons.length, 0);
Assert.equal(
document.getElementById("urlbar-engine-one-off-item-bookmarks"),
null,
"Bookmarks one-off should not exist"
);
Assert.equal(
document.getElementById("urlbar-engine-one-off-item-tabs"),
null,
"Tabs one-off should not exist"
);
Assert.equal(
document.getElementById("urlbar-engine-one-off-item-history"),
null,
"History one-off should not exist"
);
await hidePopup();
await SpecialPowers.popPrefEnv();
});
// Makes sure local search mode one-offs exist with update2.
add_task(async function localOneOffsWithUpdate2() {
// Null out _engines so that the one-offs rebuild themselves when the view
// opens.
oneOffSearchButtons._engines = null;
await doLocalOneOffsShownTest();
});
// Clicks a local search mode one-off.
add_task(async function localOneOffClick() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.update2", true],
["browser.urlbar.update2.localOneOffs", true],
["browser.urlbar.update2.oneOffsRefresh", true],
],
});
// We are explicitly using something that looks like a url, to make the test
// stricter. Even if it looks like a url, we should search.
let typedValue = "foo.bar";
// Null out _engines so that the one-offs rebuild themselves when the view
// opens.
oneOffSearchButtons._engines = null;
let rebuildPromise = BrowserTestUtils.waitForEvent(
oneOffSearchButtons,
"rebuild"
);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: typedValue,
});
await rebuildPromise;
let buttons = oneOffSearchButtons.localButtons;
Assert.ok(buttons.length, "Sanity check: Local one-offs exist");
for (let button of buttons) {
Assert.ok(button.source, "Sanity check: Button has a source");
EventUtils.synthesizeMouseAtCenter(button, {});
Assert.ok(
UrlbarTestUtils.isPopupOpen(window),
"Urlbar view is still open."
);
UrlbarTestUtils.assertSearchMode(window, {
source: button.source,
});
}
window.gURLBar.setSearchMode(null);
await hidePopup();
await SpecialPowers.popPrefEnv();
});
// Presses the Return key when a local search mode one-off is selected.
add_task(async function localOneOffReturn() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.update2", true],
["browser.urlbar.update2.localOneOffs", true],
["browser.urlbar.update2.oneOffsRefresh", true],
],
});
// We are explicitly using something that looks like a url, to make the test
// stricter. Even if it looks like a url, we should search.
let typedValue = "foo.bar";
// Null out _engines so that the one-offs rebuild themselves when the view
// opens.
oneOffSearchButtons._engines = null;
let rebuildPromise = BrowserTestUtils.waitForEvent(
oneOffSearchButtons,
"rebuild"
);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: typedValue,
});
await rebuildPromise;
let buttons = oneOffSearchButtons.localButtons;
Assert.ok(buttons.length, "Sanity check: Local one-offs exist");
let allButtons = oneOffSearchButtons.getSelectableButtons(false);
let firstLocalIndex = allButtons.length - buttons.length;
for (let i = 0; i < buttons.length; i++) {
let button = buttons[i];
// Alt+Down enough times to select the button.
let index = firstLocalIndex + i;
EventUtils.synthesizeKey("KEY_ArrowDown", {
altKey: true,
repeat: index + 1,
});
await TestUtils.waitForCondition(
() => oneOffSearchButtons.selectedButtonIndex == index,
"Waiting for local one-off to become selected"
);
assertState(0, index, typedValue);
Assert.ok(button.source, "Sanity check: Button has a source");
EventUtils.synthesizeKey("KEY_Enter");
Assert.ok(
UrlbarTestUtils.isPopupOpen(window),
"Urlbar view is still open."
);
UrlbarTestUtils.assertSearchMode(window, {
source: button.source,
});
}
window.gURLBar.setSearchMode(null);
await hidePopup();
await SpecialPowers.popPrefEnv();
});
// The bookmarks one-off should be hidden when suggest.bookmark is false.
add_task(async function hiddenBookmarksOneOff() {
await doLocalOneOffsShownTest("suggest.bookmark");
});
// The tabs one-off should be hidden when suggest.openpage is false.
add_task(async function hiddenTabsOneOff() {
await doLocalOneOffsShownTest("suggest.openpage");
});
// The history one-off should be hidden when suggest.history is false.
add_task(async function hiddenHistoryOneOff() {
await doLocalOneOffsShownTest("suggest.history");
});
/**
* Checks that the local one-offs are shown correctly.
*
* @param {boolean} prefToDisable
* If given, this should be one of the `browser.urlbar.suggest` prefs. It
* will be disabled before running the test to make sure the corresponding
* button isn't shown.
*/
async function doLocalOneOffsShownTest(prefToDisable = "") {
let prefs = [
["browser.urlbar.update2", true],
["browser.urlbar.update2.localOneOffs", true],
["browser.urlbar.update2.oneOffsRefresh", true],
];
if (prefToDisable) {
prefs.push([`browser.urlbar.${prefToDisable}`, false]);
}
await SpecialPowers.pushPrefEnv({ set: prefs });
if (prefToDisable) {
Assert.equal(
UrlbarPrefs.get(prefToDisable),
false,
`Sanity check: Pref ${prefToDisable} is disabled`
);
}
let rebuildPromise = BrowserTestUtils.waitForEvent(
oneOffSearchButtons,
"rebuild"
);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "doLocalOneOffsShownTest",
});
await rebuildPromise;
let buttons = oneOffSearchButtons.localButtons;
Assert.equal(
buttons.length,
prefToDisable ? 2 : 3,
"Expected number of local buttons"
);
let expectedSource;
let seenIDs = new Set();
for (let button of buttons) {
Assert.ok(
!seenIDs.has(button.id),
"Should not have already seen button.id"
);
seenIDs.add(button.id);
switch (button.id) {
case "urlbar-engine-one-off-item-bookmarks":
expectedSource = UrlbarUtils.RESULT_SOURCE.BOOKMARKS;
break;
case "urlbar-engine-one-off-item-tabs":
expectedSource = UrlbarUtils.RESULT_SOURCE.TABS;
break;
case "urlbar-engine-one-off-item-history":
expectedSource = UrlbarUtils.RESULT_SOURCE.HISTORY;
break;
default:
Assert.ok(false, `Unexpected local button ID: ${button.id}`);
break;
}
Assert.equal(button.source, expectedSource, "Expected button.source");
switch (prefToDisable) {
case "suggest.bookmark":
Assert.notEqual(expectedSource, UrlbarUtils.RESULT_SOURCE.BOOKMARKS);
break;
case "suggest.openpage":
Assert.notEqual(expectedSource, UrlbarUtils.RESULT_SOURCE.TABS);
break;
case "suggest.history":
Assert.notEqual(expectedSource, UrlbarUtils.RESULT_SOURCE.HISTORY);
break;
default:
if (prefToDisable) {
Assert.ok(false, `Unexpected pref: ${prefToDisable}`);
}
break;
}
}
await hidePopup();
if (prefToDisable) {
// Run the test again with the pref toggled back to true in order to test
// that the buttons automatically rebuild on the pref change and that all
// buttons are shown.
info(`Running test again with ${prefToDisable} = true`);
await SpecialPowers.pushPrefEnv({
set: [[`browser.urlbar.${prefToDisable}`, true]],
});
await doLocalOneOffsShownTest();
await SpecialPowers.popPrefEnv();
}
await SpecialPowers.popPrefEnv();
}
function assertState(result, oneOff, textValue = undefined) {
Assert.equal(
UrlbarTestUtils.getSelectedRowIndex(window),

Просмотреть файл

@ -56,10 +56,10 @@ async function enterSearchMode(window) {
EventUtils.synthesizeMouseAtCenter(oneOffs[0], {});
await searchPromise;
Assert.ok(UrlbarTestUtils.isPopupOpen(window), "Urlbar view is still open.");
Assert.ok(
UrlbarTestUtils.isInSearchMode(window, oneOffs[0].engine.name),
"The Urlbar is in search mode."
);
UrlbarTestUtils.assertSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
engineName: oneOffs[0].engine.name,
});
}
/**
@ -91,10 +91,7 @@ async function exitSearchMode(
EventUtils.synthesizeKey("KEY_Backspace");
}
Assert.equal(gURLBar.value, urlbarValue, "Urlbar value hasn't changed.");
Assert.ok(
!UrlbarTestUtils.isInSearchMode(window),
"The Urlbar is no longer in search mode."
);
UrlbarTestUtils.assertSearchMode(window, null);
} else if (clickClose) {
// We need to hover the indicator to make the close button clickable in the
// test.
@ -110,10 +107,7 @@ async function exitSearchMode(
} else {
EventUtils.synthesizeMouseAtCenter(closeButton, {});
}
Assert.ok(
!UrlbarTestUtils.isInSearchMode(window),
"The Urlbar is no longer in search mode."
);
UrlbarTestUtils.assertSearchMode(window, null);
}
}
@ -223,18 +217,19 @@ add_task(async function escape() {
EventUtils.synthesizeKey("KEY_Escape");
Assert.ok(!UrlbarTestUtils.isPopupOpen(window, "UrlbarView is closed."));
Assert.equal(gURLBar.value, TEST_QUERY, "Urlbar value hasn't changed.");
Assert.ok(
UrlbarTestUtils.isInSearchMode(window),
"The Urlbar is in search mode."
);
let oneOffs = UrlbarTestUtils.getOneOffSearchButtons(
window
).getSelectableButtons(true);
UrlbarTestUtils.assertSearchMode(window, {
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
engineName: oneOffs[0].engine.name,
});
EventUtils.synthesizeKey("KEY_Escape");
Assert.ok(!UrlbarTestUtils.isPopupOpen(window, "UrlbarView is closed."));
Assert.ok(!gURLBar.value, "Urlbar value is empty.");
Assert.ok(
!UrlbarTestUtils.isInSearchMode(window),
"The Urlbar is not in search mode."
);
UrlbarTestUtils.assertSearchMode(window, null);
});
// Tests that the indicator is removed when its close button is clicked.

Просмотреть файл

@ -110,6 +110,12 @@ urlbar-tip-icon-description =
urlbar-search-tips-onboard = Type less, find more: Search { $engineName } right from your address bar.
urlbar-search-tips-redirect-2 = Start your search in the address bar to see suggestions from { $engineName } and your browsing history.
## Local search mode indicator labels in the urlbar
urlbar-search-mode-bookmarks = Bookmarks
urlbar-search-mode-tabs = Tabs
urlbar-search-mode-history = History
##
urlbar-geolocation-blocked =
@ -197,6 +203,20 @@ search-one-offs-context-set-as-default-private =
search-one-offs-engine-with-alias =
.tooltiptext = { $engineName } ({ $alias })
## Local search mode one-off buttons
## Variables:
## $restrict (String): The restriction token corresponding to the search mode.
## Restriction tokens are special characters users can type in the urlbar to
## restrict their searches to certain sources (e.g., "*" to search only
## bookmarks).
search-one-offs-bookmarks =
.tooltiptext = Bookmarks ({ $restrict })
search-one-offs-tabs =
.tooltiptext = Tabs ({ $restrict })
search-one-offs-history =
.tooltiptext = History ({ $restrict })
## Bookmark Panel
bookmark-panel-show-editor-checkbox =

Просмотреть файл

@ -503,6 +503,47 @@
flex-grow: 1;
}
#urlbar-engine-one-off-item-bookmarks > .button-box > .button-icon {
list-style-image: url("chrome://browser/skin/bookmark.svg");
fill: #0060df; /* Blue-60 */
fill-opacity: 1;
-moz-context-properties: fill, fill-opacity;
}
#urlbar-engine-one-off-item-tabs > .button-box > .button-icon {
list-style-image: url("chrome://browser/skin/tab.svg");
fill: #008eaf; /* Teal-70 */
fill-opacity: 1;
-moz-context-properties: fill, fill-opacity;
}
#urlbar-engine-one-off-item-history > .button-box > .button-icon {
list-style-image: url("chrome://browser/skin/history.svg");
fill: #0c0c0d; /* Grey-90 */
fill-opacity: 1;
-moz-context-properties: fill, fill-opacity;
}
:root[lwt-toolbar-field-brighttext] #urlbar-engine-one-off-item-bookmarks > .button-box > .button-icon {
fill: #0a84ff; /* Blue-50 */
}
:root[lwt-toolbar-field-brighttext] #urlbar-engine-one-off-item-tabs > .button-box > .button-icon {
fill: #00c8d7; /* Teal-60 */
}
:root[lwt-toolbar-field-brighttext] #urlbar-engine-one-off-item-history > .button-box > .button-icon {
fill: currentColor;
fill-opacity: .6;
}
:root #urlbar-engine-one-off-item-bookmarks[selected] > .button-box > .button-icon,
:root #urlbar-engine-one-off-item-tabs[selected] > .button-box > .button-icon,
:root #urlbar-engine-one-off-item-history[selected] > .button-box > .button-icon {
fill: currentColor;
fill-opacity: .6;
}
/* search bar popup */
#PopupSearchAutoComplete {