w3c / webextensions

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

Proposal: Default to Active Tab in scripting.executeScript() #431

Open erosman opened 1 year ago

erosman commented 1 year ago

Preamble

Note: When using Manifest V3 or higher, use scripting.executeScript() to execute scripts.

tabs.executeScript()

scripting.executeScript() is introduced as the replacement for tabs.executeScript(), however:

Proposal

Make TabId optional and default to the active tab in scripting.executeScript() like tabs.executeScript()

Purpose

Many extensions only require "activeTab" permission and if necessary, tabs.executeScript() can be executed without any additional process or permission in MV2.

Aforementioned extensions in MV3 scripting.executeScript() would be required to include the active tab ID, e.g. via tabs.query() ~which would additionally require "tabs" permission~.

A common example is onCommand API which does not include any tab information.

tophf commented 1 year ago

Also suggested in https://crbug.com/1194519.

P.S. Note that there's no need for the tabs permission to get the tab id. The problem with the current design is only the lack of consistency with MV2 and the need to write boilerplate code.

dotproto commented 1 year ago

Nit: "active tab" is a little overloaded due to it's close association with the activeTab permission. I'd suggest using "current tab" in order to better reflect the intended meaning.

@oliverdunk, can you speak to the rationale behind making tab selection explicit when calling scripting.executeScript() in Chrome?

rdcronin commented 1 year ago

Chrome's opposed to this.

Determining the currently-active tab is fraught with complexity:

Instead of this, I think we should make it easy for extensions to find the tab they want to inject in. In cases where the extension "probably" wants to inject in the active tab (we've seen this most in response to action.onClicked, contextMenus.onClicked, commands.onCommand**, etc), we pass the tab object into the event so it's trivial to specify the ID. For cases where executeScript is being called from a background context and in response to an event that isn't tied to an event like that, I don't think we have high confidence that the developer wants to target the active tab (for instance, executeScript is often used for injecting scripts into newly opened tabs -- which may be background -- or in response to webnavigation events, which could easily be for background tabs).

One case that was brought up by @carlosjeurissen that isn't well supported here is invoking executeScript() from an extension's popup. I would rather support this (more easily) by extending tabs.getCurrent() (which does not return the active tab, but rather the tab associated with the currently-running script) to return the tab the popup has. This would make the case of injecting in the tab under the popup simple and more reliable than defaulting to the active tab (since the popup may be open a tab in a backgrounded window). I think we should file an issue for that separately.

** Firefox does not currently include a tab for commands.onCommand. Chrome does.

erosman commented 1 year ago

Instead of this, I think we should make it easy for extensions to find the tab they want to inject in.

It sounds good, and as you have mentioned, often extensions that rely on activeTab permission, use it subsequent to user-actions. Therefore, providing the tab.id with those actions should be a good solution.

Main areas where tabs.Tab would be needed

tophf commented 1 year ago

@rdcronin, 1) re "make it easy for extensions to find the tab" it would be nice if you add the popup's tabId to chrome.action similarly to chrome.devtools.inspectedWindow.tabId. 2) Re requiring the developers to use chrome.tabs.query or any other explicit method doesn't remove the inherent raciness, it just adds boilerplate code, which will be used in exactly the same incorrect fashion in those scenarios you mentioned. Changing getCurrent behavior only reduces the boilerplate by a word or two, but doesn't change the fact itself.