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) {
throw new Error("Context menu doesn't seem to be open.");
}
gContextMenu.addSearchFieldAsEngine();
gContextMenu.addSearchFieldAsEngine().catch(console.error);
break;
case "context-searchselect": {
let { searchTerms, usePrivate, principal, csp } = event.target;

View File

@@ -2346,18 +2346,34 @@ export class nsContextMenu {
});
}
addSearchFieldAsEngine() {
this.actor
.getSearchFieldEngineData(this.targetIdentifier)
.then(async ({ url, formData, charset, method }) => {
let icon = this.browser.mIconURL;
let uri = Services.io.newURI(url);
await this.window.gDialogBox.open(
"chrome://browser/content/search/addEngine.xhtml",
{ uri, formData, charset, method, icon, mode: "FORM", title: true }
);
})
.catch(console.error);
async addSearchFieldAsEngine() {
let { url, formData, charset, method } =
await this.actor.getSearchFieldEngineData(this.targetIdentifier);
let { engineInfo } = await this.window.gDialogBox.open(
"chrome://browser/content/search/addEngine.xhtml",
{
mode: "FORM",
title: true,
nameTemplate: Services.io.newURI(url).host,
}
);
// 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
// an HTML form. Depending on the scenario where it is used, different arguments
// must be supplied in an object in `window.arguments[0]`:
// - `mode` (required)
// - `title` (optional, to display a title in the window element)
// - `mode` [required] - The type of dialog: NEW, EDIT or FORM.
// - `title` [optional] - To display a title in the window element.
// - all arguments required by the constructor of the dialog class
const lazy = {};
@@ -117,10 +117,12 @@ class EditEngineDialog extends EngineDialog {
/**
* 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.
*/
constructor(engine) {
constructor({ engine }) {
super();
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
* will add a search engine based on the input's form.
* This dialog is opened via the context menu of an input and lets the
* 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 {
#formData;
#charset;
#method;
#icon;
#uri;
/**
* Initializes the dialog with data from an HTML form. The search input's
* formData value should be "{searchTerms}".
* Initializes the dialog.
*
* @param {object} args
* The arguments.
* @param {nsIURI} args.uri
* The full action url of the form.
* @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.
* @param {string} args.nameTemplate
* The initial value of the name input.
*/
constructor({ uri, formData, charset, method, icon }) {
constructor({ nameTemplate }) {
super();
this.#formData = formData;
this.#charset = charset;
this.#method = method;
this.#icon = icon;
this.#uri = uri.spec;
this._name.value = uri.host;
this._name.value = nameTemplate;
this.onNameInput();
this.onAliasInput();
document.getElementById("engineUrlRow").remove();
document.getElementById("suggestUrlRow").remove();
this._url = null;
document.getElementById("suggestUrlRow").remove();
this._suggestUrl = null;
let title = { raw: document.title };
document.documentElement.setAttribute("headertitle", JSON.stringify(title));
@@ -280,15 +267,11 @@ class NewEngineFromFormDialog extends EngineDialog {
}
onAddEngine() {
Services.search.addUserEngine({
url: this.#uri,
window.arguments[0].engineInfo = {
name: this._name.value.trim(),
// Empty string means no alias.
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();
break;
case "EDIT":
gAddEngineDialog = new EditEngineDialog(window.arguments[0].engine);
gAddEngineDialog = new EditEngineDialog(window.arguments[0]);
break;
case "FORM":
gAddEngineDialog = new NewEngineFromFormDialog(window.arguments[0]);

View File

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

View File

@@ -217,7 +217,11 @@ export class SearchModeSwitcher {
switch (topic) {
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();
}
break;