Bug 1955066 - Enter search mode after adding an HTML form as a search engine. r=urlbar-reviewers,search-reviewers,scunnane,daisuke

Differential Revision: https://phabricator.services.mozilla.com/D242187
This commit is contained in:
Moritz Beier
2025-03-26 09:58:54 +00:00
parent c90e6e3c8f
commit b3f32df67e
5 changed files with 72 additions and 61 deletions

View File

@@ -222,7 +222,7 @@ document.addEventListener(
if (!gContextMenu) { if (!gContextMenu) {
throw new Error("Context menu doesn't seem to be open."); throw new Error("Context menu doesn't seem to be open.");
} }
gContextMenu.addSearchFieldAsEngine(); gContextMenu.addSearchFieldAsEngine().catch(console.error);
break; break;
case "context-searchselect": { case "context-searchselect": {
let { searchTerms, usePrivate, principal, csp } = event.target; let { searchTerms, usePrivate, principal, csp } = event.target;

View File

@@ -2346,18 +2346,34 @@ export class nsContextMenu {
}); });
} }
addSearchFieldAsEngine() { async addSearchFieldAsEngine() {
this.actor let { url, formData, charset, method } =
.getSearchFieldEngineData(this.targetIdentifier) await this.actor.getSearchFieldEngineData(this.targetIdentifier);
.then(async ({ url, formData, charset, method }) => {
let icon = this.browser.mIconURL; let { engineInfo } = await this.window.gDialogBox.open(
let uri = Services.io.newURI(url);
await this.window.gDialogBox.open(
"chrome://browser/content/search/addEngine.xhtml", "chrome://browser/content/search/addEngine.xhtml",
{ uri, formData, charset, method, icon, mode: "FORM", title: true } {
mode: "FORM",
title: true,
nameTemplate: Services.io.newURI(url).host,
}
); );
})
.catch(console.error); // If the user saved, engineInfo contains `name` and `alias`.
// Otherwise, it's undefined.
if (engineInfo) {
let searchEngine = await Services.search.addUserEngine({
name: engineInfo.name,
alias: engineInfo.alias,
url,
formData,
charset,
method,
icon: this.browser.mIconURL,
});
this.window.gURLBar.search("", { searchEngine });
}
} }
/** /**

View File

@@ -8,8 +8,8 @@
// in about:preferences, or when adding a search engine via the context menu of // in about:preferences, or when adding a search engine via the context menu of
// an HTML form. Depending on the scenario where it is used, different arguments // an HTML form. Depending on the scenario where it is used, different arguments
// must be supplied in an object in `window.arguments[0]`: // must be supplied in an object in `window.arguments[0]`:
// - `mode` (required) // - `mode` [required] - The type of dialog: NEW, EDIT or FORM.
// - `title` (optional, to display a title in the window element) // - `title` [optional] - To display a title in the window element.
// - all arguments required by the constructor of the dialog class // - all arguments required by the constructor of the dialog class
const lazy = {}; const lazy = {};
@@ -117,10 +117,12 @@ class EditEngineDialog extends EngineDialog {
/** /**
* Initializes the dialog with information from a user search engine. * Initializes the dialog with information from a user search engine.
* *
* @param {nsISearchEngine} engine * @param {object} args
* The arguments.
* @param {nsISearchEngine} args.engine
* The search engine to edit. Must be a UserSearchEngine. * The search engine to edit. Must be a UserSearchEngine.
*/ */
constructor(engine) { constructor({ engine }) {
super(); super();
this._postData = document.getElementById("enginePostData"); this._postData = document.getElementById("enginePostData");
@@ -228,48 +230,33 @@ class EditEngineDialog extends EngineDialog {
} }
/** /**
* This dialog is opened via the context menu of an input and * This dialog is opened via the context menu of an input and lets the
* will add a search engine based on the input's form. * user choose a name and an alias for an engine. Unlike the other two
* dialogs, it does not add or change an engine in the search service,
* and instead returns the user input to the caller.
*
* The chosen name and alias are returned via `window.arguments[0].engineInfo`.
* If the user chooses to not save the engine, it's undefined.
*/ */
class NewEngineFromFormDialog extends EngineDialog { class NewEngineFromFormDialog extends EngineDialog {
#formData;
#charset;
#method;
#icon;
#uri;
/** /**
* Initializes the dialog with data from an HTML form. The search input's * Initializes the dialog.
* formData value should be "{searchTerms}".
* *
* @param {object} args * @param {object} args
* The arguments. * The arguments.
* @param {nsIURI} args.uri * @param {string} args.nameTemplate
* The full action url of the form. * The initial value of the name input.
* @param {FormData} args.formData
* The FormData of the form.
* @param {string} args.charset
* The character encoding of the form's page.
* @param {string} args.method
* The HTTP method used to submit the form.
* @param {string} args.icon
* Url of the page's favicon.
*/ */
constructor({ uri, formData, charset, method, icon }) { constructor({ nameTemplate }) {
super(); super();
this.#formData = formData; this._name.value = nameTemplate;
this.#charset = charset;
this.#method = method;
this.#icon = icon;
this.#uri = uri.spec;
this._name.value = uri.host;
this.onNameInput(); this.onNameInput();
this.onAliasInput(); this.onAliasInput();
document.getElementById("engineUrlRow").remove(); document.getElementById("engineUrlRow").remove();
document.getElementById("suggestUrlRow").remove();
this._url = null; this._url = null;
document.getElementById("suggestUrlRow").remove();
this._suggestUrl = null;
let title = { raw: document.title }; let title = { raw: document.title };
document.documentElement.setAttribute("headertitle", JSON.stringify(title)); document.documentElement.setAttribute("headertitle", JSON.stringify(title));
@@ -280,15 +267,11 @@ class NewEngineFromFormDialog extends EngineDialog {
} }
onAddEngine() { onAddEngine() {
Services.search.addUserEngine({ window.arguments[0].engineInfo = {
url: this.#uri,
name: this._name.value.trim(), name: this._name.value.trim(),
// Empty string means no alias.
alias: this._alias.value.trim(), alias: this._alias.value.trim(),
formData: this.#formData, };
charset: this.#charset,
method: this.#method,
icon: this.#icon,
});
} }
} }
@@ -307,7 +290,7 @@ window.addEventListener("DOMContentLoaded", () => {
gAddEngineDialog = new NewEngineDialog(); gAddEngineDialog = new NewEngineDialog();
break; break;
case "EDIT": case "EDIT":
gAddEngineDialog = new EditEngineDialog(window.arguments[0].engine); gAddEngineDialog = new EditEngineDialog(window.arguments[0]);
break; break;
case "FORM": case "FORM":
gAddEngineDialog = new NewEngineFromFormDialog(window.arguments[0]); gAddEngineDialog = new NewEngineFromFormDialog(window.arguments[0]);

View File

@@ -3,6 +3,14 @@
"use strict"; "use strict";
ChromeUtils.defineLazyGetter(this, "UrlbarTestUtils", () => {
const { UrlbarTestUtils: module } = ChromeUtils.importESModule(
"resource://testing-common/UrlbarTestUtils.sys.mjs"
);
module.init(this);
return module;
});
const TESTS = [ const TESTS = [
{ {
action: "/search", action: "/search",
@@ -95,13 +103,13 @@ async function addEngine(browser, selector, name, alias) {
fillTextField("engineAlias", alias, dialogWin); fillTextField("engineAlias", alias, dialogWin);
info("Saving engine."); info("Saving engine.");
let promiseAdded = SearchTestUtils.promiseSearchNotification(
SearchUtils.MODIFIED_TYPE.ADDED,
SearchUtils.TOPIC_ENGINE_MODIFIED
);
EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin); EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin);
await window.gDialogBox.dialog._closingPromise; await TestUtils.waitForCondition(
await promiseAdded; () => gURLBar.searchMode?.engineName == name
);
Assert.ok(true, "Went into search mode.");
await UrlbarTestUtils.exitSearchMode(window);
return Services.search.getEngineByName(name); return Services.search.getEngineByName(name);
} }

View File

@@ -217,7 +217,11 @@ export class SearchModeSwitcher {
switch (topic) { switch (topic) {
case "browser-search-engine-modified": { case "browser-search-engine-modified": {
if (data === "engine-default" || data === "engine-default-private") { if (
data === "engine-default" ||
data === "engine-default-private" ||
data === "engine-icon-changed"
) {
this.updateSearchIcon(); this.updateSearchIcon();
} }
break; break;