Open Bug 1800401 Opened 2 years ago Updated 5 days ago

Handlers using Async/await are prevented from using "sidebarAction.open()" and other APIs requiring user-interaction

Categories

(WebExtensions :: General, enhancement, P5)

Firefox 107
enhancement

Tracking

(Not tracked)

People

(Reporter: bluesky42624, Unassigned)

References

(Depends on 1 open bug, Blocks 1 open bug)

Details

(Whiteboard: [design-decision-approved])

Attachments

(1 file)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:107.0) Gecko/20100101 Firefox/107.0

Steps to reproduce:

  1. Set up a simple test extension with 3 files, manifest.json, background.js, and sidebar.html, like the attached example.
  2. Open Firefox
  3. Go to about:debugging
  4. Load Temporary Add-on...
  5. Select the manifest.json file
  6. Open the background script console through the Inspect button
  7. Click on the about:debugging page again to give the focus back to the browser instance
  8. Do Ctrl+Shift+S (or the exact shortcut defined in the manifest.json file before)

Actual results:

Case 1 and Case 2 works as expected, but Case 3 with the await call is denied access to the API due to the following error:

Uncaught (in promise) Error: sidebarAction.open may only be called from a user input handler

Expected results:

3 cases are provided in the background.js file, Case 1 is a simple non-async handler, Case 2 is an async handler without any await calls within, and finally Case 3 being the typical case of an async handler with an await call. All three should open the sidebarAction, since all three handlers are essentially no-ops other than that one call to browser.sidebarAction.open();.

This is especially an issue for Manifest v3 since:

  1. Manifest v3/non-persistent background pages mean, that all states need to be stored in browser.storage
  2. browser.storage is async
  3. Event handlers inside background pages commonly need to access state data, stored data, settings, etc.
  4. APIs that require being called from inside the handler for a user action. prevent any event handlers using Promises and async/await's from calling it
  5. There is no workaround, since browser.storage is completely async
  6. Data access becomes impossible from background page event handlers needing the use of APIs requiring user action

The Bugbug bot thinks this bug should belong to the 'WebExtensions::Untriaged' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.

Product: Firefox → WebExtensions
Status: UNCONFIRMED → NEW
Type: defect → enhancement
Ever confirmed: true
Whiteboard: [design-decision-needed]

The user interaction requirements are already being dropped for browserAction.openPopup (bug 1755763) and pageAction.openPopup (bug 1798425). In these cases, the action panel is a floating window on top of the current window, and dismissed when the user clicks elsewhere.

In the sidebarAction API, the panel is a sidebar (at the left). The user can identify the extension by the header that contains the extension icon and name. The sidebar content underneath can be extension-hosted content, or even a remote URL (specified by the extension). This was initially implemented in bug 1208596, with the ability to open the sidebar with user interaction in bug 1341126.

While the ability to open the sidebar without user interaction has potential for abuse, I don't think that it's a great risk, because the same capability already exists in the form of resizing the window and opening a popup next to it. There is the question on what to do when another sidebar already exists; that is already an issue with the current API, but cautiously I'd be inclined to reject the API call when that situation occurs.

If we are somehow reluctant to drop the user interaction requirement altogether, then there are various approaches we can take to balance the desire of users/extensions to open sidebar against abuse patterns:

  • Drop the user interaction requirement al together.
  • Conditionally drop the user interaction requirement.
    • e.g. only after the user has opened the panel at least once
    • and/or requiring a permission.
    • and/or a preference.
    • and/or if the user has opted in via new UI as part of bug 1787179.
  • Keep the user interaction requirement, but extent the validity of a user interaction by a number of seconds (arbitrarily chosen, e.g. 5 seconds).
  • Keep the status quo (i.e. wontfix this bug) - require user interaction for sidebarAction.open.

I'm in favor of dropping the user interaction requirement for sidebarAction.open. In discussion with the team, there were some mild concerns over potential abuse, but no strong opinions either way.

Component: Untriaged → General
See Also: → 1208596, 1341126, 1755763, 1798425

:dveditz : Do you see any concern with allowing extensions to open their sidebar without user interaction? More context is available in comment 3.

Flags: needinfo?(dveditz)
Blocks: 1392624
Severity: -- → N/A
Priority: -- → P5
See Also: → 1818959

In addition to what has been said in comment 3, the team affirmed again that consistency with browserAction.openPopup / pageAction.openPopup is desirable.

Flags: needinfo?(dveditz)
Whiteboard: [design-decision-needed] → [design-decision-approved]

The same underlying issue is still present in other async event handlers, such as situations where a value needs to be retrieved from storage before calling permissions.request() or interacting with the system clipboard.

To my knowledge the primary concern with passing a user input flag down a promise chain is that this could enable abuse scenarios where an extension holds onto a promise with a valid user interaction and consume it later without the user's knowledge. I think the most obvious way to mitigate this kind of abuse is to make a user interaction invalid a short amount of time after the input. For example, a user input flag might be consumable for up to 200 millisecond after the user initiates the interaction.

The main drawback to this approach is that a fixed time limit may feel arbitrary and unpredictable for extension developers. For example, some browser.storage operations will complete within this time period while others will not. External factors may also impact whether or not a promise chain resolves in time, such as lower powered devices or resource contention on higher power systems.

A fixed lifetime isn't ideal, but at the moment it strikes me as the most practical solution for a material problem facing developers.

Nowadays the web platform defines the concept of "transient user activation", which is a short-lived period of time where an API gated by user activation can be used. Some APIs consume the transient user activation upon invocation.

Documentation: https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation

The recommended value for the timeout is specified to be "at most a few seconds", and in practice Firefox uses 5 seconds in Firefox: https://searchfox.org/mozilla-central/rev/5756c5a3dea4f2896cdb3c8bb15d0ced5e2bf690/modules/libpref/init/StaticPrefList.yaml#4356-4360

I think that we can consider integrating the concept of user activation in the extension framework to support this use case more broadly.

The discussion above on user gesture / user activation fits better at bug 1398833. The request here is specifically about the sidebarAction.open, for which a potential solution could be to drop the user interaction requirement from the API as noted in comment 3.

Depends on: 1892655
Depends on: 1398833
No longer depends on: 1892655
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: