forked from mirrors/gecko-dev
Bug 1828334 r=Gijs,dveditz,extension-reviewers,fluent-reviewers,flod,robwu,perftest-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D185671
This commit is contained in:
parent
90c0ebb4ff
commit
4a5c1cc860
7 changed files with 139 additions and 45 deletions
|
|
@ -78,6 +78,7 @@ user_pref("privacy.trackingprotection.enabled", false);
|
||||||
user_pref("privacy.trackingprotection.introURL", "http://127.0.0.1/trackingprotection/tour");
|
user_pref("privacy.trackingprotection.introURL", "http://127.0.0.1/trackingprotection/tour");
|
||||||
user_pref("privacy.trackingprotection.pbmode.enabled", false);
|
user_pref("privacy.trackingprotection.pbmode.enabled", false);
|
||||||
user_pref("security.enable_java", false);
|
user_pref("security.enable_java", false);
|
||||||
|
user_pref("security.external_protocol_requires_permission", false);
|
||||||
user_pref("security.fileuri.strict_origin_policy", false);
|
user_pref("security.fileuri.strict_origin_policy", false);
|
||||||
user_pref("toolkit.telemetry.server", "https://127.0.0.1/telemetry-dummy/");
|
user_pref("toolkit.telemetry.server", "https://127.0.0.1/telemetry-dummy/");
|
||||||
user_pref("telemetry.fog.test.localhost_port", -1);
|
user_pref("telemetry.fog.test.localhost_port", -1);
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,11 @@ add_task(async function test_protocolHandler() {
|
||||||
// Test that handling a URL from the commandline works.
|
// Test that handling a URL from the commandline works.
|
||||||
chromeScript = SpecialPowers.loadChromeScript(() => {
|
chromeScript = SpecialPowers.loadChromeScript(() => {
|
||||||
/* eslint-env mozilla/chrome-script */
|
/* eslint-env mozilla/chrome-script */
|
||||||
|
const CONTENT_HANDLING_URL =
|
||||||
|
"chrome://mozapps/content/handling/permissionDialog.xhtml";
|
||||||
|
const { BrowserTestUtils } = ChromeUtils.importESModule(
|
||||||
|
"resource://testing-common/BrowserTestUtils.sys.mjs"
|
||||||
|
);
|
||||||
let cmdLineHandler = Cc["@mozilla.org/browser/final-clh;1"].getService(
|
let cmdLineHandler = Cc["@mozilla.org/browser/final-clh;1"].getService(
|
||||||
Ci.nsICommandLineHandler
|
Ci.nsICommandLineHandler
|
||||||
);
|
);
|
||||||
|
|
@ -207,6 +212,33 @@ add_task(async function test_protocolHandler() {
|
||||||
Ci.nsICommandLine.STATE_REMOTE_EXPLICIT
|
Ci.nsICommandLine.STATE_REMOTE_EXPLICIT
|
||||||
);
|
);
|
||||||
cmdLineHandler.handle(fakeCmdLine);
|
cmdLineHandler.handle(fakeCmdLine);
|
||||||
|
|
||||||
|
// We aren't awaiting for this promise to resolve since it returns undefined
|
||||||
|
// (because it returns the reference to the dialog window that we close, below)
|
||||||
|
// once the callback promise below finishes, and because its not needed for anything
|
||||||
|
// outside of this loadChromeScript block.
|
||||||
|
BrowserTestUtils.promiseAlertDialogOpen(
|
||||||
|
null,
|
||||||
|
CONTENT_HANDLING_URL,
|
||||||
|
{
|
||||||
|
isSubDialog: true,
|
||||||
|
async callback(dialogWin) {
|
||||||
|
is(dialogWin.document.documentURI, CONTENT_HANDLING_URL, "Open dialog is the permission dialog")
|
||||||
|
|
||||||
|
let closePromise = BrowserTestUtils.waitForEvent(
|
||||||
|
dialogWin.browsingContext.topChromeWindow,
|
||||||
|
"dialogclose",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
let dialog = dialogWin.document.querySelector("dialog");
|
||||||
|
let btn = dialog.getButton("accept");
|
||||||
|
// The security delay disables this button, just bypass it.
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.click();
|
||||||
|
return closePromise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
query = await extension.awaitMessage("test-query");
|
query = await extension.awaitMessage("test-query");
|
||||||
is(query, "?val=ext%2Bfoo%3Acmdline", "cmdline query ok");
|
is(query, "?val=ext%2Bfoo%3Acmdline", "cmdline query ok");
|
||||||
|
|
@ -220,7 +252,7 @@ add_task(async function test_protocolHandler() {
|
||||||
consoleMonitor.start([{ message: /NS_ERROR_FILE_NOT_FOUND/ }]);
|
consoleMonitor.start([{ message: /NS_ERROR_FILE_NOT_FOUND/ }]);
|
||||||
|
|
||||||
// Expect the chooser window to be open, close it.
|
// Expect the chooser window to be open, close it.
|
||||||
chromeScript = SpecialPowers.loadChromeScript(async () => {
|
chromeScript = SpecialPowers.loadChromeScript(() => {
|
||||||
/* eslint-env mozilla/chrome-script */
|
/* eslint-env mozilla/chrome-script */
|
||||||
const CONTENT_HANDLING_URL =
|
const CONTENT_HANDLING_URL =
|
||||||
"chrome://mozapps/content/handling/appChooser.xhtml";
|
"chrome://mozapps/content/handling/appChooser.xhtml";
|
||||||
|
|
@ -228,39 +260,39 @@ add_task(async function test_protocolHandler() {
|
||||||
"resource://testing-common/BrowserTestUtils.sys.mjs"
|
"resource://testing-common/BrowserTestUtils.sys.mjs"
|
||||||
);
|
);
|
||||||
|
|
||||||
let windowOpen = BrowserTestUtils.domWindowOpenedAndLoaded();
|
|
||||||
|
|
||||||
sendAsyncMessage("listenWindow");
|
sendAsyncMessage("listenWindow");
|
||||||
|
|
||||||
let window = await windowOpen;
|
// We aren't awaiting for this promise to resolve since it returns undefined
|
||||||
let gBrowser = window.gBrowser;
|
// (because it returns the reference to the dialog window that we close, below)
|
||||||
let tabDialogBox = gBrowser.getTabDialogBox(gBrowser.selectedBrowser);
|
// once the callback promise below finishes, and because its not needed for anything
|
||||||
let dialogStack = tabDialogBox.getTabDialogManager()._dialogStack;
|
// outside of this loadChromeScript block.
|
||||||
|
BrowserTestUtils.promiseAlertDialogOpen(
|
||||||
|
null,
|
||||||
|
CONTENT_HANDLING_URL,
|
||||||
|
{
|
||||||
|
isSubDialog: true,
|
||||||
|
async callback(dialogWin) {
|
||||||
|
is(dialogWin.document.documentURI, CONTENT_HANDLING_URL, "Open dialog is the app chooser dialog")
|
||||||
|
|
||||||
let checkFn = dialogEvent =>
|
let entry = dialogWin.document.getElementById("items")
|
||||||
dialogEvent.detail.dialog?._openedURL == CONTENT_HANDLING_URL;
|
.firstChild;
|
||||||
|
sendAsyncMessage("handling", {
|
||||||
|
name: entry.getAttribute("name"),
|
||||||
|
disabled: entry.disabled,
|
||||||
|
});
|
||||||
|
|
||||||
let eventPromise = BrowserTestUtils.waitForEvent(
|
let closePromise = BrowserTestUtils.waitForEvent(
|
||||||
dialogStack,
|
dialogWin.browsingContext.topChromeWindow,
|
||||||
"dialogopen",
|
"dialogclose",
|
||||||
true,
|
true,
|
||||||
checkFn
|
);
|
||||||
|
dialogWin.close();
|
||||||
|
return closePromise;
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
sendAsyncMessage("listenDialog");
|
sendAsyncMessage("listenDialog");
|
||||||
|
|
||||||
let event = await eventPromise;
|
|
||||||
|
|
||||||
let { dialog } = event.detail;
|
|
||||||
|
|
||||||
let entry = dialog._frame.contentDocument.getElementById("items")
|
|
||||||
.firstChild;
|
|
||||||
sendAsyncMessage("handling", {
|
|
||||||
name: entry.getAttribute("name"),
|
|
||||||
disabled: entry.disabled,
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog.close();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for the chrome script to attach window listener
|
// Wait for the chrome script to attach window listener
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@ permission-dialog-description-file-app =
|
||||||
permission-dialog-description-extension-app =
|
permission-dialog-description-extension-app =
|
||||||
Allow the extension { $extension } to open the { $scheme } link with { $appName }?
|
Allow the extension { $extension } to open the { $scheme } link with { $appName }?
|
||||||
|
|
||||||
|
permission-dialog-description-system-app =
|
||||||
|
Open the { $scheme } link with { $appName }?
|
||||||
|
|
||||||
|
permission-dialog-description-system-noapp =
|
||||||
|
Open the { $scheme } link?
|
||||||
|
|
||||||
## Please keep the emphasis around the hostname and scheme (ie the
|
## Please keep the emphasis around the hostname and scheme (ie the
|
||||||
## `<strong>` HTML tags). Please also keep the hostname as close to the start
|
## `<strong>` HTML tags). Please also keep the hostname as close to the start
|
||||||
## of the sentence as your language's grammar allows.
|
## of the sentence as your language's grammar allows.
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ export class nsContentDispatchChooser {
|
||||||
) {
|
) {
|
||||||
let callerHasPermission = this._hasProtocolHandlerPermission(
|
let callerHasPermission = this._hasProtocolHandlerPermission(
|
||||||
aHandler.type,
|
aHandler.type,
|
||||||
aPrincipal
|
aPrincipal,
|
||||||
|
aTriggeredExternally
|
||||||
);
|
);
|
||||||
|
|
||||||
// Force showing the dialog for links passed from outside the application.
|
// Force showing the dialog for links passed from outside the application.
|
||||||
|
|
@ -269,7 +270,7 @@ export class nsContentDispatchChooser {
|
||||||
* @param {nsIPrincipal} aPrincipal - Principal to test for permission.
|
* @param {nsIPrincipal} aPrincipal - Principal to test for permission.
|
||||||
* @returns {boolean} - true if permission is set, false otherwise.
|
* @returns {boolean} - true if permission is set, false otherwise.
|
||||||
*/
|
*/
|
||||||
_hasProtocolHandlerPermission(scheme, aPrincipal) {
|
_hasProtocolHandlerPermission(scheme, aPrincipal, aTriggeredExternally) {
|
||||||
// Permission disabled by pref
|
// Permission disabled by pref
|
||||||
if (!nsContentDispatchChooser.isPermissionEnabled) {
|
if (!nsContentDispatchChooser.isPermissionEnabled) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -285,7 +286,10 @@ export class nsContentDispatchChooser {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aPrincipal) {
|
if (
|
||||||
|
!aPrincipal ||
|
||||||
|
(aPrincipal.isSystemPrincipal && !aTriggeredExternally)
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,15 @@ let dialog = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let changeAppLink = document.getElementById("change-app");
|
let changeAppLink = document.getElementById("change-app");
|
||||||
if (this._preferredHandlerName) {
|
|
||||||
|
// allow the user to choose another application if they wish,
|
||||||
|
// but don't offer this if the protocol was opened via
|
||||||
|
// system principal (URLbar) and there's a preferred handler
|
||||||
|
if (this._preferredHandlerName && !this._principal?.isSystemPrincipal) {
|
||||||
changeAppLink.hidden = false;
|
changeAppLink.hidden = false;
|
||||||
|
|
||||||
changeAppLink.addEventListener("click", () => this.onChangeApp());
|
changeAppLink.addEventListener("click", () => this.onChangeApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("dialogaccept", () => this.onAccept());
|
document.addEventListener("dialogaccept", () => this.onAccept());
|
||||||
this.initL10n();
|
this.initL10n();
|
||||||
|
|
||||||
|
|
@ -96,6 +99,14 @@ let dialog = {
|
||||||
return "permission-dialog-description-file";
|
return "permission-dialog-description-file";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._principal?.isSystemPrincipal && this._preferredHandlerName) {
|
||||||
|
return "permission-dialog-description-system-app";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._principal?.isSystemPrincipal && !this._preferredHandlerName) {
|
||||||
|
return "permission-dialog-description-system-noapp";
|
||||||
|
}
|
||||||
|
|
||||||
// We only show the website address if the request didn't come from the top
|
// We only show the website address if the request didn't come from the top
|
||||||
// level frame. If we can't get a host to display, fall back to the copy
|
// level frame. If we can't get a host to display, fall back to the copy
|
||||||
// without host.
|
// without host.
|
||||||
|
|
@ -158,6 +169,8 @@ let dialog = {
|
||||||
|
|
||||||
// Fluent id for dialog accept button
|
// Fluent id for dialog accept button
|
||||||
let idAcceptButton;
|
let idAcceptButton;
|
||||||
|
let acceptButton = this._dialog.getButton("accept");
|
||||||
|
|
||||||
if (this._preferredHandlerName) {
|
if (this._preferredHandlerName) {
|
||||||
idAcceptButton = "permission-dialog-btn-open-link";
|
idAcceptButton = "permission-dialog-btn-open-link";
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -165,8 +178,8 @@ let dialog = {
|
||||||
|
|
||||||
let descriptionExtra = document.getElementById("description-extra");
|
let descriptionExtra = document.getElementById("description-extra");
|
||||||
descriptionExtra.hidden = false;
|
descriptionExtra.hidden = false;
|
||||||
|
acceptButton.addEventListener("click", () => this.onChangeApp());
|
||||||
}
|
}
|
||||||
let acceptButton = this._dialog.getButton("accept");
|
|
||||||
document.l10n.setAttributes(acceptButton, idAcceptButton);
|
document.l10n.setAttributes(acceptButton, idAcceptButton);
|
||||||
|
|
||||||
let description = document.getElementById("description");
|
let description = document.getElementById("description");
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,18 @@ function getSkipProtoDialogPermissionKey(aProtocolScheme) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSystemProtocol() {
|
||||||
|
// TODO add a scheme for Windows 10 or greater once support is added (see bug 1764599).
|
||||||
|
if (AppConstants.platform == "macosx") {
|
||||||
|
return "itunes";
|
||||||
|
}
|
||||||
|
|
||||||
|
info(
|
||||||
|
"Skipping this test since there isn't a suitable default protocol on this platform"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates dummy web protocol handlers used for testing.
|
* Creates dummy web protocol handlers used for testing.
|
||||||
*/
|
*/
|
||||||
|
|
@ -605,13 +617,43 @@ add_task(async function test_permission_application_set() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that we correctly handle system principals. They should always
|
* Tests that we correctly handle system principals. They should always
|
||||||
* skip the permission dialog.
|
* show the permission dialog, but give the option to choose another
|
||||||
|
* app if there isn't a default handler.
|
||||||
*/
|
*/
|
||||||
add_task(async function test_permission_system_principal() {
|
add_task(async function test_permission_system_principal() {
|
||||||
let scheme = TEST_PROTOS[0];
|
let scheme = TEST_PROTOS[0];
|
||||||
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
|
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
|
||||||
await testOpenProto(browser, scheme, {
|
await testOpenProto(browser, scheme, {
|
||||||
chooserDialogOptions: { hasCheckbox: true, actionConfirm: false },
|
permDialogOptions: {
|
||||||
|
hasCheckbox: false,
|
||||||
|
hasChangeApp: false,
|
||||||
|
chooserIsNext: true,
|
||||||
|
actionChangeApp: false,
|
||||||
|
},
|
||||||
|
triggerLoad: useTriggeringPrincipal(
|
||||||
|
Services.scriptSecurityManager.getSystemPrincipal()
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that we correctly handle system principals and show
|
||||||
|
* a simplified permission dialog if there is a default handler.
|
||||||
|
*/
|
||||||
|
add_task(async function test_permission_system_principal() {
|
||||||
|
let scheme = getSystemProtocol();
|
||||||
|
if (!scheme) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
|
||||||
|
await testOpenProto(browser, scheme, {
|
||||||
|
permDialogOptions: {
|
||||||
|
hasCheckbox: false,
|
||||||
|
hasChangeApp: false,
|
||||||
|
chooserIsNext: false,
|
||||||
|
actionChangeApp: false,
|
||||||
|
},
|
||||||
triggerLoad: useTriggeringPrincipal(
|
triggerLoad: useTriggeringPrincipal(
|
||||||
Services.scriptSecurityManager.getSystemPrincipal()
|
Services.scriptSecurityManager.getSystemPrincipal()
|
||||||
),
|
),
|
||||||
|
|
@ -762,17 +804,10 @@ add_task(async function test_no_principal() {
|
||||||
* and the user hasn't selected an alternative only the permission dialog is shown.
|
* and the user hasn't selected an alternative only the permission dialog is shown.
|
||||||
*/
|
*/
|
||||||
add_task(async function test_non_standard_protocol() {
|
add_task(async function test_non_standard_protocol() {
|
||||||
let scheme = null;
|
let scheme = getSystemProtocol();
|
||||||
// TODO add a scheme for Windows 10 or greater once support is added (see bug 1764599).
|
if (!scheme) {
|
||||||
if (AppConstants.platform == "macosx") {
|
|
||||||
scheme = "itunes";
|
|
||||||
} else {
|
|
||||||
info(
|
|
||||||
"Skipping this test since there isn't a suitable default protocol on this platform"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
|
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
|
||||||
await testOpenProto(browser, scheme, {
|
await testOpenProto(browser, scheme, {
|
||||||
permDialogOptions: {
|
permDialogOptions: {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,10 @@ add_task(async function test_helperapp() {
|
||||||
|
|
||||||
let askedUserPromise = waitForProtocolAppChooserDialog(browser, true);
|
let askedUserPromise = waitForProtocolAppChooserDialog(browser, true);
|
||||||
|
|
||||||
BrowserTestUtils.startLoadingURIString(browser, kProt + ":test");
|
gBrowser.fixupAndLoadURIString(kProt + ":test", {
|
||||||
|
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||||
|
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL,
|
||||||
|
});
|
||||||
let dialog = await Promise.race([
|
let dialog = await Promise.race([
|
||||||
wrongThingHappenedPromise,
|
wrongThingHappenedPromise,
|
||||||
askedUserPromise,
|
askedUserPromise,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue