UI Events Algorithms

A Collection of Interesting Ideas,

Issue Tracking:
GitHub
Inline In Spec
Editor:
(Google)

Abstract

This document attempts to describe, in an algorithmic manner, how UIEvents should be handled by User Agents. Because this functionality has been shipping in UAs for many years, the primary goal is to document the existing behaviors and identify areas where implementations disagree. The intent is to fold this information into the main UIEvents spec (and other specs) once there is consensus that this process described here is adequate.

1. Why?

The description of events in the current UIEvents spec is woefully underspecified. This has led to different User Agents (UAs) implementing the same feature with different behaviors.

The intent with this document is to:

Part of this process will necessarily require that we describe processes that rightfully belong in other specifications (e.g., Pointer Events or Input Events). The intent is to eventually move those portions into their appropriate home, but they are kept here while drafting this document to make it easier to discuss.

2. Native OS Requirements

The following is the set of assumptions that this specification makes about the underlying native operating system. It is expected that the native OS will provide these basic services for the UA.

In the event that the native OS does not provide these services, then the UA will need to implement them.

2.1. MouseEvent Requirements

The native OS platform will provide:

For these events, the OS will be able to provide the following info:

2.2. PointerEvent Requirements

TODO: Specify native requirements here.

2.3. KeyboardEvent Requirements

The native OS platform will provide:

2.4. IME Requirements

IMEs differ wildly on different platforms, and often have different capabilities (such as whether or not they can be "canceled") even on the same platform. What is the common set of requirements that we can rely on?

For example, on Windows, Win+'.' opens an emoji window that will generate composition events. But there is not indication from the OS that composition is about to take place. A similar problem exists with "shape writing" using the virtual keyboard.

2.5. Native Entry Points

2.5.1. Mouse

2.5.2. Touch

TODO: Include native touch events and resolve with native mouse events. How do these two event types interact on the common platforms? When are mouse events generated from touch events?

2.5.3. Keyboard

2.5.4. Clipboard

3. Event

Move to/merge with [DOM] spec Event interface.

3.1. Event Interface

