From dc423eff18b2b2654c175633cd11e28ea9279fd7 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 9 Jan 2015 16:53:35 +0000 Subject: Modes; rework blocker for contentEditable. --- content_scripts/mode.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'content_scripts/mode.coffee') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 8e37ee36..b6cb5fae 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -175,7 +175,9 @@ class Mode # suppress badge updates while exiting any existing active singleton. This prevents the badge from # flickering in some cases. Mode.badgeSuppressor.runSuppresed => - singletons[key].exit() if singletons[key] + if singletons[key] + console.log singletons[key].count, "singleton:", @name, "(deactivating)" + singletons[key].exit() singletons[key] = @ @onExit => delete singletons[key] if singletons[key] == @ -- cgit v1.2.3 From ac90db47aa2671cd663cc6a9cdf783dc30a582e9 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 00:00:11 +0000 Subject: Modes; more changes... - Better comments. - Strip unnecessary handlers for leaving post-find mode. - Simplify passKeys. - focusInput now re-bubbles its triggering keydown event. --- content_scripts/mode.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'content_scripts/mode.coffee') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index b6cb5fae..37f3a8c2 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -45,11 +45,12 @@ class Mode # If this is true, then we generate a trace of modes being activated and deactivated on the console. @debug = true - # Constants; readable shortcuts for event-handler return values. + # Constants; short, readable names for handlerStack event-handler return values. continueBubbling: true suppressEvent: false stopBubblingAndTrue: handlerStack.stopBubblingAndTrue stopBubblingAndFalse: handlerStack.stopBubblingAndFalse + restartBubbling: handlerStack.restartBubbling constructor: (@options={}) -> @handlers = [] -- cgit v1.2.3 From fdcdd0113049042c94b2b56a6b716e2da58b860e Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 08:25:02 +0000 Subject: Modes; instrument for debugging... - Set Mode.debug to true to see mode activation/deactivation on the console. - Use Mode.log() to see a list of currently-active modes. - Use handlerStack.debugOn() to enable debugging of the handler stack. --- content_scripts/mode.coffee | 55 +++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 14 deletions(-) (limited to 'content_scripts/mode.coffee') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 37f3a8c2..a33197b0 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -38,12 +38,14 @@ # myMode.exit() # externally triggered. # -# For debug only; to be stripped out. +# For debug only. count = 0 class Mode - # If this is true, then we generate a trace of modes being activated and deactivated on the console. - @debug = true + # If Mode.debug is true, then we generate a trace of modes being activated and deactivated on the console, along + # with a list of the currently active modes. + debug: true + @modes: [] # Constants; short, readable names for handlerStack event-handler return values. continueBubbling: true @@ -60,7 +62,8 @@ class Mode @name = @options.name || "anonymous" @count = ++count - console.log @count, "create:", @name if Mode.debug + @id = "#{@name}-#{@count}" + @logger "activate:", @id if @debug @push keydown: @options.keydown || null @@ -80,6 +83,7 @@ class Mode # Note. This handler ends up above the mode's own key handlers on the handler stack, so it takes # priority. @push + _name: "mode-#{@id}/exitOnEscape" "keydown": (event) => return @continueBubbling unless KeyboardUtils.isEscape event @exit event @@ -90,6 +94,7 @@ class Mode # loses the focus. if @options.exitOnBlur @push + _name: "mode-#{@id}/exitOnBlur" "blur": (event) => @alwaysContinueBubbling => @exit() if event.srcElement == @options.exitOnBlur # If @options.trackState is truthy, then the mode mainatins the current state in @enabled and @passKeys, @@ -98,6 +103,7 @@ class Mode @enabled = false @passKeys = "" @push + _name: "mode-#{@id}/registerStateChange" "registerStateChange": ({ enabled: enabled, passKeys: passKeys }) => @alwaysContinueBubbling => if enabled != @enabled or passKeys != @passKeys @@ -110,30 +116,38 @@ class Mode # from propagating to other extensions or the host page. if @options.trapAllKeyboardEvents @unshift - keydown: (event) => @alwaysContinueBubbling => - DomUtils.suppressPropagation event if event.srcElement == @options.trapAllKeyboardEvents - keypress: (event) => @alwaysContinueBubbling => - DomUtils.suppressEvent event if event.srcElement == @options.trapAllKeyboardEvents - keyup: (event) => @alwaysContinueBubbling => - DomUtils.suppressPropagation event if event.srcElement == @options.trapAllKeyboardEvents + _name: "mode-#{@id}/trapAllKeyboardEvents" + keydown: (event) => + if event.srcElement == @options.trapAllKeyboardEvents then @suppressEvent else @continueBubbling + keypress: (event) => + if event.srcElement == @options.trapAllKeyboardEvents then @suppressEvent else @continueBubbling + keyup: (event) => + if event.srcElement == @options.trapAllKeyboardEvents then @suppressEvent else @continueBubbling Mode.updateBadge() if @badge - # End of Mode.constructor(). + Mode.modes.push @ + @log() if @debug + handlerStack.debugOn() + # End of Mode constructor. push: (handlers) -> + handlers._name ||= "mode-#{@id}" @handlers.push handlerStack.push handlers unshift: (handlers) -> - @handlers.unshift handlerStack.push handlers + handlers._name ||= "mode-#{@id}" + handlers._name += "/unshifted" + @handlers.push handlerStack.unshift handlers onExit: (handler) -> @exitHandlers.push handler exit: -> if @modeIsActive - console.log @count, "exit:", @name if Mode.debug + @logger "deactivate:", @id if @debug handler() for handler in @exitHandlers handlerStack.remove handlerId for handlerId in @handlers + Mode.modes = Mode.modes.filter (mode) => mode != @ Mode.updateBadge() @modeIsActive = false @@ -177,12 +191,24 @@ class Mode # flickering in some cases. Mode.badgeSuppressor.runSuppresed => if singletons[key] - console.log singletons[key].count, "singleton:", @name, "(deactivating)" + @logger "singleton:", "deactivating #{singletons[key].id}" if @debug singletons[key].exit() singletons[key] = @ @onExit => delete singletons[key] if singletons[key] == @ + # Debugging routines. + log: -> + if Mode.modes.length == 0 + @logger "It looks like debugging is not enabled in modes.coffee." + else + @logger "active modes (top to bottom), current: #{@id}" + for mode in Mode.modes[..].reverse() + @logger " ", mode.id + + logger: (args...) -> + handlerStack.log args... + # BadgeMode is a pseudo mode for triggering badge updates on focus changes and state updates. It sits at the # bottom of the handler stack, and so it receives state changes *after* all other modes, and can override the # badge choice of the other active modes. @@ -194,6 +220,7 @@ new class BadgeMode extends Mode trackState: true @push + _name: "mode-#{@id}/focus" "focus": => @alwaysContinueBubbling -> Mode.updateBadge() chooseBadge: (badge) -> -- cgit v1.2.3 From c554d1fd5b6d81506864516b6f86a14f8672bec5 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 15:05:58 +0000 Subject: Modes; reinstate key blockers: - when the selection is contentEditable - in PostFindMode Restricted to printable characters. --- content_scripts/mode.coffee | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'content_scripts/mode.coffee') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index a33197b0..5c6201e0 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -111,23 +111,21 @@ class Mode @passKeys = passKeys @registerStateChange?() - # If @options.trapAllKeyboardEvents is truthy, then it should be an element. All keyboard events on that - # element are suppressed *after* bubbling the event down the handler stack. This prevents such events - # from propagating to other extensions or the host page. - if @options.trapAllKeyboardEvents + # If @options.suppressPrintableEvents is truthy, then it should be an element. All printable keyboard + # events on that element are suppressed, if necessary (that is, *after* bubbling down the handler stack). + # We only suppress keypress events. This is used by PostFindMode to protect active, editable elements. + # Note: We use unshift here, not push, so the handler is installed at the bottom of the stack. + if @options.suppressPrintableEvents @unshift - _name: "mode-#{@id}/trapAllKeyboardEvents" - keydown: (event) => - if event.srcElement == @options.trapAllKeyboardEvents then @suppressEvent else @continueBubbling + _name: "mode-#{@id}/suppressPrintableEvents" keypress: (event) => - if event.srcElement == @options.trapAllKeyboardEvents then @suppressEvent else @continueBubbling - keyup: (event) => - if event.srcElement == @options.trapAllKeyboardEvents then @suppressEvent else @continueBubbling + if DomUtils.isPrintable(event) and + event.srcElement == @options.suppressPrintableEvents then @suppressEvent else @continueBubbling Mode.updateBadge() if @badge Mode.modes.push @ @log() if @debug - handlerStack.debugOn() + # handlerStack.debugOn() # End of Mode constructor. push: (handlers) -> -- cgit v1.2.3 From 704ae28629154a732e20e16d56b23af265d51b85 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 16:01:40 +0000 Subject: Modes; better printable detection, move to keyboard_utils. --- content_scripts/mode.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'content_scripts/mode.coffee') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 5c6201e0..19354d94 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -119,7 +119,7 @@ class Mode @unshift _name: "mode-#{@id}/suppressPrintableEvents" keypress: (event) => - if DomUtils.isPrintable(event) and + if KeyboardUtils.isPrintable(event) and event.srcElement == @options.suppressPrintableEvents then @suppressEvent else @continueBubbling Mode.updateBadge() if @badge -- cgit v1.2.3 From 80ad0bc3087a3bf00d61bdd6c9cf48e971e22480 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 10 Jan 2015 18:52:24 +0000 Subject: Modes; re-architect key suppression and passkeys. --- content_scripts/mode.coffee | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'content_scripts/mode.coffee') diff --git a/content_scripts/mode.coffee b/content_scripts/mode.coffee index 19354d94..0fcab675 100644 --- a/content_scripts/mode.coffee +++ b/content_scripts/mode.coffee @@ -111,16 +111,18 @@ class Mode @passKeys = passKeys @registerStateChange?() - # If @options.suppressPrintableEvents is truthy, then it should be an element. All printable keyboard - # events on that element are suppressed, if necessary (that is, *after* bubbling down the handler stack). - # We only suppress keypress events. This is used by PostFindMode to protect active, editable elements. - # Note: We use unshift here, not push, so the handler is installed at the bottom of the stack. + # If @options.suppressPrintableEvents is truthy, then it should be an element. All printable keypress + # events on that element are suppressed, if necessary. They are suppressed *after* bubbling down the + # handler stack and finding no handler. This is used by PostFindMode to protect active, editable + # elements. if @options.suppressPrintableEvents - @unshift + @push _name: "mode-#{@id}/suppressPrintableEvents" keypress: (event) => - if KeyboardUtils.isPrintable(event) and - event.srcElement == @options.suppressPrintableEvents then @suppressEvent else @continueBubbling + @alwaysContinueBubbling => + if event.srcElement == @options.suppressPrintableEvents + if KeyboardUtils.isPrintable(event) + event.vimium_suppress_event = true Mode.updateBadge() if @badge Mode.modes.push @ @@ -217,6 +219,9 @@ new class BadgeMode extends Mode name: "badge" trackState: true + # FIXME(smblott) BadgeMode is currently triggering and updateBadge event on every focus event. That's a + # lot, considerably more than is necessary. Really, it only needs to trigger when we change frame, or + # when we change tab. @push _name: "mode-#{@id}/focus" "focus": => @alwaysContinueBubbling -> Mode.updateBadge() @@ -228,5 +233,19 @@ new class BadgeMode extends Mode registerStateChange: -> Mode.updateBadge() +# KeySuppressor is a pseudo mode (near the bottom of the stack) which suppresses keyboard events tagged with +# the "vimium_suppress_event" property. This allows modes higher up in the stack to tag events for +# suppression, but only after verifying that no other mode (notably, normal mode) wants to handle the event. +# Note. We also create the the one-and-only instance, here. +new class KeySuppressor extends Mode + constructor: -> + super + name: "key-suppressor" + keydown: (event) => @handle event + keypress: (event) => @handle event + keyup: (event) => @handle event + + handle: (event) -> if event.vimium_suppress_event then @suppressEvent else @continueBubbling + root = exports ? window root.Mode = Mode -- cgit v1.2.3