aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts
diff options
context:
space:
mode:
Diffstat (limited to 'background_scripts')
-rw-r--r--background_scripts/completion.coffee26
-rw-r--r--background_scripts/main.coffee129
-rw-r--r--background_scripts/settings.coffee23
3 files changed, 72 insertions, 106 deletions
diff --git a/background_scripts/completion.coffee b/background_scripts/completion.coffee
index 177892fb..6a1c0d30 100644
--- a/background_scripts/completion.coffee
+++ b/background_scripts/completion.coffee
@@ -204,7 +204,7 @@ class DomainCompleter
domains: null
filter: (queryTerms, onComplete) ->
- return onComplete([]) if queryTerms.length > 1
+ return onComplete([]) unless queryTerms.length == 1
if @domains
@performSearch(queryTerms, onComplete)
else
@@ -344,11 +344,33 @@ class SearchEngineCompleter
computeRelevancy: -> 1
refresh: ->
- this.searchEngines = root.Settings.getSearchEngines()
+ @searchEngines = SearchEngineCompleter.getSearchEngines()
getSearchEngineMatches: (queryTerms) ->
(1 < queryTerms.length and @searchEngines[queryTerms[0]]) or {}
+ # Static data and methods for parsing the configured search engines. We keep a cache of the search-engine
+ # mapping in @searchEnginesMap.
+ @searchEnginesMap: null
+
+ # Parse the custom search engines setting and cache it in SearchEngineCompleter.searchEnginesMap.
+ @parseSearchEngines: (searchEnginesText) ->
+ searchEnginesMap = SearchEngineCompleter.searchEnginesMap = {}
+ for line in searchEnginesText.split /\n/
+ tokens = line.trim().split /\s+/
+ continue if tokens.length < 2 or tokens[0].startsWith('"') or tokens[0].startsWith("#")
+ keywords = tokens[0].split ":"
+ continue unless keywords.length == 2 and not keywords[1] # So, like: [ "w", "" ].
+ searchEnginesMap[keywords[0]] =
+ url: tokens[1]
+ description: tokens[2..].join(" ")
+
+ # Fetch the search-engine map, building it if necessary.
+ @getSearchEngines: ->
+ unless SearchEngineCompleter.searchEnginesMap?
+ SearchEngineCompleter.parseSearchEngines Settings.get "searchEngines"
+ SearchEngineCompleter.searchEnginesMap
+
# A completer which calls filter() on many completers, aggregates the results, ranks them, and returns the top
# 10. Queries from the vomnibar frontend script come through a multi completer.
class MultiCompleter
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index 0c7d9343..a26fd8a8 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -26,6 +26,7 @@ validFirstKeys = {}
singleKeyCommands = []
focusedFrame = null
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
@@ -57,7 +58,7 @@ completers =
bookmarks: new MultiCompleter([completionSources.bookmarks])
tabs: new MultiCompleter([completionSources.tabs])
-chrome.runtime.onConnect.addListener((port, name) ->
+chrome.runtime.onConnect.addListener (port, name) ->
senderTabId = if port.sender.tab then port.sender.tab.id else null
# If this is a tab we've been waiting to open, execute any "tab loaded" handlers, e.g. to restore
# the tab's scroll position. Wait until domReady before doing this; otherwise operations like restoring
@@ -69,14 +70,8 @@ chrome.runtime.onConnect.addListener((port, name) ->
delete tabLoadedHandlers[senderTabId]
toCall.call()
- # domReady is the appropriate time to show the "vimium has been upgraded" message.
- # TODO: This might be broken on pages with frames.
- if (shouldShowUpgradeMessage())
- chrome.tabs.sendMessage(senderTabId, { name: "showUpgradeNotification", version: currentVersion })
-
if (portHandlers[port.name])
port.onMessage.addListener(portHandlers[port.name])
-)
chrome.runtime.onMessage.addListener((request, sender, sendResponse) ->
if (sendRequestHandlers[request.handler])
@@ -183,14 +178,6 @@ openUrlInIncognito = (request) ->
chrome.windows.create({ url: Utils.convertToUrl(request.url), incognito: true})
#
-# Called when the user has clicked the close icon on the "Vimium has been updated" message.
-# We should now dismiss that message in all tabs.
-#
-upgradeNotificationClosed = (request) ->
- Settings.set("previousVersion", currentVersion)
- sendRequestToAllTabs({ name: "hideUpgradeNotification" })
-
-#
# Copies or pastes some data (request.data) to/from the clipboard.
# We return null to avoid the return value from the copy operations being passed to sendResponse.
#
@@ -368,56 +355,23 @@ updateOpenTabs = (tab, deleteFrames = false) ->
# Frames are recreated on refresh
delete frameIdsForTab[tab.id] if deleteFrames
-setBrowserActionIcon = (tabId,path) ->
- chrome.browserAction.setIcon({ tabId: tabId, path: path })
-
-chrome.browserAction.setBadgeBackgroundColor
- # This is Vimium blue (from the icon).
- # color: [102, 176, 226, 255]
- # This is a slightly darker blue. It makes the badge more striking in the corner of the eye, and the symbol
- # easier to read.
- color: [82, 156, 206, 255]
-
-setBadge = do ->
- current = null
- timer = null
- updateBadge = (badge, tabId) -> -> chrome.browserAction.setBadgeText text: badge, tabId: tabId
- (request, sender) ->
- badge = request.badge
- if badge? and badge != current
- current = badge
- clearTimeout timer if timer
- # We wait a few moments. This avoids badge flicker when there are rapid changes.
- timer = setTimeout updateBadge(badge, sender.tab.id), 50
-
-# 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.
-# Exported via root because it's called from the page popup.
-root.updateActiveState = updateActiveState = (tabId) ->
- enabledIcon = "icons/browser_action_enabled.png"
- disabledIcon = "icons/browser_action_disabled.png"
- partialIcon = "icons/browser_action_partial.png"
- chrome.tabs.get tabId, (tab) ->
- setBadge { badge: "" }, tab: { id: tabId }
- chrome.tabs.sendMessage tabId, { name: "getActiveState" }, (response) ->
- if response
- isCurrentlyEnabled = response.enabled
- currentPasskeys = response.passKeys
- config = isEnabledForUrl { url: tab.url }, { tab: tab }
- enabled = config.isEnabledForUrl
- passKeys = config.passKeys
- if (enabled and passKeys)
- setBrowserActionIcon(tabId,partialIcon)
- else if (enabled)
- setBrowserActionIcon(tabId,enabledIcon)
- else
- 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, incognito: tab.incognito })
- else
- setBrowserActionIcon tabId, disabledIcon
+# Here's how we set the page icon. The default is "disabled", so if we do nothing else, then we get the
+# grey-out disabled icon. Thereafter, we only set tab-specific icons, so there's no need to update the icon
+# when we visit a tab on which Vimium isn't running.
+#
+# For active tabs, when a frame starts, it requests its active state via isEnabledForUrl. We also check the
+# state every time a frame gets the focus. In both cases, the frame then updates the tab's icon accordingly.
+#
+# Exclusion rule changes (from either the options page or the page popup) propagate via the subsequent focus
+# change. In particular, whenever a frame next gets the focus, it requests its new state and sets the icon
+# accordingly.
+#
+setIcon = (request, sender) ->
+ path = switch request.icon
+ when "enabled" then "icons/browser_action_enabled.png"
+ when "partial" then "icons/browser_action_partial.png"
+ when "disabled" then "icons/browser_action_disabled.png"
+ chrome.browserAction.setIcon tabId: sender.tab.id, path: path
handleUpdateScrollPosition = (request, sender) ->
updateScrollPosition(sender.tab, request.scrollX, request.scrollY)
@@ -434,7 +388,6 @@ chrome.tabs.onUpdated.addListener (tabId, changeInfo, tab) ->
runAt: "document_start"
chrome.tabs.insertCSS tabId, cssConf, -> chrome.runtime.lastError
updateOpenTabs(tab) if changeInfo.url?
- updateActiveState(tabId)
chrome.tabs.onAttached.addListener (tabId, attachedInfo) ->
# We should update all the tabs in the old window and the new window.
@@ -469,8 +422,7 @@ chrome.tabs.onRemoved.addListener (tabId) ->
tabInfoMap.deletor = -> delete tabInfoMap[tabId]
setTimeout tabInfoMap.deletor, 1000
delete frameIdsForTab[tabId]
-
-chrome.tabs.onActiveChanged.addListener (tabId, selectInfo) -> updateActiveState(tabId)
+ delete urlForTab[tabId]
unless chrome.sessions
chrome.windows.onRemoved.addListener (windowId) -> delete tabQueue[windowId]
@@ -627,16 +579,6 @@ sendRequestToAllTabs = (args) ->
for tab in window.tabs
chrome.tabs.sendMessage(tab.id, args, null))
-#
-# Returns true if the current extension version is greater than the previously recorded version in
-# localStorage, and false otherwise.
-#
-shouldShowUpgradeMessage = ->
- # Avoid showing the upgrade notification when previousVersion is undefined, which is the case for new
- # installs.
- Settings.set("previousVersion", currentVersion) unless Settings.get("previousVersion")
- Utils.compareVersions(currentVersion, Settings.get("previousVersion")) == 1
-
openOptionsPageInNewTab = ->
chrome.tabs.getSelected(null, (tab) ->
chrome.tabs.create({ url: chrome.runtime.getURL("pages/options.html"), index: tab.index + 1 }))
@@ -654,6 +596,7 @@ unregisterFrame = (request, sender) ->
handleFrameFocused = (request, sender) ->
tabId = sender.tab.id
+ urlForTab[tabId] = request.url
if frameIdsForTab[tabId]?
frameIdsForTab[tabId] =
[request.frameId, (frameIdsForTab[tabId].filter (id) -> id != request.frameId)...]
@@ -687,7 +630,6 @@ sendRequestHandlers =
unregisterFrame: unregisterFrame
frameFocused: handleFrameFocused
nextFrame: (request) -> BackgroundCommands.nextFrame 1, request.frameId
- upgradeNotificationClosed: upgradeNotificationClosed
updateScrollPosition: handleUpdateScrollPosition
copyToClipboard: copyToClipboard
pasteFromClipboard: pasteFromClipboard
@@ -696,7 +638,7 @@ sendRequestHandlers =
refreshCompleter: refreshCompleter
createMark: Marks.create.bind(Marks)
gotoMark: Marks.goto.bind(Marks)
- setBadge: setBadge
+ setIcon: setIcon
sendMessageToFrames: sendMessageToFrames
log: bgLog
@@ -727,8 +669,30 @@ if Settings.has("keyMappings")
populateValidFirstKeys()
populateSingleKeyCommands()
-if shouldShowUpgradeMessage()
- sendRequestToAllTabs({ name: "showUpgradeNotification", version: currentVersion })
+
+# Show notification on upgrade.
+showUpgradeMessage = ->
+ # Avoid showing the upgrade notification when previousVersion is undefined, which is the case for new
+ # installs.
+ Settings.set "previousVersion", currentVersion unless Settings.get "previousVersion"
+ if Utils.compareVersions(currentVersion, Settings.get "previousVersion" ) == 1
+ notificationId = "VimiumUpgradeNotification"
+ notification =
+ type: "basic"
+ iconUrl: chrome.runtime.getURL "icons/vimium.png"
+ title: "Vimium Upgrade"
+ message: "Vimium has been upgraded to version #{currentVersion}. Click here for more information."
+ isClickable: true
+ if chrome.notifications?.create?
+ chrome.notifications.create notificationId, notification, ->
+ unless chrome.runtime.lastError
+ Settings.set "previousVersion", currentVersion
+ chrome.notifications.onClicked.addListener (id) ->
+ if id == notificationId
+ openUrlInNewTab url: "https://github.com/philc/vimium#release-notes"
+ else
+ # We need to wait for the user to accept the "notifications" permission.
+ chrome.permissions.onAdded.addListener showUpgradeMessage
# Ensure that tabInfoMap is populated when Vimium is installed.
chrome.windows.getAll { populate: true }, (windows) ->
@@ -741,3 +705,4 @@ chrome.windows.getAll { populate: true }, (windows) ->
# Start pulling changes from synchronized storage.
Sync.init()
+showUpgradeMessage()
diff --git a/background_scripts/settings.coffee b/background_scripts/settings.coffee
index 3528e8a9..a4d95c81 100644
--- a/background_scripts/settings.coffee
+++ b/background_scripts/settings.coffee
@@ -33,7 +33,7 @@ root.Settings = Settings =
root.refreshCompletionKeysAfterMappingSave()
searchEngines: (value) ->
- root.Settings.parseSearchEngines value
+ root.SearchEngineCompleter.parseSearchEngines value
exclusionRules: (value) ->
root.Exclusions.postUpdateHook value
@@ -42,27 +42,6 @@ root.Settings = Settings =
performPostUpdateHook: (key, value) ->
@postUpdateHooks[key] value if @postUpdateHooks[key]
- # Here we have our functions that parse the search engines
- # this is a map that we use to store our search engines for use.
- searchEnginesMap: {}
-
- # Parse the custom search engines setting and cache it.
- parseSearchEngines: (searchEnginesText) ->
- @searchEnginesMap = {}
- for line in searchEnginesText.split /\n/
- tokens = line.trim().split /\s+/
- continue if tokens.length < 2 or tokens[0].startsWith('"') or tokens[0].startsWith("#")
- keywords = tokens[0].split ":"
- continue unless keywords.length == 2 and not keywords[1] # So, like: [ "w", "" ].
- @searchEnginesMap[keywords[0]] =
- url: tokens[1]
- description: tokens[2..].join(" ")
-
- # Fetch the search-engine map, building it if necessary.
- getSearchEngines: ->
- this.parseSearchEngines(@get("searchEngines") || "") if Object.keys(@searchEnginesMap).length == 0
- @searchEnginesMap
-
# options.coffee and options.html only handle booleans and strings; therefore all defaults must be booleans
# or strings
defaults: