Bug 615761 - Made window.external.AddSearchProvider prompts tab modal. r=johannh,Standard8

Differential Revision: https://phabricator.services.mozilla.com/D69838
This commit is contained in:
pbz 2020-04-22 17:34:16 +00:00
Родитель c788dd3c4b
Коммит efff5156a0
7 изменённых файлов: 145 добавлений и 37 удалений

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

@ -21,7 +21,12 @@ function AddSearchProvider(...args) {
); );
} }
function promiseDialogOpened() { const tabModalEnabled =
Services.prefs.getIntPref("prompts.modalType.searchService") !==
Services.prompt.MODAL_TYPE_WINDOW &&
Services.prefs.getBoolPref("prompts.tab_modal.enabled");
function promiseWindowDialogOpened() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Services.wm.addListener({ Services.wm.addListener({
onOpenWindow(xulWin) { onOpenWindow(xulWin) {
@ -40,6 +45,54 @@ function promiseDialogOpened() {
}); });
} }
function promiseTabDialogOpened() {
return new Promise(resolve => {
let browser = gBrowser.selectedBrowser;
BrowserTestUtils.waitForEvent(browser, "DOMWillOpenModalDialog").then(
() => {
// Wait for the next event tick to make sure the prompt has opened.
SimpleTest.executeSoon(() => {
let promptBox = gBrowser.getTabModalPromptBox(
gBrowser.selectedBrowser
);
resolve(promptBox.listPrompts()[0].Dialog);
});
}
);
});
}
function promiseDialogOpened() {
if (tabModalEnabled) {
return promiseTabDialogOpened();
}
return promiseWindowDialogOpened();
}
function acceptDialog(dialog) {
if (tabModalEnabled) {
dialog.ui.button0.click();
} else {
dialog.document.getElementById("commonDialog").acceptDialog();
}
return BrowserTestUtils.waitForEvent(
gBrowser.selectedBrowser,
"DOMModalDialogClosed"
);
}
function cancelDialog(dialog) {
if (tabModalEnabled) {
dialog.ui.button1.click();
} else {
dialog.document.getElementById("commonDialog").cancelDialog();
}
return BrowserTestUtils.waitForEvent(
gBrowser.selectedBrowser,
"DOMModalDialogClosed"
);
}
add_task(async function test_working() { add_task(async function test_working() {
gBrowser.selectedTab = AddSearchProvider(ROOT + "testEngine.xml"); gBrowser.selectedTab = AddSearchProvider(ROOT + "testEngine.xml");
@ -54,7 +107,7 @@ add_task(async function test_working() {
getString("addEngineConfirmation", "Foo", "example.com"), getString("addEngineConfirmation", "Foo", "example.com"),
"Should have seen the right install message" "Should have seen the right install message"
); );
dialog.document.getElementById("commonDialog").cancelDialog(); await cancelDialog(dialog);
gBrowser.removeCurrentTab(); gBrowser.removeCurrentTab();
}); });
@ -75,7 +128,7 @@ add_task(async function test_HTTP() {
getString("addEngineConfirmation", "Foo", "example.com"), getString("addEngineConfirmation", "Foo", "example.com"),
"Should have seen the right install message" "Should have seen the right install message"
); );
dialog.document.getElementById("commonDialog").cancelDialog(); await cancelDialog(dialog);
gBrowser.removeCurrentTab(); gBrowser.removeCurrentTab();
}); });
@ -94,7 +147,7 @@ add_task(async function test_relative() {
getString("addEngineConfirmation", "Foo", "example.com"), getString("addEngineConfirmation", "Foo", "example.com"),
"Should have seen the right install message" "Should have seen the right install message"
); );
dialog.document.getElementById("commonDialog").cancelDialog(); await cancelDialog(dialog);
gBrowser.removeCurrentTab(); gBrowser.removeCurrentTab();
}); });
@ -110,7 +163,7 @@ add_task(async function test_invalid() {
getString("error_invalid_engine_msg2", brandName, url), getString("error_invalid_engine_msg2", brandName, url),
"Should have seen the right error message" "Should have seen the right error message"
); );
dialog.document.getElementById("commonDialog").acceptDialog(); await acceptDialog(dialog);
gBrowser.removeCurrentTab(); gBrowser.removeCurrentTab();
}); });
@ -126,7 +179,7 @@ add_task(async function test_missing() {
getString("error_loading_engine_msg2", brandName, url), getString("error_loading_engine_msg2", brandName, url),
"Should have seen the right error message" "Should have seen the right error message"
); );
dialog.document.getElementById("commonDialog").acceptDialog(); await acceptDialog(dialog);
gBrowser.removeCurrentTab(); gBrowser.removeCurrentTab();
}); });
@ -142,7 +195,7 @@ add_task(async function test_missing_namespace() {
getString("error_invalid_engine_msg2", brandName, url), getString("error_invalid_engine_msg2", brandName, url),
"Should have seen the right error message" "Should have seen the right error message"
); );
dialog.document.getElementById("commonDialog").acceptDialog(); await acceptDialog(dialog);
gBrowser.removeCurrentTab(); gBrowser.removeCurrentTab();
}); });

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

