aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts
diff options
context:
space:
mode:
authorStephen Blott2014-09-01 12:07:24 +0100
committerStephen Blott2014-09-02 08:43:36 +0100
commit41bdac83d2fd450569013dd5cfdb78239143ba24 (patch)
tree58c06e564cb5641102fbc6583071e84e094818f7 /background_scripts
parent1685640ccabe265c9f182a0175d8ce823db35b4b (diff)
downloadvimium-41bdac83d2fd450569013dd5cfdb78239143ba24.tar.bz2
Structured passkeys, internally and on the options and popup pages.
Diffstat (limited to 'background_scripts')
-rw-r--r--background_scripts/exclusions.coffee77
-rw-r--r--background_scripts/main.coffee87
-rw-r--r--background_scripts/settings.coffee17
3 files changed, 113 insertions, 68 deletions
diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee
new file mode 100644
index 00000000..326989bf
--- /dev/null
+++ b/background_scripts/exclusions.coffee
@@ -0,0 +1,77 @@
+root = exports ? window
+
+RegexpCache =
+ cache: {}
+ get: (pattern) ->
+ if regexp = @cache[pattern]
+ regexp
+ else
+ @cache[pattern] = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$")
+
+# The Exclusions class manages the exclusion rule setting.
+# An exclusion is an object with two attributes: pattern and passKeys.
+# The exclusions are an array of such objects (because the order matters).
+
+root.Exclusions = Exclusions =
+
+ rules: Settings.get("exclusionRules")
+
+ # Return the first exclusion rule matching the URL, or null.
+ getRule: (url) ->
+ for rule in @rules
+ return rule if url.match(RegexpCache.get(rule.pattern))
+ return null
+
+ setRules: (rules) ->
+ @rules = rules.filter (rule) -> rule and rule.pattern
+ Settings.set("exclusionRules",@rules)
+
+ postUpdateHook: (rules) ->
+ @rules = rules
+
+ # Update an existing rule or add a new rule.
+ updateOrAdd: (newRule) ->
+ seen = false
+ @rules.push(newRule)
+ @setRules(@rules.map (rule) -> if rule.pattern == newRule.pattern then (if seen then null else seen = newRule) else rule)
+
+ remove: (pattern) ->
+ @setRules(@rules.filter((rule) -> rule.pattern != pattern))
+
+ # DOM handling for the options page; populate the exclusionRules option.
+ populateOption: (exclusionRulesElement,enableSaveButton) ->
+ populate = =>
+ while exclusionRulesElement.firstChild
+ exclusionRulesElement.removeChild(exclusionRulesElement.firstChild)
+ for rule in @rules
+ exclusionRulesElement.appendChild(ExclusionRule.buildRuleElement(rule,enableSaveButton))
+ exclusionRulesElement.appendChild(ExclusionRule.buildRuleElement({pattern: "", passKeys: ""},enableSaveButton))
+ populate()
+ return {
+ saveOption: =>
+ @setRules(ExclusionRule.extractRule(element) for element in exclusionRulesElement.getElementsByClassName('exclusionRow'))
+ populate()
+ restoreToDefault: =>
+ Settings.clear("exclusionRules")
+ populate()
+ }
+
+# Development and debug only.
+# Enable this (temporarily) to restore legacy exclusion rules from backup.
+if false and Settings.has("excludedUrlsBackup")
+ Settings.clear("exclusionRules")
+ Settings.set("excludedUrls",Settings.get("excludedUrlsBackup"))
+
+if not Settings.has("exclusionRules") and Settings.has("excludedUrls")
+ # Migration from the legacy exclusion rules (settings: "excludedUrls" -> "exclusionRules").
+
+ parseLegacyRules = (lines) ->
+ for line in lines.trim().split("\n").map((line) -> line.trim())
+ if line.length and line.indexOf("#") != 0 and line.indexOf('"') != 0
+ parse = line.split(/\s+/)
+ { pattern: parse[0], passKeys: parse[1..].join("") }
+
+ Exclusions.setRules(parseLegacyRules(Settings.get("excludedUrls")))
+ # We'll keep a backup of the excludedUrls setting, just in case (and for testing).
+ Settings.set("excludedUrlsBackup",Settings.get("excludedUrls")) if not Settings.has("excludedUrlsBackup")
+ Settings.clear("excludedUrls")
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index dda1beae..4111ac06 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -73,57 +73,25 @@ getCurrentTabUrl = (request, sender) -> sender.tab.url
# whether any keys should be passed through to the underlying page.
#
root.isEnabledForUrl = isEnabledForUrl = (request) ->
- # Excluded URLs are stored as a series of URL expressions and optional passKeys, separated by newlines.
- # Lines for which the first non-blank character is "#" or '"' are comments.
- excludedLines = (line.trim() for line in Settings.get("excludedUrls").split("\n"))
- excludedSpecs = (line.split(/\s+/) for line in excludedLines when line and line.indexOf("#") != 0 and line.indexOf('"') != 0)
- for spec in excludedSpecs
- url = spec[0]
- # The user can add "*" to the URL which means ".*"
- regexp = new RegExp("^" + url.replace(/\*/g, ".*") + "$")
- if request.url.match(regexp)
- passKeys = spec[1..].join("")
- if passKeys
- # Enabled, but not for these keys.
- return { isEnabledForUrl: true, passKeys: passKeys, matchingUrl: url }
- # Wholly disabled.
- return { isEnabledForUrl: false, passKeys: "", matchingUrl: url }
- # Enabled (the default).
- { isEnabledForUrl: true, passKeys: undefined, matchingUrl: undefined }
-
-# Called by the popup UI. Strips leading/trailing whitespace and ignores new empty strings. If an existing
-# exclusion rule has been changed, then the existing rule is updated. Otherwise, the new rule is added.
-root.addExcludedUrl = (url) ->
- return unless url = url.trim()
-
- parse = url.split(/\s+/)
- url = parse[0]
- passKeys = parse[1..].join(" ")
- newSpec = (if passKeys then url + " " + passKeys else url)
-
- excludedUrls = Settings.get("excludedUrls").split("\n")
- excludedUrls.push(newSpec)
-
- # Update excludedUrls.
- # Try to keep the list as unchanged as possible: same order, same comments, same blank lines.
- seenNew = false
- newExcludedUrls = []
- for spec in excludedUrls
- spec = spec.trim()
- parse = spec.split(/\s+/)
- # Keep just one copy of the new exclusion rule.
- if parse.length and parse[0] == url
- if !seenNew
- newExcludedUrls.push(newSpec)
- seenNew = true
- continue
- # And just keep everything else.
- newExcludedUrls.push(spec)
-
- Settings.set("excludedUrls", newExcludedUrls.join("\n"))
-
- chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, active: true },
- (tabs) -> updateActiveState(tabs[0].id))
+ rule = Exclusions.getRule(request.url)
+ return { rule: rule, isEnabledForUrl: true, passKeys: rule.passKeys } if rule and rule.passKeys
+ return { rule: rule, isEnabledForUrl: false, passKeys: "" } if rule
+ return { rule: rule, isEnabledForUrl: true, passKeys: "" }
+
+# Called by the popup UI. If an existing exclusion rule has been changed, then the existing rule is updated.
+# Otherwise, the new rule is added.
+root.addExclusionRule = (pattern,passKeys) ->
+ if pattern = pattern.trim()
+ Exclusions.updateOrAdd({ pattern: pattern, passKeys: passKeys })
+ chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, active: true },
+ (tabs) -> updateActiveState(tabs[0].id))
+
+# Called by the popup UI. Remove all existing exclusion rules with this pattern.
+root.removeExclusionRule = (pattern) ->
+ if pattern = pattern.trim()
+ Exclusions.remove(pattern)
+ chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, active: true },
+ (tabs) -> updateActiveState(tabs[0].id))
saveHelpDialogSettings = (request) ->
Settings.set("helpDialog_showAdvancedCommands", request.showAdvancedCommands)
@@ -389,24 +357,21 @@ updateActiveState = (tabId) ->
partialIcon = "icons/browser_action_partial.png"
chrome.tabs.get tabId, (tab) ->
chrome.tabs.sendMessage tabId, { name: "getActiveState" }, (response) ->
- console.log response
if response
isCurrentlyEnabled = response.enabled
currentPasskeys = response.passKeys
- # TODO:
- # isEnabledForUrl is quite expensive to run each time we change tab. Perhaps memoize it?
- shouldHaveConfig = isEnabledForUrl({url: tab.url})
- shouldBeEnabled = shouldHaveConfig.isEnabledForUrl
- shouldHavePassKeys = shouldHaveConfig.passKeys
- if (shouldBeEnabled and shouldHavePassKeys)
+ config = isEnabledForUrl({url: tab.url})
+ enabled = config.isEnabledForUrl
+ passKeys = config.passKeys
+ if (enabled and passKeys)
setBrowserActionIcon(tabId,partialIcon)
- else if (shouldBeEnabled)
+ else if (enabled)
setBrowserActionIcon(tabId,enabledIcon)
else
setBrowserActionIcon(tabId,disabledIcon)
# Propagate the new state only if it has changed.
- if (isCurrentlyEnabled != shouldBeEnabled || currentPasskeys != shouldHavePassKeys)
- chrome.tabs.sendMessage(tabId, { name: "setState", enabled: shouldBeEnabled, passKeys: shouldHavePassKeys })
+ if (isCurrentlyEnabled != enabled || currentPasskeys != passKeys)
+ chrome.tabs.sendMessage(tabId, { name: "setState", enabled: enabled, passKeys: passKeys })
else
# We didn't get a response from the front end, so Vimium isn't running.
setBrowserActionIcon(tabId,disabledIcon)
diff --git a/background_scripts/settings.coffee b/background_scripts/settings.coffee
index 34d6e879..63dd851b 100644
--- a/background_scripts/settings.coffee
+++ b/background_scripts/settings.coffee
@@ -35,6 +35,9 @@ root.Settings = Settings =
searchEngines: (value) ->
root.Settings.parseSearchEngines value
+ exclusionRules: (value) ->
+ root.Exclusions.postUpdateHook value
+
# postUpdateHooks convenience wrapper
performPostUpdateHook: (key, value) ->
@postUpdateHooks[key] value if @postUpdateHooks[key]
@@ -81,14 +84,14 @@ root.Settings = Settings =
div > .vimiumHintMarker > .matchingCharacter {
}
"""
- excludedUrls:
- """
- # Disable Vimium on Gmail:
- http*://mail.google.com/*
+ # Default exclusion rules.
+ exclusionRules:
+ [
+ # Disable Vimium on Google Reader, and use Gmail's own j/k bindings.
+ { pattern: "http*://www.google.com/reader/*", passKeys: "" }
+ { pattern: "http*://mail.google.com/*", passKeys: "jk" }
+ ]
- # Use Facebook's own j/k bindings:
- http*://www.facebook.com/* jk
- """
# NOTE: If a page contains both a single angle-bracket link and a double angle-bracket link, then in
# most cases the single bracket link will be "prev/next page" and the double bracket link will be
# "first/last page", so we put the single bracket first in the pattern string so that it gets searched