aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts
diff options
context:
space:
mode:
Diffstat (limited to 'background_scripts')
-rw-r--r--background_scripts/completion_engines.coffee10
-rw-r--r--background_scripts/completion_search.coffee6
-rw-r--r--background_scripts/main.coffee3
-rw-r--r--background_scripts/marks.coffee131
4 files changed, 107 insertions, 43 deletions
diff --git a/background_scripts/completion_engines.coffee b/background_scripts/completion_engines.coffee
index 9a88d491..dcbf99c6 100644
--- a/background_scripts/completion_engines.coffee
+++ b/background_scripts/completion_engines.coffee
@@ -46,14 +46,14 @@ class Google extends GoogleXMLRegexpEngine
class GoogleWithPrefix
constructor: (prefix, args...) ->
@engine = new Google args...
- @prefix = "#{prefix.trim()} "
- @queryTerms = @prefix.split /\s+/
+ @prefix = "#{prefix} "
+ @queryTerms = prefix.split /\s+/
match: (args...) -> @engine.match args...
getUrl: (queryTerms) -> @engine.getUrl [ @queryTerms..., queryTerms... ]
parse: (xhr) ->
- @engine.parse(xhr)
- .filter (suggestion) => suggestion.startsWith @prefix
- .map (suggestion) => suggestion[@prefix.length..].ltrim()
+ for suggestion in @engine.parse xhr
+ continue unless suggestion.startsWith @prefix
+ suggestion[@prefix.length..].ltrim()
# For Google Maps, we add the prefix "map of" to the query, and send it to Google's general search engine,
# then strip "map of" from the resulting suggestions.
diff --git a/background_scripts/completion_search.coffee b/background_scripts/completion_search.coffee
index d89eb278..b26194e6 100644
--- a/background_scripts/completion_search.coffee
+++ b/background_scripts/completion_search.coffee
@@ -12,7 +12,7 @@ CompletionSearch =
get: (searchUrl, url, callback) ->
xhr = new XMLHttpRequest()
xhr.open "GET", url, true
- xhr.timeout = 1000
+ xhr.timeout = 2500
xhr.ontimeout = xhr.onerror = -> callback null
xhr.send()
@@ -115,8 +115,8 @@ CompletionSearch =
console.log "GET", url if @debug
catch
suggestions = []
- # We allow failures to be cached too, but remove them after just thirty minutes.
- Utils.setTimeout 30 * 60 * 1000, => @completionCache.set completionCacheKey, null
+ # We allow failures to be cached too, but remove them after just thirty seconds.
+ Utils.setTimeout 30 * 1000, => @completionCache.set completionCacheKey, null
console.log "fail", url if @debug
callback suggestions
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index 0d56e54d..835b8a9a 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -35,7 +35,8 @@ namedKeyRegex = /^(<(?:[amc]-.|(?:[amc]-)?[a-z0-9]{2,5})>)(.*)$/
# Event handlers
selectionChangedHandlers = []
-tabLoadedHandlers = {} # tabId -> function()
+# Note. tabLoadedHandlers handlers is exported for use also by "marks.coffee".
+root.tabLoadedHandlers = {} # tabId -> function()
# A secret, available only within the current instantiation of Vimium. The secret is big, likely unguessable
# in practice, but less than 2^31.
diff --git a/background_scripts/marks.coffee b/background_scripts/marks.coffee
index 15d41205..6e5f08ba 100644
--- a/background_scripts/marks.coffee
+++ b/background_scripts/marks.coffee
@@ -1,34 +1,97 @@
-root = window.Marks = {}
-
-marks = {}
-
-root.create = (req, sender) ->
- marks[req.markName] =
- tabId: sender.tab.id
- scrollX: req.scrollX
- scrollY: req.scrollY
-
-chrome.tabs.onUpdated.addListener (tabId, changeInfo, tab) ->
- if changeInfo.url?
- removeMarksForTab tabId
-
-chrome.tabs.onRemoved.addListener (tabId, removeInfo) ->
- # XXX(jez): what about restored tabs?
- removeMarksForTab tabId
-
-removeMarksForTab = (id) ->
- for markName, mark of marks
- if mark.tabId is id
- delete marks[markName]
-
-root.goto = (req, sender) ->
- mark = marks[req.markName]
- chrome.tabs.update mark.tabId, selected: true
- chrome.tabs.sendMessage mark.tabId,
- name: "setScrollPosition"
- scrollX: mark.scrollX
- scrollY: mark.scrollY
- chrome.tabs.sendMessage mark.tabId,
- name: "showHUDforDuration",
- text: "Jumped to global mark '#{req.markName}'"
- duration: 1000
+
+Marks =
+ # This returns the key which is used for storing mark locations in chrome.storage.sync.
+ getLocationKey: (markName) -> "vimiumGlobalMark|#{markName}"
+
+ # Get the part of a URL we use for matching here (that is, everything up to the first anchor).
+ getBaseUrl: (url) -> url.split("#")[0]
+
+ # Create a global mark. We record vimiumSecret with the mark so that we can tell later, when the mark is
+ # used, whether this is the original Vimium session or a subsequent session. This affects whether or not
+ # tabId can be considered valid.
+ create: (req, sender) ->
+ chrome.storage.local.get "vimiumSecret", (items) =>
+ markInfo =
+ vimiumSecret: items.vimiumSecret
+ markName: req.markName
+ url: @getBaseUrl sender.tab.url
+ tabId: sender.tab.id
+ scrollX: req.scrollX
+ scrollY: req.scrollY
+
+ if markInfo.scrollX? and markInfo.scrollY?
+ @saveMark markInfo
+ else
+ # The front-end frame hasn't provided the scroll position (because it's not the top frame within its
+ # tab). We need to ask the top frame what its scroll position is. (With the frame Id set to 0, below,
+ # the request will only be handled by the top frame within the tab.)
+ chrome.tabs.sendMessage sender.tab.id, name: "getScrollPosition", frameId: 0, (response) =>
+ @saveMark extend markInfo, scrollX: response.scrollX, scrollY: response.scrollY
+
+ saveMark: (markInfo) ->
+ item = {}
+ item[@getLocationKey markInfo.markName] = markInfo
+ chrome.storage.sync.set item
+
+ # Goto a global mark. We try to find the original tab. If we can't find that, then we try to find another
+ # tab with the original URL, and use that. And if we can't find such an existing tab, then we create a new
+ # one. Whichever of those we do, we then set the scroll position to the original scroll position.
+ goto: (req, sender) ->
+ chrome.storage.local.get "vimiumSecret", (items) =>
+ vimiumSecret = items.vimiumSecret
+ key = @getLocationKey req.markName
+ chrome.storage.sync.get key, (items) =>
+ markInfo = items[key]
+ if not markInfo
+ # The mark is not defined.
+ chrome.tabs.sendMessage sender.tab.id,
+ name: "showHUDforDuration",
+ text: "Global mark not set: '#{req.markName}'."
+ duration: 1000
+ else if markInfo.vimiumSecret != vimiumSecret
+ # This is a different Vimium instantiation, so markInfo.tabId is definitely out of date.
+ @focusOrLaunch markInfo
+ else
+ # Check whether markInfo.tabId still exists. According to here (https://developer.chrome.com/extensions/tabs),
+ # tab Ids are unqiue within a Chrome session. So, if we find a match, we can use it.
+ chrome.tabs.get markInfo.tabId, (tab) =>
+ if not chrome.runtime.lastError and tab?.url and markInfo.url == @getBaseUrl tab.url
+ # The original tab still exists.
+ @gotoPositionInTab markInfo
+ else
+ # The original tab no longer exists.
+ @focusOrLaunch markInfo
+
+ # Focus an existing tab and scroll to the given position within it.
+ gotoPositionInTab: ({ tabId, scrollX, scrollY, markName }) ->
+ chrome.tabs.update tabId, { selected: true }, ->
+ chrome.tabs.sendMessage tabId,
+ { name: "setScrollPosition", scrollX: scrollX, scrollY: scrollY }, ->
+ chrome.tabs.sendMessage tabId,
+ name: "showHUDforDuration",
+ text: "Jumped to global mark '#{markName}'."
+ duration: 1000
+
+ # The tab we're trying to find no longer exists. We either find another tab with a matching URL and use it,
+ # or we create a new tab.
+ focusOrLaunch: (markInfo) ->
+ chrome.tabs.query { url: markInfo.url }, (tabs) =>
+ if 0 < tabs.length
+ # We have a matching tab: use it (prefering, if there are more than one, one in the current window).
+ @pickTabInWindow tabs, (tab) =>
+ @gotoPositionInTab extend markInfo, tabId: tab.id
+ else
+ # There is no existing matching tab, we'll have to create one.
+ chrome.tabs.create { url: @getBaseUrl markInfo.url }, (tab) =>
+ # Note. tabLoadedHandlers is defined in "main.coffee". The handler below will be called when the tab
+ # is loaded, its DOM is ready and it registers with the background page.
+ tabLoadedHandlers[tab.id] = => @gotoPositionInTab extend markInfo, tabId: tab.id
+
+ # Given a list of tabs, pick one in the current window, if possible, otherwise just pick any.
+ pickTabInWindow: (tabs, continuation) ->
+ chrome.windows.getCurrent ({ id }) ->
+ tabsInWindow = tabs.filter (tab) -> tab.windowId == id
+ continuation tabsInWindow[0] ? tabs[0]
+
+root = exports ? window
+root.Marks = Marks