aboutsummaryrefslogtreecommitdiffstats
path: root/content_scripts/vimium_frontend.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'content_scripts/vimium_frontend.coffee')
-rw-r--r--content_scripts/vimium_frontend.coffee214
1 files changed, 46 insertions, 168 deletions
diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee
index 667031dc..0ae3d229 100644
--- a/content_scripts/vimium_frontend.coffee
+++ b/content_scripts/vimium_frontend.coffee
@@ -5,14 +5,8 @@
# "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 ->
@@ -99,21 +93,45 @@ handlerStack.push
target = target.parentElement
true
-# Only exported for tests.
-window.initializeModes = ->
- class NormalMode extends Mode
- 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
+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: ({command: 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} repetitions of the command: #{registryEntry.description}.\n
+ Are you sure you want to continue?"""
+
+ # 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]
+ 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.
- new NormalMode
- new PassKeysMode
+ # activates/deactivates itself accordingly. normalMode is exported only for the tests.
+ window.normalMode = new NormalMode args...
new InsertMode permanent: true
Scroller.init()
@@ -122,33 +140,18 @@ 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
+ 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
@@ -159,12 +162,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" ]
- # 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
+ shouldHandleRequest ||= request.name in ["checkEnabledAfterURLChange", "openVomnibar"]
sendResponse requestHandlers[request.name](request, sender) if shouldHandleRequest
# Ensure the sendResponse callback is freed.
false
@@ -216,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()
@@ -235,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
@@ -456,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. <c-a> 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:"<ESC>", 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.
@@ -555,9 +447,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
@@ -573,18 +463,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")