Open Bug 1560766 Opened 5 years ago Updated 2 years ago

setTimeout(..,0) triggers before default browser action

Categories

(Core :: DOM: Core & HTML, defect, P3)

68 Branch
x86_64
macOS
defect

Tracking

()

Tracking Status
firefox67 --- affected
firefox68 --- affected
firefox69 --- affected

People

(Reporter: iliakan, Unassigned)

References

(Depends on 1 open bug)

Details

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36

Steps to reproduce:

Here's the full example: http://plnkr.co/edit/rZmiHdttSXNdpKkR8YbH?p=preview

I uppercase input value after setTimeout(..0) in keydown event handler.

<input id="input">
<script>
  input.onkeydown = function() {
    setTimeout(() => this.value = this.value.toUpperCase());
  };
</script>

Actual results:

The input value is sometimes uppercased, sometimes not.

Here's the demo video: https://jmp.sh/9XSROQ2

Expected results:

I expect setTimeout to run after the default browser action.

-I tested the behavior in Chrome, Safari, Edge => all of them have the new value after zero timeout. Only Firefox is different.

OS: Unspecified → macOS
Hardware: Unspecified → x86_64

Build ID 20190626215508
User Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:69.0) Gecko/20100101 Firefox/69.0

I've managed to reproduce this issue on the latest release version (v67.0.4) and also on the latest Nightly build (v69.0a1).

Status: UNCONFIRMED → NEW
Component: Untriaged → DOM: Core & HTML
Ever confirmed: true
Product: Firefox → Core
Priority: -- → P3

This depends on keypress/up timing. Nothing defines whether keypress or keyup should happen before or after setTimeout.

(In reply to Olli Pettay [:smaug] from comment #2)

This depends on keypress/up timing. Nothing defines whether keypress or keyup should happen before or after setTimeout.

Is it worth defining this?

UA is free to run setTimeout callback whenever. The question is how keydown and keypress relate to each others - whether they should be in the same task or so.
Masayuki, do you recall if this has been discussed @w3c in the context of key event handling?

Flags: needinfo?(masayuki)

The issue also randomly happens with keypress: http://next.plnkr.co/edit/HaNrKmAGiMqVEK3J (as well as with keydown)

Isn't defined that a browser action on <input type="text"> should trigger in the current event loop?

(At least all other browsers behave the same way here, different from Firefox)

(In reply to iliakan from comment #5)

Isn't defined that a browser action on <input type="text"> should trigger in
the current event loop?
Default action to what?
And Gecko does handle default action synchronously.

Browser default action (updating the input value) should happen in the same event loop with the event handler, not some time after it.

Or you suggest that the browser can update the input value any time, even after keypress/keydown handlers triggered, and other events happened? That would be strange.

Also:
| And Gecko does handle default action synchronously.

That's contrary to the demo, if we're talking about the same thing of course.

The question is default action of which event? Gecko does do default action of key events synchronously, but it is possible that other browsers use fewer or more task for key events - like, are keydown and keypress in the same task or in separate tasks (that isn't defined in any specification). If they are in separate tasks, setTimeout may run between them.

Of keydown/keypress events, of course, please see the report.

(In reply to Olli Pettay [:smaug] from comment #4)

UA is free to run setTimeout callback whenever. The question is how keydown and keypress relate to each others - whether they should be in the same task or so.
Masayuki, do you recall if this has been discussed @w3c in the context of key event handling?

I don't understand the point... and I've never seen such discussion about race condition of DOM events.

But looks like that the cause is that the keydown event listener does not call Event.preventDefault(). Therefore, there is a race between the following keypress event which will put a character into the <input> and the function kicked by setTimeout().

Flags: needinfo?(masayuki)

A question is whether keydown and keypress happen in the same task, or different tasks. But ok, that hasn't been discussed at least in W3C/key event handling design.

Dear Sirs, could you please just tell me, is it ok that setTimeout(f) initiated in keydown (or keypress) input handler, runs before its value is updated?

I haven't managed to reproduce the issue when setTimeout is used with keypress testcase. On which platform does it happen?
We do update the value on keypress, and setTimeout runs asynchronously, so it is hard to see how the issue could even happen there, unless some character value depends on multiple keypress events or something.
What do you type when you see the issue with http://next.plnkr.co/edit/HaNrKmAGiMqVEK3J ?

setTimeout used with keydown and running before keypress is ok per current specifications, since nothing says keydown and keypress happen within the same task, so user agent may schedule the timeout to run between keydown and keypress (and keypress updates <input>'s value.)

Here's a simplified test stand: http://next.plnkr.co/edit/7evX72VCmzesqeR8?preview

Just focus on input and type letters until you meet Whoops!
Reload the page and try again if doesn't reproduce.

Reproduced on MacOS 10.14 soon, on Windows 10 took a bit time (Firefox 68.0b12).

If I change keypress to keydown, it feels like it reproduces faster.

I don't understand what that test case tries to test. A new keypress event may have been handled already when the setTimeout runs, so of course 'this.value.slice(-1) != e.key' is possible.

Olli: you're right, the previous test example with uppercasing was more obvious.

Here's the 4-second video from the previous test case, where the issue got reproduced immediately: http://ilyakantor.ru/olli.mp4

P.S. I'm typing all lowercase letters, but on the first letter setTimeout triggers before the input value is updated (that's the issue) (only happens in Firefox).

P.P.S. If I replace keydown with keypress, then setTimeout always has the right value. At least I couldn't reproduce the issue.

Maybe that's related with the discussion of keydown/keypress above? :)

Yeah, so the question is that should keydown and press happen in the same task.
Masayuki, can you think of from top of your head issues if we send them as a one IPC message to the child processes?

Flags: needinfo?(masayuki)

Ideally, we should stop dispatching eKeyPress event from widget and put all information into eKeyDown event. Then, we can reduce the IPC cost for every key press to half. I filed bug 1181501, but I don't have much time to do that. It might be worthwhile me to work on it after shipping beforeinput event, though.

Flags: needinfo?(masayuki)
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.