Bug 1799632: Refactor `testOpenProto` to accept a callback to trigger protocol loads. r=Gijs

Currently this function uses six different booleans to decide which code to run
to trigger the load (many of which are used in just one test) and elsewhere I
need to add more cases. This changes the function to instead take a callback
function so individual tests can just pass their one-off functions. For the
few that are shared global functions are used to share those.

Differential Revision: https://phabricator.services.mozilla.com/D161588
This commit is contained in:
Dave Townsend 2022-11-08 15:29:49 +00:00
Родитель ddc86a0a4b
Коммит 7239bae84b
1 изменённых файлов: 114 добавлений и 157 удалений

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

@ -48,8 +48,6 @@ const NULL_PRINCIPAL_SCHEME = Services.scriptSecurityManager
.createNullPrincipal({})
.scheme.toLowerCase();
let testExtension;
/**
* Get the open protocol handler permission key for a given protocol scheme.
* @param {string} aProtocolScheme - Scheme of protocol to construct permission
@ -106,106 +104,13 @@ function testAlwaysAsk(scheme, ask) {
}
/**
* Open a test URL with the desired scheme.
* By default the load is triggered by the content principal of the browser.
* @param {MozBrowser} browser - Browser to load the test URL in.
* @param {string} scheme - Scheme of the test URL.
* @param {Object} [opts] - Options for the triggering principal.
* @param {nsIPrincipal} [opts.triggeringPrincipal] - Principal to trigger the
* load with. Defaults to the browsers content principal.
* @param {boolean} [opts.useNullPrincipal] - If true, we will trigger the load
* with a null principal.
* @param {boolean} [opts.useExtensionPrincipal] - If true, we will trigger the
* load with an extension.
* @param {boolean} [opts.omitTriggeringPrincipal] - If true, we will directly
* call the protocol handler dialogs without a principal.
* Triggers the load via a server redirect.
* @param {string} serverRedirect - The redirect type.
*/
async function triggerOpenProto(
browser,
scheme,
{
triggeringPrincipal = browser.contentPrincipal,
useNullPrincipal = false,
useExtensionPrincipal = false,
omitTriggeringPrincipal = false,
useJSRedirect = false,
serverRedirect = "",
linkToRedirect = false,
customHandlerInfo,
} = {}
) {
let uri = `${scheme}://test`;
function useServerRedirect(serverRedirect) {
return async (browser, scheme) => {
let uri = `${scheme}://test`;
if (useNullPrincipal) {
// Create and load iframe with data URI.
// This will be a null principal.
ContentTask.spawn(browser, { uri }, args => {
let frame = content.document.createElement("iframe");
frame.src = `data:text/html,<script>location.href="${args.uri}"</script>`;
content.document.body.appendChild(frame);
});
return;
}
if (useExtensionPrincipal) {
const EXTENSION_DATA = {
manifest: {
content_scripts: [
{
matches: [browser.currentURI.spec],
js: ["navigate.js"],
},
],
},
files: {
"navigate.js": `window.location.href = "${uri}";`,
},
};
testExtension = ExtensionTestUtils.loadExtension(EXTENSION_DATA);
await testExtension.startup();
return;
}
if (omitTriggeringPrincipal) {
// Directly call ContentDispatchChooser without a triggering principal
let contentDispatchChooser = Cc[
"@mozilla.org/content-dispatch-chooser;1"
].createInstance(Ci.nsIContentDispatchChooser);
let handler =
customHandlerInfo || HandlerServiceTestUtils.getHandlerInfo(scheme);
contentDispatchChooser.handleURI(
handler,
Services.io.newURI(uri),
null,
browser.browsingContext
);
return;
}
if (useJSRedirect) {
let innerParams = new URLSearchParams();
innerParams.set("uri", uri);
let params = new URLSearchParams();
params.set(
"uri",
"https://example.com/" +
ROOT_PATH +
"script_redirect.html?" +
innerParams.toString()
);
uri =
"https://example.org/" +
ROOT_PATH +
"script_redirect.html?" +
params.toString();
BrowserTestUtils.loadURI(browser, uri);
return;
}
if (serverRedirect) {
let innerParams = new URLSearchParams();
innerParams.set("uri", uri);
innerParams.set("redirectType", serverRedirect);
@ -223,29 +128,22 @@ async function triggerOpenProto(
"redirect_helper.sjs?" +
params.toString();
BrowserTestUtils.loadURI(browser, uri);
return;
}
};
}
if (linkToRedirect) {
let params = new URLSearchParams();
params.set("uri", uri);
uri =
"https://example.com/" +
ROOT_PATH +
"redirect_helper.sjs?" +
params.toString();
await ContentTask.spawn(browser, { uri }, args => {
let textLink = content.document.createElement("a");
textLink.href = args.uri;
textLink.textContent = "click me";
content.document.body.appendChild(textLink);
textLink.click();
});
return;
}
/**
* Triggers the load with a specific principal or the browser's current
* principal.
* @param {nsIPrincipal} [principal] - Principal to use to trigger the load.
*/
function useTriggeringPrincipal(principal = undefined) {
return async (browser, scheme) => {
let uri = `${scheme}://test`;
let triggeringPrincipal = principal ?? browser.contentPrincipal;
info("Loading uri: " + uri);
browser.loadURI(uri, { triggeringPrincipal });
info("Loading uri: " + uri);
browser.loadURI(uri, { triggeringPrincipal });
};
}
/**
@ -258,8 +156,8 @@ async function triggerOpenProto(
* dialog. If defined, we expect this dialog to be shown.
* @param {Object} [options.chooserDialogOptions] - Test options for the chooser
* dialog. If defined, we expect this dialog to be shown.
* @param {Object} [options.loadOptions] - Options for triggering the protocol
* load which causes the dialog to show.
* @param {Function} [options.triggerLoad] - An async callback function to
* trigger the load. Will be passed the browser and scheme to use.
* @param {nsIPrincipal} [options.triggeringPrincipal] - Principal to trigger
* the load with. Defaults to the browsers content principal.
* @returns {Promise} - A promise which resolves once the test is complete.
@ -267,7 +165,11 @@ async function triggerOpenProto(
async function testOpenProto(
browser,
scheme,
{ permDialogOptions, chooserDialogOptions, loadOptions } = {}
{
permDialogOptions,
chooserDialogOptions,
triggerLoad = useTriggeringPrincipal(),
} = {}
) {
let permDialogOpenPromise;
let chooserDialogOpenPromise;
@ -281,7 +183,7 @@ async function testOpenProto(
info("Should see chooser dialog");
chooserDialogOpenPromise = waitForProtocolAppChooserDialog(browser, true);
}
await triggerOpenProto(browser, scheme, loadOptions);
await triggerLoad(browser, scheme);
let webHandlerLoadedPromise;
let webHandlerShouldOpen =
@ -384,13 +286,6 @@ async function testOpenProto(
} else {
info("Web handler open canceled");
}
// Clean up test extension if needed.
if (testExtension) {
await testExtension.unload();
// Don't try to unload it again later!
testExtension = null;
}
}
/**
@ -711,9 +606,9 @@ add_task(async function test_permission_system_principal() {
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
await testOpenProto(browser, scheme, {
chooserDialogOptions: { hasCheckbox: true, actionConfirm: false },
loadOptions: {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
},
triggerLoad: useTriggeringPrincipal(
Services.scriptSecurityManager.getSystemPrincipal()
),
});
});
});
@ -795,8 +690,13 @@ add_task(async function test_null_principal() {
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
await testOpenProto(browser, scheme, {
loadOptions: {
useNullPrincipal: true,
triggerLoad: () => {
let uri = `${scheme}://test`;
ContentTask.spawn(browser, { uri }, args => {
let frame = content.document.createElement("iframe");
frame.src = `data:text/html,<script>location.href="${args.uri}"</script>`;
content.document.body.appendChild(frame);
});
},
permDialogOptions: {
hasCheckbox: false,
@ -821,8 +721,21 @@ add_task(async function test_no_principal() {
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
await testOpenProto(browser, scheme, {
loadOptions: {
omitTriggeringPrincipal: true,
triggerLoad: () => {
let uri = `${scheme}://test`;
let contentDispatchChooser = Cc[
"@mozilla.org/content-dispatch-chooser;1"
].createInstance(Ci.nsIContentDispatchChooser);
let handler = HandlerServiceTestUtils.getHandlerInfo(scheme);
contentDispatchChooser.handleURI(
handler,
Services.io.newURI(uri),
null,
browser.browsingContext
);
},
permDialogOptions: {
hasCheckbox: false,
@ -856,9 +769,6 @@ add_task(async function test_non_standard_protocol() {
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
await testOpenProto(browser, scheme, {
loadOptions: {
customHandlerInfo: HandlerServiceTestUtils.getHandlerInfo(scheme),
},
permDialogOptions: {
hasCheckbox: true,
hasChangeApp: true,
@ -875,15 +785,36 @@ add_task(async function test_non_standard_protocol() {
add_task(async function test_extension_principal() {
let scheme = TEST_PROTOS[0];
await BrowserTestUtils.withNewTab(ORIGIN1, async browser => {
let testExtension;
await testOpenProto(browser, scheme, {
loadOptions: {
useExtensionPrincipal: true,
triggerLoad: async () => {
let uri = `${scheme}://test`;
const EXTENSION_DATA = {
manifest: {
content_scripts: [
{
matches: [browser.currentURI.spec],
js: ["navigate.js"],
},
],
},
files: {
"navigate.js": `window.location.href = "${uri}";`,
},
};
testExtension = ExtensionTestUtils.loadExtension(EXTENSION_DATA);
await testExtension.startup();
},
chooserDialogOptions: {
hasCheckbox: true,
actionConfirm: false, // Cancel dialog
},
});
await testExtension.unload();
});
});
@ -894,9 +825,7 @@ add_task(async function test_redirect_principal() {
let scheme = TEST_PROTOS[0];
await BrowserTestUtils.withNewTab("about:blank", async browser => {
await testOpenProto(browser, scheme, {
loadOptions: {
serverRedirect: "location",
},
triggerLoad: useServerRedirect("location"),
permDialogOptions: {
checkboxOrigin: ORIGIN1,
chooserIsNext: true,
@ -914,9 +843,7 @@ add_task(async function test_redirect_principal() {
let scheme = TEST_PROTOS[0];
await BrowserTestUtils.withNewTab("about:blank", async browser => {
await testOpenProto(browser, scheme, {
loadOptions: {
serverRedirect: "refresh",
},
triggerLoad: useServerRedirect("refresh"),
permDialogOptions: {
checkboxOrigin: ORIGIN1,
chooserIsNext: true,
@ -934,9 +861,7 @@ add_task(async function test_redirect_principal() {
let scheme = TEST_PROTOS[0];
await BrowserTestUtils.withNewTab("about:blank", async browser => {
await testOpenProto(browser, scheme, {
loadOptions: {
serverRedirect: "meta-refresh",
},
triggerLoad: useServerRedirect("meta-refresh"),
permDialogOptions: {
checkboxOrigin: ORIGIN1,
chooserIsNext: true,
@ -954,8 +879,25 @@ add_task(async function test_redirect_principal_js() {
let scheme = TEST_PROTOS[0];
await BrowserTestUtils.withNewTab("about:blank", async browser => {
await testOpenProto(browser, scheme, {
loadOptions: {
useJSRedirect: true,
triggerLoad: () => {
let uri = `${scheme}://test`;
let innerParams = new URLSearchParams();
innerParams.set("uri", uri);
let params = new URLSearchParams();
params.set(
"uri",
"https://example.com/" +
ROOT_PATH +
"script_redirect.html?" +
innerParams.toString()
);
uri =
"https://example.org/" +
ROOT_PATH +
"script_redirect.html?" +
params.toString();
BrowserTestUtils.loadURI(browser, uri);
},
permDialogOptions: {
checkboxOrigin: ORIGIN1,
@ -974,8 +916,23 @@ add_task(async function test_redirect_principal_links() {
let scheme = TEST_PROTOS[0];
await BrowserTestUtils.withNewTab("about:blank", async browser => {
await testOpenProto(browser, scheme, {
loadOptions: {
linkToRedirect: true,
triggerLoad: async () => {
let uri = `${scheme}://test`;
let params = new URLSearchParams();
params.set("uri", uri);
uri =
"https://example.com/" +
ROOT_PATH +
"redirect_helper.sjs?" +
params.toString();
await ContentTask.spawn(browser, { uri }, args => {
let textLink = content.document.createElement("a");
textLink.href = args.uri;
textLink.textContent = "click me";
content.document.body.appendChild(textLink);
textLink.click();
});
},
permDialogOptions: {
checkboxOrigin: ORIGIN1,