diff options
Diffstat (limited to 'background_scripts')
| -rw-r--r-- | background_scripts/completion_engines.coffee | 10 | ||||
| -rw-r--r-- | background_scripts/completion_search.coffee | 6 | ||||
| -rw-r--r-- | background_scripts/main.coffee | 3 | ||||
| -rw-r--r-- | background_scripts/marks.coffee | 131 | 
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 | 
