diff options
| -rw-r--r-- | content_scripts/link_hints.coffee | 69 | ||||
| -rw-r--r-- | content_scripts/marks.coffee | 24 | ||||
| -rw-r--r-- | content_scripts/mode.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/mode_find.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/mode_insert.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/mode_key_handler.coffee | 36 | ||||
| -rw-r--r-- | content_scripts/mode_visual.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 6 | ||||
| -rw-r--r-- | lib/dom_utils.coffee | 29 | ||||
| -rw-r--r-- | lib/keyboard_utils.coffee | 117 | ||||
| -rw-r--r-- | pages/hud.coffee | 10 | ||||
| -rw-r--r-- | pages/vomnibar.coffee | 11 | ||||
| -rw-r--r-- | tests/dom_tests/dom_tests.coffee | 157 | ||||
| -rw-r--r-- | tests/dom_tests/phantom_runner.coffee | 24 | 
14 files changed, 147 insertions, 344 deletions
| diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee index 741d42cf..eb138caa 100644 --- a/content_scripts/link_hints.coffee +++ b/content_scripts/link_hints.coffee @@ -172,11 +172,10 @@ class LinkHintsMode        exitOnEscape: true        exitOnClick: true        keydown: @onKeyDownInMode.bind this -      keypress: @onKeyPressInMode.bind this      @hintMode.onExit (event) =>        if event?.type == "click" or (event?.type == "keydown" and -        (KeyboardUtils.isEscape(event) or event.keyCode in [keyCodes.backspace, keyCodes.deleteKey])) +        (KeyboardUtils.isEscape(event) or KeyboardUtils.isBackspace event))            HintCoordinator.sendMessage "exit", isSuccess: false      # Note(philc): Append these markers as top level children instead of as child nodes to the link itself, @@ -230,10 +229,9 @@ class LinkHintsMode        linkText: desc.linkText        stableSortCount: ++@stableSortCount -  # Handles <Shift> and <Ctrl>. +  # Handles all keyboard events.    onKeyDownInMode: (event) ->      return if event.repeat -    @keydownKeyChar = KeyboardUtils.getKeyChar(event).toLowerCase()      previousTabCount = @tabCount      @tabCount = 0 @@ -241,25 +239,25 @@ class LinkHintsMode      # NOTE(smblott) As of 1.54, the Ctrl modifier doesn't work for filtered link hints; therefore we only      # offer the control modifier for alphabet hints.  It is not clear whether we should fix this.  As of      # 16-03-28, nobody has complained. -    modifiers = [keyCodes.shiftKey] -    modifiers.push keyCodes.ctrlKey unless Settings.get "filterLinkHints" +    modifiers = ["Shift"] +    modifiers.push "Control" unless Settings.get "filterLinkHints" -    if event.keyCode in modifiers and +    if event.key in modifiers and        @mode in [ OPEN_IN_CURRENT_TAB, OPEN_WITH_QUEUE, OPEN_IN_NEW_BG_TAB, OPEN_IN_NEW_FG_TAB ]          @tabCount = previousTabCount          # Toggle whether to open the link in a new or current tab.          previousMode = @mode -        keyCode = event.keyCode +        key = event.key -        switch keyCode -          when keyCodes.shiftKey +        switch key +          when "Shift"              @setOpenLinkMode(if @mode is OPEN_IN_CURRENT_TAB then OPEN_IN_NEW_BG_TAB else OPEN_IN_CURRENT_TAB) -          when keyCodes.ctrlKey +          when "Control"              @setOpenLinkMode(if @mode is OPEN_IN_NEW_FG_TAB then OPEN_IN_NEW_BG_TAB else OPEN_IN_NEW_FG_TAB)          handlerId = handlerStack.push            keyup: (event) => -            if event.keyCode == keyCode +            if event.key == key                handlerStack.remove()                @setOpenLinkMode previousMode              true # Continue bubbling the event. @@ -268,7 +266,7 @@ class LinkHintsMode          # Therefore, we ensure that it's always removed when hint mode exits.  See #1911 and #1926.          @hintMode.onExit -> handlerStack.remove handlerId -    else if event.keyCode in [ keyCodes.backspace, keyCodes.deleteKey ] +    else if KeyboardUtils.isBackspace event        if @markerMatcher.popKeyChar()          @updateVisibleMarkers()        else @@ -276,36 +274,27 @@ class LinkHintsMode          # knows not to restart hints mode.          @hintMode.exit event -    else if event.keyCode == keyCodes.enter +    else if event.key == "Enter"        # Activate the active hint, if there is one.  Only FilterHints uses an active hint.        HintCoordinator.sendMessage "activateActiveHintMarker" if @markerMatcher.activeHintMarker -    else if event.keyCode == keyCodes.tab +    else if event.key == "Tab"        @tabCount = previousTabCount + (if event.shiftKey then -1 else 1)        @updateVisibleMarkers @tabCount -    else if event.keyCode == keyCodes.space and @markerMatcher.shouldRotateHints event +    else if event.key == " " and @markerMatcher.shouldRotateHints event        @tabCount = previousTabCount        HintCoordinator.sendMessage "rotateHints"      else        @tabCount = previousTabCount if event.ctrlKey or event.metaKey or event.altKey -      return - -    # We've handled the event, so suppress it and update the mode indicator. -    DomUtils.suppressEvent event - -  # Handles normal input. -  onKeyPressInMode: (event) -> -    return if event.repeat - -    keyChar = String.fromCharCode(event.charCode).toLowerCase() -    if keyChar -      @markerMatcher.pushKeyChar keyChar, @keydownKeyChar -      @updateVisibleMarkers() - -    # We've handled the event, so suppress it. -    DomUtils.suppressEvent event +      unless event.repeat +        if keyChar = KeyboardUtils.getKeyChar event +          keyChar = " " if keyChar == "space" +          if keyChar.length == 1 +            @markerMatcher.pushKeyChar keyChar +            @updateVisibleMarkers() +          DomUtils.consumeKeyup event    updateVisibleMarkers: (tabCount = 0) ->      {hintKeystrokeQueue, linkTextKeystrokeQueue} = @markerMatcher @@ -445,11 +434,6 @@ class LinkHintsMode  class AlphabetHints    constructor: ->      @linkHintCharacters = Settings.get("linkHintCharacters").toLowerCase() -    # We use the keyChar from keydown if the link-hint characters are all "a-z0-9".  This is the default -    # settings value, and preserves the legacy behavior (which always used keydown) for users which are -    # familiar with that behavior.  Otherwise, we use keyChar from keypress, which admits non-Latin -    # characters. See #1722. -    @useKeydown = /^[a-z0-9]*$/.test @linkHintCharacters      @hintKeystrokeQueue = []    fillInMarkers: (hintMarkers) -> @@ -478,8 +462,8 @@ class AlphabetHints      matchString = @hintKeystrokeQueue.join ""      linksMatched: hintMarkers.filter (linkMarker) -> linkMarker.hintString.startsWith matchString -  pushKeyChar: (keyChar, keydownKeyChar) -> -    @hintKeystrokeQueue.push (if @useKeydown then keydownKeyChar else keyChar) +  pushKeyChar: (keyChar) -> +    @hintKeystrokeQueue.push keyChar    popKeyChar: -> @hintKeystrokeQueue.pop()    # For alphabet hints, <Space> always rotates the hints, regardless of modifiers. @@ -535,10 +519,7 @@ class FilterHints      linksMatched: linksMatched      userMightOverType: @hintKeystrokeQueue.length == 0 and 0 < @linkTextKeystrokeQueue.length -  pushKeyChar: (keyChar, keydownKeyChar) -> -    # For filtered hints, we *always* use the keyChar value from keypress, because there is no obvious and -    # easy-to-understand meaning for choosing one of keyChar or keydownKeyChar (as there is for alphabet -    # hints). +  pushKeyChar: (keyChar) ->      if 0 <= @linkHintNumbers.indexOf keyChar        @hintKeystrokeQueue.push keyChar      # We only accept <Space> and characters which are not used for splitting (e.g. "a", "b", etc., but not "-"). @@ -901,7 +882,7 @@ class WaitForEnter extends Mode      @push        keydown: (event) => -        if event.keyCode == keyCodes.enter +        if event.key == "Enter"            @exit()            callback true # true -> isSuccess.          else if KeyboardUtils.isEscape event diff --git a/content_scripts/marks.coffee b/content_scripts/marks.coffee index 37b062ba..4a2a8203 100644 --- a/content_scripts/marks.coffee +++ b/content_scripts/marks.coffee @@ -35,8 +35,8 @@ Marks =        indicator: "Create mark..."        exitOnEscape: true        suppressAllKeyboardEvents: true -      keypress: (event) => -        keyChar = String.fromCharCode event.charCode +      keydown: (event) => +        keyChar = KeyboardUtils.getKeyChar event          @exit =>            if @isGlobalMark event, keyChar              # We record the current scroll position, but only if this is the top frame within the tab. @@ -58,27 +58,27 @@ Marks =        indicator: "Go to mark..."        exitOnEscape: true        suppressAllKeyboardEvents: true -      keypress: (event) => +      keydown: (event) =>          @exit => -          markName = String.fromCharCode event.charCode -          if @isGlobalMark event, markName +          keyChar = KeyboardUtils.getKeyChar event +          if @isGlobalMark event, keyChar              # This key must match @getLocationKey() in the back end. -            key = "vimiumGlobalMark|#{markName}" +            key = "vimiumGlobalMark|#{keyChar}"              Settings.storage.get key, (items) ->                if key of items -                chrome.runtime.sendMessage handler: 'gotoMark', markName: markName -                HUD.showForDuration "Jumped to global mark '#{markName}'", 1000 +                chrome.runtime.sendMessage handler: 'gotoMark', markName: keyChar +                HUD.showForDuration "Jumped to global mark '#{keyChar}'", 1000                else -                HUD.showForDuration "Global mark not set '#{markName}'", 1000 +                HUD.showForDuration "Global mark not set '#{keyChar}'", 1000            else -            markString = @localRegisters[markName] ? localStorage[@getLocationKey markName] +            markString = @localRegisters[keyChar] ? localStorage[@getLocationKey keyChar]              if markString?                @setPreviousPosition()                position = JSON.parse markString                window.scrollTo position.scrollX, position.scrollY -              @showMessage "Jumped to local mark", markName +              @showMessage "Jumped to local mark", keyChar              else -              @showMessage "Local mark not set", markName +              @showMessage "Local mark not set", keyChar  root = exports ? window  root.Marks =  Marks diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 2d8cc9cc..85187b2c 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -82,7 +82,7 @@ class Mode          "keydown": (event) =>            return @continueBubbling unless KeyboardUtils.isEscape event            @exit event, event.target -          DomUtils.suppressKeyupAfterEscape handlerStack +          DomUtils.consumeKeyup event      # If @options.exitOnBlur is truthy, then it should be an element.  The mode will exit when that element      # loses the focus. diff --git a/content_scripts/mode_find.coffee b/content_scripts/mode_find.coffee index 63825600..77d3762d 100644 --- a/content_scripts/mode_find.coffee +++ b/content_scripts/mode_find.coffee @@ -48,7 +48,7 @@ class PostFindMode extends SuppressPrintable        keydown: (event) =>          if KeyboardUtils.isEscape event            @exit() -          DomUtils.suppressKeyupAfterEscape handlerStack +          DomUtils.consumeKeyup event          else            handlerStack.remove()            @continueBubbling diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee index 73a24112..a4f1836d 100644 --- a/content_scripts/mode_insert.coffee +++ b/content_scripts/mode_insert.coffee @@ -26,7 +26,7 @@ class InsertMode extends Mode          # An editable element in a shadow DOM is focused; blur it.          @insertModeLock.blur()        @exit event, event.target -      DomUtils.suppressKeyupAfterEscape handlerStack +      DomUtils.consumeKeyup event      defaults =        name: "insert" diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index e206dbc6..1b3b21e7 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -12,7 +12,6 @@  # consists of a (non-empty) list of such mappings.  class KeyHandlerMode extends Mode -  keydownEvents: {}    setKeyMapping: (@keyMapping) -> @reset()    setPassKeys: (@passKeys) -> @reset()    # Only for tests. @@ -28,8 +27,6 @@ class KeyHandlerMode extends Mode      super extend options,        keydown: @onKeydown.bind this -      keypress: @onKeypress.bind this -      keyup: @onKeyup.bind this        # We cannot track keyup events if we lose the focus.        blur: (event) => @alwaysContinueBubbling => @keydownEvents = {} if event.target == window @@ -41,7 +38,7 @@ class KeyHandlerMode extends Mode          keydown: (event) =>            if KeyboardUtils.isEscape(event) and not @isInResetState()              @reset() -            DomUtils.suppressKeyupAfterEscape handlerStack +            DomUtils.consumeKeyup event            else              @continueBubbling @@ -49,45 +46,22 @@ class KeyHandlerMode extends Mode      keyChar = KeyboardUtils.getKeyCharString event      isEscape = KeyboardUtils.isEscape event      if isEscape and (@countPrefix != 0 or @keyState.length != 1) -      @keydownEvents[event.keyCode] = true -      @reset() -      @suppressEvent +      DomUtils.consumeKeyup event, => @reset()      # If the help dialog loses the focus, then Escape should hide it; see point 2 in #2045.      else if isEscape and HelpDialog?.isShowing() -      @keydownEvents[event.keyCode] = true -      HelpDialog.toggle() -      @suppressEvent +      DomUtils.consumeKeyup event, -> HelpDialog.toggle()      else if isEscape        @continueBubbling      else if @isMappedKey keyChar -      @keydownEvents[event.keyCode] = true -      @handleKeyChar keyChar -    else if not keyChar and (keyChar = KeyboardUtils.getKeyChar event) and -        (@isMappedKey(keyChar) or @isCountKey keyChar) -      # We will possibly be handling a subsequent keypress event, so suppress propagation of this event to -      # prevent triggering page event listeners (e.g. Google instant Search). -      @keydownEvents[event.keyCode] = true -      @suppressPropagation -    else -      @continueBubbling - -  onKeypress: (event) -> -    keyChar = KeyboardUtils.getKeyCharString event -    if @isMappedKey keyChar -      @handleKeyChar keyChar +      DomUtils.consumeKeyup event, => @handleKeyChar keyChar      else if @isCountKey keyChar        digit = parseInt keyChar        @reset if @keyState.length == 1 then @countPrefix * 10 + digit else digit        @suppressEvent      else -      @reset() +      @reset() if keyChar        @continueBubbling -  onKeyup: (event) -> -    return @continueBubbling unless event.keyCode of @keydownEvents -    delete @keydownEvents[event.keyCode] -    @suppressPropagation -    # This tests whether there is a mapping of keyChar in the current key state (and accounts for pass keys).    isMappedKey: (keyChar) ->      (mapping for mapping in @keyState when keyChar of mapping)[0]? and not @isPassKey keyChar diff --git a/content_scripts/mode_visual.coffee b/content_scripts/mode_visual.coffee index e4e4f541..cc1baf34 100644 --- a/content_scripts/mode_visual.coffee +++ b/content_scripts/mode_visual.coffee @@ -258,7 +258,7 @@ class VisualMode extends KeyHandlerMode        _name: "#{@id}/enter/click"        # Yank on <Enter>.        keypress: (event) => -        if event.keyCode == keyCodes.enter +        if event.key == "Enter"            unless event.metaKey or event.ctrlKey or event.altKey or event.shiftKey              @yank()              return @suppressEvent diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 2331a8cf..cdb23352 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -451,14 +451,14 @@ extend window,              name: "focus-selector"              exitOnClick: true              keydown: (event) => -              if event.keyCode == KeyboardUtils.keyCodes.tab +              if event.key == "Tab"                  hints[selectedInputIndex].classList.remove 'internalVimiumSelectedInputHint'                  selectedInputIndex += hints.length + (if event.shiftKey then -1 else 1)                  selectedInputIndex %= hints.length                  hints[selectedInputIndex].classList.add 'internalVimiumSelectedInputHint'                  DomUtils.simulateSelect visibleInputs[selectedInputIndex].element                  @suppressEvent -              else unless event.keyCode == KeyboardUtils.keyCodes.shiftKey +              else unless event.key == "Shift"                  @exit()                  # Give the new mode the opportunity to handle the event.                  @restartBubbling @@ -687,4 +687,4 @@ root.bgLog = bgLog  extend root, {handleEscapeForFindMode, handleEnterForFindMode, performFind, performBackwardsFind,    enterFindMode, focusThisFrame}  # These are exported only for the tests. -extend root, {installModes, installListeners} +extend root, {installModes} diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee index 690d9969..06db1b9b 100644 --- a/lib/dom_utils.coffee +++ b/lib/dom_utils.coffee @@ -305,15 +305,26 @@ DomUtils =      event.preventDefault()      @suppressPropagation(event) -  # Suppress the next keyup event for Escape. -  suppressKeyupAfterEscape: (handlerStack) -> -    handlerStack.push -      _name: "dom_utils/suppressKeyupAfterEscape" -      keyup: (event) -> -        return true unless KeyboardUtils.isEscape event -        @remove() -        false -    handlerStack.suppressEvent +  consumeKeyup: do -> +    handlerId = null + +    (event, callback = null) -> +      unless event.repeat +        handlerStack.remove handlerId if handlerId? +        code = event.code +        handlerId = handlerStack.push +          _name: "dom_utils/consumeKeyup" +          keyup: (event) -> +            return handlerStack.continueBubbling unless event.code == code +            @remove() +            handlerStack.suppressEvent +          # We cannot track keyup events if we lose the focus. +          blur: (event) -> +            @remove() if event.target == window +            handlerStack.continueBubbling +      callback?() +      @suppressEvent event +      handlerStack.suppressEvent    # Adapted from: http://roysharon.com/blog/37.    # This finds the element containing the selection focus. diff --git a/lib/keyboard_utils.coffee b/lib/keyboard_utils.coffee index ead8c037..35e584e3 100644 --- a/lib/keyboard_utils.coffee +++ b/lib/keyboard_utils.coffee @@ -3,28 +3,9 @@ mapKeyRegistry = {}  Utils?.monitorChromeStorage "mapKeyRegistry", (value) => mapKeyRegistry = value  KeyboardUtils = -  keyCodes: -    { ESC: 27, backspace: 8, deleteKey: 46, enter: 13, ctrlEnter: 10, space: 32, shiftKey: 16, ctrlKey: 17, f1: 112, -    f12: 123, tab: 9, downArrow: 40, upArrow: 38 } - +  # This maps event.key key names to Vimium key names.    keyNames: -    { 37: "left", 38: "up", 39: "right", 40: "down", 32: "space", 8: "backspace" } - -  # This is a mapping of the incorrect keyIdentifiers generated by Webkit on Windows during keydown events to -  # the correct identifiers, which are correctly generated on Mac. We require this mapping to properly handle -  # these keys on Windows. See https://bugs.webkit.org/show_bug.cgi?id=19906 for more details. -  keyIdentifierCorrectionMap: -    "U+00C0": ["U+0060", "U+007E"] # `~ -    "U+00BD": ["U+002D", "U+005F"] # -_ -    "U+00BB": ["U+003D", "U+002B"] # =+ -    "U+00DB": ["U+005B", "U+007B"] # [{ -    "U+00DD": ["U+005D", "U+007D"] # ]} -    "U+00DC": ["U+005C", "U+007C"] # \| -    "U+00BA": ["U+003B", "U+003A"] # ;: -    "U+00DE": ["U+0027", "U+0022"] # '" -    "U+00BC": ["U+002C", "U+003C"] # ,< -    "U+00BE": ["U+002E", "U+003E"] # .> -    "U+00BF": ["U+002F", "U+003F"] # /? +    "ArrowLeft": "left", "ArrowUp": "up", "ArrowRight": "right", "ArrowDown": "down", " ": "space", "Backspace": "backspace"    init: ->      if (navigator.userAgent.indexOf("Mac") != -1) @@ -34,19 +15,9 @@ KeyboardUtils =      else        @platform = "Windows" -  # We are migrating from using event.keyIdentifier to using event.key.  For some period of time, we must -  # support both.  This wrapper can be removed once Chrome 52 is considered too old to support.    getKeyChar: (event) -> -    # We favor using event.keyIdentifier due to Chromium's currently (Chrome 51) incorrect implementataion of -    # event.key; see #2147. -    if event.keyIdentifier? -      @getKeyCharUsingKeyIdentifier event -    else -      @getKeyCharUsingKey event - -  getKeyCharUsingKey: (event) -> -    if event.keyCode of @keyNames -      @keyNames[event.keyCode] +    if event.key of @keyNames +      @keyNames[event.key]      # It appears that event.key is not always defined (see #2453).      else if not event.key?        "" @@ -59,44 +30,28 @@ KeyboardUtils =      else        "" -  getKeyCharUsingKeyIdentifier: (event) -> -    # Handle named keys. -    keyCode = event.keyCode -    if keyCode -      if keyCode of @keyNames -        return @keyNames[keyCode] -      # Function keys. -      if @keyCodes.f1 <= keyCode <= @keyCodes.f12 -        return "f" + (1 + keyCode - keyCodes.f1) - -    keyIdentifier = event.keyIdentifier - -    # Not a letter. -    if not keyIdentifier.startsWith "U+" -      return "" +  getKeyCharString: (event) -> +    if keyChar = @getKeyChar event +      modifiers = [] -    # On Windows, the keyIdentifiers for non-letter keys are incorrect. See -    # https://bugs.webkit.org/show_bug.cgi?id=19906 for more details. -    if ((@platform == "Windows" || @platform == "Linux") && @keyIdentifierCorrectionMap[keyIdentifier]) -      correctedIdentifiers = @keyIdentifierCorrectionMap[keyIdentifier] -      keyIdentifier = if event.shiftKey then correctedIdentifiers[1] else correctedIdentifiers[0] -    unicodeKeyInHex = "0x" + keyIdentifier.substring(2) -    character = String.fromCharCode(parseInt(unicodeKeyInHex)).toLowerCase() -    if event.shiftKey then character.toUpperCase() else character +      keyChar = keyChar.toUpperCase() if event.shiftKey and keyChar.length == 1 +      # These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey). +      modifiers.push "a" if event.altKey +      modifiers.push "c" if event.ctrlKey +      modifiers.push "m" if event.metaKey -  isPrimaryModifierKey: (event) -> if (@platform == "Mac") then event.metaKey else event.ctrlKey +      keyChar = [modifiers..., keyChar].join "-" +      keyChar = "<#{keyChar}>" if 1 < keyChar.length +      keyChar = mapKeyRegistry[keyChar] ? keyChar +      keyChar -  isEscape: do -> +  isEscape: (event) -> +    # <c-[> is mapped to Escape in Vim by default. +    event.key == "Escape" || @getKeyCharString(event) == "<c-[>" -    # TODO(smblott) Change this to use event.key. -    (event) -> -      event.keyCode == @keyCodes.ESC || do => -        keyChar = @getKeyCharString event -        # <c-[> is mapped to Escape in Vim by default. -        keyChar == "<c-[>" +  isBackspace: (event) -> +    event.key in ["Backspace", "Delete"] -  # TODO. This is probably a poor way of detecting printable characters.  However, it shouldn't incorrectly -  # identify any of chrome's own keyboard shortcuts as printable.    isPrintable: (event) ->      return false if event.metaKey or event.ctrlKey or event.altKey      keyChar = @@ -106,37 +61,7 @@ KeyboardUtils =          @getKeyChar event      keyChar.length == 1 -  # Return the Vimium key representation for this keyboard event. Return a falsy value (the empty string or -  # undefined) when no Vimium representation is appropriate. -  getKeyCharString: (event) -> -    switch event.type -      when "keypress" -        # Ignore modifier keys by themselves. -        if 31 < event.keyCode -          String.fromCharCode event.charCode - -      # TODO(smblott). Currently all (almost?) keyhandling is being done on keydown.  All legacy code related -      # to key handling on keypress should be reviewed and probably removed.  This is not being done right now -      # (2017-03-22) because it is better to wait until we've verified that the change to keydown is indeed -      # correct and reliable. -      when "keydown" -        if keyChar = @getKeyChar event -          modifiers = [] - -          keyChar = keyChar.toUpperCase() if event.shiftKey and keyChar.length == 1 -          # These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey). -          modifiers.push "a" if event.altKey -          modifiers.push "c" if event.ctrlKey -          modifiers.push "m" if event.metaKey - -          keyChar = [modifiers..., keyChar].join "-" -          keyChar = "<#{keyChar}>" if 1 < keyChar.length -          keyChar = mapKeyRegistry[keyChar] ? keyChar -          keyChar -  KeyboardUtils.init()  root = exports ? window  root.KeyboardUtils = KeyboardUtils -# TODO(philc): A lot of code uses this keyCodes hash... maybe we shouldn't export it as a global. -root.keyCodes = KeyboardUtils.keyCodes diff --git a/pages/hud.coffee b/pages/hud.coffee index af528203..ac7059ec 100644 --- a/pages/hud.coffee +++ b/pages/hud.coffee @@ -16,21 +16,21 @@ document.addEventListener "keydown", (event) ->    inputElement = document.getElementById "hud-find-input"    return unless inputElement? # Don't do anything if we're not in find mode. -  if (event.keyCode in [keyCodes.backspace, keyCodes.deleteKey] and inputElement.textContent.length == 0) or -     event.keyCode == keyCodes.enter or KeyboardUtils.isEscape event +  if (KeyboardUtils.isBackspace(event) and inputElement.textContent.length == 0) or +     event.key == "Enter" or KeyboardUtils.isEscape event      UIComponentServer.postMessage        name: "hideFindMode" -      exitEventIsEnter: event.keyCode == keyCodes.enter +      exitEventIsEnter: event.key == "Enter"        exitEventIsEscape: KeyboardUtils.isEscape event -  else if event.keyCode == keyCodes.upArrow +  else if event.key == "ArrowUp"      if rawQuery = FindModeHistory.getQuery findMode.historyIndex + 1        findMode.historyIndex += 1        findMode.partialQuery = findMode.rawQuery if findMode.historyIndex == 0        setTextInInputElement inputElement, rawQuery        findMode.executeQuery() -  else if event.keyCode == keyCodes.downArrow +  else if event.key == "ArrowDown"      findMode.historyIndex = Math.max -1, findMode.historyIndex - 1      rawQuery = if 0 <= findMode.historyIndex then FindModeHistory.getQuery findMode.historyIndex else findMode.partialQuery      setTextInInputElement inputElement, rawQuery diff --git a/pages/vomnibar.coffee b/pages/vomnibar.coffee index 95ef8151..071604b7 100644 --- a/pages/vomnibar.coffee +++ b/pages/vomnibar.coffee @@ -106,17 +106,17 @@ class VomnibarUI      if (KeyboardUtils.isEscape(event))        return "dismiss"      else if (key == "up" || -        (event.shiftKey && event.keyCode == keyCodes.tab) || +        (event.shiftKey && event.key == "Tab") ||          (event.ctrlKey && (key == "k" || key == "p")))        return "up" -    else if (event.keyCode == keyCodes.tab && !event.shiftKey) +    else if (event.key == "Tab" && !event.shiftKey)        return "tab"      else if (key == "down" ||          (event.ctrlKey && (key == "j" || key == "n")))        return "down" -    else if (event.keyCode == keyCodes.enter) +    else if (event.key == "Enter")        return "enter" -    else if event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey +    else if KeyboardUtils.isBackspace event        return "delete"      null @@ -125,8 +125,7 @@ class VomnibarUI      @lastAction = action = @actionFromKeyEvent event      return true unless action # pass through -    openInNewTab = @forceNewTab || -      (event.shiftKey || event.ctrlKey || event.altKey || KeyboardUtils.isPrimaryModifierKey(event)) +    openInNewTab = @forceNewTab || event.shiftKey || event.ctrlKey || event.altKey || event.metaKey      if (action == "dismiss")        @hide()      else if action in [ "tab", "down" ] diff --git a/tests/dom_tests/dom_tests.coffee b/tests/dom_tests/dom_tests.coffee index 9088fe30..5439e119 100644 --- a/tests/dom_tests/dom_tests.coffee +++ b/tests/dom_tests/dom_tests.coffee @@ -1,33 +1,11 @@  window.vimiumDomTestsAreRunning = true  # Install frontend event handlers. -installListeners()  HUD.init()  Frame.registerFrameId chromeFrameId: 0 -installListener = (element, event, callback) -> -  element.addEventListener event, (-> callback.apply(this, arguments)), true -  getSelection = -> -    window.getSelection().toString() - -# A count of the number of keyboard events received by the page (for the most recently-sent keystroke).  E.g., -# we expect 3 if the keystroke is passed through (keydown, keypress, keyup), and 0 if it is suppressed. -pageKeyboardEventCount = 0 - -sendKeyboardEvent = (key) -> -  pageKeyboardEventCount = 0 -  response = window.callPhantom -    request: "keyboard" -    key: key - -sendKeyboardEvents = (keys) -> -  sendKeyboardEvent ch for ch in keys.split() - -# These listeners receive events after the main frontend listeners, and do not receive suppressed events. -for type in [ "keydown", "keypress", "keyup" ] -  installListener window, type, (event) -> -    pageKeyboardEventCount += 1 +  window.getSelection().toString()  commandName = commandCount = null @@ -163,6 +141,16 @@ context "jsaction matching",        linkHints.deactivateMode()        assert.equal 0, hintMarkers.length +sendKeyboardEvent = (key, type="keydown", extra={}) -> +  handlerStack.bubbleEvent type, extend extra, +    type: type +    key: key +    preventDefault: -> +    stopImmediatePropagation: -> + +sendKeyboardEvents = (keys) -> +  sendKeyboardEvent key for key in keys.split "" +  inputs = []  context "Test link hints for focusing input elements correctly", @@ -227,16 +215,16 @@ context "Test link hints for changing mode",    should "change mode on shift", ->      assert.equal "curr-tab", @linkHints.mode.name -    sendKeyboardEvent "shift-down" +    sendKeyboardEvent "Shift", "keydown"      assert.equal "bg-tab", @linkHints.mode.name -    sendKeyboardEvent "shift-up" +    sendKeyboardEvent "Shift", "keyup"      assert.equal "curr-tab", @linkHints.mode.name    should "change mode on ctrl", ->      assert.equal "curr-tab", @linkHints.mode.name -    sendKeyboardEvent "ctrl-down" +    sendKeyboardEvent "Control", "keydown"      assert.equal "fg-tab", @linkHints.mode.name -    sendKeyboardEvent "ctrl-up" +    sendKeyboardEvent "Control", "keyup"      assert.equal "curr-tab", @linkHints.mode.name  context "Alphabetical link hints", @@ -247,6 +235,7 @@ context "Alphabetical link hints",      stubSettings "linkHintCharacters", "ab"      stub window, "windowIsFocused", -> true +    document.getElementById("test-div").innerHTML = ""      # Three hints will trigger double hint chars.      createLinks 3      @linkHints = activateLinkHintsMode() @@ -258,12 +247,13 @@ context "Alphabetical link hints",    should "label the hints correctly", ->      hintMarkers = getHintMarkers()      expectedHints = ["aa", "b", "ab"] +    assert.equal 3, hintMarkers.length      for hint, i in expectedHints        assert.equal hint, hintMarkers[i].hintString    should "narrow the hints", ->      hintMarkers = getHintMarkers() -    sendKeyboardEvent "A" +    sendKeyboardEvent "a"      assert.equal "none", hintMarkers[1].style.display      assert.equal "", hintMarkers[0].style.display @@ -314,24 +304,25 @@ context "Filtered link hints",      should "narrow the hints", ->        hintMarkers = getHintMarkers() -      sendKeyboardEvent "T" -      sendKeyboardEvent "R" +      sendKeyboardEvent "t" +      sendKeyboardEvent "r"        assert.equal "none", hintMarkers[0].style.display        assert.equal "3", hintMarkers[1].hintString        assert.equal "", hintMarkers[1].style.display -      sendKeyboardEvent "A" +      sendKeyboardEvent "a"        assert.equal "1", hintMarkers[3].hintString -    # This test is the same as above, but with an extra non-matching character. +    # This test is the same as above, but with an extra non-matching character.  The effect should be the +    # same.      should "narrow the hints and ignore typing mistakes", ->        hintMarkers = getHintMarkers() -      sendKeyboardEvent "T" -      sendKeyboardEvent "R" -      sendKeyboardEvent "X" +      sendKeyboardEvent "t" +      sendKeyboardEvent "r" +      sendKeyboardEvent "x"        assert.equal "none", hintMarkers[0].style.display        assert.equal "3", hintMarkers[1].hintString        assert.equal "", hintMarkers[1].style.display -      sendKeyboardEvent "A" +      sendKeyboardEvent "a"        assert.equal "1", hintMarkers[3].hintString    context "Image hints", @@ -428,9 +419,9 @@ context "Filtered link hints",      should "use tab to select the active hint", ->        sendKeyboardEvents "abc"        assert.equal "8", @getActiveHintMarker() -      sendKeyboardEvent "tab" +      sendKeyboardEvent "Tab", "keydown"        assert.equal "7", @getActiveHintMarker() -      sendKeyboardEvent "tab" +      sendKeyboardEvent "Tab", "keydown"        assert.equal "9", @getActiveHintMarker()  context "Input focus", @@ -576,93 +567,55 @@ context "Key mapping",    should "set and call command handler", ->      sendKeyboardEvent "m"      assert.isTrue @handlerCalled -    assert.equal 0, pageKeyboardEventCount    should "not call command handler for pass keys", ->      sendKeyboardEvent "p"      assert.isFalse @handlerCalled -    assert.equal 3, pageKeyboardEventCount    should "accept a count prefix with a single digit", ->      sendKeyboardEvent "2"      sendKeyboardEvent "m"      assert.equal 2, @handlerCalledCount -    assert.equal 0, pageKeyboardEventCount    should "accept a count prefix with multiple digits", ->      sendKeyboardEvent "2"      sendKeyboardEvent "0"      sendKeyboardEvent "m"      assert.equal 20, @handlerCalledCount -    assert.equal 0, pageKeyboardEventCount    should "cancel a count prefix", ->      sendKeyboardEvent "2"      sendKeyboardEvent "z"      sendKeyboardEvent "m"      assert.equal 1, @handlerCalledCount -    assert.equal 0, pageKeyboardEventCount    should "accept a count prefix for multi-key command mappings", -> -    sendKeyboardEvent "2" +    sendKeyboardEvent "5"      sendKeyboardEvent "z"      sendKeyboardEvent "p" -    assert.equal 2, @handlerCalledCount -    assert.equal 0, pageKeyboardEventCount +    assert.equal 5, @handlerCalledCount    should "cancel a key prefix", ->      sendKeyboardEvent "z"      sendKeyboardEvent "m"      assert.equal 1, @handlerCalledCount -    assert.equal 0, pageKeyboardEventCount    should "cancel a count prefix after a prefix key", ->      sendKeyboardEvent "2"      sendKeyboardEvent "z"      sendKeyboardEvent "m"      assert.equal 1, @handlerCalledCount -    assert.equal 0, pageKeyboardEventCount    should "cancel a prefix key on escape", ->      sendKeyboardEvent "z" -    sendKeyboardEvent "escape" +    sendKeyboardEvent "Escape", "keydown"      sendKeyboardEvent "p"      assert.equal 0, @handlerCalledCount -  should "not handle escape on its own", -> -    sendKeyboardEvent "escape" -    assert.equal 2, pageKeyboardEventCount -  context "Normal mode",    setup ->      initializeModeState() -  should "suppress mapped keys", -> -    sendKeyboardEvent "m" -    assert.equal 0, pageKeyboardEventCount - -  should "not suppress unmapped keys", -> -    sendKeyboardEvent "u" -    assert.equal 3, pageKeyboardEventCount - -  should "not suppress escape", -> -    sendKeyboardEvent "escape" -    assert.equal 2, pageKeyboardEventCount - -  should "not suppress passKeys", -> -    sendKeyboardEvent "p" -    assert.equal 3, pageKeyboardEventCount - -  should "suppress passKeys with a non-empty key state (a count)", -> -    sendKeyboardEvent "5" -    sendKeyboardEvent "p" -    assert.equal 0, pageKeyboardEventCount - -  should "suppress passKeys with a non-empty key state (a key)", -> -    sendKeyboardEvent "z" -    sendKeyboardEvent "p" -    assert.equal 0, pageKeyboardEventCount -    should "invoke commands for mapped keys", ->      sendKeyboardEvent "m"      assert.equal "m", commandName @@ -706,7 +659,7 @@ context "Normal mode",      assert.equal 2, commandCount    should "accept count prefixes of length 2", -> -    sendKeyboardEvent "12" +    sendKeyboardEvents "12"      sendKeyboardEvent "m"      assert.equal 12, commandCount @@ -763,19 +716,16 @@ context "Insert mode",      initializeModeState()      @insertMode = new InsertMode global: true -  should "not suppress mapped keys in insert mode", -> -    sendKeyboardEvent "m" -    assert.equal 3, pageKeyboardEventCount -    should "exit on escape", ->      assert.isTrue @insertMode.modeIsActive -    sendKeyboardEvent "escape" +    sendKeyboardEvent "Escape", "keydown"      assert.isFalse @insertMode.modeIsActive    should "resume normal mode after leaving insert mode", -> +    assert.equal null, commandCount      @insertMode.exit()      sendKeyboardEvent "m" -    assert.equal 0, pageKeyboardEventCount +    assert.equal 1, commandCount  context "Triggering insert mode",    setup -> @@ -833,7 +783,7 @@ context "Caret mode",      assert.equal "I", getSelection()    should "exit caret mode on escape", -> -    sendKeyboardEvent "escape" +    sendKeyboardEvent "Escape", "keydown"      assert.equal "", getSelection()    should "move caret with l and h", -> @@ -868,7 +818,7 @@ context "Caret mode",      assert.equal "I", getSelection()      sendKeyboardEvents "ww"      assert.equal "a", getSelection() -    sendKeyboardEvent "escape" +    sendKeyboardEvent "Escape", "keydown"      new VisualMode      assert.equal "a", getSelection() @@ -983,16 +933,14 @@ context "Mode utilities",      test = new Mode exitOnEscape: true      assert.isTrue test.modeIsActive -    sendKeyboardEvent "escape" -    assert.equal 0, pageKeyboardEventCount +    sendKeyboardEvent "Escape", "keydown"      assert.isFalse test.modeIsActive    should "not exit on escape if not enabled", ->      test = new Mode exitOnEscape: false      assert.isTrue test.modeIsActive -    sendKeyboardEvent "escape" -    assert.equal 2, pageKeyboardEventCount +    sendKeyboardEvent "Escape", "keydown"      assert.isTrue test.modeIsActive    should "exit on blur", -> @@ -1031,21 +979,21 @@ context "PostFindMode",      assert.isFalse @postFindMode.modeIsActive    should "suppress unmapped printable keys", -> -    sendKeyboardEvent "m" -    assert.equal 0, pageKeyboardEventCount +    sendKeyboardEvent "a" +    assert.equal null, commandCount    should "be deactivated on click events", ->      handlerStack.bubbleEvent "click", target: document.activeElement      assert.isFalse @postFindMode.modeIsActive    should "enter insert mode on immediate escape", -> -    sendKeyboardEvent "escape" -    assert.equal 0, pageKeyboardEventCount +    sendKeyboardEvent "Escape", "keydown" +    assert.equal null, commandCount      assert.isFalse @postFindMode.modeIsActive    should "not enter insert mode on subsequent escapes", ->      sendKeyboardEvent "a" -    sendKeyboardEvent "escape" +    sendKeyboardEvent "Escape", "keydown"      assert.isTrue @postFindMode.modeIsActive  context "WaitForEnter", @@ -1057,14 +1005,14 @@ context "WaitForEnter",    should "exit with success on Enter", ->      assert.isTrue @waitForEnter.modeIsActive      assert.isFalse @isSuccess? -    sendKeyboardEvent "enter" +    sendKeyboardEvent "Enter", "keydown"      assert.isFalse @waitForEnter.modeIsActive      assert.isTrue @isSuccess? and @isSuccess == true    should "exit without success on Escape", ->      assert.isTrue @waitForEnter.modeIsActive      assert.isFalse @isSuccess? -    sendKeyboardEvent "escape" +    sendKeyboardEvent "Escape", "keydown"      assert.isFalse @waitForEnter.modeIsActive      assert.isTrue @isSuccess? and @isSuccess == false @@ -1075,17 +1023,6 @@ context "WaitForEnter",      assert.isTrue @waitForEnter.modeIsActive      assert.isFalse @isSuccess? -context "SuppressAllKeyboardEvents", -  setup -> -    initializeModeState() - -  should "supress keyboard events", -> -    sendKeyboardEvent "a" -    assert.equal 3, pageKeyboardEventCount -    new SuppressAllKeyboardEvents -    sendKeyboardEvent "a" -    assert.equal 0, pageKeyboardEventCount -  context "GrabBackFocus",    setup ->      testContent = "<input type='text' value='some value' id='input'/>" diff --git a/tests/dom_tests/phantom_runner.coffee b/tests/dom_tests/phantom_runner.coffee index 09d7d584..b91919bb 100644 --- a/tests/dom_tests/phantom_runner.coffee +++ b/tests/dom_tests/phantom_runner.coffee @@ -21,30 +21,6 @@ page.onError = (msg, trace) ->  page.onResourceError = (resourceError) ->    console.log(resourceError.errorString) -page.onCallback = (request) -> -  switch request.request -    when "keyboard" -      switch request.key -        when "escape" -          page.sendEvent "keydown", page.event.key.Escape -          page.sendEvent "keyup", page.event.key.Escape -        when "enter" -          page.sendEvent "keydown", page.event.key.Enter -          page.sendEvent "keyup", page.event.key.Enter -        when "tab" -          page.sendEvent "keydown", page.event.key.Tab -          page.sendEvent "keyup", page.event.key.Tab -        when "shift-down" -          page.sendEvent "keydown", page.event.key.Shift -        when "shift-up" -          page.sendEvent "keyup", page.event.key.Shift -        when "ctrl-down" -          page.sendEvent "keydown", page.event.key.Control -        when "ctrl-up" -          page.sendEvent "keyup", page.event.key.Control -        else -          page.sendEvent "keypress", request.key -  testfile = path.join(path.dirname(system.args[0]), 'dom_tests.html')  page.open testfile, (status) ->    if status != 'success' | 