A Event has the following additional internal state:

  • due to user interaction flag

    This is a proposal. See uievents/270

  • 3.2. initialize an Event

    Input

    event, the Event to initialize

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    For reference, see initialize an event, list of event flags.

    1. Initialize the following public attributes

      1. Set event.type = eventType

      2. Set event.target = eventTarget

      3. Set event.currentTarget = null (This will be set appropriately during dispatch)

      4. Set event.eventPhase = NONE (This will be set appropriately during dispatch)

      5. Set event.bubbles = true

      6. Set event.cancelable = true

      7. Set event.defaultPrevented = false

      8. Set event.composed = false // See COMPAT note for mouseenter and mouseleave

      9. Set event.isTrusted = false

      10. Set event.timeStamp = Number of milliseconds relative to the time origin

    2. Initialize the following historical attributes

      1. Set event.srcElement = eventTarget

      2. Set event.cancelBubble = alias for stopPropagation

      3. Set event.returnValue = alias for !canceled_flag

    3. Initialize the following internal state

      1. Unset event’s stop propagation flag

      2. Unset event’s stop immediate propagation flag

      3. Unset event’s canceled flag

      4. Unset event’s in passive listener flag

      5. Unset event’s composed flag

      6. Unset event’s initialized flag

      7. Unset event’s dispatch flag

    4. Initialize the following proposed internal state

      1. Unset event’s due to user interaction flag

    4. UI Event

    4.1. UIEvent Interface

    A UIEvent has the following:

  • view
  • detail
  • Along with the following historical attribute:

  • which
  • Add definition for UIEventInit

    4.2. initialize a UIEvent

    Input

    event, the UIEvent to initialize

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Initialize an Event with event, eventType and eventTarget

    2. Initialize the following public attributes

      1. Set event.view = the eventTarget’s node document’s Window object

      2. Set event.detail = 0

    3. Initialize the following historical attributes

      1. Set event.which = 0 (used by both MouseEvent and KeyboardEvent)

    4. If this event is the result of user interaction, then

      1. Set event’s due to user interaction flag

        Note: See uievents/270

      2. Set event.isTrusted = false

    Event firing for load, unload, abort, error, select. Should those be covered here or are they handled elsewhere already?

    5. Focus Event

    TODO

    6. Mouse Event

    6.1. MouseEvent Interface

    A MouseEvent has the following:

  • screenX
  • screenY
  • clientX
  • clientY
  • ctrlKey
  • shiftKey
  • altKey
  • metaKey
  • button
  • buttons
  • relatedTarget
  • getModifierState(keyArg)
  • 6.2. Global State for MouseEvent

    6.2.1. User Agent-Level State

    The UA must maintain the following values that are shared for the entire User Agent.

    A mouse button bitmask that tracks the current state of the mouse buttons.

    6.2.2. Window-Level State

    The UA must maintain the following values that are shared for the Window.

    A last mouse element value (initially undefined) that keeps track of the last Element that we sent a MouseEvent to.

    A last mouse DOM path value (initially empty) that contains a snapshot of the ancestors Elements of the last mouse element when the most recent mouse event was sent.

    6.3. initialize a MouseEvent

    Input

    event, the MouseEvent to initialize

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Initialize a UIEvent with event, eventType and eventTarget

    2. Initialize the following public attributes

      1. Set event.screenX = the x-coordinate of the position where the event occurred relative to the origin of the desktop

      2. Set event.screenY = the y-coordinate of the position where the event occurred relative to the origin of the desktop

      3. Set event.clientX = the x-coordinate of the position where the event occurred relative to the origin of the viewport

      4. Set event.clientY = the y-coordinate of the position where the event occurred relative to the origin of the viewport

      5. Set event modifiers with event

      6. Set event.button = 0

      7. Set event.buttons = mouse button bitmask

    3. Initialize PointerLock attributes for MouseEvent with event

    4. Initialize CSSOM attributes for MouseEvent with event

    6.4. create a MouseEvent

    Input

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Let event = the result of creating a new event using MouseEvent

    2. Initialize a MouseEvent with event, eventType and eventTarget

    3. Return event

    6.5. calculate MouseEvent button attribute

    Input

    mbutton, an ID that identifies a mouse button

    Output

    A button ID suitable for storing in the MouseEvent's button attribute

    1. If mbutton is the primary mouse button, then return 0

    2. If mbutton is the auxiliary (middle) mouse button, then return 1

    3. If mbutton is the secondary mouse button, then return 2

    4. If mbutton is the X1 (back) button, then return 3

    5. If mbutton is the X2 (forward) button, then return 4

    6.6. set MouseEvent attributes from native

    Input

    event, the MouseEvent to initialize

    native, the native mouse event

    Output

    None

    TODO

    1. If event.type is one of [ mousedown, mouseup ], then

      1. Let mbutton be an ID from native that identifies which mouse button was pressed

      2. Set event.button = calculate MouseEvent button attribute with mbutton

    6.7. handle native mouse down

    Input

    native, the native mousedown

    Output

    None

    1. Let mbutton be an ID from native that identifies which mouse button was pressed

    2. Update the mouse button bitmask as follows:

      1. If mbutton is the primary mouse button, then set the 0x01 bit

      2. If mbutton is the secondary mouse button, then set the 0x02 bit

      3. If mbutton is the auxiliary (middle) mouse button, then set the 0x04 bit

      Other buttons can be added starting with 0x08

    3. Let target = hit test with viewport-relative coordinates from native

    4. Let event = create a MouseEvent with "mousedown", target

    5. Set MouseEvent attributes from native with native

    6. Maybe send pointerdown event with event

    7. Let result = dispatch event at target

    8. If result is true and target is a focusable area that is click focusable, then

      1. Run the focusing steps at target

    9. if mbutton is the secondary mouse button, then

      1. Maybe show context menu with native, target

    6.8. handle native mouse up

    Input

    native, the native mouseup

    Output

    None

    Note: Other mouse events can occur between the mousedown and mouseup.

    1. Let mbutton be an ID from native that identifies which mouse button was pressed

    2. Update the mouse button bitmask as follows:

      1. If mbutton is the primary mouse button, then clear the 0x01 bit

      2. If mbutton is the secondary mouse button, then clear the 0x02 bit

      3. If mbutton is the auxiliary (middle) mouse button, then clear the 0x04 bit

    3. Let target = hit test with viewport-relative coordinates from native

    4. Let event = create a MouseEvent with "mouseup", target

    5. Set MouseEvent attributes from native with native

    6. Maybe send pointerup event with event

    7. dispatch event at target

    6.9. handle native mouse click

    Input

    native, the native mouse click

    Output

    None

    Note: The platform should call this immediately after handle native mouse up for mouseups that generate clicks.

    1. Let target = hit test with viewport-relative coordinates from native

    2. Send click event with native and target.

    6.10. send click event

    Input

    native, the native mousedown

    target, the EventTarget of the event

    Output

    None

    1. Let mbutton = 1 (primary mouse button by default)

    2. If native is valid, then

      1. Let mbutton be an ID from native that identifies which mouse button was pressed

    3. Let eventType = "auxclick"

    4. If mbutton is the primary mouse button, then

      1. Set eventType = "click"

    5. Let event = create a PointerEvent with eventType and target

    6. If native is valid, then

      1. Set MouseEvent attributes from native with event, native

      2. If event.screenX is not an integer value, then round it.

      3. If event.screenY is not an integer value, then round it.

    7. dispatch event at target

    Note: See pointerevents/100 for info about browsers using PointerEvents and rounded coordinates.

    Note: Any "default action" is handled during dispatch by triggering the activation behavior algorithm for the target. So there is no need for handle that here. However, need to verify that the existing spec handles disabled/css-pointer-events/inert/...

    Note: To handle `HTMLelement.click()`, call this algorithm with native = null and target = `HTMLelement`.

    Note: To handle keyboard-initiated clicks, call this algorithm with native = null and target = currently focused element.

    6.11. handle native mouse double click

    Input

    native, the native mouse double click

    Output

    None

    Note: This should be called immediately after handle native mouse click for mouse clicks that generate double clicks.

    1. Let mbutton be an ID from native that identifies which mouse button was pressed

    2. If mbutton is not the primary mouse button, then return

    3. Let target = hit test with viewport-relative coordinates from native

    4. Let event = create a PointerEvent with "dblclick" and target

    5. Set MouseEvent attributes from native with event, native

    6. If event.screenX is not an integer value, then round it.

    7. If event.screenY is not an integer value, then round it.

    8. dispatch event at target

    6.12. handle native mouse move

    Input

    native, the native mouse move

    Output

    None

    This algorithm makes assumptions about the dispatch of PointerEvents because they are not currently specified explicitly. Once pointerevents/285 is resolved this may need to be updated.

    1. Let target = hit test with viewport-relative coordinates from native

    2. Let targetDomPath = calculate DOM path

    3. Generate events for leaving the current element:

      1. If last mouse element is defined and not equal to target, then

        1. Let mouseout = create a MouseEvent with "mouseout" and last mouse element

        TODO: Set mouseout attributes from native. +CSSOM attributes

        1. Maybe send pointerout event with mouseout

        2. Dispatch mouseout at target

          Verify behavior when canceled (appears to have no effect).

        3. Let leaveElements be a copy of last mouse DOM path with all elements common to targetDomPath removed.

        4. For each element in leaveElements, do

          Handle case where element has been deleted. Also case where it has been moved: Should the DOM mutation have triggered a mouseleave event? Should we sent it now? Should it be dropped? Need to verify what current browsers do.

          1. Let mouseleave = create a MouseEvent with "mouseleave" and element

          2. Set mouseleave.bubbles = false

          3. Set mouseleave.cancelable = false

          4. Set mouseleave.composed = false

            Compat: Value of event.composed. Spec says false. Chrome/Linux = true Firefox/Linux = false

          5. Maybe send pointerleave event with mouseleave

          6. Let result = dispatch mouseleave at element

    4. Generate events for entering the new element:

      1. If target is not last mouse element, then

        1. Let mouseover = create a MouseEvent with "mouseover" and target

        TODO: Set mouseout attributes from native. +CSSOM attributes

        1. Maybe send pointerover event with mouseover

        2. Dispatch mouseout at target

          Verify behavior when canceled (appears to have no effect).

        3. Let enterElements be a copy of targetDomPath with all elements common to last mouse DOM path removed.

        4. For each element in enterElements, do

          Handle case where element has been deleted or moved.

          1. Let mouseenter = create a MouseEvent with "mouseenter" and element

          2. Set mouseenter.bubbles = false

          3. Set mouseenter.cancelable = false

          4. Set mouseenter.composed = false

            Compat: Value of event.composed. Spec says false. Chrome/Linux = true Firefox/Linux = false

          5. Maybe send pointerenter event with mouseenter

          Compat for shadow DOM elements Chrome/Linux fires this event at the element and the shadow root

          1. Let result = dispatch mouseenter at element

        5. Set last mouse element to target

        6. Set last mouse DOM path to targetDomPath

    5. Let mousemove = create a MouseEvent with "mousemove" and element

    6. Set PointerLock attributes for mousemove

    7. Maybe send pointermove event with mousemove

    8. Dispatch mousemove at element

    Input

    native, the native mousedown or pointer event

    target, the EventTarget of the event

    Output

    None

    1. Let menuevent = create a PointerEvent with "contextmenu", target

    2. If native is valid, then

      1. Set MouseEvent attributes from native with native

    3. Let result = dispatch menuevent at target

    4. If result is true, then show the UA context menu

    Note: To handle a context menu triggered by the keyboard, call this algorithm with native = null and target = currently focused element.

    7. Wheel Event

    TODO

    8. Input Event

    8.1. initialize an InputEvent

    Input

    e, the Event to initialize

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Initialize a UIEvent with e, eventType and eventTarget

    2. Initialize the following public attributes

      1. Set event.data = null

      2. Set event.isComposing = false

      3. Set event.inputType = ""

      4. Set event.composed = true

        Note: Setting composed to true matches current UA behavior, but it makes more sense for this to be false. See whatwg/html/5453 for discussion.

    3. Initialize the additional editing attributes from [[Input Events]]

      Move the following into InputEvents spec (level 1 or level 2) since that’s where these attributes are defined.

      1. Set e.dataTransfer = false

      2. Set e.targetRanges = []

    8.2. create an InputEvent

    Input

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Let e = the result of creating a new event using InputEvent

    2. Initialize an InputEvent with e, eventType and eventTarget

    3. Return e

    8.3. fire key input events

    Input

    key, a DOMString containing the string corresponding to the key

    target, the EventTarget of the event

    Output

    None

    1. If suppress key input events flag is set, then

      1. Exit (since the keydown was canceled)

    Handle historical keypress event here. Return if cancelled.

    1. Let inputType = null

    2. Let data = null

    How much of this can be moved into the [[Input Events]] spec? Issue: List more keys here

    1. If key is "Backspace", then inputType = "deleteContentBackward"

    2. If key is "Delete", then inputType = "deleteContentForward"

    3. Otherwise,

      1. inputType = "insertText"

      inputType should be "insertParagraph" or "insertLineBreak" when pressing Enter.

      1. data = key

    4. If inputType is not null, then

      Note: Need to verify Firefox behavior. Can be enabled by changing the "dom.input_events.beforeinput_enabled" pref in "about:config"

      1. Let result = fire an InputEvent with "beforeinput", inputType and data

      2. If result is false, then return.

      3. Let textInputData be data.

      4. Let shouldFireTextInput to true.

      5. If inputType is either "insertParagraph" or "insertLineBreak", then:

        1. Set textInputData to "\n".

        2. If target is an HTMLInputElement, then set shouldFireTextInput to false.

      6. If inputType is not one of "insertText", "insertParagraph" or "insertLineBreak", then set shouldFireTextInput to false.

      7. If shouldFireTextInput is true, then:

        1. Set result = fire a TextEvent with "textInput", and textInputData

        Note: The "textInput" event is obsolete.

        1. If result is false, then return.

      Note: Perform DOM update here. Insert key into target element

      Note: Compat: For insertFromPaste, Chrome has data = null, Firefox has data = same as beforeinput.

      1. Fire an InputEvent with "input", inputType and data

    8.4. fire an InputEvent

    Input

    eventType, a DOMString containing the event type

    inputType, a DOMString containing the input event type

    data, a DOMString containing event data

    Output

    None

    What about target ranges? Need to add support here.

    The target for input events should consider focus, but should also ensure that the element is editable and being modified. Compare: fire a compositionevent

    1. Let target = currently focused area of a top-level browsing context

    2. Let event = result of create an InputEvent with eventType, target

    3. Set event.inputType = inputType

    4. Set event.data = data

    5. Return the result of dispatch event at target

    9. Text Event

    Note: TextEvent is obsolete.

    9.1. TextEvent Interface

    See IDL definition in UI Events.

    The data attribute must return the value it was initialized to.

    The initTextEvent(type, bubbles, cancelable, view, data) method steps are:

    1. If this’s dispatch flag is set, then return.

    2. Initialize a UIEvent with this, type and eventTarget

    3. Set this.bubbles = bubbles

    4. Set this.cancelable = cancelable

    5. Set this.view = view

      The bubbles/cancelable/view should be parameters to "Initialize a UIEvent" instead of being set twice.

    6. Set this.data = data

    9.2. initialize a TextEvent

    Input

    e, the Event to initialize

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Initialize a UIEvent with e, eventType and eventTarget

    2. Initialize the following public attributes

      1. Set event.data = ""

    9.3. create a TextEvent

    Input

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Let e = the result of creating a new event using TextEvent

    2. Initialize a TextEvent with e, eventType and eventTarget

    3. Return e

    9.4. fire a TextEvent

    Input

    eventType, a DOMString containing the event type

    data, a DOMString containing event data, or null

    Output

    None

    1. Let target = currently focused area of a top-level browsing context

    2. Let event = result of create a TextEvent with eventType, target

    3. If data is null, set data to the empty string.

    4. Set event.data = data

    5. Return the result of dispatch event at target

    10. Keyboard Event

    10.1. KeyboardEvent Interface

    A KeyboardEvent has the following:

  • DOM_KEY_LOCATION_STANDARD
  • DOM_KEY_LOCATION_LEFT
  • DOM_KEY_LOCATION_RIGHT
  • DOM_KEY_LOCATION_NUMPAD
  • key
  • code
  • location
  • ctrlKey
  • shiftKey
  • altKey
  • metaKey
  • repeat
  • isComposing
  • getModifierState(keyArg) - handled by get modifier state
  • Along with the following historical attributes:

  • keyCode
  • keyChar
  • And the following internal state:

  • shift flag
  • control flag
  • alt flag
  • altgraph flag
  • meta flag
  • capslock flag
  • numlock flag
  • 10.2. Global State for KeyboardEvent

    10.2.1. User Agent-Level State

    The UA must maintain the following values that are shared for the entire User Agent.

    A key modifier state (initially empty) that keeps track of the current state of each modifier key available on the system.

    10.2.2. Window-Level State

    The UA must maintain the following values that are shared for the Window.

    A suppress key input events flag (initially false) that is used to suppress input events when the initial keydown event is canceled.

    10.3. initialize a KeyboardEvent

    Input

    event, the KeyboardEvent to initialize

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Initialize a UIEvent with event, eventType and eventTarget

    2. Initialize the following public attributes

      1. Set event.key = ""

      2. Set event.code = ""

      3. Set event.location = DOM_KEY_LOCATION_STANDARD

      4. Set event.shiftKey = false

      5. Set event.ctrlKey = false

      6. Set event.altKey = false

      7. Set event.metaKey = false

      8. Set event.repeat = false

      9. Set event.isComposing = false

    3. Initialize the following historical attributes

      1. Set event.keyCode = 0

      2. Set event.charCode = 0

    4. Initialize the following internal state

      Need to review: ScrollLock, Hyper, Super, and (for virtual keyboards) Symbol, SymbolLock

      1. Unset event’s shift flag

      2. Unset event’s control flag

      3. Unset event’s alt flag

      4. Unset event’s altgraph flag

      5. Unset event’s meta flag

      6. Unset event’s capslock flag

      7. Unset event’s numlock flag

    10.4. create a KeyboardEvent

    Input

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Let e = the result of creating a new event using KeyboardEvent

    2. Initialize a KeyboardEvent with e, eventType and eventTarget

    3. Return e

    10.5. set event modifiers

    Input

    event, the KeyboardEvent or MouseEvent to update

    Output

    None

    eval: ScrollLock, Hyper, Super, and (for virtual keyboards) Symbol, SymbolLock

    1. Set event’s shift flag if key modifier state includes "Shift", unset it otherwise

    2. Set event’s control flag if key modifier state includes "Control", unset it otherwise

    3. Set event’s alt flag if key modifier state includes "Alt", unset it otherwise

    4. Set event’s altgraph flag if key modifier state includes "AltGraph", unset it otherwise

    5. Set event’s meta flag if key modifier state includes "Meta", unset it otherwise

    6. Set event’s capslock flag if key modifier state includes "CapsLock", unset it otherwise

    7. Set event’s numlock flag if key modifier state includes "NumLock", unset it otherwise

    8. Set event.shiftKey = true if the event’s shift flag is set, false otherwise

    9. Set event.ctrlKey = true if the event’s control flag is set, false otherwise

    10. Set event.altKey = true if the event’s alt flag or altgraph flag is set, false otherwise

    11. Set event.metaKey = true if the event’s meta flag is set, false otherwise

    10.6. set KeyboardEvent attributes

    Input

    event, the KeyboardEvent to initialize

    nativeKey, native key info

    Output

    None

    1. Set the following public attributes

      1. Set event.key = the key attribute value for this key press

        Needs more formal description of non-printable characters

        Need to include description of how AltGraph should be handled. See uievents/147

      2. Set event.code = the key code attribute value corresponding to the physical key that was pressed.

      3. Set event.location = the result of calculate key location with event.code

      4. if nativeKey indicates that this is a repeat key event

        1. Set event.repeat

      5. set event modifiers with event

    2. Set the following historical attributes

      TODO: For historical reasons, we can’t have a formal description of these fields without recognizing that certain UAs produce different values.

      1. Set event.keyCode = ???

      2. Set event.charCode = ???

      3. Set event.which = ???

    10.7. calculate key location

    Input

    code, the code for the key

    Output

    The DOM key location for the specified key

    1. If code is any of [ "AltLeft", "ControlLeft", "MetaLeft", "ShiftLeft"], then

      1. return DOM_KEY_LOCATION_LEFT

    2. If code is any of [ "AltRight", "ControlRight", "MetaRight", "ShiftRight"], then

      1. return DOM_KEY_LOCATION_RIGHT

    3. If code matches any of the values in the numpad section, then

      1. return DOM_KEY_LOCATION_NUMPAD

    4. return DOM_KEY_LOCATION_STANDARD

    10.8. get modifier state

    Input

    event, the KeyboardEvent to initialize

    modifier, a DOMString containing the name of the modifier key

    Output

    True if the modifier is currently set, false otherwise

    1. If modifier is "Shift", then return true if event’s shift flag is set

    2. If modifier is "Control", then return true if event’s control flag is set

    3. If modifier is "Alt", then return true if event’s alt flag is set

    4. If modifier is "AltGraph", then return true if event’s altgraph flag is set

    5. If modifier is "Meta", then return true if event’s meta flag is set

    6. If modifier is "CapsLock", then return true if event’s capslock flag is set

    7. If modifier is "NumLock", then return true if event’s numlock flag is set

    8. Return false

    10.9. handle pre browser key

    Input

    key, basic info about the key

    Output

    True is the User Agent has already handled this key

    1. If Keyboard Lock is enabled, then return false

      Note: Keyboard Lock is not an official standard. This is demonstrating how it might be integrated.

    These key shortcuts are defined by the User Agent (e.g., Cmd-T on Mac). They should be defined in such a way that the UA is in control.

    Need a more complete list of these pre-keydown browser keys, or a better description of the sort of keys the UA handles.

    1. If key is the shortcut to create a new tab, then

      1. Create a new tab

      2. Return true

    2. If key is the shortcut to close a window, then

      1. Close the current tab

      2. Return true

    IME is often provided by the native OS. So the requirements need to be defined and added to the IME Requirements section.

    How should IME keys be represented. We need the native OS to call enter composition mode and exit composition mode

    When should begin composition be called? How should that be hooked with the native IME calls.

    1. Return false

    10.10. handle post browser key

    Input

    key, basic info about the key

    Output

    True is the User Agent has already handled this key

    1. If Keyboard Lock is enabled, then return false

      Placeholder until there’s an actual agreed-upon definition for Keyboard Lock.

    2. If key is:

      1. The shortcut to cut (e.g., ctrl-x), then Handle native cut

      2. The shortcut to copy (e.g., ctrl-c), then Handle native copy

      3. The shortcut to paste (e.g., ctrl-v), then Handle native paste

        Need more complete set of post-keydown browser keys. Perhaps define a set of common browser keys (clipboard, open/close tabs, quit).

      4. And then return true

    3. Return false

    10.11. handle native key down

    Input

    nativeKey, native key info

    Output

    None

    1. Update global modifier key state

    2. If nativKey is left or right Shift key, then add "Shift" to key modifier state

    3. If nativKey is left or right Control key, then add "Control" to key modifier state

    4. If nativKey is left or right Alt key, then add "Alt" to key modifier state

    5. If nativKey is AltGraph key, then add "AltGraph" to key modifier state

    6. If nativKey is left or right Meta key, then add "Meta" to key modifier state

    7. If handle pre browser key with nativeKey, then

      Note: Chrome/Firefox handle some BrowserKeys before generating keydown events. Tested with ctrl-t (to create new tab)

      1. exit

    8. Let target = currently focused area of a top-level browsing context

    9. Let event = result of create a KeyboardEvent with "keydown", target

    10. set KeyboardEvent attributes with event, nativeKey

    11. Let result = dispatch event at target

      Note: when in composition, canceling the keydown has no effect (tested: macOS Chrome; TODO: test other configs)

    12. If the in composition flag is set, then

      1. handle composition key and exit

    13. Unset the suppress key input events flag

    14. If result = false, then

      1. Set the suppress key input events flag

    15. If native platform has separate CHAR event, then

      Is this check necessary? Can native CHAR events be ignored or do they contain needed info?

      1. Exit (because CHAR event will trigger handle native key press)

    1. Call handle native key press with nativeKey

    10.12. handle native key up

    Input

    nativeKey, native key info

    Output

    None

    1. Update global modifier key state

      1. If nativeKey is left or right Shift key, then remove "Shift" to key modifier state

      2. If nativeKey is left or right Control key, then remove "Control" to key modifier state

      3. If nativeKey is left or right Alt key, then remove "Alt" to key modifier state

      4. If nativeKey is AltGraph key, then remove "AltGraph" to key modifier state

      5. If nativeKey is left or right Meta key, then remove "Meta" to key modifier state

    2. Let target = currently focused area of a top-level browsing context

    3. Let event = result of create a KeyboardEvent with "keyup", target

    4. set KeyboardEvent attributes with event, nativeKey

    5. dispatch event at target

    10.13. handle native key press

    Input

    nativeKey, native key info

    Output

    None

    Note: This may be called directly if the native platform generates CHAR events that are separate from keydown events. Or it may be called from handle native key down.

    1. If composition mode flag is set, then

      1. Handle composition key with nativeKey

      2. Exit

    2. If handle post browser key, then exit

    3. Let target = currently focused area of a top-level browsing context

      What should happen if the event focus has changed since the keydown event? What is the appropriate target for the keyup? The current focus or the previous? Are there security issues (e.g., if it’s not safe to edit the current focus)?

    4. Let key = extract info from nativeKey

    5. Fire key input events with key and target

    11. Composition Event

    11.1. Global State for CompositionEvent

    11.1.1. Window-Level State

    The UA must maintain the following values that are shared for the Window.

    A composition mode flag (initially false) that is set if the native IME is enabled and the next key press will trigger compositionstart.

    An in composition flag (initially false) that tracks if the system is currently in the middle of input composition (i.e., after compositionstart but before compositionend).

    11.2. initialize a CompositionEvent

    Input

    e, the Event to initialize

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Initialize a UIEvent with e, eventType and eventTarget

    2. Initialize the following public attributes

      1. Set event.data = ""

        Note: This attribute has the same name as an attribute in InputEvent.

    11.3. create a CompositionEvent

    Input

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Let e = the result of creating a new event using CompositionEvent

    2. Initialize a CompositionEvent with e, eventType and eventTarget

    3. Return e

    11.4. fire a CompositionEvent

    Input

    eventType, a DOMString containing the event type

    data, a DOMString containing event data

    Output

    None

    1. Let target = currently focused area of a top-level browsing context

      The target for compositionstart should be determined by the element with focus, but subsequent composition events should be fired on that same element. Need to test what happens when focus changes during composition. On the Mac, if you change focus, the composition target changes. On Windows, behavior depends on the IME.

    2. Let event = result of create a CompositionEvent with eventType, target

    3. Set event.data = data

    4. Return the result of dispatch event at target

    11.5. enter composition mode

    Input

    None

    Output

    None

    This is an oversimplification. We need to native OS to call then when the user enters composition mode.

    1. Set the composition mode flag

    11.6. exit composition mode

    Input

    None

    Output

    None

    1. End composition

    2. Unset the composition mode flag

    11.7. handle composition key

    Input

    None

    Output

    None

    Note: This is called when a key is added to the composition buffer, or when the user navigates between composition options.

    Note: This is where the IME handles the UI part to navigate the candidates or accept all or part of the text. Not sure how much (if any) of this makes sense to "specify" here since it is controlled by the IME.

    1. If special IME key, then

    2. If arrow, then select new candidate

    3. If enter, then accept the current candidate, run end composition and exit

      1. ...

    Need agreement on event order for compositionupdate and beforeinput. Chrome currently does bi -> cu, whereas Safari does cu -> bi. Firefox doesn’t yet produce bi, but prefers cu -> bi. See input-events/49 and uievents/66

    1. Let data = the current composition candidate string

    2. Fire a CompositionEvent with "compositionupdate" and data

      Verify compositionupdate cannot be canceled

    3. Let result = fire an InputEvent with "beforeinput", "insertCompositionText" and data

      Not all beforeinput events can be canceled - need to encode that here

    4. If result is true, then

      1. Update the DOM with the composition text

      2. Fire an InputEvent with "input", "insertCompositionText" and data

        Note: Not cancelable

    11.8. begin composition

    Input

    None

    Output

    None

    1. If in composition flag, then exit

    2. Let data = currently selected text

    3. Let result = fire a CompositionEvent with "compositionstart" and data

      Document what happens when result is not true (event canceled)

    4. Set in composition flag

    5. Handle composition key

    11.9. end composition

    Input

    None

    Output

    None

    1. If in composition flag is not set, then exit

    Compat: Chrome sends out beforeinput/compositionupdate/textInput/input sequence before compositionend (tested on macOS). Compare with Firefox.

    1. Set result = fire a TextEvent with "textInput", and data

      Note: The "textInput" event is obsolete.

    2. If result is false, then return.

    3. fire an input event here.

    4. Let data = the current composition candidate string

    5. fire a CompositionEvent with "compositionend" and data

      Document what to do with result

    6. If Firefox:

      Compat: Firefox sends input events after compositionend. Not sure when the DOM is updated relative to compositionend (tested on macOS). Need to resolve this.

      1. Fire an InputEvent with "input", "insertCompositionText" and data

    7. Unset the in composition flag

    12. Pointer Event

    The contents of this section should eventually be moved into the PointerEvent spec.

    12.1. initialize a PointerEvent

    Input

    event, the PointerEvent to initialize

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Initialize a MouseEvent with event, eventType and eventTarget

    TODO - initialize the pointerevent attributes

    12.2. create a PointerEvent

    Input

    eventType, a DOMString containing the event type

    eventTarget, the EventTarget of the event

    Output

    None

    1. Let event = the result of creating a new event using PointerEvent

    2. Initialize a PointerEvent with event, eventType and eventTarget

    3. Return event

    12.3. create PointerEvent from MouseEvent

    Input

    eventType, a DOMString containing the event type

    mouseevent, the corresponding MouseEvent

    Output

    None

    1. Let event = the result of creating a new event using PointerEvent

    2. Let target = mouseevent.target

    3. Initialize a PointerEvent with event, eventType and target

    4. Copy MouseEvent attributes from mouseevent into event

    5. Return event

    12.4. maybe send pointerout event

    Input

    mouseout, the corresponding mouseout MouseEvent

    Output

    None

    1. Let pointerout = create PointerEvent from MouseEvent with "pointerout" and mouseout

    2. Set pointerevent attributes

      TODO

    3. Let target = mouseout.target

    4. dispatch pointerout at target

    12.5. maybe send pointerleave event

    Input

    mouseout, the corresponding mouseout MouseEvent

    Output

    None

    1. Let pointerout = create PointerEvent from MouseEvent with "pointerout" and mouseout

    2. Set pointerevent attributes

      TODO

    3. Let target = mouseout.target

    4. dispatch pointerout at target

    12.6. maybe send pointerover event

    Input

    mouseout, the corresponding mouseout MouseEvent

    Output

    None

    1. Let pointerout = create PointerEvent from MouseEvent with "pointerout" and mouseout

    2. Set pointerevent attributes

      TODO

    3. Let target = mouseout.target

    4. dispatch pointerout at target

    12.7. maybe send pointerenter event

    Input

    mouseout, the corresponding mouseout MouseEvent

    Output

    None

    1. Let pointerout = create PointerEvent from MouseEvent with "pointerout" and mouseout

    2. Set pointerevent attributes

      TODO

    3. Let target = mouseout.target

    4. dispatch pointerout at target

    12.8. maybe send pointermove event

    Input

    mouseout, the corresponding mouseout MouseEvent

    Output

    None

    Can this send pointermove and pointerrawupdate? Or do we need 2 methods?

    What is needed to properly define how pointermove events are coalesced?

    1. Let pointerout = create PointerEvent from MouseEvent with "pointerout" and mouseout

    2. Set pointerevent attributes

      TODO

    3. Let target = mouseout.target

    4. dispatch pointerout at target

    12.9. maybe send pointerdown event

    Input

    mouseout, the corresponding mouseout MouseEvent

    Output

    None

    Unlike mousedown events, pointerdown events are not nested when multiple buttons are pressed. The MouseEvent is passed so that the fields can be copied into the PointerEvent.

    1. Let pointerout = create PointerEvent from MouseEvent with "pointerout" and mouseout

    2. Set pointerevent attributes

      TODO

    3. Let target = mouseout.target

    4. dispatch pointerout at target

    12.10. maybe send pointerup event

    Input

    mouseout, the corresponding mouseout MouseEvent

    Output

    None

    Unlike mouseup events, pointerup events are not nested when multiple buttons are pressed. The MouseEvent is passed so that the fields can be copied into the PointerEvent.

    1. Let pointerout = create PointerEvent from MouseEvent with "pointerout" and mouseout

    2. Set pointerevent attributes

      TODO

    3. Let target = mouseout.target

    4. dispatch pointerout at target

    13. Pointer Lock

    The contents of this section should eventually be moved into the [PointerLock] spec

    Note: The algorithms here are an oversimplification since they don’t account for the lock/unlock state the or the pointer leaving and re-entering the UA screen boundaries. That should be fixed as part of moving this section to the PointerLock spec.

    13.1. MouseEvent Interface Extension

    A MouseEvent has the following:

  • movementX
  • movementY
  • 13.2. Global State for PointerLock

    13.2.1. Window-Level State

    The UA must maintain the following values that are shared for the Window.

    A last mouse move value (initially undefined) that records the position of the last mousemove event.

    13.3. initialize PointerLock attributes for MouseEvent

    Input

    event, a MouseEvent

    Output

    None

    1. Set event.movementX = 0

    2. Set event.movementY = 0

    13.4. set PointerLock attributes for mousemove

    Input

    event, a MouseEvent

    Output

    None

    1. If event.type is not "mousemove", then exit

    2. If last mouse move is not defined, then

      1. Set event.movementX = 0

      2. Set event.movementY = 0

    3. Otherwise,

      1. Set event.movementX = event.screenX - last mouse move’s x-coordinate

      2. Set event.movementY = event.screenX - last mouse move’s y-coordinate

    4. Set last mouse move = ( event.screenX, event.screenY )

    14. Keyboard Lock

    KeyboardLock is not yet an accepted standard and is included here as a placeholder to explore how it might be integrated.

    The contents of this section should eventually be moved into the [WICG-Keyboard-Lock] spec

    14.1. Global State for Keyboard Lock

    14.1.1. User Agent-Level State

    The UA must maintain the following values that are shared for the entire User Agent.

    TODO

    14.1.2. Window-Level State

    The UA must maintain the following values that are shared for the Window.

    A keyboard lock enable flag (initially false) that tracks whether Keyboard Lock is currently enabled.

    14.2. Keyboard Lock is enabled

    Input

    None

    Output

    True if keyboard lock is enabled.

    1. Return keyboard lock enable flag

    15. Core DOM

    These definitions probably belong in another spec.

    15.1. hit test

    Input

    pos, the x,y coordinates relative to the viewport

    Output

    The frontmost DOM element at pos

    To account for inert or disabled elements. this should call elementsFromPoint and reject invalid elements

    1. Return [CSSOM-View]'s elementFromPoint with pos

    15.2. calculate DOM path

    Input

    element, the starting element

    Output

    The list of ancestor elements for the given element

    1. Let path = A list that contains element

    This needs a proper definition to add ancestors to path.

    1. Return path

    16. CSSOM

    The contents of this section should eventually be moved into the [CSSOM] spec

    16.1. initialize CSSOM attributes for MouseEvent

    Input

    event, a MouseEvent

    Output

    None

    1. Set event.pageX according to pageX

    2. Set event.pageY according to pageY

    3. Set event.x according to x

    4. Set event.y according to y

    5. Set event.offsetX according to offsetX

    6. Set event.offsetY according to offsetY

    17. Clipboard

    Should these be moved into the Clipboard spec?

    17.1. handle native cut

    Input

    None

    Output

    None

    Define when the "cut" event is fired.

    1. Let data = null

    2. Let result = fire an InputEvent with "beforeinput", "deleteByCut" and data

    3. If result is true, then

    4. Copy selected text to clipboard

    5. Remove selected text from DOM target element

    6. Fire an InputEvent with "input", "deleteByCut" and data

    17.2. handle native copy

    Input

    None

    Output

    None

    Note: No input events fired

    Define when "copy" event is fired. Should that be in the Clipboard Spec?

    1. Update clipboard with current selection

    17.3. handle native paste

    Input

    None

    Output

    None

    Note: This is intended to be called when the UA triggers a paste action (via user action)

    Define when the "paste" event is fired.

    1. Let data = the text on the clipboard being pasted

    2. Let result = fire an InputEvent with "beforeinput", "insertFromPaste" and data

    3. If result is false, then return.

    4. Set result = fire a TextEvent with "textInput", and data

      Note: The "textInput" event is obsolete.

    5. If result is false, then return.

    6. Paste clipboard contents into DOM target element

    7. Fire an InputEvent with "input", "insertFromPaste" and data

    18. Acknowledgements

    Thanks to the following for their contributions to this document:

    Conformance

    Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

    All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

    Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

    This is an example of an informative example.

    Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

    Note, this is an informative note.

    Index

    Terms defined by this specification

    Terms defined by reference

    References

    Normative References

    [CSS21]
    Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. URL: https://drafts.csswg.org/css2/
    [CSSOM-View]
    Simon Pieters. CSSOM View Module. URL: https://drafts.csswg.org/cssom-view/
    [DOM]
    Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
    [HTML]
    Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
    [POINTEREVENTS3]
    Patrick Lauke; Robert Flack. Pointer Events. URL: https://w3c.github.io/pointerevents/
    [RFC2119]
    S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
    [UIEVENTS]
    Gary Kacmarcik; Travis Leithead. UI Events. URL: https://w3c.github.io/uievents/
    [UIEVENTS-KEY]
    Travis Leithead; Gary Kacmarcik. UI Events KeyboardEvent key Values. URL: https://w3c.github.io/uievents-key/
    [WEBIDL]
    Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

    Informative References

    [CSSOM]
    Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). URL: https://drafts.csswg.org/cssom/
    [PointerLock]
    Vincent Scheib. Pointer Lock. URL: https://w3c.github.io/pointerlock/
    [WICG-Keyboard-Lock]
    Keyboard Lock. cg-draft. URL: https://wicg.github.io/keyboard-lock/

    Issues Index

    IMEs differ wildly on different platforms, and often have different capabilities (such as whether or not they can be "canceled") even on the same platform. What is the common set of requirements that we can rely on?
    For example, on Windows, Win+'.' opens an emoji window that will generate composition events. But there is not indication from the OS that composition is about to take place. A similar problem exists with "shape writing" using the virtual keyboard.
    Move to/merge with [DOM] spec Event interface.
    This is a proposal. See uievents/270
    Add definition for UIEventInit
    Event firing for load, unload, abort, error, select. Should those be covered here or are they handled elsewhere already?
    TODO
    TODO
    Other buttons can be added starting with 0x08
    This algorithm makes assumptions about the dispatch of PointerEvents because they are not currently specified explicitly. Once pointerevents/285 is resolved this may need to be updated.
    TODO: Set mouseout attributes from native. +CSSOM attributes
    Verify behavior when canceled (appears to have no effect).
    Handle case where element has been deleted. Also case where it has been moved: Should the DOM mutation have triggered a mouseleave event? Should we sent it now? Should it be dropped? Need to verify what current browsers do.
    Compat: Value of event.composed. Spec says false. Chrome/Linux = true Firefox/Linux = false
    TODO: Set mouseout attributes from native. +CSSOM attributes
    Verify behavior when canceled (appears to have no effect).
    Handle case where element has been deleted or moved.
    Compat: Value of event.composed. Spec says false. Chrome/Linux = true Firefox/Linux = false
    Compat for shadow DOM elements Chrome/Linux fires this event at the element and the shadow root
    TODO
    Move the following into InputEvents spec (level 1 or level 2) since that’s where these attributes are defined.
    Handle historical keypress event here. Return if cancelled.
    How much of this can be moved into the [[Input Events]] spec? Issue: List more keys here
    inputType should be "insertParagraph" or "insertLineBreak" when pressing Enter.
    What about target ranges? Need to add support here.
    The target for input events should consider focus, but should also ensure that the element is editable and being modified. Compare: fire a compositionevent
    Need to review: ScrollLock, Hyper, Super, and (for virtual keyboards) Symbol, SymbolLock
    eval: ScrollLock, Hyper, Super, and (for virtual keyboards) Symbol, SymbolLock
    Needs more formal description of non-printable characters
    Need to include description of how AltGraph should be handled. See uievents/147
    TODO: For historical reasons, we can’t have a formal description of these fields without recognizing that certain UAs produce different values.
    These key shortcuts are defined by the User Agent (e.g., Cmd-T on Mac). They should be defined in such a way that the UA is in control.
    Need a more complete list of these pre-keydown browser keys, or a better description of the sort of keys the UA handles.
    IME is often provided by the native OS. So the requirements need to be defined and added to the IME Requirements section.
    How should IME keys be represented. We need the native OS to call enter composition mode and exit composition mode
    When should begin composition be called? How should that be hooked with the native IME calls.
    Placeholder until there’s an actual agreed-upon definition for Keyboard Lock.
    Need more complete set of post-keydown browser keys. Perhaps define a set of common browser keys (clipboard, open/close tabs, quit).
    Is this check necessary? Can native CHAR events be ignored or do they contain needed info?
    What should happen if the event focus has changed since the keydown event? What is the appropriate target for the keyup? The current focus or the previous? Are there security issues (e.g., if it’s not safe to edit the current focus)?
    The target for compositionstart should be determined by the element with focus, but subsequent composition events should be fired on that same element. Need to test what happens when focus changes during composition. On the Mac, if you change focus, the composition target changes. On Windows, behavior depends on the IME.
    This is an oversimplification. We need to native OS to call then when the user enters composition mode.
    Need agreement on event order for compositionupdate and beforeinput. Chrome currently does bi -> cu, whereas Safari does cu -> bi. Firefox doesn’t yet produce bi, but prefers cu -> bi. See input-events/49 and uievents/66
    Verify compositionupdate cannot be canceled
    Not all beforeinput events can be canceled - need to encode that here
    Document what happens when result is not true (event canceled)
    Compat: Chrome sends out beforeinput/compositionupdate/textInput/input sequence before compositionend (tested on macOS). Compare with Firefox.
    fire an input event here.
    Document what to do with result
    Compat: Firefox sends input events after compositionend. Not sure when the DOM is updated relative to compositionend (tested on macOS). Need to resolve this.
    The contents of this section should eventually be moved into the PointerEvent spec.
    TODO - initialize the pointerevent attributes
    TODO
    TODO
    TODO
    TODO
    Can this send pointermove and pointerrawupdate? Or do we need 2 methods?
    What is needed to properly define how pointermove events are coalesced?
    TODO
    Unlike mousedown events, pointerdown events are not nested when multiple buttons are pressed. The MouseEvent is passed so that the fields can be copied into the PointerEvent.
    TODO
    Unlike mouseup events, pointerup events are not nested when multiple buttons are pressed. The MouseEvent is passed so that the fields can be copied into the PointerEvent.
    TODO
    The contents of this section should eventually be moved into the [PointerLock] spec
    KeyboardLock is not yet an accepted standard and is included here as a placeholder to explore how it might be integrated.
    The contents of this section should eventually be moved into the [WICG-Keyboard-Lock] spec
    TODO
    These definitions probably belong in another spec.
    To account for inert or disabled elements. this should call elementsFromPoint and reject invalid elements
    This needs a proper definition to add ancestors to path.
    The contents of this section should eventually be moved into the [CSSOM] spec
    Should these be moved into the Clipboard spec?
    Define when the "cut" event is fired.
    Define when "copy" event is fired. Should that be in the Clipboard Spec?
    Define when the "paste" event is fired.