aboutsummaryrefslogtreecommitdiffstats
path: root/content_scripts/mode_insert.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'content_scripts/mode_insert.coffee')
-rw-r--r--content_scripts/mode_insert.coffee53
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