@ -4614,6 +4614,10 @@ pref("browser.sanitizer.loglevel", "Warn");
// To disable blocking of auth prompts, set the limit to -1. // To disable blocking of auth prompts, set the limit to -1.
pref("prompts.authentication_dialog_abuse_limit", 2); pref("prompts.authentication_dialog_abuse_limit", 2);
// The prompt type to use for search service prompts.
// See nsIPromptService::MODAL_TYPE fields for possible values.
pref("prompts.modalType.searchService", 2);
// Payment Request API preferences // Payment Request API preferences
pref("dom.payments.loglevel", "Warn"); pref("dom.payments.loglevel", "Warn");
pref("dom.payments.defaults.saveCreditCard", false); pref("dom.payments.defaults.saveCreditCard", false);

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

@ -7,6 +7,9 @@
var EXPORTED_SYMBOLS = ["SidebarSearchParent"]; var EXPORTED_SYMBOLS = ["SidebarSearchParent"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
ChromeUtils.defineModuleGetter( ChromeUtils.defineModuleGetter(
this, this,
@ -14,6 +17,13 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/NetUtil.jsm" "resource://gre/modules/NetUtil.jsm"
); );
XPCOMUtils.defineLazyPreferenceGetter(
this,
"promptModalType",
"prompts.modalType.searchService",
Services.prompt.MODAL_TYPE_WINDOW
);
class SidebarSearchParent extends JSWindowActorParent { class SidebarSearchParent extends JSWindowActorParent {
receiveMessage(msg) { receiveMessage(msg) {
if (msg.name == "Search:AddEngine") { if (msg.name == "Search:AddEngine") {
@ -69,12 +79,23 @@ class SidebarSearchParent extends JSWindowActorParent {
brandName, brandName,
engineURL.spec, engineURL.spec,
]); ]);
Services.ww.getNewPrompter(browser.ownerGlobal).alert(title, msg); Services.prompt.asyncAlert(
this.browsingContext,
promptModalType,
title,
msg
);
return; return;
} }
Services.search Services.search
.addEngine(engineURL.spec, iconURL ? iconURL.spec : null, true) .addEngine(
engineURL.spec,
iconURL ? iconURL.spec : null,
true,
null,
this.browsingContext
)
.catch(ex => .catch(ex =>
Cu.reportError( Cu.reportError(
"Unable to add search engine to the search service: " + ex "Unable to add search engine to the search service: " + ex

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

@ -20,6 +20,13 @@ XPCOMUtils.defineLazyServiceGetters(this, {
gChromeReg: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"], gChromeReg: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"],
}); });
XPCOMUtils.defineLazyPreferenceGetter(
this,
"promptModalType",
"prompts.modalType.searchService",
Services.prompt.MODAL_TYPE_WINDOW
);
const BinaryInputStream = Components.Constructor( const BinaryInputStream = Components.Constructor(
"@mozilla.org/binaryinputstream;1", "@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream", "nsIBinaryInputStream",
@ -759,6 +766,9 @@ EngineURL.prototype = {
* @param {boolean} options.isBuiltin * @param {boolean} options.isBuiltin
* Indicates whether the engine is a app-provided or not. If it is, it will * Indicates whether the engine is a app-provided or not. If it is, it will
* be treated as read-only. * be treated as read-only.
* @param {BrowsingContext} [options.reqContext]
* The browsing context of the site the request to add the engine came from.
* If provided, this will act as the parent for any prompt dialogs.
*/ */
function SearchEngine(options = {}) { function SearchEngine(options = {}) {
if (!("isBuiltin" in options)) { if (!("isBuiltin" in options)) {
@ -771,6 +781,7 @@ function SearchEngine(options = {}) {
this._definedAlias = null; this._definedAlias = null;
this._urls = []; this._urls = [];
this._metaData = {}; this._metaData = {};
this._reqContext = options.reqContext;
let file, uri; let file, uri;
if ("name" in options) { if ("name" in options) {
@ -1012,7 +1023,7 @@ SearchEngine.prototype = {
return null; return null;
}, },
_confirmAddEngine() { async _confirmAddEngine() {
var stringBundle = Services.strings.createBundle(SEARCH_BUNDLE); var stringBundle = Services.strings.createBundle(SEARCH_BUNDLE);
var titleMessage = stringBundle.GetStringFromName("addEngineConfirmTitle"); var titleMessage = stringBundle.GetStringFromName("addEngineConfirmTitle");
@ -1037,28 +1048,37 @@ SearchEngine.prototype = {
"addEngineAddButtonLabel" "addEngineAddButtonLabel"
); );
var ps = Services.prompt; let ps = Services.prompt;
var buttonFlags = let buttonFlags =
ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 + ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1 + ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1 +
ps.BUTTON_POS_0_DEFAULT; ps.BUTTON_POS_0_DEFAULT;
var checked = { value: false }; let promptResult;
try {
promptResult = await ps.asyncConfirmEx(
this._reqContext,
promptModalType,
titleMessage,
dialogMessage,
buttonFlags,
addButtonLabel,
null,
null, // button 1 & 2 names not used
checkboxMessage,
false
);
} catch (error) {
// Prompting failed or the user navigated away, deny the request.
return { confirmed: false, useNow: false };
}
// confirmEx returns the index of the button that was pressed. Since "Add" // confirmEx returns the index of the button that was pressed. Since "Add"
// is button 0, we want to return the negation of that value. // is button 0, we want to return the negation of that value.
var confirm = !ps.confirmEx( return {
null, confirmed: !promptResult.getProperty("buttonNumClicked"),
titleMessage, useNow: promptResult.getProperty("checked"),
dialogMessage, };
buttonFlags,
addButtonLabel,
null,
null, // button 1 & 2 names not used
checkboxMessage,
checked
);
return { confirmed: confirm, useNow: checked.value };
}, },
/** /**
@ -1071,7 +1091,7 @@ SearchEngine.prototype = {
* @param {nsISearchEngine} engine * @param {nsISearchEngine} engine
* The engine being loaded. * The engine being loaded.
*/ */
_onLoad(bytes, engine) { async _onLoad(bytes, engine) {
/** /**
* Handle an error during the load of an engine by notifying the engine's * Handle an error during the load of an engine by notifying the engine's
* error callback, if any. * error callback, if any.
@ -1106,7 +1126,12 @@ SearchEngine.prototype = {
engine._location, engine._location,
]); ]);
Services.ww.getNewPrompter(null).alert(title, text); Services.prompt.asyncAlert(
engine._reqContext,
promptModalType,
title,
text
);
} }
if (!bytes) { if (!bytes) {
@ -1179,7 +1204,7 @@ SearchEngine.prototype = {
// This property is only ever true for engines added via // This property is only ever true for engines added via
// nsISearchService::addEngine. // nsISearchService::addEngine.
if (engine._confirm) { if (engine._confirm) {
var confirmation = engine._confirmAddEngine(); var confirmation = await engine._confirmAddEngine();
SearchUtils.log( SearchUtils.log(
"_onLoad: confirm is " + "_onLoad: confirm is " +
confirmation.confirmed + confirmation.confirmed +

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

@ -2820,12 +2820,13 @@ SearchService.prototype = {
return params; return params;
}, },
async addEngine(engineURL, iconURL, confirm, extensionID) { async addEngine(engineURL, iconURL, confirm, extensionID, reqContext) {
SearchUtils.log('addEngine: Adding "' + engineURL + '".'); SearchUtils.log('addEngine: Adding "' + engineURL + '".');
await this.init(); await this.init();
let errCode; let errCode;
try { try {
var engine = new SearchEngine({ var engine = new SearchEngine({
reqContext,
uri: engineURL, uri: engineURL,
isBuiltin: false, isBuiltin: false,
}); });

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

@ -7,6 +7,8 @@
interface nsIURI; interface nsIURI;
interface nsIInputStream; interface nsIInputStream;
webidl BrowsingContext;
[scriptable, uuid(5799251f-5b55-4df7-a9e7-0c27812c469a)] [scriptable, uuid(5799251f-5b55-4df7-a9e7-0c27812c469a)]
interface nsISearchSubmission : nsISupports interface nsISearchSubmission : nsISupports
{ {
@ -301,11 +303,16 @@ interface nsISearchService : nsISupports
* @param extensionID [optional] * @param extensionID [optional]
* Optional: The correct extensionID if called by an add-on. * Optional: The correct extensionID if called by an add-on.
* *
* @param reqContext [optional]
* Optional: The browsing context of the site the request to add the
* engine came from. If provided, this will act as the prompt parent.
*
* @throws NS_ERROR_FAILURE if the description file cannot be successfully * @throws NS_ERROR_FAILURE if the description file cannot be successfully
* loaded. * loaded.
*/ */
Promise addEngine(in AString engineURL, in AString iconURL, in boolean confirm, Promise addEngine(in AString engineURL, in AString iconURL, in boolean confirm,
[optional] in AString extensionID); [optional] in AString extensionID,
[optional] in BrowsingContext reqContext);
/** /**
* Adds a new search engine, without asking the user for confirmation and * Adds a new search engine, without asking the user for confirmation and

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

@ -14,12 +14,10 @@ const { MockRegistrar } = ChromeUtils.import(
// Only need to stub the methods actually called by nsSearchService // Only need to stub the methods actually called by nsSearchService
var promptService = { var promptService = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptService]), QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptService]),
confirmEx() {}, asyncConfirmEx() {},
}; asyncAlert() {},
var prompt = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
alert() {},
}; };
// Override the prompt service and nsIPrompt, since the search service currently // Override the prompt service and nsIPrompt, since the search service currently
// prompts in response to certain installation failures we test here // prompts in response to certain installation failures we test here
// XXX this should disappear once bug 863474 is fixed // XXX this should disappear once bug 863474 is fixed
@ -27,7 +25,6 @@ MockRegistrar.register(
"@mozilla.org/embedcomp/prompt-service;1", "@mozilla.org/embedcomp/prompt-service;1",
promptService promptService
); );
MockRegistrar.register("@mozilla.org/prompter;1", prompt);
// First test inits the search service // First test inits the search service
add_task(async function init_search_service() { add_task(async function init_search_service() {