diff options
| author | Stephen Blott | 2014-09-01 12:07:24 +0100 | 
|---|---|---|
| committer | Stephen Blott | 2014-09-02 08:43:36 +0100 | 
| commit | 41bdac83d2fd450569013dd5cfdb78239143ba24 (patch) | |
| tree | 58c06e564cb5641102fbc6583071e84e094818f7 /background_scripts | |
| parent | 1685640ccabe265c9f182a0175d8ce823db35b4b (diff) | |
| download | vimium-41bdac83d2fd450569013dd5cfdb78239143ba24.tar.bz2 | |
Structured passkeys, internally and on the options and popup pages.
Diffstat (limited to 'background_scripts')
| -rw-r--r-- | background_scripts/exclusions.coffee | 77 | ||||
| -rw-r--r-- | background_scripts/main.coffee | 87 | ||||
| -rw-r--r-- | background_scripts/settings.coffee | 17 | 
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  | 
