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,
|
replaceUrlInTab: true,
|
||||||
searchInitialized: true,
|
searchInitialized: true,
|
||||||
sidebarActionFor: true,
|
sidebarActionFor: true,
|
||||||
tabGetSender: true,
|
|
||||||
tabTracker: true,
|
tabTracker: true,
|
||||||
waitForTabLoaded: true,
|
waitForTabLoaded: true,
|
||||||
windowTracker: true,
|
windowTracker: true,
|
||||||
|
|||||||
@@ -44,34 +44,6 @@ function isPrivateTab(nativeTab) {
|
|||||||
return PrivateBrowsingUtils.isBrowserPrivate(nativeTab.linkedBrowser);
|
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 */
|
/* eslint-disable mozilla/balanced-listeners */
|
||||||
extensions.on("uninstalling", (msg, extension) => {
|
extensions.on("uninstalling", (msg, extension) => {
|
||||||
if (extension.uninstallURL) {
|
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 */
|
/* eslint-disable mozilla/balanced-listeners */
|
||||||
extensions.on("page-shutdown", (type, context) => {
|
extensions.on("page-shutdown", (type, context) => {
|
||||||
if (context.viewType == "tab") {
|
if (context.viewType == "tab") {
|
||||||
|
|||||||
@@ -219,24 +219,25 @@ class EmbedderPort {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GeckoViewConnection {
|
class GeckoViewConnection {
|
||||||
constructor(sender, nativeApp, allowContentMessaging) {
|
constructor(sender, target, nativeApp, allowContentMessaging) {
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
|
// Map from the extension MessageSender to the android GeckoBundle format.
|
||||||
|
sender.extensionId = sender.id;
|
||||||
|
this.target = target;
|
||||||
this.nativeApp = nativeApp;
|
this.nativeApp = nativeApp;
|
||||||
this.allowContentMessaging = allowContentMessaging;
|
this.allowContentMessaging = allowContentMessaging;
|
||||||
|
|
||||||
if (!this.allowContentMessaging && !sender.verified) {
|
if (!allowContentMessaging && sender.envType !== "addon_child") {
|
||||||
throw new Error(`Unexpected messaging sender: ${JSON.stringify(sender)}`);
|
throw new Error(`Unexpected messaging sender: ${JSON.stringify(sender)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get dispatcher() {
|
get dispatcher() {
|
||||||
const target = this.sender.actor.browsingContext.top.embedderElement;
|
|
||||||
|
|
||||||
if (this.sender.envType === "addon_child") {
|
if (this.sender.envType === "addon_child") {
|
||||||
// If this is a WebExtension Page we will have a GeckoSession associated
|
// If this is a WebExtension Page we will have a GeckoSession associated
|
||||||
// to it and thus a dispatcher.
|
// to it and thus a dispatcher.
|
||||||
const dispatcher = GeckoViewUtils.getDispatcherForWindow(
|
const dispatcher = GeckoViewUtils.getDispatcherForWindow(
|
||||||
target.ownerGlobal
|
this.target.ownerGlobal
|
||||||
);
|
);
|
||||||
if (dispatcher) {
|
if (dispatcher) {
|
||||||
return dispatcher;
|
return dispatcher;
|
||||||
@@ -252,7 +253,7 @@ class GeckoViewConnection {
|
|||||||
// If this message came from a content script, send the message to
|
// If this message came from a content script, send the message to
|
||||||
// the corresponding tab messenger so that GeckoSession can pick it
|
// the corresponding tab messenger so that GeckoSession can pick it
|
||||||
// up.
|
// up.
|
||||||
return GeckoViewUtils.getDispatcherForWindow(target.ownerGlobal);
|
return GeckoViewUtils.getDispatcherForWindow(this.target.ownerGlobal);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Uknown sender envType: ${this.sender.envType}`);
|
throw new Error(`Uknown sender envType: ${this.sender.envType}`);
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ const { BaseConduit } = ChromeUtils.import(
|
|||||||
"resource://gre/modules/ConduitsChild.jsm"
|
"resource://gre/modules/ConduitsChild.jsm"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { WebNavigationFrames } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/WebNavigationFrames.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
const BATCH_TIMEOUT_MS = 250;
|
const BATCH_TIMEOUT_MS = 250;
|
||||||
const ADDON_ENV = new Set(["addon_child", "devtools_child"]);
|
const ADDON_ENV = new Set(["addon_child", "devtools_child"]);
|
||||||
|
|
||||||
@@ -134,14 +138,24 @@ const Hub = {
|
|||||||
throw new Error(`Missing WindowGlobalParent for ${extensionId}`);
|
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.
|
* Save info about a new remote conduit.
|
||||||
* @param {ConduitAddress} address
|
* @param {ConduitAddress} address
|
||||||
* @param {ConduitsParent} actor
|
* @param {ConduitsParent} actor
|
||||||
*/
|
*/
|
||||||
recvConduitOpened(address, actor) {
|
recvConduitOpened(address, actor) {
|
||||||
address.actor = actor;
|
this.fillInAddress(address, actor);
|
||||||
address.verified = this.verifyEnv(address);
|
|
||||||
this.remotes.set(address.id, address);
|
this.remotes.set(address.id, address);
|
||||||
this.byActor.get(actor).add(address);
|
this.byActor.get(actor).add(address);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -305,11 +305,9 @@ class Port {
|
|||||||
* basics of sendMessage, onMessage, connect and onConnect.
|
* basics of sendMessage, onMessage, connect and onConnect.
|
||||||
*/
|
*/
|
||||||
class Messenger {
|
class Messenger {
|
||||||
constructor(context, sender) {
|
constructor(context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.conduit = context.openConduit(this, {
|
this.conduit = context.openConduit(this, {
|
||||||
url: sender.url,
|
|
||||||
frameId: sender.frameId,
|
|
||||||
childId: context.childManager.id,
|
childId: context.childManager.id,
|
||||||
query: ["NativeMessage", "RuntimeMessage", "PortConnect"],
|
query: ["NativeMessage", "RuntimeMessage", "PortConnect"],
|
||||||
recv: ["RuntimeMessage", "PortConnect"],
|
recv: ["RuntimeMessage", "PortConnect"],
|
||||||
|
|||||||
@@ -955,7 +955,7 @@ class ContentScriptContextChild extends BaseContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defineLazyGetter(ContentScriptContextChild.prototype, "messenger", function() {
|
defineLazyGetter(ContentScriptContextChild.prototype, "messenger", function() {
|
||||||
return new Messenger(this, { frameId: this.frameId, url: this.url });
|
return new Messenger(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
defineLazyGetter(
|
defineLazyGetter(
|
||||||
|
|||||||
@@ -31,11 +31,6 @@ ChromeUtils.defineModuleGetter(
|
|||||||
"Schemas",
|
"Schemas",
|
||||||
"resource://gre/modules/Schemas.jsm"
|
"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_ADDON = "webextension-scripts-addon";
|
||||||
const CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS = "webextension-scripts-devtools";
|
const CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS = "webextension-scripts-devtools";
|
||||||
@@ -182,21 +177,13 @@ class ExtensionBaseContextChild extends BaseContext {
|
|||||||
this.setContentWindow(contentWindow);
|
this.setContentWindow(contentWindow);
|
||||||
this.browsingContextId = contentWindow.docShell.browsingContext.id;
|
this.browsingContextId = contentWindow.docShell.browsingContext.id;
|
||||||
|
|
||||||
// This is the MessageSender property passed to extension.
|
|
||||||
let sender = { id: extension.id };
|
|
||||||
if (viewType == "tab") {
|
if (viewType == "tab") {
|
||||||
sender.frameId = WebNavigationFrames.getFrameId(contentWindow);
|
|
||||||
sender.tabId = tabId;
|
|
||||||
Object.defineProperty(this, "tabId", {
|
Object.defineProperty(this, "tabId", {
|
||||||
value: tabId,
|
value: tabId,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (uri) {
|
|
||||||
sender.url = uri.spec;
|
|
||||||
}
|
|
||||||
this.sender = sender;
|
|
||||||
|
|
||||||
Schemas.exportLazyGetter(contentWindow, "browser", () => {
|
Schemas.exportLazyGetter(contentWindow, "browser", () => {
|
||||||
let browserObj = Cu.createObjectIn(contentWindow);
|
let browserObj = Cu.createObjectIn(contentWindow);
|
||||||
@@ -255,7 +242,7 @@ class ExtensionBaseContextChild extends BaseContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defineLazyGetter(ExtensionBaseContextChild.prototype, "messenger", function() {
|
defineLazyGetter(ExtensionBaseContextChild.prototype, "messenger", function() {
|
||||||
return new Messenger(this, this.sender);
|
return new Messenger(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
class ExtensionPageContextChild extends ExtensionBaseContextChild {
|
class ExtensionPageContextChild extends ExtensionBaseContextChild {
|
||||||
|
|||||||
@@ -274,15 +274,12 @@ const ProxyMessenger = {
|
|||||||
|
|
||||||
openNative(nativeApp, sender) {
|
openNative(nativeApp, sender) {
|
||||||
let context = ParentAPIManager.getContextById(sender.childId);
|
let context = ParentAPIManager.getContextById(sender.childId);
|
||||||
let { extension } = context;
|
if (context.extension.hasPermission("geckoViewAddons")) {
|
||||||
if (extension.hasPermission("geckoViewAddons")) {
|
|
||||||
let allowMessagingFromContent = extension.hasPermission(
|
|
||||||
"nativeMessagingFromContent"
|
|
||||||
);
|
|
||||||
return new GeckoViewConnection(
|
return new GeckoViewConnection(
|
||||||
sender,
|
this.getSender(context, sender),
|
||||||
|
sender.actor.browsingContext.top.embedderElement,
|
||||||
nativeApp,
|
nativeApp,
|
||||||
allowMessagingFromContent
|
context.extension.hasPermission("nativeMessagingFromContent")
|
||||||
);
|
);
|
||||||
} else if (sender.verified) {
|
} else if (sender.verified) {
|
||||||
return new NativeApp(context, nativeApp);
|
return new NativeApp(context, nativeApp);
|
||||||
@@ -294,11 +291,21 @@ const ProxyMessenger = {
|
|||||||
return this.openNative(nativeApp, sender).sendMessage(holder);
|
return this.openNative(nativeApp, sender).sendMessage(holder);
|
||||||
},
|
},
|
||||||
|
|
||||||
getSender(extension, source) {
|
getSender(context, source) {
|
||||||
let { extensionId, envType, frameId, url, actor, id } = source;
|
let sender = {
|
||||||
let sender = { id: extensionId, envType, frameId, url, contextId: id };
|
contextId: source.id,
|
||||||
let target = actor.browsingContext.top.embedderElement;
|
id: source.extensionId,
|
||||||
apiManager.global.tabGetSender(extension, target, sender);
|
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;
|
return sender;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -322,7 +329,8 @@ const ProxyMessenger = {
|
|||||||
}
|
}
|
||||||
await extension.wakeupBackground?.();
|
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);
|
arg.topBC = arg.tabId && this.getTopBrowsingContextId(arg.tabId);
|
||||||
return arg.tabId ? "tab" : "messenger";
|
return arg.tabId ? "tab" : "messenger";
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ ChromeUtils.defineModuleGetter(
|
|||||||
"AddonManager",
|
"AddonManager",
|
||||||
"resource://gre/modules/AddonManager.jsm"
|
"resource://gre/modules/AddonManager.jsm"
|
||||||
);
|
);
|
||||||
|
ChromeUtils.defineModuleGetter(
|
||||||
|
this,
|
||||||
|
"AppConstants",
|
||||||
|
"resource://gre/modules/AppConstants.jsm"
|
||||||
|
);
|
||||||
ChromeUtils.defineModuleGetter(
|
ChromeUtils.defineModuleGetter(
|
||||||
this,
|
this,
|
||||||
"Extension",
|
"Extension",
|
||||||
@@ -445,6 +450,12 @@ ExtensionTestCommon = class ExtensionTestCommon {
|
|||||||
* @returns {Extension}
|
* @returns {Extension}
|
||||||
*/
|
*/
|
||||||
static generate(data) {
|
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);
|
let file = this.generateXPI(data);
|
||||||
|
|
||||||
flushJarCache(file.path);
|
flushJarCache(file.path);
|
||||||
|
|||||||
@@ -13,11 +13,8 @@
|
|||||||
|
|
||||||
add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
|
add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
|
||||||
let extension = ExtensionTestUtils.loadExtension({
|
let extension = ExtensionTestUtils.loadExtension({
|
||||||
useAddonManager: "permanent",
|
androidBrowserTest: true,
|
||||||
manifest: {
|
manifest: {
|
||||||
applications: {
|
|
||||||
gecko: { id: "blah@android" },
|
|
||||||
},
|
|
||||||
content_scripts: [{
|
content_scripts: [{
|
||||||
matches: ["http://mochi.test/*/file_sample.html?tabs.sendMessage"],
|
matches: ["http://mochi.test/*/file_sample.html?tabs.sendMessage"],
|
||||||
js: ["cs.js"],
|
js: ["cs.js"],
|
||||||
@@ -90,6 +87,68 @@ add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
|
|||||||
await extension.unload();
|
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user