From 41bdac83d2fd450569013dd5cfdb78239143ba24 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 1 Sep 2014 12:07:24 +0100 Subject: Structured passkeys, internally and on the options and popup pages. --- background_scripts/exclusions.coffee | 77 +++++++++++++++++++++++++++++++ background_scripts/main.coffee | 87 +++++++++++------------------------- background_scripts/settings.coffee | 17 ++++--- 3 files changed, 113 insertions(+), 68 deletions(-) create mode 100644 background_scripts/exclusions.coffee (limited to 'background_scripts') 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 -- cgit v1.2.3 From f2596ae0eaaa09a1ec5d641f048d7472f19c812b Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 2 Sep 2014 10:44:42 +0100 Subject: Retain excludedUrls setting, so testers can revert to previous versions. --- background_scripts/exclusions.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'background_scripts') diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee index 326989bf..8242c81b 100644 --- a/background_scripts/exclusions.coffee +++ b/background_scripts/exclusions.coffee @@ -74,4 +74,6 @@ if not Settings.has("exclusionRules") and Settings.has("excludedUrls") 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") + # TODO (smblott): Uncomment the following line. It's commented for now so that anyone trying out this code + # can revert to previous versions. + # Settings.clear("excludedUrls") -- cgit v1.2.3 From 76150e7b982b5cc6b68ad0dcf36c70382fdea30d Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 2 Sep 2014 13:47:14 +0100 Subject: Structured exclusion rules: Fix typos and minor issues. --- background_scripts/main.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'background_scripts') diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 4111ac06..47b215e5 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -74,9 +74,9 @@ getCurrentTabUrl = (request, sender) -> sender.tab.url # root.isEnabledForUrl = isEnabledForUrl = (request) -> 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: "" } + newIsEnabled = !rule or rule.passKeys + newPassKeys = if newIsEnabled and rule then rule.passKeys else "" + { rule: rule, isEnabledForUrl: newIsEnabled, passKeys: newPassKeys } # 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. -- cgit v1.2.3 From 2f27d4590ba30f5a443aedff12d9611a83a4f771 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 2 Sep 2014 16:46:17 +0100 Subject: Structured exclusion rules: Simplify isEnabledForUrl. --- background_scripts/main.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'background_scripts') diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 47b215e5..4e6f406e 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -74,9 +74,11 @@ getCurrentTabUrl = (request, sender) -> sender.tab.url # root.isEnabledForUrl = isEnabledForUrl = (request) -> rule = Exclusions.getRule(request.url) - newIsEnabled = !rule or rule.passKeys - newPassKeys = if newIsEnabled and rule then rule.passKeys else "" - { rule: rule, isEnabledForUrl: newIsEnabled, passKeys: newPassKeys } + { + rule: rule + isEnabledForUrl: not rule or rule.passKeys + passKeys: rule?.passKeys or "" + } # 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. -- cgit v1.2.3 From 643e49aa3109b9a25b38ce5d6b59bb11bc6337b1 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 5 Sep 2014 10:59:20 +0100 Subject: Structured passkeys; changes following code review; major rewrite of options. --- background_scripts/exclusions.coffee | 47 +++++++++++++++--------------------- background_scripts/main.coffee | 4 +-- background_scripts/settings.coffee | 6 ++--- 3 files changed, 24 insertions(+), 33 deletions(-) (limited to 'background_scripts') diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee index 8242c81b..3a8ef1e7 100644 --- a/background_scripts/exclusions.coffee +++ b/background_scripts/exclusions.coffee @@ -23,8 +23,9 @@ root.Exclusions = Exclusions = return null setRules: (rules) -> + # Callers map a rule to null to have it deleted, and rules without a pattern are useless. @rules = rules.filter (rule) -> rule and rule.pattern - Settings.set("exclusionRules",@rules) + Settings.set("exclusionRules", @rules) postUpdateHook: (rules) -> @rules = rules @@ -33,37 +34,29 @@ root.Exclusions = Exclusions = 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) + @setRules @rules.map (rule) -> + if rule.pattern == newRule.pattern + 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() - } + @setRules(@rules.filter((rule) -> rule and rule.pattern != pattern)) # 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")) + Settings.set("excludedUrls", Settings.get("excludedUrlsBackup")) if not Settings.has("exclusionRules") and Settings.has("excludedUrls") - # Migration from the legacy exclusion rules (settings: "excludedUrls" -> "exclusionRules"). + # Migration from the legacy representation of exclusion rules. + # + # In Vimium 1.45 and in github/master on 27 August, 2014, exclusion rules are represented by the setting: + # excludedUrls: "http*://www.google.com/reader/*\nhttp*://mail.google.com/* jk" + # + # The new (equivalent) settings is: + # exclusionRules: [ { pattern: "http*://www.google.com/reader/*", passKeys: "" }, { pattern: "http*://mail.google.com/*", passKeys: "jk" } ] parseLegacyRules = (lines) -> for line in lines.trim().split("\n").map((line) -> line.trim()) @@ -72,8 +65,6 @@ if not Settings.has("exclusionRules") and Settings.has("excludedUrls") { 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") - # TODO (smblott): Uncomment the following line. It's commented for now so that anyone trying out this code - # can revert to previous versions. - # Settings.clear("excludedUrls") + # We'll keep a backup of the "excludedUrls" setting, just in case. + 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 4e6f406e..352cfa48 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -80,8 +80,8 @@ root.isEnabledForUrl = isEnabledForUrl = (request) -> passKeys: rule?.passKeys or "" } -# 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. +# Called by the popup UI. +# If the URL pattern matches an existing rule, then the existing rule is updated. Otherwise, a new rule is created. root.addExclusionRule = (pattern,passKeys) -> if pattern = pattern.trim() Exclusions.updateOrAdd({ pattern: pattern, passKeys: passKeys }) diff --git a/background_scripts/settings.coffee b/background_scripts/settings.coffee index 63dd851b..7150fcba 100644 --- a/background_scripts/settings.coffee +++ b/background_scripts/settings.coffee @@ -61,6 +61,7 @@ root.Settings = Settings = # or strings defaults: scrollStepSize: 60 + keyMappings: "# Insert your prefered key mappings here." linkHintCharacters: "sadfjklewcmpgh" linkHintNumbers: "0123456789" filterLinkHints: false @@ -87,9 +88,8 @@ root.Settings = Settings = # 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" } + # Disable Vimium on Gmail. + { pattern: "http*://mail.google.com/*", passKeys: "" } ] # NOTE: If a page contains both a single angle-bracket link and a double angle-bracket link, then in -- cgit v1.2.3