It's a long and complex journey from a key being pressed and the code in a JavaScript keydown
event listener being run. Assuming a USB keyboard, the path through hardware and software layers is something like this:
- keyboard (hardware layer)
- USB controller (hardware layer)
- USB controller driver (kernel layer)
- USB generic driver (kernel layer)
- HID generic driver (kernel layer)
- HID keyboard driver (kernel layer)
- OS UI event processing (kernel/user layer)
- browser UI event loop (user layer)
- browser UI event handler (user layer)
- JavaScript engine event handler (user layer)
keydown
event listener (user layer)
Most of this doesn't work how you're probably assuming. In particular there's no keyboard interrupt. Instead there's a USB interrupt but it doesn't quite work the way you might assume. When you press a key on USB keyboard it doesn't result in a message being sent to the computer and an interrupt generated. Instead the keyboard adds the key press to its internal queue and waits for the computer to poll it.
This is because USB communication is entirely scheduled by the USB host (the computer). USB devices aren't allowed to talk on the bus except in response to a request by the host. As USB transactions are scheduled into one millisecond frames, normally an operating system will only poll a USB keyboard once a millisecond, asking it to report any events that have occurred since it last polled the device. Only once the keyboard responds to this request (or maybe when all the scheduled transfers in the frame have completed), will the USB controller generate an interrupt.
The response from the keyboard will be in the form a HID (Human Interface Device) report. The HID stack will decode it to see what keyboard events have been reported, converting them into a format common to all keyboard types. This will be further processed by some sort of user-interface layer in the operating system (eg. The "Win32" API layer on Windows, or an X Server on Linux) and then put on the UI event queue for the browser.
The browser will not be "interrupted" as a result of the key being pressed. Instead the browser will have a main UI interface event loop and events will only be processed one a time at a single defined point in the program. All this loop does is pull events from the UI event queue and dispatches them to the appropriate code in the browser. When the queue is empty, which is normally almost all of the time, the loop simply waits for an event. While it's waiting, the event loop's thread is not scheduled by the OS and is not running on any CPU.
Once the UI event loop gets a keyboard event, it's passed onto the browser's keyboard event handler which will then pass it to the JavaScript engine. The engine will then execute the function assigned as the keydown
event listener. The engine may have its own event queue, as JavaScript events are normally also only processed one at a time.
None of this will be written in assembly except a tiny amount of code in the kernel that acts as a landing point for interrupts and system calls before calling the C code that actually dispatches them.