Skip to content
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

Pausing (alert, confirm, prompt) #4439

Open
annevk opened this issue Mar 22, 2019 · 2 comments
Open

Pausing (alert, confirm, prompt) #4439

annevk opened this issue Mar 22, 2019 · 2 comments

Comments

@annevk
Copy link
Member

annevk commented Mar 22, 2019

From @bzbarsky on IRC:

As far as alert() goes...
Isn't everyone more or less moving to a mode where alert() doesn't block the UI?
And hence things can happen
Which things kinda depends
And whether it should block the event loop is unclear
But basically, if page A window.opens() page B
Which is same-origin
and page B does an alert()
Should the user still be able to interact with A?
In Gecko, the answer is "yes"
What do other browsers do nowadays?

It seems this very much depends on the setup. For

const B = window.open();
onmouseup = () => console.log(1);
B.alert();

Safari immediately opens and focuses B (if popups are allowed) and then trying to focus the A results in the alert dialog being closed and 1 being logged. (It also seems like A might be reloaded due to an animation in the address bar, not sure what's up.)

In Chrome and Firefox the alert dialog stays up while A logs 1.

For

const B = window.open();
onmouseup = () => {
  console.log(1);
  B.alert();
  console.log(2);
};

Safari logs 1 and then blocks until the alert dialog in B is closed or B itself is closed. Mouseups are queued and each gets a new dialog once processed. (Dialogs can be of varying sizes, seems like a bug.) Firefox does this as well.

Chrome logs 1, 2 for each mouseup in A. As for the alert, you only have to dismiss it once, no matter how many mouseups. I suspect that means it returns early, but still shows the alert.

For

const B = window.open();
onmouseup = () => {
  console.log(1);
  console.log(B.confirm());
  console.log(2);
};
B.alert();

Chrome logs 1, false, 2 for each mouseup in A. The main difference is that all dialogs are dismissed when you interact with A.

What happens if the user then closes B while the alert is up (which is something that more and more browsers, if not all at this point, are allowing)?

This is treated the same as closing the dialog, afaict (though I did not test return values of confirm/prompt).


Now if there's only a single window involved alert() does very much act in a blocking matter. No timeouts, no UI input, no microtask, etc. So there's a lot of subtlety at play here.

This was referenced Mar 22, 2019
@annevk
Copy link
Member Author

annevk commented Mar 25, 2019

With promises you get something like

const B = window.open();
onmouseup = () => {
  Promise.resolve().then(() => console.log("promise"));
  console.log(1);
}
Promise.resolve().then(() => B.alert()).then(() => console.log("promise more"));

which in Safari does the same alert closing thing when using the mouse to switch focus to the opener window so "promise more" is logged first.

In Chrome the alert is not closed, but "promise more" is immediately logged. Chrome's strategy for alert and related dialogs is to sometimes return early, but leave the dialog up, as far as I can tell.

In Firefox "promise more" is only logged once you close the alert, so other microtasks are processed in a "nested" event loop. (Apparently Firefox can process (micro)tasks per top-level browsing context, which is a somewhat different architecture from the specification.)

@bzbarsky
Copy link
Contributor

Apparently Firefox can process (micro)tasks per top-level browsing context

Hmm. What we (Gecko) seem to have is a way to mark some documents as "in a sync operation" (including alert) and we have a concept of a microtask being "suppressed" which for certain types of microtasks corresponds to them being associated with a document that is in a sync operation. We then skip over suppressed microtasks (leaving them in the queue) when processing the microtask queue.

We also have an "is processing microtasks" flag that is not a simple boolean but an integer, and indicates what event loop nesting level is processing microtasks, so we do end up processing (other) microtasks when we spin the event loop, even if it happens in the middle of a microtask.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants