Bug 1787379 - Add origin to runtime.MessageSender. r=robwu,zombie,geckoview-reviewers

Note that we intentionally return "null" for null principals *and* file
scheme, which is - at the time of writing - different than Chromium.

Differential Revision: https://phabricator.services.mozilla.com/D206989
This commit is contained in:
William Durand 2024-04-10 21:48:40 +00:00
parent a5f77b83e9
commit 4385dce6fc
8 changed files with 225 additions and 4 deletions

View File

@ -8,11 +8,17 @@ function extensionScript() {
let FRAME_URL = browser.runtime.getManifest().content_scripts[0].matches[0];
// Cannot use :8888 in the manifest because of bug 1468162.
FRAME_URL = FRAME_URL.replace("mochi.test", "mochi.test:8888");
let FRAME_ORIGIN = new URL(FRAME_URL).origin;
browser.runtime.onConnect.addListener(port => {
browser.test.assertEq(port.sender.tab, undefined, "Sender is not a tab");
browser.test.assertEq(port.sender.frameId, undefined, "frameId unset");
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
browser.test.assertEq(
port.sender.origin,
FRAME_ORIGIN,
"Expected sender origin"
);
port.onMessage.addListener(msg => {
browser.test.assertEq("pong", msg, "Reply from content script");

View File

@ -2,12 +2,16 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const FILE_URL = Services.io.newFileURI(
new FileUtils.File(getTestFilePath("file_dummy.html"))
).spec;
add_task(async function test_sender_url() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
content_scripts: [
{
matches: ["http://mochi.test/*"],
matches: ["http://mochi.test/*", "file:///*"],
run_at: "document_start",
js: ["script.js"],
},
@ -18,6 +22,7 @@ add_task(async function test_sender_url() {
browser.runtime.onMessage.addListener((msg, sender) => {
browser.test.log("Message received.");
browser.test.sendMessage("sender.url", sender.url);
browser.test.sendMessage("sender.origin", sender.origin);
});
},
@ -53,6 +58,9 @@ add_task(async function test_sender_url() {
let url = await extension.awaitMessage("sender.url");
is(url, image, `Correct sender.url: ${url}`);
let origin = await extension.awaitMessage("sender.origin");
is(origin, "http://mochi.test:8888", `Correct sender.origin: ${origin}`);
let wentBack = awaitNewTab();
await browser.goBack();
await wentBack;
@ -60,6 +68,17 @@ add_task(async function test_sender_url() {
await browser.goForward();
url = await extension.awaitMessage("sender.url");
is(url, image, `Correct sender.url: ${url}`);
origin = await extension.awaitMessage("sender.origin");
is(origin, "http://mochi.test:8888", `Correct sender.origin: ${origin}`);
});
await BrowserTestUtils.withNewTab(FILE_URL, async () => {
let url = await extension.awaitMessage("sender.url");
ok(url.endsWith("/file_dummy.html"), `Correct sender.url: ${url}`);
let origin = await extension.awaitMessage("sender.origin");
is(origin, "null", `Correct sender.origin: ${origin}`);
});
await extension.unload();

View File

@ -97,7 +97,7 @@ add_task(async function test_geckoViewAddons_missing() {
const ERROR_NATIVE_MESSAGE_FROM_BACKGROUND =
"Native manifests are not supported on android";
const ERROR_NATIVE_MESSAGE_FROM_CONTENT =
/^Native messaging not allowed: \{.*"envType":"content_child","url":"http:\/\/example\.com\/dummy"\}$/;
/^Native messaging not allowed: \{.*"envType":"content_child","url":"http:\/\/example\.com\/dummy"\,"origin":"http:\/\/example\.com"}$/;
async function testBackground() {
await browser.test.assertRejects(
@ -136,7 +136,7 @@ add_task(async function test_geckoViewAddons_missing() {
// without the nativeMessagingFromContent permission.
add_task(async function test_nativeMessagingFromContent_missing() {
const ERROR_NATIVE_MESSAGE_FROM_CONTENT_NO_PERM =
/^Unexpected messaging sender: \{.*"envType":"content_child","url":"http:\/\/example\.com\/dummy"\}$/;
/^Unexpected messaging sender: \{.*"envType":"content_child","url":"http:\/\/example\.com\/dummy"\,"origin":"http:\/\/example\.com"}$/;
function testBackground() {
// sendNativeMessage / connectNative are expected to succeed, but we
// are not testing that here because XpcshellTestRunnerService does not

View File

@ -303,7 +303,8 @@ const ProxyMessenger = {
};
if (JSWindowActorParent.isInstance(source.actor)) {
let browser = source.actor.browsingContext.top.embedderElement;
let { currentWindowContext, top } = source.actor.browsingContext;
let browser = top.embedderElement;
let data =
browser && apiManager.global.tabTracker.getBrowserData(browser);
if (data?.tabId > 0) {
@ -311,6 +312,13 @@ const ProxyMessenger = {
// frameId is documented to only be set if sender.tab is set.
sender.frameId = source.frameId;
}
let principal = currentWindowContext.documentPrincipal;
// We intend the serialization of null principals *and* file scheme to be
// "null".
sender.origin = new URL(principal.originNoSuffix).origin;
} else if (source.verified) {
sender.origin = `moz-extension://${extension.uuid}`;
}
return sender;

View File

@ -18,6 +18,7 @@ function background() {
browser.test.assertTrue(port.sender.url.endsWith("file_sample.html"), "URL correct");
browser.test.assertTrue(port.sender.tab.url.endsWith("file_sample.html"), "tab URL correct");
browser.test.assertEq(port.sender.frameId, 0, "frameId of top frame");
browser.test.assertEq(new URL(port.sender.url).origin, port.sender.origin, "sender origin correct");
let expected = "message 1";
port.onMessage.addListener(msg => {

View File

@ -21,6 +21,7 @@ function backgroundScript(token) {
browser.runtime.onConnect.addListener(port => {
browser.test.assertTrue(port.sender.url.endsWith("file_sample.html"), "sender url correct");
browser.test.assertTrue(port.sender.tab.url.endsWith("file_sample.html"), "sender url correct");
browser.test.assertEq(new URL(port.sender.url).origin, port.sender.origin, "sender origin correct");
let tabId = port.sender.tab.id;
browser.tabs.connect(tabId, {name: token});

View File

@ -30,12 +30,15 @@ add_task(async function connect_from_background_frame() {
}
async function background() {
const FRAME_URL = "https://example.com/tests/toolkit/components/extensions/test/mochitest/file_sample.html";
const FRAME_ORIGIN = new URL(FRAME_URL).origin;
browser.runtime.onConnect.addListener(port => {
// The next two assertions are the reason for this being a mochitest
// instead of a xpcshell test.
browser.test.assertEq(port.sender.tab, undefined, "Sender is not a tab");
browser.test.assertEq(port.sender.frameId, undefined, "frameId unset");
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
browser.test.assertEq(port.sender.origin, FRAME_ORIGIN, "Expected sender origin");
port.onMessage.addListener(msg => {
browser.test.assertEq("pong", msg, "Reply from content script");
port.disconnect();
@ -88,6 +91,8 @@ add_task(async function connect_from_content_script_in_frame() {
async function background() {
const TAB_URL = "https://example.org/tests/toolkit/components/extensions/test/mochitest/file_contains_iframe.html";
const FRAME_URL = "https://example.org/tests/toolkit/components/extensions/test/mochitest/file_contains_img.html";
const FRAME_ORIGIN = new URL(FRAME_URL).origin;
let createdTab;
browser.runtime.onConnect.addListener(port => {
// The next two assertions are the reason for this being a mochitest
@ -95,6 +100,7 @@ add_task(async function connect_from_content_script_in_frame() {
browser.test.assertEq(port.sender.tab.url, TAB_URL, "Sender is the tab");
browser.test.assertTrue(port.sender.frameId > 0, "frameId is set");
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
browser.test.assertEq(port.sender.origin, FRAME_ORIGIN, "Expected sender origin");
browser.test.assertEq(createdTab.id, port.sender.tab.id, "Tab to close");
browser.tabs.remove(port.sender.tab.id).then(() => {

View File

@ -11,6 +11,10 @@
<script>
"use strict";
const {
WebExtensionPolicy
} = SpecialPowers.Cu.getGlobalForObject(SpecialPowers.Services);
add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
@ -27,6 +31,10 @@ add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
browser.runtime.onMessage.addListener(async (msg, sender) => {
browser.test.assertEq(msg, "page-script-ready");
browser.test.assertEq(sender.url, browser.runtime.getURL("page.html"));
browser.test.assertEq(
sender.origin,
new URL(browser.runtime.getURL("/")).origin
);
let tabId = sender.tab.id;
let response = await browser.tabs.sendMessage(tabId, "tab-sendMessage");
@ -147,6 +155,178 @@ add_task(async function test_tabs_sendMessage_using_frameId() {
await extension.unload();
});
add_task(async function test_tabs_sendMessage_aboutBlank() {
const PATH = "tests/toolkit/components/extensions/test/mochitest/file_with_about_blank.html";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
content_scripts: [
{
match_about_blank: true,
matches: ["*://mochi.test/*/file_with_about_blank.html"],
all_frames: true,
js: ["cs.js"],
},
],
},
background() {
browser.runtime.onMessage.addListener((msg, { url, origin }) => {
browser.test.assertEq("cs", msg, "expected message from cs.js");
const kind = url.startsWith("about:") ? url : "top";
switch (kind) {
case "top":
browser.test.assertTrue(
url.endsWith("/file_with_about_blank.html"),
"expected correct url"
);
browser.test.assertEq(
"http://mochi.test:8888",
origin,
"expected correct origin"
);
break;
case "about:blank":
browser.test.assertEq("about:blank", url, "expected correct url");
browser.test.assertEq(
"http://mochi.test:8888",
origin,
"expected correct origin"
);
break;
case "about:srcdoc":
browser.test.assertEq("about:srcdoc", url, "expected correct url");
browser.test.assertEq(
"http://mochi.test:8888",
origin,
"expected correct origin"
);
break;
default:
browser.test.fail(`Unexpected kind: ${kind}`);
}
browser.test.sendMessage(`done:${kind}`);
});
browser.test.sendMessage("ready");
},
files: {
"cs.js"() {
browser.runtime.sendMessage("cs");
},
},
});
await extension.startup();
await extension.awaitMessage("ready");
let win = window.open("http://mochi.test:8888/" + PATH);
await extension.awaitMessage("done:top");
await extension.awaitMessage("done:about:blank");
await extension.awaitMessage("done:about:srcdoc");
win.close();
await extension.unload();
});
add_task(async function test_tabs_sendMessage_opaqueOrigin() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
content_scripts: [
{
match_about_blank: true,
// The combination of `matches` and `exclude_matches` below allows us
// to only inject in the top-level about:blank.
matches: ["*://*/*"],
exclude_matches: ["<all_urls>"],
js: ["cs.js"],
},
],
},
async background() {
let tab;
browser.runtime.onMessage.addListener(async (msg, { url, origin }) => {
browser.test.assertEq("cs", msg, "expected message from cs.js");
browser.test.assertEq("about:blank", url, "expected correct url");
browser.test.assertEq("null", origin, "expected correct origin");
await browser.tabs.remove(tab.id);
browser.test.sendMessage("done");
});
tab = await browser.tabs.create({ url: "about:blank" });
},
files: {
"cs.js"() {
browser.runtime.sendMessage("cs");
},
},
});
await extension.startup();
await extension.awaitMessage("done");
await extension.unload();
});
add_task(async function test_tabs_sendMessage_blob() {
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1724099
if (!WebExtensionPolicy.useRemoteWebExtensions) {
await SpecialPowers.pushPrefEnv({
set: [["security.allow_unsafe_parent_loads", true]],
});
}
let extension = ExtensionTestUtils.loadExtension({
async background() {
browser.runtime.onMessage.addListener(async (msg, { url, origin }) => {
browser.test.assertEq("script", msg, "expected message from script.js");
browser.test.assertTrue(
url.startsWith("blob:moz-extension://"),
"expected correct url"
);
browser.test.assertEq(
new URL(browser.runtime.getURL("/")).origin,
origin,
"expected correct origin"
);
browser.test.sendMessage("done");
});
const blob = new Blob(
[`<script src="${browser.runtime.getURL("script.js")}"><\/script>`],
{ type: "text/html" }
);
const iframe = document.createElement("iframe");
iframe.src = URL.createObjectURL(blob);
document.body.appendChild(iframe);
},
files: {
"script.js"() {
browser.runtime.sendMessage("script");
},
},
});
await extension.startup();
await extension.awaitMessage("done");
await extension.unload();
if (!WebExtensionPolicy.useRemoteWebExtensions) {
await SpecialPowers.popPrefEnv();
}
});
</script>
</body>
</html>