From 7c5fb2c312b9140c2dd091f792535ae8f592ecdb Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 26 Feb 2016 16:47:17 +0000 Subject: Key bindings; initial "generic" class. This implements a generic front-end class for key handling (a la normal mode). Also: - supports count prefixes (or not) - supports multi-key mappings (longer than two) Also included is a very poor-man's demo. See the bottom of mode_key_handler.coffee for some hard-wired key bindings. IMPORTANT: This does not actually work as Vimium. It's just a demo. --- content_scripts/mode_key_handler.coffee | 158 ++++++++++++++++++++++++++++++++ content_scripts/vimium_frontend.coffee | 3 +- 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 content_scripts/mode_key_handler.coffee (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee new file mode 100644 index 00000000..c79f1991 --- /dev/null +++ b/content_scripts/mode_key_handler.coffee @@ -0,0 +1,158 @@ + +# The important data structure here is the "keyState". The key state is a non-empty list of objects, the keys +# of which are key names, and the values are other key-mapping objects or commands (strings). Key-mapping +# objects can be arbitrarily nested; so we support any length of multi-key mapping. +# +# Whenever we consume a key, we append a new copy of the global key mapping to the key state (hence, the +# global mappings are always available, and the key state is always non-empty). + +class KeyHandlerMode extends Mode + useCount: true + countPrefix: 0 + keydownEvents: {} + keyState: [] + + constructor: (options) -> + # A function accepting a command name and a count; required. + @commandHandler = options.commandHandler ? (->) + # A Key mapping structure; required. + @keyMapping = options.keyMapping ? {} + @useCount = false if options.noCount + @reset() + + # We don't pass these options on to super(). + options = Utils.copyObjectOmittingProperties options, "commandHandler", "keyMapping", "noCount" + + super extend options, + keydown: @onKeydown.bind this + keypress: @onKeypress.bind this + keyup: @onKeyup.bind this + # We cannot track matching keydown/keyup events if we lose the focus. + blur: (event) => @alwaysContinueBubbling => + @keydownEvents = {} if event.target == window + + onKeydown: (event) -> + keyChar = KeyboardUtils.getKeyCharString event + + if KeyboardUtils.isEscape event + if @isInResetState() + @continueBubbling + else + @reset() + DomUtils.suppressKeyupAfterEscape handlerStack + false # Suppress event. + + else if keyChar and @keyCharIsKeyStatePrefix keyChar + @advanceKeyState keyChar + commands = @keyState.filter (entry) -> "string" == typeof entry + @invokeCommand commands[0] if 0 < commands.length + false # Suppress event. + + else + # We did not handle the event, but we might handle the subsequent keypress event. If we *will* be + # handling that event, then we need to suppress propagation of this keydown event to prevent triggering + # page features like Google instant search. + keyChar = KeyboardUtils.getKeyChar event + if keyChar and (@keyCharIsKeyStatePrefix(keyChar) or @isCountKey keyChar) + DomUtils.suppressPropagation event + @keydownEvents[@getEventCode event] = true + @stopBubblingAndTrue + else + @countPrefix = 0 if keyChar + @continueBubbling + + onKeypress: (event) -> + keyChar = KeyboardUtils.getKeyCharString event + if keyChar and @keyCharIsKeyStatePrefix keyChar + @advanceKeyState keyChar + commands = @keyState.filter (entry) -> "string" == typeof entry + @invokeCommand commands[0] if 0 < commands.length + false # Suppress event. + else if keyChar and @isCountKey keyChar + @countPrefix = @countPrefix * 10 + parseInt keyChar + false # Suppress event. + else + @continueBubbling + + onKeyup: (event) -> + eventCode = @getEventCode event + if eventCode of @keydownEvents + delete @keydownEvents[eventCode] + DomUtils.suppressPropagation event + @stopBubblingAndTrue + else + @continueBubbling + + # This tests whether keyChar is a prefix of any current mapping in the key state. + keyCharIsKeyStatePrefix: (keyChar) -> + for mapping in @keyState + return true if keyChar of mapping + false + + # This is called whenever a keyChar is matched. We keep any existing entries matching keyChar, and append a + # new copy of the global key mappings. + advanceKeyState: (keyChar) -> + newKeyState = + for mapping in @keyState + continue unless keyChar of mapping + mapping[keyChar] + @keyState = [newKeyState..., @keyMapping] + + # This is called to invoke a command and reset the key state. + invokeCommand: (command) -> + countPrefix = if 0 < @countPrefix then @countPrefix else 1 + @reset() + @commandHandler command, countPrefix + + # Reset the state (as if no keys had been handled). + reset: -> + @countPrefix = 0 + @keyState = [@keyMapping] + + # This tests whether we are in the reset state. It is used to check whether we should be using escape to + # reset the key state, or passing it to the page. + isInResetState: -> + @countPrefix == 0 and @keyState.length == 1 + + # This tests whether keyChar should be treated as a count key. + isCountKey: (keyChar) -> + return false unless @useCount and keyChar.length == 1 + if 0 < @countPrefix + '0' <= keyChar <= '9' + else + '1' <= keyChar <= '9' + + getEventCode: (event) -> event.keyCode + +# Demo/test code. +# A (very) poor-man's normal mode. + +demoKeyMapping = + j: "scrollDown" + k: "scrollUp" + i: "enterInsertMode" + g: + g: "scrollToTop" + a: "scrollToTop" + z: "scrollToBottom" + i: "focusInput" + # A three-key binding. + a: + b: + c: "enterInsertMode" + # And this should override "j" on its own. + j: "enterInsertMode" + +demoCommandHandler = (command, count) -> + switch command + when "scrollDown" then scrollDown() + when "scrollUp" then scrollUp() + when "scrollToTop" then scrollToTop count + when "scrollToBottom" then scrollToBottom() + when "enterInsertMode" then enterInsertMode() + when "focusInput" then focusInput count + +root = exports ? window +root.KeyHandlerMode = KeyHandlerMode +root.demoKeyMapping = demoKeyMapping +root.demoCommandHandler = demoCommandHandler diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 667031dc..bce5f632 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -112,7 +112,8 @@ window.initializeModes = -> # Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and # activates/deactivates itself accordingly. - new NormalMode + # new NormalMode + new KeyHandlerMode commandHandler: demoCommandHandler, keyMapping: demoKeyMapping, indicator: "Demo mode." new PassKeysMode new InsertMode permanent: true Scroller.init() -- cgit v1.2.3 From 34b1fb0f4e2ce1696c17e703d0bc43463355d6ba Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 27 Feb 2016 14:20:12 +0000 Subject: Key bindings; initial partially-functioning version. --- content_scripts/mode_key_handler.coffee | 43 +++------------------------------ content_scripts/vimium_frontend.coffee | 28 ++++++++++++++++----- 2 files changed, 25 insertions(+), 46 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index c79f1991..6e04addb 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -1,11 +1,4 @@ -# The important data structure here is the "keyState". The key state is a non-empty list of objects, the keys -# of which are key names, and the values are other key-mapping objects or commands (strings). Key-mapping -# objects can be arbitrarily nested; so we support any length of multi-key mapping. -# -# Whenever we consume a key, we append a new copy of the global key mapping to the key state (hence, the -# global mappings are always available, and the key state is always non-empty). - class KeyHandlerMode extends Mode useCount: true countPrefix: 0 @@ -15,8 +8,6 @@ class KeyHandlerMode extends Mode constructor: (options) -> # A function accepting a command name and a count; required. @commandHandler = options.commandHandler ? (->) - # A Key mapping structure; required. - @keyMapping = options.keyMapping ? {} @useCount = false if options.noCount @reset() @@ -31,6 +22,8 @@ class KeyHandlerMode extends Mode blur: (event) => @alwaysContinueBubbling => @keydownEvents = {} if event.target == window + setKeyMapping: (@keyMapping) -> @reset() + onKeydown: (event) -> keyChar = KeyboardUtils.getKeyCharString event @@ -65,7 +58,7 @@ class KeyHandlerMode extends Mode keyChar = KeyboardUtils.getKeyCharString event if keyChar and @keyCharIsKeyStatePrefix keyChar @advanceKeyState keyChar - commands = @keyState.filter (entry) -> "string" == typeof entry + commands = @keyState.filter (entry) -> entry.command @invokeCommand commands[0] if 0 < commands.length false # Suppress event. else if keyChar and @isCountKey keyChar @@ -124,35 +117,5 @@ class KeyHandlerMode extends Mode getEventCode: (event) -> event.keyCode -# Demo/test code. -# A (very) poor-man's normal mode. - -demoKeyMapping = - j: "scrollDown" - k: "scrollUp" - i: "enterInsertMode" - g: - g: "scrollToTop" - a: "scrollToTop" - z: "scrollToBottom" - i: "focusInput" - # A three-key binding. - a: - b: - c: "enterInsertMode" - # And this should override "j" on its own. - j: "enterInsertMode" - -demoCommandHandler = (command, count) -> - switch command - when "scrollDown" then scrollDown() - when "scrollUp" then scrollUp() - when "scrollToTop" then scrollToTop count - when "scrollToBottom" then scrollToBottom() - when "enterInsertMode" then enterInsertMode() - when "focusInput" then focusInput count - root = exports ? window root.KeyHandlerMode = KeyHandlerMode -root.demoKeyMapping = demoKeyMapping -root.demoCommandHandler = demoCommandHandler diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index bce5f632..b46175fb 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -101,19 +101,35 @@ handlerStack.push # Only exported for tests. window.initializeModes = -> - class NormalMode extends Mode + class NormalMode extends KeyHandlerMode constructor: -> super name: "normal" indicator: false # There is no mode indicator in normal mode. - keydown: (event) => onKeydown.call @, event - keypress: (event) => onKeypress.call @, event - keyup: (event) => onKeyup.call @, event + commandHandler: @commandHandler.bind this + keyMapping: {} + + chrome.storage.local.get "normalModeKeyStateMapping", (items) => + @setKeyMapping items.normalModeKeyStateMapping + + chrome.storage.onChanged.addListener (changes, area) => + if area == "local" and changes.normalModeKeyStateMapping?.newValue + @setKeyMapping changes.normalModeKeyStateMapping.newValue + + commandHandler: (registryEntry, count) -> + # TODO: Special handling of Vomnibar. + if registryEntry.isBackgroundCommand + true # Not yet implemnted. + else + count = 1 if registryEntry.noRepeat + if registryEntry.passCountToFunction + Utils.invokeCommandString registryEntry.command, [count] + else + Utils.invokeCommandString registryEntry.command for i in [0...count] # Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and # activates/deactivates itself accordingly. - # new NormalMode - new KeyHandlerMode commandHandler: demoCommandHandler, keyMapping: demoKeyMapping, indicator: "Demo mode." + new NormalMode new PassKeysMode new InsertMode permanent: true Scroller.init() -- cgit v1.2.3 From 3f63ceb19046157692eac9e9a13486af7e50a57e Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 27 Feb 2016 14:44:34 +0000 Subject: Key bindings; partially functioning w/ backgound commands. --- content_scripts/vimium_frontend.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index b46175fb..26942a30 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -117,11 +117,14 @@ window.initializeModes = -> @setKeyMapping changes.normalModeKeyStateMapping.newValue commandHandler: (registryEntry, count) -> + count *= registryEntry.options.count ? 1 + count = 1 if registryEntry.noRepeat + # TODO: Repeat limit. # TODO: Special handling of Vomnibar. + # TODO: Fix passKeys. if registryEntry.isBackgroundCommand - true # Not yet implemnted. + chrome.runtime.sendMessage { handler: "runBackgroundCommand", frameId, registryEntry, count} else - count = 1 if registryEntry.noRepeat if registryEntry.passCountToFunction Utils.invokeCommandString registryEntry.command, [count] else -- cgit v1.2.3 From 47de80f2fcb03c8741ab46308ce982209f74f6ac Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 27 Feb 2016 15:03:09 +0000 Subject: Key bindings; fix passkeys. --- content_scripts/mode_key_handler.coffee | 20 +++++++++++++------- content_scripts/mode_passkeys.coffee | 16 +++++++++------- content_scripts/vimium_frontend.coffee | 4 ++-- 3 files changed, 24 insertions(+), 16 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 6e04addb..7f03dd64 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -35,7 +35,7 @@ class KeyHandlerMode extends Mode DomUtils.suppressKeyupAfterEscape handlerStack false # Suppress event. - else if keyChar and @keyCharIsKeyStatePrefix keyChar + else if keyChar and @mappingForKeyChar keyChar @advanceKeyState keyChar commands = @keyState.filter (entry) -> "string" == typeof entry @invokeCommand commands[0] if 0 < commands.length @@ -46,7 +46,7 @@ class KeyHandlerMode extends Mode # handling that event, then we need to suppress propagation of this keydown event to prevent triggering # page features like Google instant search. keyChar = KeyboardUtils.getKeyChar event - if keyChar and (@keyCharIsKeyStatePrefix(keyChar) or @isCountKey keyChar) + if keyChar and (@mappingForKeyChar(keyChar) or @isCountKey keyChar) DomUtils.suppressPropagation event @keydownEvents[@getEventCode event] = true @stopBubblingAndTrue @@ -56,7 +56,7 @@ class KeyHandlerMode extends Mode onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event - if keyChar and @keyCharIsKeyStatePrefix keyChar + if keyChar and @mappingForKeyChar keyChar @advanceKeyState keyChar commands = @keyState.filter (entry) -> entry.command @invokeCommand commands[0] if 0 < commands.length @@ -76,11 +76,12 @@ class KeyHandlerMode extends Mode else @continueBubbling - # This tests whether keyChar is a prefix of any current mapping in the key state. - keyCharIsKeyStatePrefix: (keyChar) -> + # This returns the first mapping for which keyChar is mapped. The return value is truthy if a match is found + # and falsy otherwise. + mappingForKeyChar: (keyChar) -> for mapping in @keyState - return true if keyChar of mapping - false + return mapping if keyChar of mapping + null # This is called whenever a keyChar is matched. We keep any existing entries matching keyChar, and append a # new copy of the global key mappings. @@ -115,6 +116,11 @@ class KeyHandlerMode extends Mode else '1' <= keyChar <= '9' + # True if keyChar would be the first character of a command mapping. This is used by passKeys to decide + # whether keyChar is a continuation of a command which the user has already begin entering. + isFirstKeyChar: (keyChar) -> + @countPrefix == 0 and @keyMapping == @mappingForKeyChar keyChar + getEventCode: (event) -> event.keyCode root = exports ? window diff --git a/content_scripts/mode_passkeys.coffee b/content_scripts/mode_passkeys.coffee index 631eb621..426e5369 100644 --- a/content_scripts/mode_passkeys.coffee +++ b/content_scripts/mode_passkeys.coffee @@ -1,6 +1,6 @@ class PassKeysMode extends Mode - constructor: -> + constructor: (@normalMode) -> super name: "passkeys" trackState: true # Maintain @enabled, @passKeys and @keyQueue. @@ -8,14 +8,16 @@ class PassKeysMode extends Mode keypress: (event) => @handleKeyChar event, String.fromCharCode event.charCode keyup: (event) => @handleKeyChar event, KeyboardUtils.getKeyChar event - # Keystrokes are *never* considered passKeys if the keyQueue is not empty. So, for example, if 't' is a - # passKey, then 'gt' and '99t' will neverthless be handled by Vimium. + # Keystrokes are *never* considered passKeys if the user has begin entering a command. So, for example, if + # 't' is a passKey, then 'gt' and '99t' will neverthless be handled by Vimium. handleKeyChar: (event, keyChar) -> return @continueBubbling if event.altKey or event.ctrlKey or event.metaKey - if keyChar and not @keyQueue and keyChar.length == 1 and 0 <= @passKeys.indexOf keyChar - @stopBubblingAndTrue - else - @continueBubbling + return @continueBubbling unless keyChar and keyChar.length == 1 + # Test whether the user has already begun entering a command. + return @continueBubbling unless @normalMode.isFirstKeyChar keyChar + return @continueBubbling unless 0 <= @passKeys.indexOf keyChar + # This is a passkey. + @stopBubblingAndTrue root = exports ? window root.PassKeysMode = PassKeysMode diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 26942a30..7fe12d62 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -132,8 +132,8 @@ window.initializeModes = -> # Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and # activates/deactivates itself accordingly. - new NormalMode - new PassKeysMode + normalMode = new NormalMode + new PassKeysMode normalMode new InsertMode permanent: true Scroller.init() -- cgit v1.2.3 From d388eddd192d925cda43700d7090ce8d52499df0 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 27 Feb 2016 15:25:28 +0000 Subject: Key bindings; remove legacy code. --- content_scripts/vimium_frontend.coffee | 154 ++------------------------------- 1 file changed, 7 insertions(+), 147 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 7fe12d62..0cb402b8 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -5,14 +5,9 @@ # "domReady". # -keyPort = null isEnabledForUrl = true isIncognitoMode = chrome.extension.inIncognitoContext passKeys = null -keyQueue = null -# The user's operating system. -currentCompletionKeys = "" -validFirstKeys = "" # We track whther the current window has the focus or not. windowIsFocused = do -> @@ -119,9 +114,13 @@ window.initializeModes = -> commandHandler: (registryEntry, count) -> count *= registryEntry.options.count ? 1 count = 1 if registryEntry.noRepeat - # TODO: Repeat limit. + + if registryEntry.repeatLimit? and registryEntry.repeatLimit < count + return unless confirm """ + You have asked Vimium to perform #{count} repeats of the command: #{registryEntry.description}.\n + Are you sure you want to continue? """ + # TODO: Special handling of Vomnibar. - # TODO: Fix passKeys. if registryEntry.isBackgroundCommand chrome.runtime.sendMessage { handler: "runBackgroundCommand", frameId, registryEntry, count} else @@ -142,30 +141,13 @@ window.initializeModes = -> # initializePreDomReady = -> checkIfEnabledForUrl() - refreshCompletionKeys() - - # Send the key to the key handler in the background page. - keyPort = chrome.runtime.connect({ name: "keyDown" }) - # If the port is closed, the background page has gone away (since we never close it ourselves). Disable all - # our event listeners, and stub out chrome.runtime.sendMessage/connect (to prevent errors). - # TODO(mrmr1993): Do some actual cleanup to free resources, hide UI, etc. - keyPort.onDisconnect.addListener -> - isEnabledForUrl = false - chrome.runtime.sendMessage = -> - chrome.runtime.connect = -> - window.removeEventListener "focus", onFocus requestHandlers = showHUDforDuration: handleShowHUDforDuration toggleHelpDialog: (request) -> if frameId == request.frameId then HelpDialog.toggle request.dialogHtml focusFrame: (request) -> if (frameId == request.frameId) then focusThisFrame request - refreshCompletionKeys: refreshCompletionKeys getScrollPosition: -> scrollX: window.scrollX, scrollY: window.scrollY setScrollPosition: setScrollPosition - executePageCommand: executePageCommand - currentKeyQueue: (request) -> - keyQueue = request.keyQueue - handlerStack.bubbleEvent "registerKeyQueue", { keyQueue: keyQueue } # A frame has received the focus. We don't care here (the Vomnibar/UI-component handles this). frameFocused: -> checkEnabledAfterURLChange: checkEnabledAfterURLChange @@ -179,7 +161,7 @@ initializePreDomReady = -> return if request.handler and not request.name shouldHandleRequest = isEnabledForUrl # We always handle the message if it's one of these listed message types. - shouldHandleRequest ||= request.name in [ "executePageCommand", "checkEnabledAfterURLChange" ] + shouldHandleRequest ||= request.name in [ "checkEnabledAfterURLChange" ] # Requests with a frameId of zero should always and only be handled in the main/top frame (regardless of # whether Vimium is enabled there). if request.frameId == 0 and DomUtils.isTopFrame() @@ -255,28 +237,6 @@ unregisterFrame = -> frameId: frameId tab_is_closing: DomUtils.isTopFrame() -executePageCommand = (request) -> - commandType = request.command.split(".")[0] - # Vomnibar commands are handled in the tab's main/top frame. They are handled even if Vimium is otherwise - # disabled in the frame. - if commandType == "Vomnibar" - if DomUtils.isTopFrame() - # We pass the frameId from request. That's the frame which originated the request, so that's the frame - # which should receive the focus when the vomnibar closes. - Utils.invokeCommandString request.command, [ request.frameId, request.registryEntry ] - refreshCompletionKeys request - return - - # All other commands are handled in their frame (but only if Vimium is enabled). - return unless frameId == request.frameId and isEnabledForUrl - - if request.registryEntry.passCountToFunction - Utils.invokeCommandString(request.command, [request.count]) - else - Utils.invokeCommandString(request.command) for i in [0...request.count] - - refreshCompletionKeys(request) - handleShowHUDforDuration = ({ text, duration }) -> if DomUtils.isTopFrame() DomUtils.documentReady -> HUD.showForDuration text, duration @@ -476,94 +436,6 @@ extend window, targetElement: document.activeElement indicator: false -# Track which keydown events we have handled, so that we can subsequently suppress the corresponding keyup -# event. -KeydownEvents = - handledEvents: {} - - getEventCode: (event) -> event.keyCode - - push: (event) -> - @handledEvents[@getEventCode event] = true - - # Yields truthy or falsy depending upon whether a corresponding keydown event is present (and removes that - # event). - pop: (event) -> - detailString = @getEventCode event - value = @handledEvents[detailString] - delete @handledEvents[detailString] - value - - clear: -> @handledEvents = {} - -handlerStack.push - _name: "KeydownEvents-cleanup" - blur: (event) -> KeydownEvents.clear() if event.target == window; true - -# -# Sends everything except i & ESC to the handler in background_page. i & ESC are special because they control -# insert mode which is local state to the page. The key will be are either a single ascii letter or a -# key-modifier pair, e.g. for control a. -# -# Note that some keys will only register keydown events and not keystroke events, e.g. ESC. -# -# @/this, here, is the the normal-mode Mode object. -onKeypress = (event) -> - keyChar = KeyboardUtils.getKeyCharString event - if keyChar - if currentCompletionKeys.indexOf(keyChar) != -1 or isValidFirstKey keyChar - DomUtils.suppressEvent(event) - keyPort.postMessage keyChar:keyChar, frameId:frameId - return @stopBubblingAndTrue - - keyPort.postMessage keyChar:keyChar, frameId:frameId - - return @continueBubbling - -# @/this, here, is the the normal-mode Mode object. -onKeydown = (event) -> - keyChar = KeyboardUtils.getKeyCharString event - - if (HelpDialog.showing && KeyboardUtils.isEscape(event)) - HelpDialog.hide() - DomUtils.suppressEvent event - KeydownEvents.push event - return @stopBubblingAndTrue - - else - if (keyChar) - if (currentCompletionKeys.indexOf(keyChar) != -1 or isValidFirstKey(keyChar)) - DomUtils.suppressEvent event - KeydownEvents.push event - keyPort.postMessage({ keyChar:keyChar, frameId:frameId }) - return @stopBubblingAndTrue - - keyPort.postMessage({ keyChar:keyChar, frameId:frameId }) - - else if (KeyboardUtils.isEscape(event)) - keyPort.postMessage({ keyChar:"", frameId:frameId }) - - # Added to prevent propagating this event to other listeners if it's one that'll trigger a Vimium command. - # The goal is to avoid the scenario where Google Instant Search uses every keydown event to dump us - # back into the search box. As a side effect, this should also prevent overriding by other sites. - # - # Subject to internationalization issues since we're using keyIdentifier instead of charCode (in keypress). - # - # TOOD(ilya): Revisit this. Not sure it's the absolute best approach. - if not keyChar && - (currentCompletionKeys.indexOf(KeyboardUtils.getKeyChar(event)) != -1 || - isValidFirstKey(KeyboardUtils.getKeyChar(event))) - DomUtils.suppressPropagation(event) - KeydownEvents.push event - return @stopBubblingAndTrue - - return @continueBubbling - -# @/this, here, is the the normal-mode Mode object. -onKeyup = (event) -> - return @continueBubbling unless KeydownEvents.pop event - DomUtils.suppressPropagation(event) - @stopBubblingAndTrue # Checks if Vimium should be enabled or not in this frame. As a side effect, it also informs the background # page whether this frame has the focus, allowing the background page to track the active frame's URL. @@ -593,18 +465,6 @@ checkIfEnabledForUrl = (frameIsFocused = windowIsFocused()) -> checkEnabledAfterURLChange = -> checkIfEnabledForUrl() if windowIsFocused() -# Exported to window, but only for DOM tests. -window.refreshCompletionKeys = (response) -> - if (response) - currentCompletionKeys = response.completionKeys - - if (response.validFirstKeys) - validFirstKeys = response.validFirstKeys - else - chrome.runtime.sendMessage({ handler: "getCompletionKeys" }, refreshCompletionKeys) - -isValidFirstKey = (keyChar) -> - validFirstKeys[keyChar] || /^[1-9]/.test(keyChar) window.handleEscapeForFindMode = -> document.body.classList.remove("vimiumFindMode") -- cgit v1.2.3 From 6487c09d98b791b3211679d2f1bf970394a696e6 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 27 Feb 2016 16:05:23 +0000 Subject: Key bindings; rewire vomnibar. --- content_scripts/vimium_frontend.coffee | 10 ++++------ content_scripts/vomnibar.coffee | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 0cb402b8..4acac352 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -120,7 +120,6 @@ window.initializeModes = -> You have asked Vimium to perform #{count} repeats of the command: #{registryEntry.description}.\n Are you sure you want to continue? """ - # TODO: Special handling of Vomnibar. if registryEntry.isBackgroundCommand chrome.runtime.sendMessage { handler: "runBackgroundCommand", frameId, registryEntry, count} else @@ -136,6 +135,9 @@ window.initializeModes = -> new InsertMode permanent: true Scroller.init() +openVomnibar = ({sourceFrameId, registryEntry}) -> + Utils.invokeCommandString registryEntry.command, [sourceFrameId, registryEntry] if DomUtils.isTopFrame() + # # Complete initialization work that sould be done prior to DOMReady. # @@ -151,6 +153,7 @@ initializePreDomReady = -> # A frame has received the focus. We don't care here (the Vomnibar/UI-component handles this). frameFocused: -> checkEnabledAfterURLChange: checkEnabledAfterURLChange + openVomnibar: openVomnibar chrome.runtime.onMessage.addListener (request, sender, sendResponse) -> # In the options page, we will receive requests from both content and background scripts. ignore those @@ -162,11 +165,6 @@ initializePreDomReady = -> shouldHandleRequest = isEnabledForUrl # We always handle the message if it's one of these listed message types. shouldHandleRequest ||= request.name in [ "checkEnabledAfterURLChange" ] - # Requests with a frameId of zero should always and only be handled in the main/top frame (regardless of - # whether Vimium is enabled there). - if request.frameId == 0 and DomUtils.isTopFrame() - request.frameId = frameId - shouldHandleRequest = true sendResponse requestHandlers[request.name](request, sender) if shouldHandleRequest # Ensure the sendResponse callback is freed. false diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index 55a46777..67a79ff4 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -7,7 +7,6 @@ Vomnibar = # Parse any additional options from the command's registry entry. Currently, this only includes a flag of # the form "keyword=X", for direct activation of a custom search engine. parseRegistryEntry: (registryEntry = { options: [] }, callback = null) -> - options = {} searchEngines = Settings.get("searchEngines") ? "" SearchEngines.refreshAndUse searchEngines, (engines) -> callback? registryEntry.options -- cgit v1.2.3 From 560112448d00fc9c69c9db03de8fdd5412c700dc Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 27 Feb 2016 16:16:53 +0000 Subject: Key bindings; rewire vomnibar (tweaked). --- content_scripts/vimium_frontend.coffee | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 4acac352..5c594d1c 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -135,9 +135,6 @@ window.initializeModes = -> new InsertMode permanent: true Scroller.init() -openVomnibar = ({sourceFrameId, registryEntry}) -> - Utils.invokeCommandString registryEntry.command, [sourceFrameId, registryEntry] if DomUtils.isTopFrame() - # # Complete initialization work that sould be done prior to DOMReady. # @@ -153,7 +150,8 @@ initializePreDomReady = -> # A frame has received the focus. We don't care here (the Vomnibar/UI-component handles this). frameFocused: -> checkEnabledAfterURLChange: checkEnabledAfterURLChange - openVomnibar: openVomnibar + openVomnibar: ({sourceFrameId, registryEntry}) -> + Utils.invokeCommandString registryEntry.command, [sourceFrameId, registryEntry] if DomUtils.isTopFrame() chrome.runtime.onMessage.addListener (request, sender, sendResponse) -> # In the options page, we will receive requests from both content and background scripts. ignore those -- cgit v1.2.3 From 18b8db13fa5184e0cc3ac5fd6645620fdb9d5cef Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 27 Feb 2016 17:33:05 +0000 Subject: Key bindings; tweaks and fixes. --- content_scripts/mode_key_handler.coffee | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 7f03dd64..9b044923 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -36,10 +36,7 @@ class KeyHandlerMode extends Mode false # Suppress event. else if keyChar and @mappingForKeyChar keyChar - @advanceKeyState keyChar - commands = @keyState.filter (entry) -> "string" == typeof entry - @invokeCommand commands[0] if 0 < commands.length - false # Suppress event. + @handleKeyChar event, keyChar else # We did not handle the event, but we might handle the subsequent keypress event. If we *will* be @@ -57,12 +54,9 @@ class KeyHandlerMode extends Mode onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event if keyChar and @mappingForKeyChar keyChar - @advanceKeyState keyChar - commands = @keyState.filter (entry) -> entry.command - @invokeCommand commands[0] if 0 < commands.length - false # Suppress event. + @handleKeyChar event, keyChar else if keyChar and @isCountKey keyChar - @countPrefix = @countPrefix * 10 + parseInt keyChar + @reset @countPrefix * 10 + parseInt keyChar false # Suppress event. else @continueBubbling @@ -76,6 +70,12 @@ class KeyHandlerMode extends Mode else @continueBubbling + handleKeyChar: (event, keyChar) -> + @advanceKeyState keyChar + commands = @keyState.filter (entry) -> entry.command + @invokeCommand commands[0] if 0 < commands.length + false # Suppress event. + # This returns the first mapping for which keyChar is mapped. The return value is truthy if a match is found # and falsy otherwise. mappingForKeyChar: (keyChar) -> @@ -98,9 +98,9 @@ class KeyHandlerMode extends Mode @reset() @commandHandler command, countPrefix - # Reset the state (as if no keys had been handled). - reset: -> - @countPrefix = 0 + # Reset the state (as if no keys had been handled), but retaining the count - if one is provided. + reset: (count = 0) -> + @countPrefix = count @keyState = [@keyMapping] # This tests whether we are in the reset state. It is used to check whether we should be using escape to -- cgit v1.2.3 From f89b58f04af3eae05de9e999c39b2fe047fe6f4a Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 28 Feb 2016 06:48:03 +0000 Subject: Key bindings; more tweaks and fixes. Miscellaneous fixes and tweaks, including: - Reinstate key logging. - Fix count handling in line with expected behaviour in #2024. - Remove `noCount` option; we don't need it. - Simplify logic in various places. Fixes #2024. --- content_scripts/mode.coffee | 11 +---- content_scripts/mode_key_handler.coffee | 72 +++++++++++++-------------------- content_scripts/mode_passkeys.coffee | 13 +++--- content_scripts/vimium_frontend.coffee | 3 +- 4 files changed, 36 insertions(+), 63 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 454a6ed9..efbc9cf4 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -120,20 +120,13 @@ class Mode singletons[key] = this # If @options.trackState is truthy, then the mode mainatins the current state in @enabled and @passKeys, - # and calls @registerStateChange() (if defined) whenever the state changes. The mode also tracks the - # current keyQueue in @keyQueue. + # and calls @registerStateChange() (if defined) whenever the state changes. if @options.trackState @enabled = false @passKeys = "" - @keyQueue = "" @push _name: "mode-#{@id}/registerStateChange" - registerStateChange: ({ enabled: enabled, passKeys: passKeys }) => @alwaysContinueBubbling => - if enabled != @enabled or passKeys != @passKeys - @enabled = enabled - @passKeys = passKeys - @registerStateChange?() - registerKeyQueue: ({ keyQueue: keyQueue }) => @alwaysContinueBubbling => @keyQueue = keyQueue + registerStateChange: ({ enabled: @enabled, passKeys: @passKeys }) => @alwaysContinueBubbling => # If @options.passInitialKeyupEvents is set, then we pass initial non-printable keyup events to the page # or to other extensions (because the corresponding keydown events were passed). This is used when diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 9b044923..c9cab244 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -1,32 +1,25 @@ class KeyHandlerMode extends Mode - useCount: true countPrefix: 0 keydownEvents: {} keyState: [] constructor: (options) -> - # A function accepting a command name and a count; required. @commandHandler = options.commandHandler ? (->) - @useCount = false if options.noCount - @reset() - - # We don't pass these options on to super(). - options = Utils.copyObjectOmittingProperties options, "commandHandler", "keyMapping", "noCount" + @setKeyMapping options.commandHandler ? {} + delete options[option] for option in ["commandHandler", "keyMapping"] super extend options, keydown: @onKeydown.bind this keypress: @onKeypress.bind this keyup: @onKeyup.bind this # We cannot track matching keydown/keyup events if we lose the focus. - blur: (event) => @alwaysContinueBubbling => - @keydownEvents = {} if event.target == window + blur: (event) => @alwaysContinueBubbling => @keydownEvents = {} if event.target == window setKeyMapping: (@keyMapping) -> @reset() onKeydown: (event) -> keyChar = KeyboardUtils.getKeyCharString event - if KeyboardUtils.isEscape event if @isInResetState() @continueBubbling @@ -39,16 +32,14 @@ class KeyHandlerMode extends Mode @handleKeyChar event, keyChar else - # We did not handle the event, but we might handle the subsequent keypress event. If we *will* be - # handling that event, then we need to suppress propagation of this keydown event to prevent triggering - # page features like Google instant search. + # We did not handle the event, but we might handle a subsequent keypress. If we will be handling that + # event, then we suppress propagation of this keydown to prevent triggering page events. keyChar = KeyboardUtils.getKeyChar event if keyChar and (@mappingForKeyChar(keyChar) or @isCountKey keyChar) DomUtils.suppressPropagation event - @keydownEvents[@getEventCode event] = true + @keydownEvents[event.keyCode] = true @stopBubblingAndTrue else - @countPrefix = 0 if keyChar @continueBubbling onKeypress: (event) -> @@ -56,50 +47,46 @@ class KeyHandlerMode extends Mode if keyChar and @mappingForKeyChar keyChar @handleKeyChar event, keyChar else if keyChar and @isCountKey keyChar - @reset @countPrefix * 10 + parseInt keyChar + digit = parseInt keyChar + @reset if @keyState.length == 1 then @countPrefix * 10 + digit else digit false # Suppress event. else + @reset() @continueBubbling onKeyup: (event) -> - eventCode = @getEventCode event - if eventCode of @keydownEvents - delete @keydownEvents[eventCode] + if event.keyCode of @keydownEvents + delete @keydownEvents[event.keyCode] DomUtils.suppressPropagation event @stopBubblingAndTrue else @continueBubbling handleKeyChar: (event, keyChar) -> + bgLog "Handling key #{keyChar}, mode=#{@name}." @advanceKeyState keyChar commands = @keyState.filter (entry) -> entry.command - @invokeCommand commands[0] if 0 < commands.length + if 0 < commands.length + countPrefix = if 0 < @countPrefix then @countPrefix else 1 + @reset() + bgLog "Calling mode=#{@name}, command=#{commands[0].command}, count=#{countPrefix}." + @commandHandler commands[0], countPrefix false # Suppress event. - # This returns the first mapping for which keyChar is mapped. The return value is truthy if a match is found - # and falsy otherwise. + # This returns the first key-state entry for which keyChar is mapped. The return value is truthy if a match + # is found and falsy otherwise. mappingForKeyChar: (keyChar) -> - for mapping in @keyState - return mapping if keyChar of mapping - null + (mapping for mapping in @keyState when keyChar of mapping)[0] - # This is called whenever a keyChar is matched. We keep any existing entries matching keyChar, and append a - # new copy of the global key mappings. + # This is called whenever a keyChar is matched. We keep any existing mappings matching keyChar, and append + # a new copy of the mode's global key mappings. advanceKeyState: (keyChar) -> - newKeyState = - for mapping in @keyState - continue unless keyChar of mapping - mapping[keyChar] - @keyState = [newKeyState..., @keyMapping] - - # This is called to invoke a command and reset the key state. - invokeCommand: (command) -> - countPrefix = if 0 < @countPrefix then @countPrefix else 1 - @reset() - @commandHandler command, countPrefix + newMappings = (mapping[keyChar] for mapping in @keyState when keyChar of mapping) + @keyState = [newMappings..., @keyMapping] # Reset the state (as if no keys had been handled), but retaining the count - if one is provided. reset: (count = 0) -> + bgLog "Clearing key queue, set count=#{count}." @countPrefix = count @keyState = [@keyMapping] @@ -110,18 +97,15 @@ class KeyHandlerMode extends Mode # This tests whether keyChar should be treated as a count key. isCountKey: (keyChar) -> - return false unless @useCount and keyChar.length == 1 + return false unless keyChar.length == 1 if 0 < @countPrefix '0' <= keyChar <= '9' else '1' <= keyChar <= '9' - # True if keyChar would be the first character of a command mapping. This is used by passKeys to decide - # whether keyChar is a continuation of a command which the user has already begin entering. + # Test whether keyChar would be the very first character of a command mapping. isFirstKeyChar: (keyChar) -> - @countPrefix == 0 and @keyMapping == @mappingForKeyChar keyChar - - getEventCode: (event) -> event.keyCode + keyChar and @countPrefix == 0 and (@mappingForKeyChar(keyChar) == @keyMapping or @isCountKey keyChar) root = exports ? window root.KeyHandlerMode = KeyHandlerMode diff --git a/content_scripts/mode_passkeys.coffee b/content_scripts/mode_passkeys.coffee index 426e5369..a68fe3be 100644 --- a/content_scripts/mode_passkeys.coffee +++ b/content_scripts/mode_passkeys.coffee @@ -3,20 +3,17 @@ class PassKeysMode extends Mode constructor: (@normalMode) -> super name: "passkeys" - trackState: true # Maintain @enabled, @passKeys and @keyQueue. + trackState: true # Maintain @passKeys. keydown: (event) => @handleKeyChar event, KeyboardUtils.getKeyChar event keypress: (event) => @handleKeyChar event, String.fromCharCode event.charCode keyup: (event) => @handleKeyChar event, KeyboardUtils.getKeyChar event - # Keystrokes are *never* considered passKeys if the user has begin entering a command. So, for example, if - # 't' is a passKey, then 'gt' and '99t' will neverthless be handled by Vimium. + # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if + # 't' is a passKey, then 'gt' and '99t' are neverthless be handled by Vimium. handleKeyChar: (event, keyChar) -> return @continueBubbling if event.altKey or event.ctrlKey or event.metaKey - return @continueBubbling unless keyChar and keyChar.length == 1 - # Test whether the user has already begun entering a command. - return @continueBubbling unless @normalMode.isFirstKeyChar keyChar - return @continueBubbling unless 0 <= @passKeys.indexOf keyChar - # This is a passkey. + return @continueBubbling unless keyChar and @normalMode.isFirstKeyChar keyChar + return @continueBubbling unless keyChar.length == 1 and 0 <= @passKeys.indexOf keyChar @stopBubblingAndTrue root = exports ? window diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 5c594d1c..3220b084 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -7,7 +7,6 @@ isEnabledForUrl = true isIncognitoMode = chrome.extension.inIncognitoContext -passKeys = null # We track whther the current window has the focus or not. windowIsFocused = do -> @@ -162,7 +161,7 @@ initializePreDomReady = -> return if request.handler and not request.name shouldHandleRequest = isEnabledForUrl # We always handle the message if it's one of these listed message types. - shouldHandleRequest ||= request.name in [ "checkEnabledAfterURLChange" ] + shouldHandleRequest ||= request.name in ["checkEnabledAfterURLChange", "openVomnibar"] sendResponse requestHandlers[request.name](request, sender) if shouldHandleRequest # Ensure the sendResponse callback is freed. false -- cgit v1.2.3 From 6e37f605fe45ee5eca03153c35c55c231e703c95 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 28 Feb 2016 11:07:21 +0000 Subject: Key bindings; minor tweaks. --- content_scripts/mode.coffee | 3 +-- content_scripts/mode_key_handler.coffee | 25 ++++++------------------- 2 files changed, 7 insertions(+), 21 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index efbc9cf4..8a84457e 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -119,8 +119,7 @@ class Mode @deactivateSingleton @options.singleton singletons[key] = this - # If @options.trackState is truthy, then the mode mainatins the current state in @enabled and @passKeys, - # and calls @registerStateChange() (if defined) whenever the state changes. + # If @options.trackState is truthy, then the mode mainatins the current state in @enabled and @passKeys. if @options.trackState @enabled = false @passKeys = "" diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index c9cab244..e38a7051 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -21,16 +21,14 @@ class KeyHandlerMode extends Mode onKeydown: (event) -> keyChar = KeyboardUtils.getKeyCharString event if KeyboardUtils.isEscape event - if @isInResetState() + if @countPrefix == 0 and @keyState.length == 1 @continueBubbling else @reset() DomUtils.suppressKeyupAfterEscape handlerStack false # Suppress event. - else if keyChar and @mappingForKeyChar keyChar @handleKeyChar event, keyChar - else # We did not handle the event, but we might handle a subsequent keypress. If we will be handling that # event, then we suppress propagation of this keydown to prevent triggering page events. @@ -84,26 +82,15 @@ class KeyHandlerMode extends Mode newMappings = (mapping[keyChar] for mapping in @keyState when keyChar of mapping) @keyState = [newMappings..., @keyMapping] - # Reset the state (as if no keys had been handled), but retaining the count - if one is provided. - reset: (count = 0) -> - bgLog "Clearing key queue, set count=#{count}." - @countPrefix = count + # Reset the state (as if no keys had been handled), but optionally retaining the count provided. + reset: (@countPrefix = 0) -> + bgLog "Clearing key queue, set count=#{@countPrefix}." @keyState = [@keyMapping] - # This tests whether we are in the reset state. It is used to check whether we should be using escape to - # reset the key state, or passing it to the page. - isInResetState: -> - @countPrefix == 0 and @keyState.length == 1 - - # This tests whether keyChar should be treated as a count key. isCountKey: (keyChar) -> - return false unless keyChar.length == 1 - if 0 < @countPrefix - '0' <= keyChar <= '9' - else - '1' <= keyChar <= '9' + keyChar.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' - # Test whether keyChar would be the very first character of a command mapping. + # This tests whether keyChar would be the very first character of a command mapping. isFirstKeyChar: (keyChar) -> keyChar and @countPrefix == 0 and (@mappingForKeyChar(keyChar) == @keyMapping or @isCountKey keyChar) -- cgit v1.2.3 From cf7a03dc26415528bc690147ba72c08e67dd65c8 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 28 Feb 2016 11:54:52 +0000 Subject: Key bindings; more minor tweaks. --- content_scripts/vimium_frontend.coffee | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 3220b084..2fc2dc35 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -117,15 +117,14 @@ window.initializeModes = -> if registryEntry.repeatLimit? and registryEntry.repeatLimit < count return unless confirm """ You have asked Vimium to perform #{count} repeats of the command: #{registryEntry.description}.\n - Are you sure you want to continue? """ + Are you sure you want to continue?""" if registryEntry.isBackgroundCommand - chrome.runtime.sendMessage { handler: "runBackgroundCommand", frameId, registryEntry, count} + chrome.runtime.sendMessage {handler: "runBackgroundCommand", frameId, registryEntry, count} + else if registryEntry.passCountToFunction + Utils.invokeCommandString registryEntry.command, [count] else - if registryEntry.passCountToFunction - Utils.invokeCommandString registryEntry.command, [count] - else - Utils.invokeCommandString registryEntry.command for i in [0...count] + Utils.invokeCommandString registryEntry.command for i in [0...count] # Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and # activates/deactivates itself accordingly. -- cgit v1.2.3 From 520b63cb1d64fb5a293988122007bd05bacc49db Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 28 Feb 2016 14:01:16 +0000 Subject: Key bindings; fix tests... ... and fix two bugs: - not suppressing keyup event after keyChar matched in keydown. - we cannot check the passKeys keyChar in keyup because the key state has changed; so we track what the next keyup response should be. --- content_scripts/mode_key_handler.coffee | 5 ++- content_scripts/mode_passkeys.coffee | 7 +++- content_scripts/vimium_frontend.coffee | 67 ++++++++++++++++----------------- 3 files changed, 42 insertions(+), 37 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index e38a7051..f7be977b 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -6,7 +6,7 @@ class KeyHandlerMode extends Mode constructor: (options) -> @commandHandler = options.commandHandler ? (->) - @setKeyMapping options.commandHandler ? {} + @setKeyMapping options.keyMapping ? {} delete options[option] for option in ["commandHandler", "keyMapping"] super extend options, @@ -28,7 +28,10 @@ class KeyHandlerMode extends Mode DomUtils.suppressKeyupAfterEscape handlerStack false # Suppress event. else if keyChar and @mappingForKeyChar keyChar + @keydownEvents[event.keyCode] = true @handleKeyChar event, keyChar + else if keyChar + @continueBubbling else # We did not handle the event, but we might handle a subsequent keypress. If we will be handling that # event, then we suppress propagation of this keydown to prevent triggering page events. diff --git a/content_scripts/mode_passkeys.coffee b/content_scripts/mode_passkeys.coffee index a68fe3be..7efa559c 100644 --- a/content_scripts/mode_passkeys.coffee +++ b/content_scripts/mode_passkeys.coffee @@ -1,12 +1,15 @@ class PassKeysMode extends Mode constructor: (@normalMode) -> + @nextKeyup = @continueBubbling super name: "passkeys" trackState: true # Maintain @passKeys. keydown: (event) => @handleKeyChar event, KeyboardUtils.getKeyChar event keypress: (event) => @handleKeyChar event, String.fromCharCode event.charCode - keyup: (event) => @handleKeyChar event, KeyboardUtils.getKeyChar event + keyup: => ([_, @nextKeyup] = [@nextKeyup, @continueBubbling])[0] + # We cannot track keyup events if we lose the focus. + blur: (event) => @alwaysContinueBubbling => @nextKeyup = @continueBubbling if event.target == window # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if # 't' is a passKey, then 'gt' and '99t' are neverthless be handled by Vimium. @@ -14,7 +17,7 @@ class PassKeysMode extends Mode return @continueBubbling if event.altKey or event.ctrlKey or event.metaKey return @continueBubbling unless keyChar and @normalMode.isFirstKeyChar keyChar return @continueBubbling unless keyChar.length == 1 and 0 <= @passKeys.indexOf keyChar - @stopBubblingAndTrue + @nextKeyup = @stopBubblingAndTrue root = exports ? window root.PassKeysMode = PassKeysMode diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 2fc2dc35..e821b136 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -93,42 +93,41 @@ handlerStack.push target = target.parentElement true -# Only exported for tests. -window.initializeModes = -> - class NormalMode extends KeyHandlerMode - constructor: -> - super - name: "normal" - indicator: false # There is no mode indicator in normal mode. - commandHandler: @commandHandler.bind this - keyMapping: {} - - chrome.storage.local.get "normalModeKeyStateMapping", (items) => - @setKeyMapping items.normalModeKeyStateMapping - - chrome.storage.onChanged.addListener (changes, area) => - if area == "local" and changes.normalModeKeyStateMapping?.newValue - @setKeyMapping changes.normalModeKeyStateMapping.newValue - - commandHandler: (registryEntry, count) -> - count *= registryEntry.options.count ? 1 - count = 1 if registryEntry.noRepeat - - if registryEntry.repeatLimit? and registryEntry.repeatLimit < count - return unless confirm """ - You have asked Vimium to perform #{count} repeats of the command: #{registryEntry.description}.\n - Are you sure you want to continue?""" - - if registryEntry.isBackgroundCommand - chrome.runtime.sendMessage {handler: "runBackgroundCommand", frameId, registryEntry, count} - else if registryEntry.passCountToFunction - Utils.invokeCommandString registryEntry.command, [count] - else - Utils.invokeCommandString registryEntry.command for i in [0...count] - +class NormalMode extends KeyHandlerMode + constructor: (options = {}) -> + super extend options, + name: "normal" + indicator: false # There is no mode indicator in normal mode. + commandHandler: @commandHandler.bind this + + chrome.storage.local.get "normalModeKeyStateMapping", (items) => + @setKeyMapping items.normalModeKeyStateMapping + + chrome.storage.onChanged.addListener (changes, area) => + if area == "local" and changes.normalModeKeyStateMapping?.newValue + @setKeyMapping changes.normalModeKeyStateMapping.newValue + + commandHandler: (registryEntry, count) -> + count *= registryEntry.options.count ? 1 + count = 1 if registryEntry.noRepeat + + if registryEntry.repeatLimit? and registryEntry.repeatLimit < count + return unless confirm """ + You have asked Vimium to perform #{count} repeats of the command: #{registryEntry.description}.\n + Are you sure you want to continue?""" + + if registryEntry.isBackgroundCommand + chrome.runtime.sendMessage {handler: "runBackgroundCommand", frameId, registryEntry, count} + else if registryEntry.passCountToFunction + Utils.invokeCommandString registryEntry.command, [count] + else + Utils.invokeCommandString registryEntry.command for i in [0...count] + +# Only exported for tests; also, "args..." is only for the tests. +window.initializeModes = (args...) -> # Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and # activates/deactivates itself accordingly. - normalMode = new NormalMode + normalMode = new NormalMode args... new PassKeysMode normalMode new InsertMode permanent: true Scroller.init() -- cgit v1.2.3 From b63683a1026e12bfd7bff6b8745d18e3b858bf92 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 28 Feb 2016 14:13:52 +0000 Subject: Key bindings; yet more minor tweaks. --- content_scripts/mode_key_handler.coffee | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index f7be977b..265ceed1 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -32,16 +32,14 @@ class KeyHandlerMode extends Mode @handleKeyChar event, keyChar else if keyChar @continueBubbling - else + else if (keyChar = KeyboardUtils.getKeyChar event) and (@mappingForKeyChar(keyChar) or @isCountKey keyChar) # We did not handle the event, but we might handle a subsequent keypress. If we will be handling that # event, then we suppress propagation of this keydown to prevent triggering page events. - keyChar = KeyboardUtils.getKeyChar event - if keyChar and (@mappingForKeyChar(keyChar) or @isCountKey keyChar) - DomUtils.suppressPropagation event - @keydownEvents[event.keyCode] = true - @stopBubblingAndTrue - else - @continueBubbling + DomUtils.suppressPropagation event + @keydownEvents[event.keyCode] = true + @stopBubblingAndTrue + else + @continueBubbling onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event -- cgit v1.2.3 From b0d2f3bfbc36232c235f913131c78a5bb76b59c3 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 28 Feb 2016 15:35:10 +0000 Subject: Key bindings; and yet more minor tweaks. --- content_scripts/mode_key_handler.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 265ceed1..97966644 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -93,7 +93,7 @@ class KeyHandlerMode extends Mode # This tests whether keyChar would be the very first character of a command mapping. isFirstKeyChar: (keyChar) -> - keyChar and @countPrefix == 0 and (@mappingForKeyChar(keyChar) == @keyMapping or @isCountKey keyChar) + @countPrefix == 0 and (@mappingForKeyChar(keyChar) == @keyMapping or @isCountKey keyChar) root = exports ? window root.KeyHandlerMode = KeyHandlerMode -- cgit v1.2.3 From f2bced459457dcc962d4bafe2fdf2e6245506ee3 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 28 Feb 2016 16:07:25 +0000 Subject: Key bindings; tweaks. --- content_scripts/mode.coffee | 2 +- content_scripts/mode_key_handler.coffee | 18 +++++++----------- content_scripts/vimium_frontend.coffee | 4 ++-- 3 files changed, 10 insertions(+), 14 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 8a84457e..3327b58f 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -125,7 +125,7 @@ class Mode @passKeys = "" @push _name: "mode-#{@id}/registerStateChange" - registerStateChange: ({ enabled: @enabled, passKeys: @passKeys }) => @alwaysContinueBubbling => + registerStateChange: ({ enabled: @enabled, passKeys: @passKeys }) => @continueBubbling # If @options.passInitialKeyupEvents is set, then we pass initial non-printable keyup events to the page # or to other extensions (because the corresponding keydown events were passed). This is used when diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 97966644..567552c0 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -1,31 +1,27 @@ class KeyHandlerMode extends Mode - countPrefix: 0 keydownEvents: {} - keyState: [] + setKeyMapping: (@keyMapping) -> @reset() constructor: (options) -> @commandHandler = options.commandHandler ? (->) @setKeyMapping options.keyMapping ? {} - delete options[option] for option in ["commandHandler", "keyMapping"] super extend options, keydown: @onKeydown.bind this keypress: @onKeypress.bind this keyup: @onKeyup.bind this - # We cannot track matching keydown/keyup events if we lose the focus. + # We cannot track keyup events if we lose the focus. blur: (event) => @alwaysContinueBubbling => @keydownEvents = {} if event.target == window - setKeyMapping: (@keyMapping) -> @reset() - onKeydown: (event) -> keyChar = KeyboardUtils.getKeyCharString event if KeyboardUtils.isEscape event if @countPrefix == 0 and @keyState.length == 1 @continueBubbling else + @keydownEvents[event.keyCode] = true @reset() - DomUtils.suppressKeyupAfterEscape handlerStack false # Suppress event. else if keyChar and @mappingForKeyChar keyChar @keydownEvents[event.keyCode] = true @@ -33,10 +29,10 @@ class KeyHandlerMode extends Mode else if keyChar @continueBubbling else if (keyChar = KeyboardUtils.getKeyChar event) and (@mappingForKeyChar(keyChar) or @isCountKey keyChar) - # We did not handle the event, but we might handle a subsequent keypress. If we will be handling that - # event, then we suppress propagation of this keydown to prevent triggering page events. - DomUtils.suppressPropagation event + # We will probably 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 + DomUtils.suppressPropagation event @stopBubblingAndTrue else @continueBubbling @@ -69,7 +65,7 @@ class KeyHandlerMode extends Mode countPrefix = if 0 < @countPrefix then @countPrefix else 1 @reset() bgLog "Calling mode=#{@name}, command=#{commands[0].command}, count=#{countPrefix}." - @commandHandler commands[0], countPrefix + @commandHandler command: commands[0], count: countPrefix, event: event false # Suppress event. # This returns the first key-state entry for which keyChar is mapped. The return value is truthy if a match diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index e821b136..ec666a23 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -107,13 +107,13 @@ class NormalMode extends KeyHandlerMode if area == "local" and changes.normalModeKeyStateMapping?.newValue @setKeyMapping changes.normalModeKeyStateMapping.newValue - commandHandler: (registryEntry, count) -> + commandHandler: ({command: registryEntry, count, event}) -> count *= registryEntry.options.count ? 1 count = 1 if registryEntry.noRepeat if registryEntry.repeatLimit? and registryEntry.repeatLimit < count return unless confirm """ - You have asked Vimium to perform #{count} repeats of the command: #{registryEntry.description}.\n + You have asked Vimium to perform #{count} repetitions of the command: #{registryEntry.description}.\n Are you sure you want to continue?""" if registryEntry.isBackgroundCommand -- cgit v1.2.3 From 9cfa00bc7db3e07c3abbeb09e483d9fdf20bbc17 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 29 Feb 2016 06:07:01 +0000 Subject: Key bindings; refactor passKeys. Previously, the key-handling logic (keyQueue, etc) was and the backend whereas passKeys were handled in the content scripts - so they were a long way apart. Now that they're in the same place, it makes more sense to integrate passKey handling into the regular key handling, because they depend upon the same data structures. --- content_scripts/mode_key_handler.coffee | 42 ++++++++++++++++++++------------- content_scripts/mode_passkeys.coffee | 23 ------------------ content_scripts/vimium_frontend.coffee | 4 ++-- 3 files changed, 27 insertions(+), 42 deletions(-) delete mode 100644 content_scripts/mode_passkeys.coffee (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 567552c0..60633895 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -24,30 +24,33 @@ class KeyHandlerMode extends Mode @reset() false # Suppress event. else if keyChar and @mappingForKeyChar keyChar - @keydownEvents[event.keyCode] = true - @handleKeyChar event, keyChar + @unlessKeyCharIsPassKey keyChar, => + @keydownEvents[event.keyCode] = true + @handleKeyChar event, keyChar else if keyChar @continueBubbling else if (keyChar = KeyboardUtils.getKeyChar event) and (@mappingForKeyChar(keyChar) or @isCountKey keyChar) # We will probably 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 - DomUtils.suppressPropagation event - @stopBubblingAndTrue + @unlessKeyCharIsPassKey keyChar, => + @keydownEvents[event.keyCode] = true + DomUtils.suppressPropagation event + @stopBubblingAndTrue else @continueBubbling onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event - if keyChar and @mappingForKeyChar keyChar - @handleKeyChar event, keyChar - else if keyChar and @isCountKey keyChar - digit = parseInt keyChar - @reset if @keyState.length == 1 then @countPrefix * 10 + digit else digit - false # Suppress event. - else - @reset() - @continueBubbling + @unlessKeyCharIsPassKey keyChar, => + if keyChar and @mappingForKeyChar keyChar + @handleKeyChar event, keyChar + else if keyChar and @isCountKey keyChar + digit = parseInt keyChar + @reset if @keyState.length == 1 then @countPrefix * 10 + digit else digit + false # Suppress event. + else + @reset() + @continueBubbling onKeyup: (event) -> if event.keyCode of @keydownEvents @@ -87,9 +90,14 @@ class KeyHandlerMode extends Mode isCountKey: (keyChar) -> keyChar.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' - # This tests whether keyChar would be the very first character of a command mapping. - isFirstKeyChar: (keyChar) -> - @countPrefix == 0 and (@mappingForKeyChar(keyChar) == @keyMapping or @isCountKey keyChar) + # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if + # 't' is a passKey, then 'gt' and '99t' are neverthless handled as regular keys. + unlessKeyCharIsPassKey: (keyChar, nonPassKeyCallback) -> + if (@passKeys? and keyChar?.length == 1 and 0 <= @passKeys.indexOf(keyChar) and + @countPrefix == 0 and @keyState.length == 1) + @stopBubblingAndTrue + else + nonPassKeyCallback() root = exports ? window root.KeyHandlerMode = KeyHandlerMode diff --git a/content_scripts/mode_passkeys.coffee b/content_scripts/mode_passkeys.coffee deleted file mode 100644 index 7efa559c..00000000 --- a/content_scripts/mode_passkeys.coffee +++ /dev/null @@ -1,23 +0,0 @@ - -class PassKeysMode extends Mode - constructor: (@normalMode) -> - @nextKeyup = @continueBubbling - super - name: "passkeys" - trackState: true # Maintain @passKeys. - keydown: (event) => @handleKeyChar event, KeyboardUtils.getKeyChar event - keypress: (event) => @handleKeyChar event, String.fromCharCode event.charCode - keyup: => ([_, @nextKeyup] = [@nextKeyup, @continueBubbling])[0] - # We cannot track keyup events if we lose the focus. - blur: (event) => @alwaysContinueBubbling => @nextKeyup = @continueBubbling if event.target == window - - # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if - # 't' is a passKey, then 'gt' and '99t' are neverthless be handled by Vimium. - handleKeyChar: (event, keyChar) -> - return @continueBubbling if event.altKey or event.ctrlKey or event.metaKey - return @continueBubbling unless keyChar and @normalMode.isFirstKeyChar keyChar - return @continueBubbling unless keyChar.length == 1 and 0 <= @passKeys.indexOf keyChar - @nextKeyup = @stopBubblingAndTrue - -root = exports ? window -root.PassKeysMode = PassKeysMode diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index ec666a23..46b21ed0 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -98,6 +98,7 @@ class NormalMode extends KeyHandlerMode super extend options, name: "normal" indicator: false # There is no mode indicator in normal mode. + trackState: true # Maintain @passKeys. commandHandler: @commandHandler.bind this chrome.storage.local.get "normalModeKeyStateMapping", (items) => @@ -127,8 +128,7 @@ class NormalMode extends KeyHandlerMode window.initializeModes = (args...) -> # Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and # activates/deactivates itself accordingly. - normalMode = new NormalMode args... - new PassKeysMode normalMode + new NormalMode args... new InsertMode permanent: true Scroller.init() -- cgit v1.2.3 From e4193e2752ee7132ff16a7ba977857f70df2946b Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 29 Feb 2016 06:31:43 +0000 Subject: Key bindings; tweaks. --- content_scripts/mode_key_handler.coffee | 10 +++++----- content_scripts/vimium_frontend.coffee | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 60633895..8c4ded43 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -63,12 +63,12 @@ class KeyHandlerMode extends Mode handleKeyChar: (event, keyChar) -> bgLog "Handling key #{keyChar}, mode=#{@name}." @advanceKeyState keyChar - commands = @keyState.filter (entry) -> entry.command - if 0 < commands.length - countPrefix = if 0 < @countPrefix then @countPrefix else 1 + command = (@keyState.filter (entry) -> entry.command)[0] + if command? + count = if 0 < @countPrefix then @countPrefix else 1 @reset() - bgLog "Calling mode=#{@name}, command=#{commands[0].command}, count=#{countPrefix}." - @commandHandler command: commands[0], count: countPrefix, event: event + bgLog "Calling mode=#{@name}, command=#{command.command}, count=#{count}." + @commandHandler {command, count} false # Suppress event. # This returns the first key-state entry for which keyChar is mapped. The return value is truthy if a match diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 46b21ed0..a4dd6986 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -108,7 +108,7 @@ class NormalMode extends KeyHandlerMode if area == "local" and changes.normalModeKeyStateMapping?.newValue @setKeyMapping changes.normalModeKeyStateMapping.newValue - commandHandler: ({command: registryEntry, count, event}) -> + commandHandler: ({command: registryEntry, count}) -> count *= registryEntry.options.count ? 1 count = 1 if registryEntry.noRepeat -- cgit v1.2.3 From 29708b3297a645966f7943682f8f7b8c2a332297 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 29 Feb 2016 06:48:27 +0000 Subject: Key bindings; refactor passKeys distribution. It makes more sense to pass the passKeys directly to normalMode. So, do so, and remove the trackState mode option - which isn't otherwise being used. --- content_scripts/mode.coffee | 8 -------- content_scripts/vimium_frontend.coffee | 11 +++++------ 2 files changed, 5 insertions(+), 14 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 3327b58f..205b8288 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -119,14 +119,6 @@ class Mode @deactivateSingleton @options.singleton singletons[key] = this - # If @options.trackState is truthy, then the mode mainatins the current state in @enabled and @passKeys. - if @options.trackState - @enabled = false - @passKeys = "" - @push - _name: "mode-#{@id}/registerStateChange" - registerStateChange: ({ enabled: @enabled, passKeys: @passKeys }) => @continueBubbling - # If @options.passInitialKeyupEvents is set, then we pass initial non-printable keyup events to the page # or to other extensions (because the corresponding keydown events were passed). This is used when # activating link hints, see #1522. diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index a4dd6986..5401cf22 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -94,11 +94,12 @@ handlerStack.push true class NormalMode extends KeyHandlerMode + setPassKeys: (@passKeys) -> + constructor: (options = {}) -> super extend options, name: "normal" indicator: false # There is no mode indicator in normal mode. - trackState: true # Maintain @passKeys. commandHandler: @commandHandler.bind this chrome.storage.local.get "normalModeKeyStateMapping", (items) => @@ -127,8 +128,8 @@ class NormalMode extends KeyHandlerMode # Only exported for tests; also, "args..." is only for the tests. window.initializeModes = (args...) -> # Install the permanent modes. The permanently-installed insert mode tracks focus/blur events, and - # activates/deactivates itself accordingly. - new NormalMode args... + # activates/deactivates itself accordingly. normalMode is exported only for the tests. + window.normalMode = new NormalMode args... new InsertMode permanent: true Scroller.init() @@ -440,9 +441,7 @@ checkIfEnabledForUrl = (frameIsFocused = windowIsFocused()) -> if HUD.isReady() and not isEnabledForUrl # Quickly hide any HUD we might already be showing, e.g. if we entered insert mode on page load. HUD.hide() - handlerStack.bubbleEvent "registerStateChange", - enabled: isEnabledForUrl - passKeys: passKeys + normalMode?.setPassKeys passKeys # Update the page icon, if necessary. if windowIsFocused() chrome.runtime.sendMessage -- cgit v1.2.3 From 70151ea99cb86bbbf268ecb737337aa7cc266e2d Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 29 Feb 2016 07:23:31 +0000 Subject: Key bindings; and yet more tweaks. --- content_scripts/mode_key_handler.coffee | 28 +++++++++++++--------------- content_scripts/vimium_frontend.coffee | 2 -- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 8c4ded43..ecfcacd6 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -2,6 +2,7 @@ class KeyHandlerMode extends Mode keydownEvents: {} setKeyMapping: (@keyMapping) -> @reset() + setPassKeys: (@passKeys) -> constructor: (options) -> @commandHandler = options.commandHandler ? (->) @@ -23,15 +24,15 @@ class KeyHandlerMode extends Mode @keydownEvents[event.keyCode] = true @reset() false # Suppress event. - else if keyChar and @mappingForKeyChar keyChar + else if keyChar and @keyCharIsMapped keyChar @unlessKeyCharIsPassKey keyChar, => @keydownEvents[event.keyCode] = true @handleKeyChar event, keyChar else if keyChar @continueBubbling - else if (keyChar = KeyboardUtils.getKeyChar event) and (@mappingForKeyChar(keyChar) or @isCountKey keyChar) - # We will probably be handling a subsequent keypress event, so suppress propagation of this event to - # prevent triggering page event listeners (e.g. Google instant Search). + else if (keyChar = KeyboardUtils.getKeyChar event) and (@keyCharIsMapped(keyChar) or @isCountKey keyChar) + # It looks like we will be handling a subsequent keypress event, so suppress propagation of this event + # to prevent triggering page event listeners (e.g. Google instant Search). @unlessKeyCharIsPassKey keyChar, => @keydownEvents[event.keyCode] = true DomUtils.suppressPropagation event @@ -42,9 +43,9 @@ class KeyHandlerMode extends Mode onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event @unlessKeyCharIsPassKey keyChar, => - if keyChar and @mappingForKeyChar keyChar + if keyChar and @keyCharIsMapped keyChar @handleKeyChar event, keyChar - else if keyChar and @isCountKey keyChar + else if @isCountKey keyChar digit = parseInt keyChar @reset if @keyState.length == 1 then @countPrefix * 10 + digit else digit false # Suppress event. @@ -71,13 +72,10 @@ class KeyHandlerMode extends Mode @commandHandler {command, count} false # Suppress event. - # This returns the first key-state entry for which keyChar is mapped. The return value is truthy if a match - # is found and falsy otherwise. - mappingForKeyChar: (keyChar) -> - (mapping for mapping in @keyState when keyChar of mapping)[0] + keyCharIsMapped: (keyChar) -> + (mapping for mapping in @keyState when keyChar of mapping)[0]? - # This is called whenever a keyChar is matched. We keep any existing mappings matching keyChar, and append - # a new copy of the mode's global key mappings. + # The next key state is the current mappings matching keyChar plus @keyMapping. advanceKeyState: (keyChar) -> newMappings = (mapping[keyChar] for mapping in @keyState when keyChar of mapping) @keyState = [newMappings..., @keyMapping] @@ -88,13 +86,13 @@ class KeyHandlerMode extends Mode @keyState = [@keyMapping] isCountKey: (keyChar) -> - keyChar.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' + keyChar?.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if # 't' is a passKey, then 'gt' and '99t' are neverthless handled as regular keys. unlessKeyCharIsPassKey: (keyChar, nonPassKeyCallback) -> - if (@passKeys? and keyChar?.length == 1 and 0 <= @passKeys.indexOf(keyChar) and - @countPrefix == 0 and @keyState.length == 1) + if @passKeys and @countPrefix == 0 and @keyState.length == 1 and + keyChar?.length == 1 and 0 <= @passKeys.indexOf keyChar @stopBubblingAndTrue else nonPassKeyCallback() diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 5401cf22..01b02985 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -94,8 +94,6 @@ handlerStack.push true class NormalMode extends KeyHandlerMode - setPassKeys: (@passKeys) -> - constructor: (options = {}) -> super extend options, name: "normal" -- cgit v1.2.3 From 7bf8cb11db08c61417a4d72b95c2905a4869cf67 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 29 Feb 2016 11:53:05 +0000 Subject: Key bindings; small tweaks... - simplify pass key condition - don't keep key-parsing Regexp in memory - we should reset the key state when the pass keys change --- content_scripts/mode_key_handler.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index ecfcacd6..78ce3680 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -2,7 +2,7 @@ class KeyHandlerMode extends Mode keydownEvents: {} setKeyMapping: (@keyMapping) -> @reset() - setPassKeys: (@passKeys) -> + setPassKeys: (@passKeys) -> @reset() constructor: (options) -> @commandHandler = options.commandHandler ? (->) @@ -91,8 +91,7 @@ class KeyHandlerMode extends Mode # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if # 't' is a passKey, then 'gt' and '99t' are neverthless handled as regular keys. unlessKeyCharIsPassKey: (keyChar, nonPassKeyCallback) -> - if @passKeys and @countPrefix == 0 and @keyState.length == 1 and - keyChar?.length == 1 and 0 <= @passKeys.indexOf keyChar + if @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "") @stopBubblingAndTrue else nonPassKeyCallback() -- cgit v1.2.3 From ff6d4b92df924b93de4366b1f0e3cc7411ac8065 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 29 Feb 2016 15:43:26 +0000 Subject: Key bindings; more small tweaks. --- content_scripts/mode_key_handler.coffee | 66 +++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 28 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 78ce3680..df177c4a 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -1,9 +1,26 @@ +# Example key mapping (@keyMapping): +# i: +# command: "enterInsertMode", ... # This is a registryEntry object (as too are the other commands). +# g: +# g: +# command: "scrollToTop", ... +# t: +# command: "nextTab", ... +# +# This key-mapping structure is generated by Commands.generateKeyStateMapping() and may be arbitrarily deep. +# Observe that @keyMapping["g"] is itself also a valid key mapping. At any point, the key state (@keyState) +# consists of a (non-empty) list of such mappings. class KeyHandlerMode extends Mode keydownEvents: {} setKeyMapping: (@keyMapping) -> @reset() setPassKeys: (@passKeys) -> @reset() + # Reset the key state, optionally retaining the count provided. + reset: (@countPrefix = 0) -> + bgLog "Clearing key state, set count=#{@countPrefix}." + @keyState = [@keyMapping] + constructor: (options) -> @commandHandler = options.commandHandler ? (->) @setKeyMapping options.keyMapping ? {} @@ -17,22 +34,22 @@ class KeyHandlerMode extends Mode onKeydown: (event) -> keyChar = KeyboardUtils.getKeyCharString event - if KeyboardUtils.isEscape event - if @countPrefix == 0 and @keyState.length == 1 - @continueBubbling - else - @keydownEvents[event.keyCode] = true - @reset() - false # Suppress event. - else if keyChar and @keyCharIsMapped keyChar + isEscape = KeyboardUtils.isEscape event + if isEscape and @countPrefix == 0 and @keyState.length == 1 + @continueBubbling + else if isEscape + @keydownEvents[event.keyCode] = true + @reset() + false # Suppress event. + else if @keyCharIsMapped keyChar @unlessKeyCharIsPassKey keyChar, => @keydownEvents[event.keyCode] = true @handleKeyChar event, keyChar else if keyChar @continueBubbling else if (keyChar = KeyboardUtils.getKeyChar event) and (@keyCharIsMapped(keyChar) or @isCountKey keyChar) - # It looks like we will be handling a subsequent keypress event, so suppress propagation of this event - # to prevent triggering page event listeners (e.g. Google instant Search). + # It looks like 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). @unlessKeyCharIsPassKey keyChar, => @keydownEvents[event.keyCode] = true DomUtils.suppressPropagation event @@ -43,7 +60,7 @@ class KeyHandlerMode extends Mode onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event @unlessKeyCharIsPassKey keyChar, => - if keyChar and @keyCharIsMapped keyChar + if @keyCharIsMapped keyChar @handleKeyChar event, keyChar else if @isCountKey keyChar digit = parseInt keyChar @@ -61,35 +78,28 @@ class KeyHandlerMode extends Mode else @continueBubbling + # This tests whether there is a mapping of keyChar in the current key state. + keyCharIsMapped: (keyChar) -> + (mapping for mapping in @keyState when keyChar of mapping)[0]? + handleKeyChar: (event, keyChar) -> bgLog "Handling key #{keyChar}, mode=#{@name}." - @advanceKeyState keyChar - command = (@keyState.filter (entry) -> entry.command)[0] + # Advance the key state. The new key state is the current mappings of keyChar, plus @keyMapping. + @keyState = (mapping[keyChar] for mapping in @keyState when keyChar of mapping) + @keyState.push @keyMapping + command = (mapping for mapping in @keyState when mapping.command)[0] if command? count = if 0 < @countPrefix then @countPrefix else 1 - @reset() bgLog "Calling mode=#{@name}, command=#{command.command}, count=#{count}." + @reset() @commandHandler {command, count} false # Suppress event. - keyCharIsMapped: (keyChar) -> - (mapping for mapping in @keyState when keyChar of mapping)[0]? - - # The next key state is the current mappings matching keyChar plus @keyMapping. - advanceKeyState: (keyChar) -> - newMappings = (mapping[keyChar] for mapping in @keyState when keyChar of mapping) - @keyState = [newMappings..., @keyMapping] - - # Reset the state (as if no keys had been handled), but optionally retaining the count provided. - reset: (@countPrefix = 0) -> - bgLog "Clearing key queue, set count=#{@countPrefix}." - @keyState = [@keyMapping] - isCountKey: (keyChar) -> keyChar?.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if - # 't' is a passKey, then 'gt' and '99t' are neverthless handled as regular keys. + # 't' is a passKey, then the "t"-s of 'gt' and '99t' are neverthless handled as regular keys. unlessKeyCharIsPassKey: (keyChar, nonPassKeyCallback) -> if @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "") @stopBubblingAndTrue -- cgit v1.2.3 From 201451d94edefa8c873d417f5f6190d993204b5e Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 1 Mar 2016 13:13:08 +0000 Subject: Key bindings; move Vomnibar commands back to content scripts. --- content_scripts/vimium_frontend.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'content_scripts') diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 01b02985..72be5510 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -116,7 +116,11 @@ class NormalMode extends KeyHandlerMode You have asked Vimium to perform #{count} repetitions of the command: #{registryEntry.description}.\n Are you sure you want to continue?""" - if registryEntry.isBackgroundCommand + # The Vomnibar needs special handling because it is always activated in the tab's main frame. + if registryEntry.command.startsWith "Vomnibar." + chrome.runtime.sendMessage + handler: "sendMessageToFrames", message: {name: "openVomnibar", sourceFrameId: frameId, registryEntry} + else if registryEntry.isBackgroundCommand chrome.runtime.sendMessage {handler: "runBackgroundCommand", frameId, registryEntry, count} else if registryEntry.passCountToFunction Utils.invokeCommandString registryEntry.command, [count] -- cgit v1.2.3 From 91498d457f4398ab467fda4ec67b7444b1335cd3 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 1 Mar 2016 13:26:35 +0000 Subject: Key bindings; and yet more tiny tweaks. --- content_scripts/mode_key_handler.coffee | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index df177c4a..397e64c8 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -45,11 +45,10 @@ class KeyHandlerMode extends Mode @unlessKeyCharIsPassKey keyChar, => @keydownEvents[event.keyCode] = true @handleKeyChar event, keyChar - else if keyChar - @continueBubbling - else if (keyChar = KeyboardUtils.getKeyChar event) and (@keyCharIsMapped(keyChar) or @isCountKey keyChar) - # It looks like 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). + else if not keyChar and (keyChar = KeyboardUtils.getKeyChar event) and + (@keyCharIsMapped(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). @unlessKeyCharIsPassKey keyChar, => @keydownEvents[event.keyCode] = true DomUtils.suppressPropagation event @@ -71,12 +70,10 @@ class KeyHandlerMode extends Mode @continueBubbling onKeyup: (event) -> - if event.keyCode of @keydownEvents - delete @keydownEvents[event.keyCode] - DomUtils.suppressPropagation event - @stopBubblingAndTrue - else - @continueBubbling + return @continueBubbling unless event.keyCode of @keydownEvents + delete @keydownEvents[event.keyCode] + DomUtils.suppressPropagation event + @stopBubblingAndTrue # This tests whether there is a mapping of keyChar in the current key state. keyCharIsMapped: (keyChar) -> -- cgit v1.2.3 From 91bcedbf4aebbd2691ef68ca96094206a942a1b8 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Wed, 2 Mar 2016 08:44:42 +0000 Subject: Key bindings; simplify pass-key logic. --- content_scripts/mode_key_handler.coffee | 45 +++++++++++++++------------------ 1 file changed, 20 insertions(+), 25 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 397e64c8..fc4d485e 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -42,32 +42,29 @@ class KeyHandlerMode extends Mode @reset() false # Suppress event. else if @keyCharIsMapped keyChar - @unlessKeyCharIsPassKey keyChar, => - @keydownEvents[event.keyCode] = true - @handleKeyChar event, keyChar + @keydownEvents[event.keyCode] = true + @handleKeyChar event, keyChar else if not keyChar and (keyChar = KeyboardUtils.getKeyChar event) and (@keyCharIsMapped(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). - @unlessKeyCharIsPassKey keyChar, => - @keydownEvents[event.keyCode] = true - DomUtils.suppressPropagation event - @stopBubblingAndTrue + @keydownEvents[event.keyCode] = true + DomUtils.suppressPropagation event + @stopBubblingAndTrue else @continueBubbling onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event - @unlessKeyCharIsPassKey keyChar, => - if @keyCharIsMapped keyChar - @handleKeyChar event, keyChar - else if @isCountKey keyChar - digit = parseInt keyChar - @reset if @keyState.length == 1 then @countPrefix * 10 + digit else digit - false # Suppress event. - else - @reset() - @continueBubbling + if @keyCharIsMapped keyChar + @handleKeyChar event, keyChar + else if @isCountKey keyChar + digit = parseInt keyChar + @reset if @keyState.length == 1 then @countPrefix * 10 + digit else digit + false # Suppress event. + else + @reset() + @continueBubbling onKeyup: (event) -> return @continueBubbling unless event.keyCode of @keydownEvents @@ -75,9 +72,9 @@ class KeyHandlerMode extends Mode DomUtils.suppressPropagation event @stopBubblingAndTrue - # This tests whether there is a mapping of keyChar in the current key state. + # This tests whether there is a mapping of keyChar in the current key state (and accounts for pass keys). keyCharIsMapped: (keyChar) -> - (mapping for mapping in @keyState when keyChar of mapping)[0]? + (mapping for mapping in @keyState when keyChar of mapping)[0]? and not @isPassKey keyChar handleKeyChar: (event, keyChar) -> bgLog "Handling key #{keyChar}, mode=#{@name}." @@ -93,15 +90,13 @@ class KeyHandlerMode extends Mode false # Suppress event. isCountKey: (keyChar) -> - keyChar?.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' + keyChar?.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' and + not @isPassKey keyChar # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if # 't' is a passKey, then the "t"-s of 'gt' and '99t' are neverthless handled as regular keys. - unlessKeyCharIsPassKey: (keyChar, nonPassKeyCallback) -> - if @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "") - @stopBubblingAndTrue - else - nonPassKeyCallback() + isPassKey: (keyChar) -> + @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "") root = exports ? window root.KeyHandlerMode = KeyHandlerMode -- cgit v1.2.3 From a049281ea12e2d31813277ef7777815175881f6b Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Wed, 2 Mar 2016 11:26:07 +0000 Subject: Key bindings; miscellaneous... - remove unused "event" parameter - move methods around to put like with like - simplify some expressions - one better method name --- content_scripts/mode_key_handler.coffee | 39 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index fc4d485e..fdcac430 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -41,11 +41,11 @@ class KeyHandlerMode extends Mode @keydownEvents[event.keyCode] = true @reset() false # Suppress event. - else if @keyCharIsMapped keyChar + else if @isMappedKey keyChar @keydownEvents[event.keyCode] = true - @handleKeyChar event, keyChar + @handleKeyChar keyChar else if not keyChar and (keyChar = KeyboardUtils.getKeyChar event) and - (@keyCharIsMapped(keyChar) or @isCountKey keyChar) + (@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 @@ -56,8 +56,8 @@ class KeyHandlerMode extends Mode onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event - if @keyCharIsMapped keyChar - @handleKeyChar event, keyChar + if @isMappedKey keyChar + @handleKeyChar keyChar else if @isCountKey keyChar digit = parseInt keyChar @reset if @keyState.length == 1 then @countPrefix * 10 + digit else digit @@ -73,30 +73,29 @@ class KeyHandlerMode extends Mode @stopBubblingAndTrue # This tests whether there is a mapping of keyChar in the current key state (and accounts for pass keys). - keyCharIsMapped: (keyChar) -> + isMappedKey: (keyChar) -> (mapping for mapping in @keyState when keyChar of mapping)[0]? and not @isPassKey keyChar - handleKeyChar: (event, keyChar) -> + # This tests whether keyChar is a digit (and accounts for pass keys). + isCountKey: (keyChar) -> + keyChar and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' and not @isPassKey keyChar + + # Keystrokes are *never* considered pass keys if the user has begun entering a command. So, for example, if + # 't' is a passKey, then the "t"-s of 'gt' and '99t' are neverthless handled as regular keys. + isPassKey: (keyChar) -> + @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "") + + handleKeyChar: (keyChar) -> bgLog "Handling key #{keyChar}, mode=#{@name}." # Advance the key state. The new key state is the current mappings of keyChar, plus @keyMapping. - @keyState = (mapping[keyChar] for mapping in @keyState when keyChar of mapping) - @keyState.push @keyMapping - command = (mapping for mapping in @keyState when mapping.command)[0] - if command? + @keyState = [(mapping[keyChar] for mapping in @keyState when keyChar of mapping)..., @keyMapping] + command = (mapping for mapping in @keyState when "command" of mapping)[0] + if command count = if 0 < @countPrefix then @countPrefix else 1 bgLog "Calling mode=#{@name}, command=#{command.command}, count=#{count}." @reset() @commandHandler {command, count} false # Suppress event. - isCountKey: (keyChar) -> - keyChar?.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9' and - not @isPassKey keyChar - - # Keystrokes are *never* considered passKeys if the user has begun entering a command. So, for example, if - # 't' is a passKey, then the "t"-s of 'gt' and '99t' are neverthless handled as regular keys. - isPassKey: (keyChar) -> - @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "") - root = exports ? window root.KeyHandlerMode = KeyHandlerMode -- cgit v1.2.3 From 9a22be04cb0b5cef3dcffea84cbbd21565906bd9 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Wed, 2 Mar 2016 11:44:18 +0000 Subject: Key bindings; do not reset on passKeys change. This reinstates the legacy behaviour in the following case: - `g` - change tab - change back to the original tab - `g` - ..... which scrolls to top. It is not obvious that this is the best behaviour, but it is the legacy behaviour, and it certainly isn't unreasonable. --- content_scripts/mode_key_handler.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index fdcac430..118cf70d 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -14,7 +14,7 @@ class KeyHandlerMode extends Mode keydownEvents: {} setKeyMapping: (@keyMapping) -> @reset() - setPassKeys: (@passKeys) -> @reset() + setPassKeys: (@passKeys) -> # Reset the key state, optionally retaining the count provided. reset: (@countPrefix = 0) -> -- cgit v1.2.3 From 632c97eb3485f05c7f813deea788512156116fbb Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Thu, 3 Mar 2016 05:54:58 +0000 Subject: Key bindings; reset on passKeys change. Normal mode updates the pass keys every time the frame changes (so, also every time we change tab). Here, we reset the key state too. Resetting the key state makes sense when, for example, the user has changed the pass keys. However, it also changes a status quo/master behaviour: - `g`, change-tab-with-mouse, change-back, `g` -- previously this scrolled to top; now it does not. --- content_scripts/mode_key_handler.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'content_scripts') diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 118cf70d..fdcac430 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -14,7 +14,7 @@ class KeyHandlerMode extends Mode keydownEvents: {} setKeyMapping: (@keyMapping) -> @reset() - setPassKeys: (@passKeys) -> + setPassKeys: (@passKeys) -> @reset() # Reset the key state, optionally retaining the count provided. reset: (@countPrefix = 0) -> -- cgit v1.2.3 From fb67cfdd2ca8c09453cc896fd02d08ed5a74a8a4 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 4 Mar 2016 08:51:39 +0000 Subject: Key bindings; disable on disconnect. This reinstates the feature whereby we disable the content script when we lose contact with the background page, e.g., on upgrade. From my investigations, this doesn't appear to be absolutely necessary. Nevertheless, it's cleaner like this. --- content_scripts/vimium_frontend.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'content_scripts') diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 72be5510..0ae3d229 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -214,7 +214,11 @@ window.addEventListener "hashchange", onFocus # initializeOnDomReady = -> # Tell the background page we're in the dom ready state. - chrome.runtime.connect({ name: "domReady" }) + chrome.runtime.connect(name: "domReady").onDisconnect.addListener -> + # We disable content scripts when we lose contact with the background page. + isEnabledForUrl = false + chrome.runtime.sendMessage = -> + window.removeEventListener "focus", onFocus # We only initialize the vomnibar in the tab's main frame, because it's only ever opened there. Vomnibar.init() if DomUtils.isTopFrame() HUD.init() -- cgit v1.2.3