]> git.gir.st - VimFx.git/blob - extension/lib/events-frame.coffee
Fix blacklisting on already loaded tabs
[VimFx.git] / extension / lib / events-frame.coffee
1 ###
2 # Copyright Simon Lydell 2015.
3 #
4 # This file is part of VimFx.
5 #
6 # VimFx is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # VimFx is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with VimFx. If not, see <http://www.gnu.org/licenses/>.
18 ###
19
20 # This file is the equivalent to events.coffee, but for frame scripts.
21
22 messageManager = require('./message-manager')
23 utils = require('./utils')
24
25 class FrameEventManager
26 constructor: (@vim) ->
27
28 listen: utils.listen.bind(null, FRAME_SCRIPT_ENVIRONMENT)
29 listenOnce: utils.listenOnce.bind(null, FRAME_SCRIPT_ENVIRONMENT)
30
31 addListeners: ->
32 messageManager.listen('locationChange', @vim.resetState.bind(@vim))
33
34 # If the page already was fully loaded when VimFx was initialized, send the
35 # 'DOMWindowCreated' message straight away.
36 if @vim.content.document.readyState == 'complete'
37 messageManager.send('DOMWindowCreated')
38 else
39 @listen('DOMWindowCreated', (event) =>
40 messageManager.send('DOMWindowCreated')
41 )
42
43 @listen('click', (event) =>
44 if @vim.mode == 'hints' and event.isTrusted
45 messageManager.send('enterMode', {mode: 'normal'})
46 )
47
48 @listen('overflow', (event) =>
49 return unless computedStyle = @vim.content.getComputedStyle(event.target)
50 return if computedStyle.getPropertyValue('overflow') == 'hidden'
51 @vim.state.scrollableElements.add(event.target)
52 )
53
54 @listen('underflow', (event) =>
55 @vim.state.scrollableElements.delete(event.target)
56 )
57
58 @listen('keydown', (event) =>
59 suppress = @vim.onInput(event)
60
61 # From this line on, the rest of the code in `addListeners` is more or
62 # less devoted to autofocus prevention. When enabled, focus events that
63 # occur before the user has interacted with page are prevented.
64 #
65 # If this keydown event wasn’t suppressed (`not suppress`), it’s an
66 # obvious interaction with the page. If it _was_ suppressed, though, it’s
67 # an interaction depending on the command triggered; if it calls
68 # `vim.markPageInteraction()` or not.
69 @vim.markPageInteraction() unless suppress
70 )
71
72 # Clicks are always counted as page interaction. Listen for 'mousedown'
73 # instead of 'click' to mark the interaction as soon as possible.
74 @listen('mousedown', (event) => @vim.markPageInteraction())
75
76 @listen('focus', (event) =>
77 target = event.originalTarget
78
79 options = @vim.options(['prevent_autofocus', 'prevent_autofocus_modes'])
80
81 # Save the last focused text input regardless of whether that input might
82 # be blurred because of autofocus prevention.
83 if utils.isTextInputElement(target)
84 @vim.state.lastFocusedTextInput = target
85
86 focusManager = Cc['@mozilla.org/focus-manager;1']
87 .getService(Ci.nsIFocusManager)
88
89 # Blur the focus target, if autofocus prevention is enabled…
90 if options.prevent_autofocus and
91 @vim.mode in options.prevent_autofocus_modes and
92 # …and the user has interacted with the page…
93 not @vim.state.hasInteraction and
94 # …and the event is programmatic (not caused by clicks or keypresses)…
95 focusManager.getLastFocusMethod(null) == 0 and
96 # …and the target may steal most keystrokes.
97 (utils.isTextInputElement(target) or utils.isContentEditable(target))
98 # Some sites (such as icloud.com) re-focuses inputs if they are blurred,
99 # causing an infinite loop of autofocus prevention and re-focusing.
100 # Therefore, blur events that happen just after an autofocus prevention
101 # are suppressed.
102 @listenOnce('blur', utils.suppressEvent)
103 target.blur()
104 )
105
106 @listen('blur', (event) =>
107 target = event.originalTarget
108
109 # If a text input is blurred in a background tab, it most likely means
110 # that the user switched tab, for example by pressing `<c-tab>`, while the
111 # text input was focused. The 'TabSelect' event fires first, then the
112 # 'blur' event. In this case, when switching back to that tab, the text
113 # input will be re-focused (because it was focused when you left the tab).
114 # This case is kept track of so that the autofocus prevention does not
115 # catch it.
116 if utils.isTextInputElement(target) or utils.isContentEditable(target)
117 messageManager.send('vimMethod', {method: 'isCurrent'}, (isCurrent) =>
118 @vim.state.shouldRefocus = not isCurrent
119 # Note that when switching to a non-Firefox window, blur events happen
120 # as usual, but `isCurrent` will be `true`. (`@vim` is still the
121 # current vim object in the current Firefox window, but the current
122 # Firefox window is not the current OS window). `shouldRefocus` should
123 # still be `true` in this case, though. However, it doesn’t matter
124 # that it isn’t, because it is only used in the 'TabSelect' event,
125 # which does not fire when returning from another window.
126 )
127 )
128
129 messageManager.listen('TabSelect', =>
130 # Reset `hasInteraction` when (re-)selecting a tab, in order to prevent
131 # the common “automatically re-focus when switching back to the tab”
132 # behaviour many sites have, unless a text input _should_ be re-focused
133 # when coming back to the tab (see above).
134 if @vim.state.shouldRefocus
135 @vim.state.hasInteraction = true
136 @vim.state.shouldRefocus = false
137 else
138 @vim.state.hasInteraction = false
139 )
140
141 module.exports = FrameEventManager
Imprint / Impressum