зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
d9488224c3
Коммит
33b61e1ce5
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче