Bug 1708243 - Part 2: stop using sender data from the child process r=robwu,agi
Differential Revision: https://phabricator.services.mozilla.com/D123351
This commit is contained in:
@@ -26,7 +26,6 @@ module.exports = {
|
||||
replaceUrlInTab: true,
|
||||
searchInitialized: true,
|
||||
sidebarActionFor: true,
|
||||
tabGetSender: true,
|
||||
tabTracker: true,
|
||||
waitForTabLoaded: true,
|
||||
windowTracker: true,
|
||||
|
||||
@@ -44,34 +44,6 @@ function isPrivateTab(nativeTab) {
|
||||
return PrivateBrowsingUtils.isBrowserPrivate(nativeTab.linkedBrowser);
|
||||
}
|
||||
|
||||
// This function is pretty tightly tied to Extension.jsm.
|
||||
// Its job is to fill in the |tab| property of the sender.
|
||||
const getSender = (extension, target, sender) => {
|
||||
let tabId;
|
||||
if ("tabId" in sender) {
|
||||
// The message came from a privileged extension page running in a tab. In
|
||||
// that case, it should include a tabId property (which is filled in by the
|
||||
// page-open listener below).
|
||||
tabId = sender.tabId;
|
||||
delete sender.tabId;
|
||||
} else if (
|
||||
ExtensionCommon.instanceOf(target, "XULFrameElement") ||
|
||||
ExtensionCommon.instanceOf(target, "HTMLIFrameElement")
|
||||
) {
|
||||
tabId = tabTracker.getBrowserData(target).tabId;
|
||||
}
|
||||
|
||||
if (tabId) {
|
||||
let tab = extension.tabManager.get(tabId, null);
|
||||
if (tab) {
|
||||
sender.tab = tab.convert();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Used by Extension.jsm
|
||||
global.tabGetSender = getSender;
|
||||
|
||||
/* eslint-disable mozilla/balanced-listeners */
|
||||
extensions.on("uninstalling", (msg, extension) => {
|
||||
if (extension.uninstallURL) {
|
||||
|
||||
@@ -599,31 +599,6 @@ extensions.on("startup", (type, extension) => {
|
||||
);
|
||||
});
|
||||
|
||||
// This function is pretty tightly tied to Extension.jsm.
|
||||
// Its job is to fill in the |tab| property of the sender.
|
||||
const getSender = (extension, target, sender) => {
|
||||
let tabId = -1;
|
||||
if ("tabId" in sender) {
|
||||
// The message came from a privileged extension page running in a tab. In
|
||||
// that case, it should include a tabId property (which is filled in by the
|
||||
// page-open listener below).
|
||||
tabId = sender.tabId;
|
||||
delete sender.tabId;
|
||||
} else if (ChromeUtils.getClassName(target) == "XULFrameElement") {
|
||||
tabId = tabTracker.getBrowserData(target).tabId;
|
||||
}
|
||||
|
||||
if (tabId != null && tabId >= 0) {
|
||||
const tab = extension.tabManager.get(tabId, null);
|
||||
if (tab) {
|
||||
sender.tab = tab.convert();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Used by Extension.jsm
|
||||
global.tabGetSender = getSender;
|
||||
|
||||
/* eslint-disable mozilla/balanced-listeners */
|
||||
extensions.on("page-shutdown", (type, context) => {
|
||||
if (context.viewType == "tab") {
|
||||
|
||||
@@ -219,24 +219,25 @@ class EmbedderPort {
|
||||
}
|
||||
|
||||
class GeckoViewConnection {
|
||||
constructor(sender, nativeApp, allowContentMessaging) {
|
||||
constructor(sender, target, nativeApp, allowContentMessaging) {
|
||||
this.sender = sender;
|
||||
// Map from the extension MessageSender to the android GeckoBundle format.
|
||||
sender.extensionId = sender.id;
|
||||
this.target = target;
|
||||
this.nativeApp = nativeApp;
|
||||
this.allowContentMessaging = allowContentMessaging;
|
||||
|
||||
if (!this.allowContentMessaging && !sender.verified) {
|
||||
if (!allowContentMessaging && sender.envType !== "addon_child") {
|
||||
throw new Error(`Unexpected messaging sender: ${JSON.stringify(sender)}`);
|
||||
}
|
||||
}
|
||||
|
||||
get dispatcher() {
|
||||
const target = this.sender.actor.browsingContext.top.embedderElement;
|
||||
|
||||
if (this.sender.envType === "addon_child") {
|
||||
// If this is a WebExtension Page we will have a GeckoSession associated
|
||||
// to it and thus a dispatcher.
|
||||
const dispatcher = GeckoViewUtils.getDispatcherForWindow(
|
||||
target.ownerGlobal
|
||||
this.target.ownerGlobal
|
||||
);
|
||||
if (dispatcher) {
|
||||
return dispatcher;
|
||||
@@ -252,7 +253,7 @@ class GeckoViewConnection {
|
||||
// If this message came from a content script, send the message to
|
||||
// the corresponding tab messenger so that GeckoSession can pick it
|
||||
// up.
|
||||
return GeckoViewUtils.getDispatcherForWindow(target.ownerGlobal);
|
||||
return GeckoViewUtils.getDispatcherForWindow(this.target.ownerGlobal);
|
||||
}
|
||||
|
||||
throw new Error(`Uknown sender envType: ${this.sender.envType}`);
|
||||
|
||||
@@ -56,6 +56,10 @@ const { BaseConduit } = ChromeUtils.import(
|
||||
"resource://gre/modules/ConduitsChild.jsm"
|
||||
);
|
||||
|
||||
const { WebNavigationFrames } = ChromeUtils.import(
|
||||
"resource://gre/modules/WebNavigationFrames.jsm"
|
||||
);
|
||||
|
||||
const BATCH_TIMEOUT_MS = 250;
|
||||
const ADDON_ENV = new Set(["addon_child", "devtools_child"]);
|
||||
|
||||
@@ -134,14 +138,24 @@ const Hub = {
|
||||
throw new Error(`Missing WindowGlobalParent for ${extensionId}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill in common address fields knowable from the parent process.
|
||||
* @param {ConduitAddress} address
|
||||
* @param {ConduitsParent} actor
|
||||
*/
|
||||
fillInAddress(address, actor) {
|
||||
address.actor = actor;
|
||||
address.verified = this.verifyEnv(address);
|
||||
address.frameId = WebNavigationFrames.getFrameId(actor.browsingContext);
|
||||
},
|
||||
|
||||
/**
|
||||
* Save info about a new remote conduit.
|
||||
* @param {ConduitAddress} address
|
||||
* @param {ConduitsParent} actor
|
||||
*/
|
||||
recvConduitOpened(address, actor) {
|
||||
address.actor = actor;
|
||||
address.verified = this.verifyEnv(address);
|
||||
this.fillInAddress(address, actor);
|
||||
this.remotes.set(address.id, address);
|
||||
this.byActor.get(actor).add(address);
|
||||
},
|
||||
|
||||
@@ -305,11 +305,9 @@ class Port {
|
||||
* basics of sendMessage, onMessage, connect and onConnect.
|
||||
*/
|
||||
class Messenger {
|
||||
constructor(context, sender) {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.conduit = context.openConduit(this, {
|
||||
url: sender.url,
|
||||
frameId: sender.frameId,
|
||||
childId: context.childManager.id,
|
||||
query: ["NativeMessage", "RuntimeMessage", "PortConnect"],
|
||||
recv: ["RuntimeMessage", "PortConnect"],
|
||||
|
||||
@@ -955,7 +955,7 @@ class ContentScriptContextChild extends BaseContext {
|
||||
}
|
||||
|
||||
defineLazyGetter(ContentScriptContextChild.prototype, "messenger", function() {
|
||||
return new Messenger(this, { frameId: this.frameId, url: this.url });
|
||||
return new Messenger(this);
|
||||
});
|
||||
|
||||
defineLazyGetter(
|
||||
|
||||
@@ -31,11 +31,6 @@ ChromeUtils.defineModuleGetter(
|
||||
"Schemas",
|
||||
"resource://gre/modules/Schemas.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"WebNavigationFrames",
|
||||
"resource://gre/modules/WebNavigationFrames.jsm"
|
||||
);
|
||||
|
||||
const CATEGORY_EXTENSION_SCRIPTS_ADDON = "webextension-scripts-addon";
|
||||
const CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS = "webextension-scripts-devtools";
|
||||
@@ -182,21 +177,13 @@ class ExtensionBaseContextChild extends BaseContext {
|
||||
this.setContentWindow(contentWindow);
|
||||
this.browsingContextId = contentWindow.docShell.browsingContext.id;
|
||||
|
||||
// This is the MessageSender property passed to extension.
|
||||
let sender = { id: extension.id };
|
||||
if (viewType == "tab") {
|
||||
sender.frameId = WebNavigationFrames.getFrameId(contentWindow);
|
||||
sender.tabId = tabId;
|
||||
Object.defineProperty(this, "tabId", {
|
||||
value: tabId,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
if (uri) {
|
||||
sender.url = uri.spec;
|
||||
}
|
||||
this.sender = sender;
|
||||
|
||||
Schemas.exportLazyGetter(contentWindow, "browser", () => {
|
||||
let browserObj = Cu.createObjectIn(contentWindow);
|
||||
@@ -255,7 +242,7 @@ class ExtensionBaseContextChild extends BaseContext {
|
||||
}
|
||||
|
||||
defineLazyGetter(ExtensionBaseContextChild.prototype, "messenger", function() {
|
||||
return new Messenger(this, this.sender);
|
||||
return new Messenger(this);
|
||||
});
|
||||
|
||||
class ExtensionPageContextChild extends ExtensionBaseContextChild {
|
||||
|
||||
@@ -274,15 +274,12 @@ const ProxyMessenger = {
|
||||
|
||||
openNative(nativeApp, sender) {
|
||||
let context = ParentAPIManager.getContextById(sender.childId);
|
||||
let { extension } = context;
|
||||
if (extension.hasPermission("geckoViewAddons")) {
|
||||
let allowMessagingFromContent = extension.hasPermission(
|
||||
"nativeMessagingFromContent"
|
||||
);
|
||||
if (context.extension.hasPermission("geckoViewAddons")) {
|
||||
return new GeckoViewConnection(
|
||||
sender,
|
||||
this.getSender(context, sender),
|
||||
sender.actor.browsingContext.top.embedderElement,
|
||||
nativeApp,
|
||||
allowMessagingFromContent
|
||||
context.extension.hasPermission("nativeMessagingFromContent")
|
||||
);
|
||||
} else if (sender.verified) {
|
||||
return new NativeApp(context, nativeApp);
|
||||
@@ -294,11 +291,21 @@ const ProxyMessenger = {
|
||||
return this.openNative(nativeApp, sender).sendMessage(holder);
|
||||
},
|
||||
|
||||
getSender(extension, source) {
|
||||
let { extensionId, envType, frameId, url, actor, id } = source;
|
||||
let sender = { id: extensionId, envType, frameId, url, contextId: id };
|
||||
let target = actor.browsingContext.top.embedderElement;
|
||||
apiManager.global.tabGetSender(extension, target, sender);
|
||||
getSender(context, source) {
|
||||
let sender = {
|
||||
contextId: source.id,
|
||||
id: source.extensionId,
|
||||
envType: source.envType,
|
||||
frameId: source.frameId,
|
||||
url: context.uri.spec,
|
||||
};
|
||||
|
||||
let { tabId } = apiManager.global.tabTracker.getBrowserData(
|
||||
source.actor.browsingContext.top.embedderElement
|
||||
);
|
||||
if (tabId > 0) {
|
||||
sender.tab = context.extension.tabManager.get(tabId, null)?.convert();
|
||||
}
|
||||
return sender;
|
||||
},
|
||||
|
||||
@@ -322,7 +329,8 @@ const ProxyMessenger = {
|
||||
}
|
||||
await extension.wakeupBackground?.();
|
||||
|
||||
arg.sender = this.getSender(extension, sender);
|
||||
let context = ParentAPIManager.getContextById(sender.childId);
|
||||
arg.sender = this.getSender(context, sender);
|
||||
arg.topBC = arg.tabId && this.getTopBrowsingContextId(arg.tabId);
|
||||
return arg.tabId ? "tab" : "messenger";
|
||||
},
|
||||
|
||||
@@ -26,6 +26,11 @@ ChromeUtils.defineModuleGetter(
|
||||
"AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Extension",
|
||||
@@ -445,6 +450,12 @@ ExtensionTestCommon = class ExtensionTestCommon {
|
||||
* @returns {Extension}
|
||||
*/
|
||||
static generate(data) {
|
||||
// An android-browser shared test, needs addon manager on GeckoView.
|
||||
if (data.androidBrowserTest && AppConstants.platform === "android") {
|
||||
data.useAddonManager ??= "permanent";
|
||||
this.setExtensionID(data);
|
||||
}
|
||||
|
||||
let file = this.generateXPI(data);
|
||||
|
||||
flushJarCache(file.path);
|
||||
|
||||
@@ -13,11 +13,8 @@
|
||||
|
||||
add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
useAddonManager: "permanent",
|
||||
androidBrowserTest: true,
|
||||
manifest: {
|
||||
applications: {
|
||||
gecko: { id: "blah@android" },
|
||||
},
|
||||
content_scripts: [{
|
||||
matches: ["http://mochi.test/*/file_sample.html?tabs.sendMessage"],
|
||||
js: ["cs.js"],
|
||||
@@ -90,6 +87,68 @@ add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_tabs_sendMessage_using_frameId() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
androidBrowserTest: true,
|
||||
manifest: {
|
||||
content_scripts: [
|
||||
{
|
||||
matches: ["http://mochi.test/*/file_contains_iframe.html"],
|
||||
run_at: "document_start",
|
||||
js: ["cs_top.js"],
|
||||
},
|
||||
{
|
||||
matches: ["http://example.org/*/file_contains_img.html"],
|
||||
js: ["cs_iframe.js"],
|
||||
all_frames: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
background() {
|
||||
browser.runtime.onMessage.addListener(async (msg, sender) => {
|
||||
let { tab, frameId } = sender;
|
||||
browser.test.assertEq(msg, "cs_iframe_ready", "Iframe cs ready.");
|
||||
browser.test.assertTrue(frameId > 0, "Not from the top frame.");
|
||||
|
||||
let response = await browser.tabs.sendMessage(tab.id, "msg");
|
||||
browser.test.assertEq(response, "cs_top", "Top cs responded first.");
|
||||
|
||||
response = await browser.tabs.sendMessage(tab.id, "msg", { frameId });
|
||||
browser.test.assertEq(response, "cs_iframe", "Iframe cs reponded.");
|
||||
|
||||
browser.test.sendMessage("done");
|
||||
});
|
||||
browser.test.sendMessage("ready");
|
||||
},
|
||||
|
||||
files: {
|
||||
"cs_top.js"() {
|
||||
browser.test.log("Top content script loaded.")
|
||||
browser.runtime.onMessage.addListener(async () => "cs_top");
|
||||
},
|
||||
"cs_iframe.js"() {
|
||||
browser.test.log("Iframe content script loaded.")
|
||||
browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||
browser.test.log("Iframe content script received message.")
|
||||
setTimeout(() => sendResponse("cs_iframe"), 100);
|
||||
return true;
|
||||
});
|
||||
browser.runtime.sendMessage("cs_iframe_ready");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("ready");
|
||||
|
||||
let win = window.open("file_contains_iframe.html");
|
||||
await extension.awaitMessage("done");
|
||||
win.close();
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user