aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts
diff options
context:
space:
mode:
Diffstat (limited to 'background_scripts')
-rw-r--r--background_scripts/commands.coffee32
-rw-r--r--background_scripts/main.coffee173
2 files changed, 33 insertions, 172 deletions
diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee
index ab9992b3..7429b6f0 100644
--- a/background_scripts/commands.coffee
+++ b/background_scripts/commands.coffee
@@ -2,6 +2,13 @@ Commands =
init: ->
for own command, description of commandDescriptions
@addCommand(command, description[0], description[1])
+ @loadKeyMappings Settings.get "keyMappings"
+ Settings.postUpdateHooks["keyMappings"] = @loadKeyMappings.bind this
+
+ loadKeyMappings: (customKeyMappings) ->
+ @clearKeyMappingsAndSetDefaults()
+ @parseCustomKeyMappings customKeyMappings
+ @generateKeyStateMapping()
availableCommands: {}
keyToCommandRegistry: {}
@@ -94,6 +101,25 @@ Commands =
@keyToCommandRegistry = {}
@mapKeyToCommand { key, command } for own key, command of defaultKeyMappings
+ # This generates a nested key-to-command mapping structure. There is an example in mode_key_handler.coffee.
+ generateKeyStateMapping: ->
+ # Keys are either literal characters, or "named" - for example <a-b> (alt+b), <left> (left arrow) or <f12>
+ # This regular expression captures two groups: the first is a named key, the second is the remainder of
+ # the string.
+ namedKeyRegex = /^(<(?:[amc]-.|(?:[amc]-)?[a-z0-9]{2,5})>)(.*)$/
+ keyStateMapping = {}
+ for own keys, registryEntry of @keyToCommandRegistry
+ currentMapping = keyStateMapping
+ while 0 < keys.length
+ [key, keys] = if 0 == keys.search namedKeyRegex then [RegExp.$1, RegExp.$2] else [keys[0], keys[1..]]
+ if currentMapping[key]?.command
+ break # Do not overwrite existing command bindings, they take priority.
+ else if 0 < keys.length
+ currentMapping = currentMapping[key] ?= {}
+ else
+ currentMapping[key] = registryEntry
+ chrome.storage.local.set normalModeKeyStateMapping: keyStateMapping
+
# An ordered listing of all available commands, grouped by type. This is the order they will
# be shown in the help page.
commandGroups:
@@ -371,11 +397,5 @@ commandDescriptions =
Commands.init()
-# Register postUpdateHook for keyMappings setting.
-Settings.postUpdateHooks["keyMappings"] = (value) ->
- Commands.clearKeyMappingsAndSetDefaults()
- Commands.parseCustomKeyMappings value
- refreshCompletionKeysAfterMappingSave()
-
root = exports ? window
root.Commands = Commands
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index 7c970866..49199cae 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -19,17 +19,9 @@ chrome.runtime.onInstalled.addListener ({ reason }) ->
func tab.id, { file: file, allFrames: contentScripts.all_frames }, checkLastRuntimeError
currentVersion = Utils.getCurrentVersion()
-keyQueue = "" # Queue of keys typed
-validFirstKeys = {}
-singleKeyCommands = []
frameIdsForTab = {}
root.urlForTab = {}
-# Keys are either literal characters, or "named" - for example <a-b> (alt+b), <left> (left arrow) or <f12>
-# This regular expression captures two groups: the first is a named key, the second is the remainder of
-# the string.
-namedKeyRegex = /^(<(?:[amc]-.|(?:[amc]-)?[a-z0-9]{2,5})>)(.*)$/
-
# This is exported for use by "marks.coffee".
root.tabLoadedHandlers = {} # tabId -> function()
@@ -189,14 +181,6 @@ fetchFileContents = (extensionFileName) ->
req.send()
req.responseText
-#
-# Returns the keys that can complete a valid command given the current key queue.
-#
-getCompletionKeysRequest = (request, keysToCheck = "") ->
- name: "refreshCompletionKeys"
- completionKeys: generateCompletionKeys(keysToCheck)
- validFirstKeys: validFirstKeys
-
TabOperations =
# Opens the url in the current tab.
openUrlInCurrentTab: (request, callback = (->)) ->
@@ -385,148 +369,13 @@ chrome.tabs.onUpdated.addListener (tabId, changeInfo, tab) ->
# End action functions
-splitKeyIntoFirstAndSecond = (key) ->
- if (key.search(namedKeyRegex) == 0)
- { first: RegExp.$1, second: RegExp.$2 }
+runBackgroundCommand = ({frameId, registryEntry, count}, sender) ->
+ if registryEntry.passCountToFunction
+ BackgroundCommands[registryEntry.command] count, frameId
+ else if registryEntry.noRepeat
+ BackgroundCommands[registryEntry.command] frameId
else
- { first: key[0], second: key.slice(1) }
-
-getActualKeyStrokeLength = (key) ->
- if (key.search(namedKeyRegex) == 0)
- 1 + getActualKeyStrokeLength(RegExp.$2)
- else
- key.length
-
-populateValidFirstKeys = ->
- for own key of Commands.keyToCommandRegistry
- if (getActualKeyStrokeLength(key) == 2)
- validFirstKeys[splitKeyIntoFirstAndSecond(key).first] = true
-
-populateSingleKeyCommands = ->
- for own key of Commands.keyToCommandRegistry
- if (getActualKeyStrokeLength(key) == 1)
- singleKeyCommands.push(key)
-
-# Invoked by options.coffee.
-root.refreshCompletionKeysAfterMappingSave = ->
- validFirstKeys = {}
- singleKeyCommands = []
-
- populateValidFirstKeys()
- populateSingleKeyCommands()
-
- sendRequestToAllTabs(getCompletionKeysRequest())
-
-# Generates a list of keys that can complete a valid command given the current key queue or the one passed in
-generateCompletionKeys = (keysToCheck) ->
- splitHash = splitKeyQueue(keysToCheck || keyQueue)
- command = splitHash.command
- count = splitHash.count
-
- completionKeys = singleKeyCommands.slice(0)
-
- if (getActualKeyStrokeLength(command) == 1)
- for own key of Commands.keyToCommandRegistry
- splitKey = splitKeyIntoFirstAndSecond(key)
- if (splitKey.first == command)
- completionKeys.push(splitKey.second)
-
- completionKeys
-
-splitKeyQueue = (queue) ->
- match = /([1-9][0-9]*)?(.*)/.exec(queue)
- count = parseInt(match[1], 10)
- command = match[2]
-
- { count: count, command: command }
-
-handleKeyDown = (sender) -> (request, port) ->
- key = request.keyChar
- if (key == "<ESC>")
- logMessage "clearing keyQueue", sender
- keyQueue = ""
- else
- logMessage "checking keyQueue: [#{keyQueue + key}]", sender
- keyQueue = checkKeyQueue(keyQueue + key, port.sender.tab.id, request.frameId)
- logMessage "new KeyQueue: #{keyQueue}", sender
- # Tell the content script whether there are keys in the queue.
- # FIXME: There is a race condition here. The behaviour in the content script depends upon whether this message gets
- # back there before or after the next keystroke.
- # That being said, I suspect there are other similar race conditions here, for example in checkKeyQueue().
- # Steve (23 Aug, 14).
- chrome.tabs.sendMessage(port.sender.tab.id,
- name: "currentKeyQueue",
- keyQueue: keyQueue)
-
-checkKeyQueue = (keysToCheck, tabId, frameId) ->
- refreshedCompletionKeys = false
- splitHash = splitKeyQueue(keysToCheck)
- command = splitHash.command
- count = splitHash.count
-
- return keysToCheck if command.length == 0
- count = 1 if isNaN(count)
-
- if (Commands.keyToCommandRegistry[command])
- registryEntry = Commands.keyToCommandRegistry[command]
- runCommand = true
- count *= registryEntry.options.count ? 1
-
- if registryEntry.noRepeat
- count = 1
- else if registryEntry.repeatLimit and count > registryEntry.repeatLimit
- runCommand = confirm """
- You have asked Vimium to perform #{count} repeats of the command:
- #{Commands.availableCommands[registryEntry.command].description}
-
- Are you sure you want to continue?
- """
-
- if runCommand
- if not registryEntry.isBackgroundCommand
- chrome.tabs.sendMessage tabId,
- name: "executePageCommand"
- command: registryEntry.command
- frameId: frameId
- count: count
- completionKeys: generateCompletionKeys ""
- registryEntry: registryEntry
- refreshedCompletionKeys = true
- else
- if registryEntry.passCountToFunction
- BackgroundCommands[registryEntry.command](count, frameId)
- else if registryEntry.noRepeat
- BackgroundCommands[registryEntry.command](frameId)
- else
- repeatFunction(BackgroundCommands[registryEntry.command], count, 0, frameId)
-
- newKeyQueue = ""
- else if (getActualKeyStrokeLength(command) > 1)
- splitKey = splitKeyIntoFirstAndSecond(command)
-
- # The second key might be a valid command by its self.
- if (Commands.keyToCommandRegistry[splitKey.second])
- newKeyQueue = checkKeyQueue(splitKey.second, tabId, frameId)
- else
- newKeyQueue = (if validFirstKeys[splitKey.second] then splitKey.second else "")
- else
- newKeyQueue = (if validFirstKeys[command] then count.toString() + command else "")
-
- # If we haven't sent the completion keys piggybacked on executePageCommand,
- # send them by themselves.
- unless refreshedCompletionKeys
- chrome.tabs.sendMessage(tabId, getCompletionKeysRequest(null, newKeyQueue), null)
-
- newKeyQueue
-
-#
-# Message all tabs. Args should be the arguments hash used by the Chrome sendRequest API.
-#
-sendRequestToAllTabs = (args) ->
- chrome.windows.getAll({ populate: true }, (windows) ->
- for window in windows
- for tab in window.tabs
- chrome.tabs.sendMessage(tab.id, args, null))
+ repeatFunction BackgroundCommands[registryEntry.command], count, 0, frameId
openOptionsPageInNewTab = ->
chrome.tabs.getSelected(null, (tab) ->
@@ -574,11 +423,10 @@ bgLog = (request, sender) ->
# Port handler mapping
portHandlers =
- keyDown: handleKeyDown,
completions: handleCompletions
sendRequestHandlers =
- getCompletionKeys: getCompletionKeysRequest
+ runBackgroundCommand: runBackgroundCommand
getCurrentTabUrl: getCurrentTabUrl
openUrlInNewTab: TabOperations.openUrlInNewTab
openUrlInIncognito: TabOperations.openUrlInIncognito
@@ -624,13 +472,6 @@ window.runTests = -> open(chrome.runtime.getURL('tests/dom_tests/dom_tests.html'
#
# Begin initialization.
#
-Commands.clearKeyMappingsAndSetDefaults()
-
-if Settings.has("keyMappings")
- Commands.parseCustomKeyMappings(Settings.get("keyMappings"))
-
-populateValidFirstKeys()
-populateSingleKeyCommands()
# Show notification on upgrade.
showUpgradeMessage = ->