diff options
Diffstat (limited to 'content_scripts/mode_insert.coffee')
| -rw-r--r-- | content_scripts/mode_insert.coffee | 53 | 
1 files changed, 27 insertions, 26 deletions
diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee index 9a2d5ce1..144b0be6 100644 --- a/content_scripts/mode_insert.coffee +++ b/content_scripts/mode_insert.coffee @@ -1,5 +1,5 @@ -# This mode is installed when insert mode is active. +# This mode is installed only when insert mode is active.  class InsertMode extends Mode    constructor: (options = {}) ->      defaults = @@ -11,10 +11,10 @@ class InsertMode extends Mode        keyup: (event) => @stopBubblingAndTrue        exitOnEscape: true        blurOnExit: true +      targetElement: null -    options = extend defaults, options -    options.exitOnBlur = options.targetElement || null -    super options +    options.exitOnBlur ||= options.targetElement +    super extend defaults, options      triggerSuppressor.suppress()    exit: (event = null) -> @@ -34,8 +34,8 @@ class InsertMode extends Mode  #   - On a keydown event in a contentEditable element.  #   - When a focusable element receives the focus.  # -# The trigger can be suppressed via triggerSuppressor; see InsertModeBlocker, below. -# This mode is permanently installed fairly low down on the handler stack. +# The trigger can be suppressed via triggerSuppressor; see InsertModeBlocker, below.  This mode is permanently +# installed (just above normal mode and passkeys mode) on the handler stack.  class InsertModeTrigger extends Mode    constructor: ->      super @@ -44,7 +44,7 @@ class InsertModeTrigger extends Mode          triggerSuppressor.unlessSuppressed =>            # Some sites (e.g. inbox.google.com) change the contentEditable attribute on the fly (see #1245);            # and unfortunately, the focus event happens *before* the change is made.  Therefore, we need to -          # check again whether the active element is contentEditable. +          # check (on every keydown) whether the active element is contentEditable.            return @continueBubbling unless document.activeElement?.isContentEditable            new InsertMode              targetElement: document.activeElement @@ -58,7 +58,7 @@ class InsertModeTrigger extends Mode                new InsertMode                  targetElement: event.target -    # We may already have focussed an input, so check. +    # We may have already focussed an input element, so check.      if document.activeElement and DomUtils.isEditable document.activeElement        new InsertMode          targetElement: document.activeElement @@ -66,12 +66,13 @@ class InsertModeTrigger extends Mode  # Used by InsertModeBlocker to suppress InsertModeTrigger; see below.  triggerSuppressor = new Utils.Suppressor true # Note: true == @continueBubbling -# Suppresses InsertModeTrigger.  This is used by various modes (usually by inheritance) to prevent +# Suppresses InsertModeTrigger.  This is used by various modes (usually via inheritance) to prevent  # unintentionally dropping into insert mode on focusable elements.  class InsertModeBlocker extends Mode    constructor: (options = {}) ->      triggerSuppressor.suppress()      options.name ||= "insert-blocker" +    # See "click" handler below for an explanation of options.onClickMode.      options.onClickMode ||= InsertMode      super options      @onExit -> triggerSuppressor.unsuppress() @@ -79,27 +80,29 @@ class InsertModeBlocker extends Mode      @push        "click": (event) =>          @alwaysContinueBubbling => -          # The user knows best; so, if the user clicks on something, we get out of the way. +          # The user knows best; so, if the user clicks on something, the insert-mode blocker gets out of the +          # way.            @exit event -          # However, there's a corner case.  If the active element is focusable, then we would have been in -          # insert mode had we not been blocking the trigger.  Now, clicking on the element will not generate -          # a new focus event, so the insert-mode trigger will not fire.  We have to handle this case -          # specially.  @options.onClickMode is the mode to use. +          # However, there's a corner case.  If the active element is focusable, then, had we not been +          # blocking the trigger, we would already have been in insert mode.  Now, a click on that element +          # will not generate a new focus event, so the insert-mode trigger will not fire.  We have to handle +          # this case specially.  @options.onClickMode specifies the mode to use (by default, insert mode).            if document.activeElement and -              event.target == document.activeElement and DomUtils.isEditable document.activeElement +             event.target == document.activeElement and DomUtils.isEditable document.activeElement              new @options.onClickMode                targetElement: document.activeElement  # There's some unfortunate feature interaction with chrome's content editable handling.  If the selection is  # content editable and a descendant of the active element, then chrome focuses it on any unsuppressed keyboard -# events.  This has the unfortunate effect of dropping us unintentally into insert mode.  See #1415. -# This mode sits near the bottom of the handler stack and suppresses keyboard events if: +# event.  This has the unfortunate effect of dropping us unintentally into insert mode.  See #1415. +# A single instance of this mode sits near the bottom of the handler stack and suppresses keyboard events if:  #   - they haven't been handled by any other mode (so not by normal mode, passkeys mode, insert mode, and so -#     on), and +#     on),  #   - the selection is content editable, and  #   - the selection is a descendant of the active element.  # This should rarely fire, typically only on fudged keypresses in normal mode.  And, even then, only in the -# circumstances outlined above.  So it shouldn't normally block other extensions or the page itself from +# circumstances outlined above.  So, we shouldn't usually be blocking keyboard events for other extensions or +# the page itself.  # handling keyboard events.  new class ContentEditableTrap extends Mode    constructor: -> @@ -109,17 +112,15 @@ new class ContentEditableTrap extends Mode        keypress: (event) => @handle => @suppressEvent        keyup: (event) => @handle => @suppressEvent -  # True if the selection is content editable and a descendant of the active element.  In this situation, -  # chrome unilaterally focuses the element containing the anchor, dropping us into insert mode. +  handle: (func) -> if @isContentEditableFocused() then func() else @continueBubbling + +  # True if the selection is content editable and a descendant of the active element.    isContentEditableFocused: ->      element = document.getSelection()?.anchorNode?.parentElement -    return element?.isContentEditable? and -             document.activeElement? and +    return element?.isContentEditable and +             document.activeElement and               DomUtils.isDOMDescendant document.activeElement, element -  handle: (func) -> -    if @isContentEditableFocused() then func() else @continueBubbling -  root = exports ? window  root.InsertMode = InsertMode  root.InsertModeTrigger = InsertModeTrigger  | 
