w3c / webextensions

Charter and administrivia for the WebExtensions Community Group (WECG)
Other
595 stars 56 forks source link

request: allow to retrieve a `tabId` and `documentId` from the content script #469

Open fregante opened 11 months ago

fregante commented 11 months ago

This was already requested in the previous thread, but it was probably too late by then, so I'll extract it into its own request


Could this be expanded to include the tab ID as well?

browser.runtime.getTarget(window) // {tabId: 1, frameId: 0}
browser.runtime.getTarget(iframe) // {tabId: 1, frameId: 63748}

It's a pretty common request to know "this" tab ID: https://stackoverflow.com/q/6202953/288906 (123 upvotes)

Currently this requires a ping to the runtime, which may or may not be "quick".

Including the recently-added documentId would be great. There's a lot of potential here so limiting it to just a frameId seems overly specific; returning an object makes it more flexible for future usage without having to create specific APIs.

bershanskiy commented 11 months ago

Once browsers support both browser.runtime.getTarget() and chrome.runtime.getContexts(), one could trivially retrieve both tabId and documentId by combining them together. Of course, there is some inefficiency in two consecutive async calls and some potential data race in frames being navigated, loaded, etc.

fregante commented 11 months ago

runtime.getContexts like extension.getViews is not available in content scripts, while runtime.getFrameId is only available in content scripts, so you can't combine the two.

tophf commented 11 months ago

Ideally this info must be available synchronously because async call may take several seconds to complete on a slower device if the site is busy (e.g. compiling/running a big js or building its virtual DOM or whatever else). AFAIK documentId in Chrome is stored in the document's renderer, so it can be exposed as a field in chrome.runtime.

Rob--W commented 11 months ago

I'm supportive of the capability requests, i.e. the ability to retrieve the current tabId/documentId, asynchronously.

I'd like this to be part of the "Contexts" feature (getContexts) rather than separate synchronous methods. The runtime.getContexts() proposal at https://github.com/w3c/webextensions/blob/main/proposals/runtime_get_contexts.md includes the relevant part already: https://github.com/w3c/webextensions/blob/10a84ee95a36646b88b538a1722ca4b2b2fff863/proposals/runtime_get_contexts.md#L264-L279

fregante commented 11 months ago

That makes sense but then one has to match the objects returned with each <iframe> element manually. Also I'd need this API in the content script, not just on extension pages (unlike the previous getViews)

Rob--W commented 11 months ago

That makes sense but then one has to match the objects returned with each <iframe> element manually.

What is the use case for this?

Also I'd need this API in the content script, not just on extension pages (unlike the previous getViews)

In my comment I referenced the part of the proposal that calls out the potential to introduce getCurrentContext, AND making the concept available to content scripts.

fregante commented 11 months ago

In my comment

Sorry, on mobile the link opened the page without deep-linking, I didn't see the specific section.

That makes sense but then one has to match the objects returned with each <iframe> element manually.

What is the use case for this?

One of the usages was to enable communication between frames, e.g.:

Other than postMessage, which isn't particularly convenient nor safe (https://github.com/w3c/webextensions/issues/77), this can only be done:

This new API would allow:

// From a `chrome-extension://` iframe to a `http://` top frame
const {tabId, iframeId} = await browser.runtime.getTarget(window.top)
chrome.tabs.sendMessage(tabId, message, {frameId})

and:

// From a `chrome-extension://` top frame to a `http://` iframe
const {tabId, iframeId} = await browser.runtime.getTarget($('iframe'))
chrome.tabs.sendMessage(tabId, message, {frameId})

or via runtime message routing in any direction


I suppose https://github.com/w3c/webextensions/issues/77 would be a more direct solution to this, but it would not be enough "iframe to iframe" communication, because they don't have access to each other's iframe element.

tophf commented 11 months ago

browser.runtime.getTarget($('iframe')) browser.runtime.getTarget(window.top)

Since window.top is a Window, it would be more consistent to use frameElem.contentWindow here. It would also allow using a Window-typed event.source in a message.

wfjsw commented 4 months ago

This is also important that this is synchronous as I can use this ID to do browser.scripting.executeScript on the page that the content script runs since the injection.target is mandatory and acquiring this asynchronously would miss the document-start timepoint.


I'm looking for a way to securely do sendMessage from MAIN world script to background service worker. Currently it would be impossible to do this without involving postMessage.

tophf commented 4 months ago

@wfjsw, it's not possible to achieve a real document_start with executeScript because it injects asynchronously, messaging is asynchronous as well. A real document_start can be achieved by using a declared or registered content script (chrome.scripting.registerContentScripts).

I'm looking for a way to securely do sendMessage from MAIN world script to background service worker. Currently it would be impossible to do this without involving postMessage.

It will be implemented as a separate API to communicate between the worlds securely. Note that your code in the MAIN world may run in an already poisoned JS environment (https://crbug.com/40202434 applies to all browsers). Currently the only secure way is to create a temporary iframe to extract dispatchEvent/addEventListener/etc. to establish a secure channel, it's very complicated and you can see how it's done in Violentmonkey. In ManifestV3 it requires using the userScripts API permission, which is problematic because it misrepresents the function of the extensions, also users in Chrome must enable "developer mode" in chrome://extensions.