aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts/main.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'background_scripts/main.coffee')
-rw-r--r--background_scripts/main.coffee182
1 files changed, 120 insertions, 62 deletions
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index f564f477..352cfa48 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -24,9 +24,11 @@ completionSources =
history: new HistoryCompleter()
domains: new DomainCompleter()
tabs: new TabCompleter()
+ seachEngines: new SearchEngineCompleter()
completers =
omni: new MultiCompleter([
+ completionSources.seachEngines,
completionSources.bookmarks,
completionSources.history,
completionSources.domains])
@@ -67,30 +69,31 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) ->
getCurrentTabUrl = (request, sender) -> sender.tab.url
#
-# Checks the user's preferences in local storage to determine if Vimium is enabled for the given URL.
+# Checks the user's preferences in local storage to determine if Vimium is enabled for the given URL, and
+# whether any keys should be passed through to the underlying page.
#
-isEnabledForUrl = (request) ->
- # excludedUrls are stored as a series of URL expressions separated by newlines.
- excludedUrls = Settings.get("excludedUrls").split("\n")
- isEnabled = true
- for url in excludedUrls
- # The user can add "*" to the URL which means ".*"
- regexp = new RegExp("^" + url.replace(/\*/g, ".*") + "$")
- isEnabled = false if request.url.match(regexp)
- { isEnabledForUrl: isEnabled }
-
-# Called by the popup UI. Strips leading/trailing whitespace and ignores empty strings.
-root.addExcludedUrl = (url) ->
- return unless url = url.trim()
-
- excludedUrls = Settings.get("excludedUrls")
- return if excludedUrls.indexOf(url) >= 0
-
- excludedUrls += "\n" + url
- Settings.set("excludedUrls", excludedUrls)
-
- chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, active: true },
- (tabs) -> updateActiveState(tabs[0].id))
+root.isEnabledForUrl = isEnabledForUrl = (request) ->
+ rule = Exclusions.getRule(request.url)
+ {
+ rule: rule
+ isEnabledForUrl: not rule or rule.passKeys
+ passKeys: rule?.passKeys or ""
+ }
+
+# Called by the popup UI.
+# If the URL pattern matches an existing rule, then the existing rule is updated. Otherwise, a new rule is created.
+root.addExclusionRule = (pattern,passKeys) ->
+ if pattern = pattern.trim()
+ Exclusions.updateOrAdd({ pattern: pattern, passKeys: passKeys })
+ chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, active: true },
+ (tabs) -> updateActiveState(tabs[0].id))
+
+# Called by the popup UI. Remove all existing exclusion rules with this pattern.
+root.removeExclusionRule = (pattern) ->
+ if pattern = pattern.trim()
+ Exclusions.remove(pattern)
+ chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, active: true },
+ (tabs) -> updateActiveState(tabs[0].id))
saveHelpDialogSettings = (request) ->
Settings.set("helpDialog_showAdvancedCommands", request.showAdvancedCommands)
@@ -217,6 +220,12 @@ repeatFunction = (func, totalCount, currentCount, frameId) ->
-> repeatFunction(func, totalCount, currentCount + 1, frameId),
frameId)
+moveTab = (callback, direction) ->
+ chrome.tabs.getSelected(null, (tab) ->
+ # Use Math.max to prevent -1 as the new index, otherwise the tab of index n will wrap to the far RHS when
+ # moved left by exactly (n+1) places.
+ chrome.tabs.move(tab.id, {index: Math.max(0, tab.index + direction) }, callback))
+
# Start action functions
# These are commands which are bound to keystroke which must be handled by the background page. They are
@@ -228,8 +237,9 @@ BackgroundCommands =
chrome.tabs.duplicate(tab.id)
selectionChangedHandlers.push(callback))
moveTabToNewWindow: (callback) ->
- chrome.tabs.getSelected(null, (tab) ->
- chrome.windows.create({tabId: tab.id}))
+ chrome.tabs.query {active: true, currentWindow: true}, (tabs) ->
+ tab = tabs[0]
+ chrome.windows.create {tabId: tab.id, incognito: tab.incognito}
nextTab: (callback) -> selectTab(callback, "next")
previousTab: (callback) -> selectTab(callback, "previous")
firstTab: (callback) -> selectTab(callback, "first")
@@ -238,29 +248,38 @@ BackgroundCommands =
chrome.tabs.getSelected(null, (tab) ->
chrome.tabs.remove(tab.id))
restoreTab: (callback) ->
- # TODO(ilya): Should this be getLastFocused instead?
- chrome.windows.getCurrent((window) ->
- return unless (tabQueue[window.id] && tabQueue[window.id].length > 0)
- tabQueueEntry = tabQueue[window.id].pop()
- # Clean out the tabQueue so we don't have unused windows laying about.
- delete tabQueue[window.id] if (tabQueue[window.id].length == 0)
-
- # We have to chain a few callbacks to set the appropriate scroll position. We can't just wait until the
- # tab is created because the content script is not available during the "loading" state. We need to
- # wait until that's over before we can call setScrollPosition.
- chrome.tabs.create({ url: tabQueueEntry.url, index: tabQueueEntry.positionIndex }, (tab) ->
- tabLoadedHandlers[tab.id] = ->
- chrome.tabs.sendMessage(tab.id,
- name: "setScrollPosition",
- scrollX: tabQueueEntry.scrollX,
- scrollY: tabQueueEntry.scrollY)
- callback()))
+ # TODO: remove if-else -block when adopted into stable
+ if chrome.sessions
+ chrome.sessions.restore(null, (restoredSession) -> callback())
+ else
+ # TODO(ilya): Should this be getLastFocused instead?
+ chrome.windows.getCurrent((window) ->
+ return unless (tabQueue[window.id] && tabQueue[window.id].length > 0)
+ tabQueueEntry = tabQueue[window.id].pop()
+ # Clean out the tabQueue so we don't have unused windows laying about.
+ delete tabQueue[window.id] if (tabQueue[window.id].length == 0)
+
+ # We have to chain a few callbacks to set the appropriate scroll position. We can't just wait until the
+ # tab is created because the content script is not available during the "loading" state. We need to
+ # wait until that's over before we can call setScrollPosition.
+ chrome.tabs.create({ url: tabQueueEntry.url, index: tabQueueEntry.positionIndex }, (tab) ->
+ tabLoadedHandlers[tab.id] = ->
+ chrome.tabs.sendRequest(tab.id,
+ name: "setScrollPosition",
+ scrollX: tabQueueEntry.scrollX,
+ scrollY: tabQueueEntry.scrollY)
+ callback()))
openCopiedUrlInCurrentTab: (request) -> openUrlInCurrentTab({ url: Clipboard.paste() })
openCopiedUrlInNewTab: (request) -> openUrlInNewTab({ url: Clipboard.paste() })
+ togglePinTab: (request) ->
+ chrome.tabs.getSelected(null, (tab) ->
+ chrome.tabs.update(tab.id, { pinned: !tab.pinned }))
showHelp: (callback, frameId) ->
chrome.tabs.getSelected(null, (tab) ->
chrome.tabs.sendMessage(tab.id,
{ name: "toggleHelpDialog", dialogHtml: helpDialogHtml(), frameId:frameId }))
+ moveTabLeft: (count) -> moveTab(null, -count)
+ moveTabRight: (count) -> moveTab(null, count)
nextFrame: (count) ->
chrome.tabs.getSelected(null, (tab) ->
frames = framesForTab[tab.id].frames
@@ -272,6 +291,30 @@ BackgroundCommands =
chrome.tabs.sendMessage(tab.id, { name: "focusFrame", frameId: frames[newIndex].id, highlight: true }))
+ closeTabsOnLeft: -> removeTabsRelative "before"
+ closeTabsOnRight: -> removeTabsRelative "after"
+ closeOtherTabs: -> removeTabsRelative "both"
+
+# Remove tabs before, after, or either side of the currently active tab
+removeTabsRelative = (direction) ->
+ chrome.tabs.query {currentWindow: true}, (tabs) ->
+ chrome.tabs.query {currentWindow: true, active: true}, (activeTabs) ->
+ activeTabIndex = activeTabs[0].index
+
+ shouldDelete = switch direction
+ when "before"
+ (index) -> index < activeTabIndex
+ when "after"
+ (index) -> index > activeTabIndex
+ when "both"
+ (index) -> index != activeTabIndex
+
+ toRemove = []
+ for tab in tabs
+ if not tab.pinned and shouldDelete tab.index
+ toRemove.push tab.id
+ chrome.tabs.remove toRemove
+
# Selects a tab before or after the currently selected tab.
# - direction: "next", "previous", "first" or "last".
selectTab = (callback, direction) ->
@@ -304,32 +347,36 @@ updateOpenTabs = (tab) ->
# Frames are recreated on refresh
delete framesForTab[tab.id]
-# Updates the browserAction icon to indicated whether Vimium is enabled or disabled on the current page.
-# Also disables Vimium if it is currently enabled but should be disabled according to the url blacklist.
+setBrowserActionIcon = (tabId,path) ->
+ chrome.browserAction.setIcon({ tabId: tabId, path: path })
+
+# Updates the browserAction icon to indicate whether Vimium is enabled or disabled on the current page.
+# Also propagates new enabled/disabled/passkeys state to active window, if necessary.
# This lets you disable Vimium on a page without needing to reload.
-#
-# Three situations are considered:
-# 1. Active tab is disabled -> disable icon
-# 2. Active tab is enabled and should be enabled -> enable icon
-# 3. Active tab is enabled but should be disabled -> disable icon and disable vimium
updateActiveState = (tabId) ->
enabledIcon = "icons/browser_action_enabled.png"
disabledIcon = "icons/browser_action_disabled.png"
- chrome.tabs.get(tabId, (tab) ->
- # Default to disabled state in case we can't connect to Vimium, primarily for the "New Tab" page.
- chrome.browserAction.setIcon({ path: disabledIcon })
- chrome.tabs.sendMessage(tabId, { name: "getActiveState" }, (response) ->
- isCurrentlyEnabled = (response? && response.enabled)
- shouldBeEnabled = isEnabledForUrl({url: tab.url}).isEnabledForUrl
-
- if (isCurrentlyEnabled)
- if (shouldBeEnabled)
- chrome.browserAction.setIcon({ path: enabledIcon })
+ partialIcon = "icons/browser_action_partial.png"
+ chrome.tabs.get tabId, (tab) ->
+ chrome.tabs.sendMessage tabId, { name: "getActiveState" }, (response) ->
+ if response
+ isCurrentlyEnabled = response.enabled
+ currentPasskeys = response.passKeys
+ config = isEnabledForUrl({url: tab.url})
+ enabled = config.isEnabledForUrl
+ passKeys = config.passKeys
+ if (enabled and passKeys)
+ setBrowserActionIcon(tabId,partialIcon)
+ else if (enabled)
+ setBrowserActionIcon(tabId,enabledIcon)
else
- chrome.browserAction.setIcon({ path: disabledIcon })
- chrome.tabs.sendMessage(tabId, { name: "disableVimium" })
+ setBrowserActionIcon(tabId,disabledIcon)
+ # Propagate the new state only if it has changed.
+ if (isCurrentlyEnabled != enabled || currentPasskeys != passKeys)
+ chrome.tabs.sendMessage(tabId, { name: "setState", enabled: enabled, passKeys: passKeys })
else
- chrome.browserAction.setIcon({ path: disabledIcon })))
+ # We didn't get a response from the front end, so Vimium isn't running.
+ setBrowserActionIcon(tabId,disabledIcon)
handleUpdateScrollPosition = (request, sender) ->
updateScrollPosition(sender.tab, request.scrollX, request.scrollY)
@@ -458,6 +505,14 @@ handleKeyDown = (request, port) ->
console.log("checking keyQueue: [", keyQueue + key, "]")
keyQueue = checkKeyQueue(keyQueue + key, port.sender.tab.id, request.frameId)
console.log("new KeyQueue: " + keyQueue)
+ # 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
@@ -596,3 +651,6 @@ chrome.windows.getAll { populate: true }, (windows) ->
createScrollPositionHandler = ->
(response) -> updateScrollPosition(tab, response.scrollX, response.scrollY) if response?
chrome.tabs.sendMessage(tab.id, { name: "getScrollPosition" }, createScrollPositionHandler())
+
+# Start pulling changes from synchronized storage.
+Sync.init()