diff options
Diffstat (limited to 'background_scripts/main.coffee')
| -rw-r--r-- | background_scripts/main.coffee | 99 |
1 files changed, 63 insertions, 36 deletions
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 72f87a7e..e3188a26 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -5,6 +5,7 @@ root = exports ? window chrome.runtime.onInstalled.addListener ({ reason }) -> # See https://developer.chrome.com/extensions/runtime#event-onInstalled return if reason in [ "chrome_update", "shared_module_update" ] + return if Utils.isFirefox() manifest = chrome.runtime.getManifest() # Content scripts loaded on every page should be in the same group. We assume it is the first. contentScripts = manifest.content_scripts[0] @@ -19,7 +20,7 @@ chrome.runtime.onInstalled.addListener ({ reason }) -> func tab.id, { file: file, allFrames: contentScripts.all_frames }, checkLastRuntimeError frameIdsForTab = {} -portsForTab = {} +root.portsForTab = {} root.urlForTab = {} # This is exported for use by "marks.coffee". @@ -50,6 +51,10 @@ completers = completionHandlers = filter: (completer, request, port) -> completer.filter request, (response) -> + # NOTE(smblott): response contains `relevancyFunction` (function) properties which cause postMessage, + # below, to fail in Firefox. See #2576. We cannot simply delete these methods, as they're needed + # elsewhere. Converting the response to JSON and back is a quick and easy way to sanitize the object. + response = JSON.parse JSON.stringify 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). @@ -99,10 +104,27 @@ TabOperations = tabConfig = url: Utils.convertToUrl request.url index: request.tab.index + 1 - selected: true + active: true windowId: request.tab.windowId - openerTabId: request.tab.id - chrome.tabs.create tabConfig, callback + tabConfig.active = request.active if request.active? + # Firefox does not support "about:newtab" in chrome.tabs.create. + delete tabConfig["url"] if tabConfig["url"] == Settings.defaults.newTabUrl + + # Firefox <57 throws an error when openerTabId is used (issue 1238314). + canUseOpenerTabId = not (Utils.isFirefox() and Utils.compareVersions(Utils.firefoxVersion(), "57") < 0) + tabConfig.openerTabId = request.tab.id if canUseOpenerTabId + + chrome.tabs.create tabConfig, -> callback request + + # Opens request.url in new window and switches to it. + openUrlInNewWindow: (request, callback = (->)) -> + winConfig = + url: Utils.convertToUrl request.url + active: true + winConfig.active = request.active if request.active? + # Firefox does not support "about:newtab" in chrome.tabs.create. + delete tabConfig["url"] if tabConfig["url"] == Settings.defaults.newTabUrl + chrome.windows.create winConfig, callback toggleMuteTab = do -> muteTab = (tab) -> chrome.tabs.update tab.id, {muted: !tab.mutedInfo.muted} @@ -126,12 +148,12 @@ toggleMuteTab = do -> # selectSpecificTab = (request) -> chrome.tabs.get(request.id, (tab) -> - chrome.windows.update(tab.windowId, { focused: true }) - chrome.tabs.update(request.id, { selected: true })) + chrome.windows?.update(tab.windowId, { focused: true }) + chrome.tabs.update(request.id, { active: true })) moveTab = ({count, tab, registryEntry}) -> count = -count if registryEntry.command == "moveTabLeft" - chrome.tabs.getAllInWindow null, (tabs) -> + chrome.tabs.query { currentWindow: true }, (tabs) -> pinnedCount = (tabs.filter (tab) -> tab.pinned).length minIndex = if tab.pinned then 0 else pinnedCount maxIndex = (if tab.pinned then pinnedCount else tabs.length) - 1 @@ -166,13 +188,20 @@ BackgroundCommands = [if request.tab.incognito then "chrome://newtab" else chrome.runtime.getURL newTabUrl] else [newTabUrl] - urls = request.urls[..].reverse() - do openNextUrl = (request) -> - if 0 < urls.length - TabOperations.openUrlInNewTab (extend request, {url: urls.pop()}), (tab) -> - openNextUrl extend request, {tab, tabId: tab.id} - else - callback request + if request.registryEntry.options.incognito or request.registryEntry.options.window + windowConfig = + url: request.urls + focused: true + incognito: request.registryEntry.options.incognito ? false + chrome.windows.create windowConfig, -> callback request + else + urls = request.urls[..].reverse() + do openNextUrl = (request) -> + if 0 < urls.length + TabOperations.openUrlInNewTab (extend request, {url: urls.pop()}), (tab) -> + openNextUrl extend request, {tab, tabId: tab.id} + else + callback request duplicateTab: mkRepeatCommand (request, callback) -> chrome.tabs.duplicate request.tabId, (tab) -> callback extend request, {tab, tabId: tab.id} moveTabToNewWindow: ({count, tab}) -> @@ -192,8 +221,6 @@ BackgroundCommands = startTabIndex = Math.max 0, Math.min activeTabIndex, tabs.length - count chrome.tabs.remove (tab.id for tab in tabs[startTabIndex...startTabIndex + count]) restoreTab: mkRepeatCommand (request, callback) -> chrome.sessions.restore null, callback request - openCopiedUrlInCurrentTab: (request) -> TabOperations.openUrlInCurrentTab extend request, url: Clipboard.paste() - openCopiedUrlInNewTab: (request) -> @createTab extend request, url: Clipboard.paste() togglePinTab: ({tab}) -> chrome.tabs.update tab.id, {pinned: !tab.pinned} toggleMuteTab: toggleMuteTab moveTabLeft: moveTab @@ -226,7 +253,7 @@ removeTabsRelative = (direction, {tab: activeTab}) -> # Selects a tab before or after the currently selected tab. # - direction: "next", "previous", "first" or "last". selectTab = (direction, {count, tab}) -> - chrome.tabs.getAllInWindow null, (tabs) -> + chrome.tabs.query { currentWindow: true }, (tabs) -> if 1 < tabs.length toSelect = switch direction @@ -238,12 +265,11 @@ selectTab = (direction, {count, tab}) -> Math.min tabs.length - 1, count - 1 when "last" Math.max 0, tabs.length - count - chrome.tabs.update tabs[toSelect].id, selected: true + chrome.tabs.update tabs[toSelect].id, active: true -chrome.tabs.onUpdated.addListener (tabId, changeInfo, tab) -> - return unless changeInfo.status == "loading" # Only do this once per URL change. +chrome.webNavigation.onCommitted.addListener ({tabId, frameId}) -> cssConf = - allFrames: true + frameId: frameId code: Settings.get("userDefinedLinkHintCss") runAt: "document_start" chrome.tabs.insertCSS tabId, cssConf, -> chrome.runtime.lastError @@ -275,8 +301,9 @@ for icon in [ENABLED_ICON, DISABLED_ICON, PARTIAL_ICON] Frames = onConnect: (sender, port) -> [tabId, frameId] = [sender.tab.id, sender.frameId] - port.onDisconnect.addListener -> Frames.unregisterFrame {tabId, frameId} + port.onDisconnect.addListener -> Frames.unregisterFrame {tabId, frameId, port} port.postMessage handler: "registerFrameId", chromeFrameId: frameId + (portsForTab[tabId] ?= {})[frameId] = port # Return our onMessage handler for this port. (request, port) => @@ -284,13 +311,12 @@ Frames = registerFrame: ({tabId, frameId, port}) -> frameIdsForTab[tabId].push frameId unless frameId in frameIdsForTab[tabId] ?= [] - (portsForTab[tabId] ?= {})[frameId] = port - unregisterFrame: ({tabId, frameId}) -> - # FrameId 0 is the top/main frame. We never unregister that frame. If the tab is closing, then we tidy - # up elsewhere. If the tab is navigating to a new page, then a new top frame will be along soon. - # This mitigates against the unregister and register messages arriving out of order. See #2125. - if 0 < frameId + unregisterFrame: ({tabId, frameId, port}) -> + # Check that the port trying to unregister the frame hasn't already been replaced by a new frame + # registering. See #2125. + registeredPort = portsForTab[tabId]?[frameId] + if registeredPort == port or not registeredPort if tabId of frameIdsForTab frameIdsForTab[tabId] = (fId for fId in frameIdsForTab[tabId] when fId != frameId) if tabId of portsForTab @@ -299,10 +325,11 @@ Frames = isEnabledForUrl: ({request, tabId, port}) -> urlForTab[tabId] = request.url if request.frameIsFocused + request.isFirefox = Utils.isFirefox() # Update the value for Utils.isFirefox in the frontend. enabledState = Exclusions.isEnabledForUrl request.url if request.frameIsFocused - chrome.browserAction.setIcon tabId: tabId, imageData: do -> + chrome.browserAction.setIcon? tabId: tabId, imageData: do -> enabledStateIcon = if not enabledState.isEnabledForUrl DISABLED_ICON @@ -333,7 +360,7 @@ handleFrameFocused = ({tabId, frameId}) -> # Rotate through frames to the frame count places after frameId. cycleToFrame = (frames, frameId, count = 0) -> - # We can't always track which frame chrome has focussed, but here we learn that it's frameId; so add an + # We can't always track which frame chrome has focused, 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]...] @@ -362,7 +389,8 @@ HintCoordinator = prepareToActivateMode: (tabId, originatingFrameId, {modeIndex, isVimiumHelpDialog}) -> @tabState[tabId] = {frameIds: frameIdsForTab[tabId][..], hintDescriptors: {}, originatingFrameId, modeIndex} - @tabState[tabId].ports = extend {}, portsForTab[tabId] + @tabState[tabId].ports = {} + frameIdsForTab[tabId].map (frameId) => @tabState[tabId].ports[frameId] = portsForTab[tabId][frameId] @sendMessage "getHintDescriptors", tabId, {modeIndex, isVimiumHelpDialog} # Receive hint descriptors from all frames and activate link-hints mode when we have them all. @@ -401,15 +429,14 @@ sendRequestHandlers = # getCurrentTabUrl is used by the content scripts to get their full URL, because window.location cannot help # with Chrome-specific URLs like "view-source:http:..". getCurrentTabUrl: ({tab}) -> tab.url - openUrlInNewTab: (request) -> TabOperations.openUrlInNewTab request + openUrlInNewTab: mkRepeatCommand (request, callback) -> TabOperations.openUrlInNewTab request, callback + openUrlInNewWindow: (request) -> TabOperations.openUrlInNewWindow request openUrlInIncognito: (request) -> chrome.windows.create incognito: true, url: Utils.convertToUrl request.url openUrlInCurrentTab: TabOperations.openUrlInCurrentTab openOptionsPageInNewTab: (request) -> chrome.tabs.create url: chrome.runtime.getURL("pages/options.html"), index: request.tab.index + 1 frameFocused: handleFrameFocused nextFrame: BackgroundCommands.nextFrame - copyToClipboard: Clipboard.copy.bind Clipboard - pasteFromClipboard: Clipboard.paste.bind Clipboard selectSpecificTab: selectSpecificTab createMark: Marks.create.bind(Marks) gotoMark: Marks.goto.bind(Marks) @@ -426,7 +453,7 @@ chrome.tabs.onRemoved.addListener (tabId) -> delete cache[tabId] for cache in [frameIdsForTab, urlForTab, portsForTab, HintCoordinator.tabState] chrome.storage.local.get "findModeRawQueryListIncognito", (items) -> if items.findModeRawQueryListIncognito - chrome.windows.getAll null, (windows) -> + chrome.windows?.getAll null, (windows) -> for window in windows return if window.incognito # There are no remaining incognito-mode tabs, and findModeRawQueryListIncognito is set. @@ -467,7 +494,7 @@ do showUpgradeMessage = -> Settings.set "previousVersion", currentVersion chrome.notifications.onClicked.addListener (id) -> if id == notificationId - chrome.tabs.getSelected null, (tab) -> + chrome.tabs.query { active: true, currentWindow: true }, ([tab]) -> TabOperations.openUrlInNewTab {tab, tabId: tab.id, url: "https://github.com/philc/vimium#release-notes"} else # We need to wait for the user to accept the "notifications" permission. |
