-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow to cancel method calls #415
Comments
An example of confusing behavior caused by inability to cancel |
Making all calls cancellable never was my goal. Of course, methods completing almost instantly do not need any modification. E.g. I do not expect any delay for a call of I am familiar only with a part of WebExtensions API, so it would be hard for me to provide the exhaustive list of methods that should be made abortable. Often I am even unsure whether particular one should be considered as |
I am against reducing of ability to cancel a call to just a timeout. I am open to discussion of alternatives to async function extensionAction(execCtx, tab) {
const havePermissions = await chrome.permissions.request(
{permissions}, {signal: execCtx.signal});
// Do something else
}
var abortController;
async function actionListener(tab) {
abortController?.abort(new Error("New user action"));
abortController = new AbortController();
await extensionAction({signal: abortController.signal}, tab);
}
chrome.action.onClicked.addListener(tab => void actionListener(tab)); I have tried a more realistic proof of concept in my extension. It is Firefox-only since it relies on an API method unavailable in Chrome. Certainly I can not make |
Some WebExtensions API methods may take significant amount of time before their results are ready. Ability to cancel execution of such functions should reduce amount of efforts required to reach predictable behavior of browser extensions and decent user experience. It can be achieved, for example, by using
AbortSignal
argument similar as it was done forfetch()
.API methods that should be cancellable
The functions considered further are examples, it is not an exhaustive list.
permissions.request
User actions (
action
,contextMenus
,commands
) may ask for additional optional permissions. The user may start another add-on action when a permission request created from the previous action is still displayed.Currently there is no way to withdraw permission request popups, so users have to make decision for all of them even for stale ones that do not lead to any further execution of the extension code anymore. Cancelling the stale prompt for addition privileges before a new request should be an improvement for user interface.
scripting.executeScript
Starting of a content script may be postponed till completion of some heavy JavaScript running by the web page. Too late result may be confusing for the user.
Currently implementing of cancellable call is doable, but the price for developers is increased complexity of code. It is possible to pass to contents scripts a deadline timestamp accordingly to some timeout. If execution of a content script is delayed due to waiting for an asynchronous call, it may be possible to notify the script by another
scripting.executeScript
call or by sending a message. ThePromise
returned byscripting.executeScript
may be combined intoPromise.race
with another one responsible for timeout or another abort reason.offscreen.createDocument
Due to a programming error, an
offscreen
document may stuck in loading state if a script is spinning in a loop.Currently there is no way to destroy the problem page on timeout (cromium bug)`.
The issue should not be wide spread. An infinite loops inside loading stage scripts normally should be found during testing and should not happen in production code. While
offscreen.createDocument
is not cancellable, developers should prefer minimal page load code and passing a task using messaging instead of adding query parameters to the document URL.Adding
AbortSignal
argumentThe methods discussed above have the optional callback argument. It may be extended to accept an object with optional fields
as an additional alternative to the current variant
callback?: function
.I can not estimate cost of implementation of the
AbortSignal
argument in the code of browsers. Handling arguments that may have different types increases code complexity, but I do not see an alternative since the optional callback arguments are retained in Manifest V3 despite methods may returnPromise
objects now.Example of API simplification:
desctopCapture
the
desctopCapture.cancelChooseDesktopMedia
method may be unnecessary if the successor ofdesktopCapture.chooseDesktopMedia
accepts anAbortSignal
.The current function
with
(streamId: string, options: object) => void
callback signature may be replaced by a function returning aPromise
to a{streamId: string, options: object}
object and having the last argument similar to the described above alternativefunction? | { callback?: function, signal?: AbortSignal}?
.desktopMediaRequestId
becomes unnecessary.Keeping the current name
desktopCapture.chooseDesktopMedia
and allowing the signal option in the last argument means return typedependent on passed arguments. It may be confusing for developers and error prone. Perhaps it is better to add a new method and to preserve the existing pair for compatibility.
Merging API calls can not be applied to the
offscreen.createDocument
andoffscreen.closeDocument
pair because their calls may be separated by unload and load again cycle, so anAbortSignal
will be lost. However aboratbleoffscreen.createDocument
may be an improvement.For this particular API it would be a cosmetic improvement with some maintenance cost. Old functions must be kept for backward compatibility.
Alternatives
A disadvantage of the approach with
AbortSignal
is that some boilerplate code is necessary to combine several signals. Consider anAbortSignal.timeout
for a specificscripting.executeScript
call and anAbortController
created for the wholeaction.onClicked
listener with intention of aborting when another listener of a user action is invoked.A
Promise
may be used instead ofAbortSignal
. When the promise is settled (normally rejected), the execution of API callreceived the promise as its argument should be interrupted and an exception should be thrown.
Unlike
AbortSignal
,Promise
objects may be directly used inPromise.all
,Promise.race
,Promise.allSettled
methods, but not infetch()
. Usually thereject()
function obtained fromPromise
constructor callback should saved as a variable in addition to the createdPromise
. It is similar to theAbortController
andAbortSignal
pair.Currently it is possible to use
runtime.Port.disconnect()
to terminate connections. Usage of such approach functions returning a single value, not a sequence of messages, means overhead for no value.Perhaps introducing some custom interface instead of
AbortSignal
orPromise
is not reasonable.Closing remarks
Cancellable API methods may remove some obstacles on the way toward reliably working extensions providing decent user experience.
Discussion of the proposal in the chromium-extensions group was not active: Idea: AbortSignal argument of functions that may cause arbitrary delay. Tue, 27 Jun 2023.
Thanks to Oliver Dunk for the
cancelChooseDesktopMedia
example.The text was updated successfully, but these errors were encountered: