diff options
Diffstat (limited to 'background_scripts/main.coffee')
| -rw-r--r-- | background_scripts/main.coffee | 128 |
1 files changed, 76 insertions, 52 deletions
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index e782a217..a13d9d98 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -16,7 +16,7 @@ chrome.runtime.onInstalled.addListener ({ reason }) -> for tab in tabs for [ func, files ] in jobs for file in files - func tab.id, { file: file, allFrames: contentScripts.allFrames }, checkLastRuntimeError + func tab.id, { file: file, allFrames: contentScripts.all_frames }, checkLastRuntimeError currentVersion = Utils.getCurrentVersion() tabQueue = {} # windowId -> Array @@ -43,20 +43,36 @@ chrome.storage.local.set vimiumSecret: Math.floor Math.random() * 2000000000 completionSources = - bookmarks: new BookmarkCompleter() - history: new HistoryCompleter() - domains: new DomainCompleter() - tabs: new TabCompleter() - seachEngines: new SearchEngineCompleter() + bookmarks: new BookmarkCompleter + history: new HistoryCompleter + domains: new DomainCompleter + tabs: new TabCompleter + searchEngines: new SearchEngineCompleter completers = - omni: new MultiCompleter([ - completionSources.seachEngines, - completionSources.bookmarks, - completionSources.history, - completionSources.domains]) - bookmarks: new MultiCompleter([completionSources.bookmarks]) - tabs: new MultiCompleter([completionSources.tabs]) + omni: new MultiCompleter [ + completionSources.bookmarks + completionSources.history + completionSources.domains + completionSources.searchEngines + ] + bookmarks: new MultiCompleter [completionSources.bookmarks] + tabs: new MultiCompleter [completionSources.tabs] + +completionHandlers = + filter: (completer, request, port) -> + completer.filter request, (response) -> + # We use try here because this may fail if the sender has already navigated away from the original page. + # This can happen, for example, when posting completion suggestions from the SearchEngineCompleter + # (which is done asynchronously). + try + port.postMessage extend request, extend response, handler: "completions" + + refresh: (completer, _, port) -> completer.refresh port + cancel: (completer, _, port) -> completer.cancel port + +handleCompletions = (request, port) -> + completionHandlers[request.handler] completers[request.name], request, port chrome.runtime.onConnect.addListener (port, name) -> senderTabId = if port.sender.tab then port.sender.tab.id else null @@ -215,13 +231,6 @@ handleSettings = (request, port) -> values[key] = Settings.get key for own key of values port.postMessage { values } -refreshCompleter = (request) -> completers[request.name].refresh() - -whitespaceRegexp = /\s+/ -filterCompleter = (args, port) -> - queryTerms = if (args.query == "") then [] else args.query.split(whitespaceRegexp) - completers[args.name].filter(queryTerms, (results) -> port.postMessage({ id: args.id, results: results })) - chrome.tabs.onSelectionChanged.addListener (tabId, selectionInfo) -> if (selectionChangedHandlers.length > 0) selectionChangedHandlers.pop().call() @@ -259,10 +268,10 @@ BackgroundCommands = 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") - lastTab: (callback) -> selectTab(callback, "last") + nextTab: (count) -> selectTab "next", count + previousTab: (count) -> selectTab "previous", count + firstTab: -> selectTab "first" + lastTab: -> selectTab "last" removeTab: (callback) -> chrome.tabs.getSelected(null, (tab) -> chrome.tabs.remove(tab.id) @@ -302,13 +311,9 @@ BackgroundCommands = moveTabLeft: (count) -> moveTab(null, -count) moveTabRight: (count) -> moveTab(null, count) nextFrame: (count,frameId) -> - chrome.tabs.getSelected(null, (tab) -> - frames = frameIdsForTab[tab.id] - # We can't always track which frame chrome has focussed, but here we learn that it's frameId; so add an - # additional offset such that we do indeed start from frameId. - count = (count + Math.max 0, frameIdsForTab[tab.id].indexOf frameId) % frames.length - frames = frameIdsForTab[tab.id] = [frames[count..]..., frames[0...count]...] - chrome.tabs.sendMessage(tab.id, { name: "focusFrame", frameId: frames[0], highlight: true })) + chrome.tabs.getSelected null, (tab) -> + frameIdsForTab[tab.id] = cycleToFrame frameIdsForTab[tab.id], frameId, count + chrome.tabs.sendMessage tab.id, name: "focusFrame", frameId: frameIdsForTab[tab.id][0], highlight: true mainFrame: -> chrome.tabs.getSelected null, (tab) -> # The front end interprets a frameId of 0 to mean the main/top from. @@ -340,21 +345,23 @@ removeTabsRelative = (direction) -> # Selects a tab before or after the currently selected tab. # - direction: "next", "previous", "first" or "last". -selectTab = (callback, direction) -> - chrome.tabs.getAllInWindow(null, (tabs) -> +selectTab = (direction, count = 1) -> + chrome.tabs.getAllInWindow null, (tabs) -> return unless tabs.length > 1 - chrome.tabs.getSelected(null, (currentTab) -> - switch direction - when "next" - toSelect = tabs[(currentTab.index + 1 + tabs.length) % tabs.length] - when "previous" - toSelect = tabs[(currentTab.index - 1 + tabs.length) % tabs.length] - when "first" - toSelect = tabs[0] - when "last" - toSelect = tabs[tabs.length - 1] - selectionChangedHandlers.push(callback) - chrome.tabs.update(toSelect.id, { selected: true }))) + chrome.tabs.getSelected null, (currentTab) -> + toSelect = + switch direction + when "next" + currentTab.index + count + when "previous" + currentTab.index - count + when "first" + 0 + when "last" + tabs.length - 1 + # Bring toSelect into the range [0,tabs.length). + toSelect = (toSelect + tabs.length * Math.abs count) % tabs.length + chrome.tabs.update tabs[toSelect].id, selected: true updateOpenTabs = (tab, deleteFrames = false) -> # Chrome might reuse the tab ID of a recently removed tab. @@ -389,7 +396,8 @@ setIcon = (request, sender) -> chrome.browserAction.setIcon tabId: sender.tab.id, path: path handleUpdateScrollPosition = (request, sender) -> - updateScrollPosition(sender.tab, request.scrollX, request.scrollY) + # See note regarding sender.tab at unregisterFrame. + updateScrollPosition sender.tab, request.scrollX, request.scrollY if sender.tab? updateScrollPosition = (tab, scrollX, scrollY) -> tabInfoMap[tab.id].scrollX = scrollX @@ -602,7 +610,10 @@ registerFrame = (request, sender) -> (frameIdsForTab[sender.tab.id] ?= []).push request.frameId unregisterFrame = (request, sender) -> - tabId = sender.tab.id + # When a tab is closing, Chrome sometimes passes messages without sender.tab. Therefore, we guard against + # this. + tabId = sender.tab?.id + return unless tabId? if frameIdsForTab[tabId]? if request.tab_is_closing updateOpenTabs sender.tab, true @@ -613,14 +624,21 @@ handleFrameFocused = (request, sender) -> tabId = sender.tab.id # Cycle frameIdsForTab to the focused frame. However, also ensure that we don't inadvertently register a # frame which wasn't previously registered (such as a frameset). - if frameIdsForTab[tabId]? and request.frameId in frameIdsForTab[tabId] - frameIdsForTab[tabId] = - [request.frameId, (frameIdsForTab[tabId].filter (id) -> id != request.frameId)...] + if frameIdsForTab[tabId]? + frameIdsForTab[tabId] = cycleToFrame frameIdsForTab[tabId], request.frameId # Inform all frames that a frame has received the focus. chrome.tabs.sendMessage sender.tab.id, name: "frameFocused" focusFrameId: request.frameId +# Rotate through frames to the frame count places after frameId. +cycleToFrame = (frames, frameId, count = 0) -> + frames ||= [] + # We can't always track which frame chrome has focussed, but here we learn that it's frameId; so add an + # additional offset such that we do indeed start from frameId. + count = (count + Math.max 0, frames.indexOf frameId) % frames.length + [frames[count..]..., frames[0...count]...] + # Send a message to all frames in the current tab. sendMessageToFrames = (request, sender) -> chrome.tabs.sendMessage sender.tab.id, request.message @@ -633,7 +651,7 @@ bgLog = (request, sender) -> portHandlers = keyDown: handleKeyDown, settings: handleSettings, - filterCompleter: filterCompleter + completions: handleCompletions sendRequestHandlers = getCompletionKeys: getCompletionKeysRequest @@ -651,7 +669,6 @@ sendRequestHandlers = pasteFromClipboard: pasteFromClipboard isEnabledForUrl: isEnabledForUrl selectSpecificTab: selectSpecificTab - refreshCompleter: refreshCompleter createMark: Marks.create.bind(Marks) gotoMark: Marks.goto.bind(Marks) setIcon: setIcon @@ -672,6 +689,13 @@ chrome.tabs.onRemoved.addListener (tabId) -> # There are no remaining incognito-mode tabs, and findModeRawQueryListIncognito is set. chrome.storage.local.remove "findModeRawQueryListIncognito" +# Tidy up tab caches when tabs are removed. We cannot rely on unregisterFrame because Chrome does not always +# provide sender.tab there. +# NOTE(smblott) (2015-05-05) This may break restoreTab on legacy Chrome versions, but we'll be moving to +# chrome.sessions support only soon anyway. +chrome.tabs.onRemoved.addListener (tabId) -> + delete cache[tabId] for cache in [ frameIdsForTab, urlForTab, tabInfoMap ] + # Convenience function for development use. window.runTests = -> open(chrome.runtime.getURL('tests/dom_tests/dom_tests.html')) |
