aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts
diff options
context:
space:
mode:
authorStephen Blott2015-05-06 11:50:28 +0100
committerStephen Blott2015-05-06 11:50:28 +0100
commitf330e3243bc27f1a19040fb386fc877fe82fbefe (patch)
treebe6b6087103fc954d8b80558906ae2c1aa6e55bc /background_scripts
parent5752c0ead0a65fc2329515509f66e00bd6ee2f60 (diff)
downloadvimium-f330e3243bc27f1a19040fb386fc877fe82fbefe.tar.bz2
Search completion; yet more tweaks.
Diffstat (limited to 'background_scripts')
-rw-r--r--background_scripts/completion.coffee11
-rw-r--r--background_scripts/completion_engines.coffee63
-rw-r--r--background_scripts/main.coffee4
3 files changed, 41 insertions, 37 deletions
diff --git a/background_scripts/completion.coffee b/background_scripts/completion.coffee
index bffb9700..18bfafd1 100644
--- a/background_scripts/completion.coffee
+++ b/background_scripts/completion.coffee
@@ -8,8 +8,7 @@
# A completer is a class which has three functions:
# - filter(query, onComplete): "query" will be whatever the user typed into the Vomnibox.
# - refresh(): (optional) refreshes the completer's data source (e.g. refetches the list of bookmarks).
-# - userIsTyping(): (optional) informs the completer that the user is typing (and pending completions may no
-# longer be needed).
+# - cancel(): (optional) cancels any pending, cancelable action.
class Suggestion
showRelevancy: true # Set this to true to render relevancy when debugging the ranking scores.
@@ -351,8 +350,8 @@ class TabCompleter
class SearchEngineCompleter
searchEngines: {}
- userIsTyping: ->
- CompletionEngines.userIsTyping()
+ cancel: ->
+ CompletionEngines.cancel()
filter: (queryTerms, onComplete) ->
suggestions = []
@@ -462,8 +461,8 @@ class MultiCompleter
refresh: ->
completer.refresh?() for completer in @completers
- userIsTyping: ->
- completer.userIsTyping?() for completer in @completers
+ cancel: ->
+ completer.cancel?() for completer in @completers
filter: (queryTerms, onComplete) ->
# Allow only one query to run at a time.
diff --git a/background_scripts/completion_engines.coffee b/background_scripts/completion_engines.coffee
index 1386256f..b5caadd7 100644
--- a/background_scripts/completion_engines.coffee
+++ b/background_scripts/completion_engines.coffee
@@ -22,8 +22,9 @@ class RegexpEngine
constructor: (@regexps) ->
match: (searchUrl) -> Utils.matchesAnyRegexp @regexps, searchUrl
-# Several Google completion engines package responses in this way.
+# Several Google completion engines package XML responses in this way.
class GoogleXMLRegexpEngine extends RegexpEngine
+ doNotCache: true
parse: (xhr) ->
for suggestion in xhr.responseXML.getElementsByTagName "suggestion"
continue unless suggestion = suggestion.getAttribute "data"
@@ -50,6 +51,7 @@ class Youtube extends GoogleXMLRegexpEngine
"http://suggestqueries.google.com/complete/search?client=youtube&ds=yt&xml=t&q=#{Utils.createSearchQuery queryTerms}"
class Wikipedia extends RegexpEngine
+ doNotCache: true
# Example search URL: http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s
constructor: ->
super [ new RegExp "^https?://[a-z]+\.wikipedia\.org/" ]
@@ -96,8 +98,8 @@ class DuckDuckGo extends RegexpEngine
suggestion.phrase for suggestion in JSON.parse xhr.responseText
# A dummy search engine which is guaranteed to match any search URL, but never produces completions. This
-# allows the rest of the logic to be written knowing that there will be a search engine match.
-class DummySearchEngine
+# allows the rest of the logic to be written knowing that there will always be a completion engine match.
+class DummyCompletionEngine
match: -> true
# We return a useless URL which we know will succeed, but which won't generate any network traffic.
getUrl: -> chrome.runtime.getURL "content_scripts/vimium.css"
@@ -110,13 +112,19 @@ completionEngines = [
Wikipedia
Bing
Amazon
- DummySearchEngine
+ DummyCompletionEngine
]
+# A note on caching.
+# Some completion engines allow caching, and Chrome serves up cached responses to requests (e.g. Google,
+# Wikipedia, YouTube). Others do not (e.g. Bing, DuckDuckGo, Amazon). A completion engine can set
+# @doNotCache to a truthy value to disable caching in cases where it is unnecessary.
+
CompletionEngines =
debug: true
- # The amount of time to wait for new completions before launching the HTTP request.
+ # The amount of time to wait for new requests before launching the HTTP request. The intention is to cut
+ # down on the number of HTTP requests we issue.
delay: 250
get: (searchUrl, url, callback) ->
@@ -128,11 +136,12 @@ CompletionEngines =
xhr.onreadystatechange = ->
if xhr.readyState == 4
+ console.log xhr.getAllResponseHeaders()
callback(if xhr.status == 200 then xhr else null)
- # Look up the search-completion engine for this searchUrl. Because of DummySearchEngine, above, we know
- # there will always be a match. Imagining that there may be many completion engines, and knowing that this
- # is called for every input event in the vomnibar, we cache the result.
+ # Look up the completion engine for this searchUrl. Because of DummyCompletionEngine, above, we know there
+ # will always be a match. Imagining that there may be many completion engines, and knowing that this is
+ # called for every input event in the vomnibar, we cache the result.
lookupEngine: (searchUrl) ->
@engineCache ?= new SimpleCache 30 * 60 * 60 * 1000 # 30 hours (these are small, we can keep them longer).
if @engineCache.has searchUrl
@@ -145,29 +154,25 @@ CompletionEngines =
# This is the main entry point.
# - searchUrl is the search engine's URL, e.g. Settings.get("searchUrl"), or a custome search engine's URL.
# This is only used as a key for determining the relevant completion engine.
- # - queryTerms are the queryTerms.
+ # - queryTerms are the query terms.
# - callback will be applied to a list of suggestion strings (which may be an empty list, if anything goes
# wrong).
complete: (searchUrl, queryTerms, callback) ->
@mostRecentHandler = null
+ query = queryTerms.join ""
- # We can't complete empty queries.
- return callback [] unless 0 < queryTerms.length
-
- if 1 == queryTerms.length
- # We don't complete URLs.
- return callback [] if Utils.isUrl queryTerms[0]
- # We don't complete less then three characters: the results are usually useless. This also prevents
- # one- and two-character custom search engine keywords from being sent to the default completer (e.g.
- # the initial "w" before typing "w something").
- return callback [] unless 2 < queryTerms[0].length
+ # We don't complete less then three characters: the results are usually useless. This also prevents
+ # one- and two-character custom search engine keywords from being sent to the default completer (e.g.
+ # the initial "w" before typing "w something" for Wikipedia).
+ return callback [] unless 3 <= query.length
- # We don't complete Javascript URLs.
- return callback [] if Utils.hasJavascriptPrefix queryTerms[0]
+ # We don't complete regular URLs or Javascript URLs.
+ return callback [] if 1 == queryTerms.length and Utils.isUrl query
+ return callback [] if Utils.hasJavascriptPrefix query
# Cache completions. However, completions depend upon both the searchUrl and the query terms. So we need
- # to generate a key. We mix in some junk generated by pwgen. A key clash is possible, but vanishingly
- # unlikely.
+ # to generate a key. We mix in some junk generated by pwgen. A key clash might be possible, but
+ # vanishingly unlikely.
junk = "//Zi?ei5;o//"
completionCacheKey = searchUrl + junk + queryTerms.join junk
@completionCache ?= new SimpleCache 60 * 60 * 1000, 2000 # One hour, 2000 entries.
@@ -180,8 +185,7 @@ CompletionEngines =
callback @completionCache.get completionCacheKey
return
- fetchSuggestions = (callback) =>
- engine = @lookupEngine searchUrl
+ fetchSuggestions = (engine, callback) =>
url = engine.getUrl queryTerms
query = queryTerms.join(" ").toLowerCase()
@get searchUrl, url, (xhr = null) =>
@@ -215,14 +219,15 @@ CompletionEngines =
@inTransit ?= {}
unless @inTransit[completionCacheKey]?.push callback
queue = @inTransit[completionCacheKey] = []
- fetchSuggestions (suggestions) =>
- callback @completionCache.set completionCacheKey, suggestions
+ engine = @lookupEngine searchUrl
+ fetchSuggestions engine, (suggestions) =>
+ callback @completionCache.set completionCacheKey, suggestions unless engine.doNotCache
delete @inTransit[completionCacheKey]
console.log "callbacks", queue.length, completionCacheKey if @debug and 0 < queue.length
callback suggestions for callback in queue
- userIsTyping: ->
- console.log "reset (typing)" if @debug and @mostRecentHandler?
+ cancel: ->
+ console.log "cancel (user is typing)" if @debug and @mostRecentHandler?
@mostRecentHandler = null
root = exports ? window
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index 066e4cb6..c1be9f8f 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -65,8 +65,8 @@ completionHandlers =
queryTerms = args.query.split(/\s+/).filter (s) -> 0 < s.length
completer.filter queryTerms, (results) -> port.postMessage id: args.id, results: results
- refreshCompleter: (completer) -> completer.refresh()
- userIsTyping: (completer) -> completer.userIsTyping()
+ refresh: (completer) -> completer.refresh()
+ cancel: (completer) -> completer.cancel()
handleCompletions = (args, port) ->
completionHandlers[args.handler] completers[args.name], args, port