aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhil Crosby2014-08-27 00:50:00 -0700
committerPhil Crosby2014-08-27 00:50:00 -0700
commit3e393b2143ca82796a923b5fe050eb688d1b85a6 (patch)
tree2d79e0028cc1ba8eab7205a9fd5f6c2cef0cdae5
parent43caeb82d91e7cb88b085cb6e33a61a108322875 (diff)
parentb599492ea27c8c6bab38e87ef343968f5fcf58e7 (diff)
downloadvimium-3e393b2143ca82796a923b5fe050eb688d1b85a6.tar.bz2
Merge pull request #1143 from smblott-github/passkeys
Passkeys: Pass some keys to the underlying web page without wholly disabling Vimium.
-rw-r--r--background_scripts/main.coffee114
-rw-r--r--background_scripts/settings.coffee4
-rw-r--r--content_scripts/vimium_frontend.coffee78
-rw-r--r--icons/browser_action_partial.pngbin0 -> 34384 bytes
-rw-r--r--icons/icon48partial.pngbin0 -> 3815 bytes
-rw-r--r--pages/options.html25
-rw-r--r--pages/popup.coffee19
-rw-r--r--pages/popup.html6
-rw-r--r--tests/unit_tests/exclusion_test.coffee71
-rw-r--r--tests/unit_tests/test_chrome_stubs.coffee43
10 files changed, 279 insertions, 81 deletions
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index 431d9a31..dda1beae 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -69,27 +69,58 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) ->
getCurrentTabUrl = (request, sender) -> sender.tab.url
#
-# Checks the user's preferences in local storage to determine if Vimium is enabled for the given URL.
+# Checks the user's preferences in local storage to determine if Vimium is enabled for the given URL, and
+# whether any keys should be passed through to the underlying page.
#
-isEnabledForUrl = (request) ->
- # excludedUrls are stored as a series of URL expressions separated by newlines.
- excludedUrls = Settings.get("excludedUrls").split("\n")
- isEnabled = true
- for url in excludedUrls
+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, ".*") + "$")
- isEnabled = false if request.url.match(regexp)
- { isEnabledForUrl: isEnabled }
-
-# Called by the popup UI. Strips leading/trailing whitespace and ignores empty strings.
+ 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()
- excludedUrls = Settings.get("excludedUrls")
- return if excludedUrls.indexOf(url) >= 0
+ parse = url.split(/\s+/)
+ url = parse[0]
+ passKeys = parse[1..].join(" ")
+ newSpec = (if passKeys then url + " " + passKeys else url)
- excludedUrls += "\n" + url
- Settings.set("excludedUrls", excludedUrls)
+ 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))
@@ -346,32 +377,39 @@ updateOpenTabs = (tab) ->
# Frames are recreated on refresh
delete framesForTab[tab.id]
-# Updates the browserAction icon to indicated whether Vimium is enabled or disabled on the current page.
-# Also disables Vimium if it is currently enabled but should be disabled according to the url blacklist.
+setBrowserActionIcon = (tabId,path) ->
+ chrome.browserAction.setIcon({ tabId: tabId, path: path })
+
+# Updates the browserAction icon to indicate whether Vimium is enabled or disabled on the current page.
+# Also propagates new enabled/disabled/passkeys state to active window, if necessary.
# This lets you disable Vimium on a page without needing to reload.
-#
-# Three situations are considered:
-# 1. Active tab is disabled -> disable icon
-# 2. Active tab is enabled and should be enabled -> enable icon
-# 3. Active tab is enabled but should be disabled -> disable icon and disable vimium
updateActiveState = (tabId) ->
enabledIcon = "icons/browser_action_enabled.png"
disabledIcon = "icons/browser_action_disabled.png"
- chrome.tabs.get(tabId, (tab) ->
- # Default to disabled state in case we can't connect to Vimium, primarily for the "New Tab" page.
- chrome.browserAction.setIcon({ path: disabledIcon })
- chrome.tabs.sendMessage(tabId, { name: "getActiveState" }, (response) ->
- isCurrentlyEnabled = (response? && response.enabled)
- shouldBeEnabled = isEnabledForUrl({url: tab.url}).isEnabledForUrl
-
- if (isCurrentlyEnabled)
- if (shouldBeEnabled)
- chrome.browserAction.setIcon({ path: enabledIcon })
+ 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)
+ setBrowserActionIcon(tabId,partialIcon)
+ else if (shouldBeEnabled)
+ setBrowserActionIcon(tabId,enabledIcon)
else
- chrome.browserAction.setIcon({ path: disabledIcon })
- chrome.tabs.sendMessage(tabId, { name: "disableVimium" })
+ 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 })
else
- chrome.browserAction.setIcon({ path: disabledIcon })))
+ # We didn't get a response from the front end, so Vimium isn't running.
+ setBrowserActionIcon(tabId,disabledIcon)
handleUpdateScrollPosition = (request, sender) ->
updateScrollPosition(sender.tab, request.scrollX, request.scrollY)
@@ -500,6 +538,14 @@ handleKeyDown = (request, port) ->
console.log("checking keyQueue: [", keyQueue + key, "]")
keyQueue = checkKeyQueue(keyQueue + key, port.sender.tab.id, request.frameId)
console.log("new KeyQueue: " + keyQueue)
+ # Tell the content script whether there are keys in the queue.
+ # FIXME: There is a race condition here. The behaviour in the content script depends upon whether this message gets
+ # back there before or after the next keystroke.
+ # That being said, I suspect there are other similar race conditions here, for example in checkKeyQueue().
+ # Steve (23 Aug, 14).
+ chrome.tabs.sendMessage(port.sender.tab.id,
+ name: "currentKeyQueue",
+ keyQueue: keyQueue)
checkKeyQueue = (keysToCheck, tabId, frameId) ->
refreshedCompletionKeys = false
diff --git a/background_scripts/settings.coffee b/background_scripts/settings.coffee
index 175f3262..34d6e879 100644
--- a/background_scripts/settings.coffee
+++ b/background_scripts/settings.coffee
@@ -83,7 +83,11 @@ root.Settings = Settings =
"""
excludedUrls:
"""
+ # Disable Vimium on Gmail:
http*://mail.google.com/*
+
+ # 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
diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee
index 565c9e61..137b9d1a 100644
--- a/content_scripts/vimium_frontend.coffee
+++ b/content_scripts/vimium_frontend.coffee
@@ -13,8 +13,11 @@ findModeQueryHasResults = false
findModeAnchorNode = null
isShowingHelpDialog = false
keyPort = null
-# Users can disable Vimium on URL patterns via the settings page.
+# Users can disable Vimium on URL patterns via the settings page. The following two variables
+# (isEnabledForUrl and passKeys) control Vimium's enabled/disabled behaviour.
isEnabledForUrl = true
+passKeys = null
+keyQueue = null
# The user's operating system.
currentCompletionKeys = null
validFirstKeys = null
@@ -115,42 +118,46 @@ initializePreDomReady = ->
getScrollPosition: -> scrollX: window.scrollX, scrollY: window.scrollY
setScrollPosition: (request) -> setScrollPosition request.scrollX, request.scrollY
executePageCommand: executePageCommand
- getActiveState: -> { enabled: isEnabledForUrl }
- disableVimium: disableVimium
+ getActiveState: -> { enabled: isEnabledForUrl, passKeys: passKeys }
+ setState: setState
+ currentKeyQueue: (request) -> keyQueue = request.keyQueue
chrome.runtime.onMessage.addListener (request, sender, sendResponse) ->
- # in the options page, we will receive requests from both content and background scripts. ignore those
+ # In the options page, we will receive requests from both content and background scripts. ignore those
# from the former.
return if sender.tab and not sender.tab.url.startsWith 'chrome-extension://'
- return unless isEnabledForUrl or request.name == 'getActiveState'
+ return unless isEnabledForUrl or request.name == 'getActiveState' or request.name == 'setState'
+ # These requests are delivered to the options page, but there are no handlers there.
+ return if request.handler == "registerFrame" or request.handler == "frameFocused"
sendResponse requestHandlers[request.name](request, sender)
- # Ensure the sendResponse callback is freed.
false
-#
-# This is called once the background page has told us that Vimium should be enabled for the current URL.
-#
-initializeWhenEnabled = ->
- document.addEventListener("keydown", onKeydown, true)
- document.addEventListener("keypress", onKeypress, true)
- document.addEventListener("keyup", onKeyup, true)
- document.addEventListener("focus", onFocusCapturePhase, true)
- document.addEventListener("blur", onBlurCapturePhase, true)
- document.addEventListener("DOMActivate", onDOMActivate, true)
- enterInsertModeIfElementIsFocused()
+# Wrapper to install event listeners. Syntactic sugar.
+installListener = (event, callback) -> document.addEventListener(event, callback, true)
#
-# Used to disable Vimium without needing to reload the page.
-# This is called if the current page's url is blacklisted using the popup UI.
+# This is called once the background page has told us that Vimium should be enabled for the current URL.
+# We enable/disable Vimium by toggling isEnabledForUrl. The alternative, installing or uninstalling
+# listeners, is error prone. It's more difficult to keep track of the state.
#
-disableVimium = ->
- document.removeEventListener("keydown", onKeydown, true)
- document.removeEventListener("keypress", onKeypress, true)
- document.removeEventListener("keyup", onKeyup, true)
- document.removeEventListener("focus", onFocusCapturePhase, true)
- document.removeEventListener("blur", onBlurCapturePhase, true)
- document.removeEventListener("DOMActivate", onDOMActivate, true)
- isEnabledForUrl = false
+installedListeners = false
+initializeWhenEnabled = (newPassKeys=undefined) ->
+ isEnabledForUrl = true
+ passKeys = passKeys if typeof(newPassKeys) != 'undefined'
+ if (!installedListeners)
+ installListener "keydown", (event) -> if isEnabledForUrl then onKeydown(event) else true
+ installListener "keypress", (event) -> if isEnabledForUrl then onKeypress(event) else true
+ installListener "keyup", (event) -> if isEnabledForUrl then onKeyup(event) else true
+ installListener "focus", (event) -> if isEnabledForUrl then onFocusCapturePhase(event) else true
+ installListener "blur", (event) -> if isEnabledForUrl then onBlurCapturePhase(event)
+ installListener "DOMActivate", (event) -> if isEnabledForUrl then onDOMActivate(event)
+ enterInsertModeIfElementIsFocused()
+ installedListeners = true
+
+setState = (request) ->
+ isEnabledForUrl = request.enabled
+ passKeys = request.passKeys
+ initializeWhenEnabled(passKeys) if isEnabledForUrl and !installedListeners
#
# The backend needs to know which frame has focus.
@@ -321,6 +328,15 @@ extend window,
false
+# Should this keyChar be passed to the underlying page?
+# Keystrokes are *never* considered passKeys if the keyQueue is not empty. So, for example, if 't' is a
+# passKey, then 'gt' and '99t' will neverthless be handled by vimium.
+# TODO: This currently only works for unmodified keys (so not for '<c-a>', or the like). It's not clear if
+# this is a problem or not. I don't recall coming across a web page with modifier key bindings. Such
+# bindings might be too likely to conflict with browsers' native bindings.
+isPassKey = ( keyChar ) ->
+ !keyQueue and passKeys and 0 <= passKeys.indexOf keyChar
+
handledKeydownEvents = []
#
@@ -349,6 +365,9 @@ onKeypress = (event) ->
handleKeyCharForFindMode(keyChar)
DomUtils.suppressEvent(event)
else if (!isInsertMode() && !findMode)
+ # Is this keyChar is to be passed to the underlying page?
+ if (isPassKey keyChar)
+ return undefined
if (currentCompletionKeys.indexOf(keyChar) != -1)
DomUtils.suppressEvent(event)
@@ -431,6 +450,9 @@ onKeydown = (event) ->
else if (KeyboardUtils.isEscape(event))
keyPort.postMessage({ keyChar:"<ESC>", frameId:frameId })
+ else if isPassKey KeyboardUtils.getKeyChar(event)
+ return undefined
+
# Added to prevent propagating this event to other listeners if it's one that'll trigger a Vimium command.
# The goal is to avoid the scenario where Google Instant Search uses every keydown event to dump us
# back into the search box. As a side effect, this should also prevent overriding by other sites.
@@ -466,7 +488,7 @@ checkIfEnabledForUrl = ->
chrome.runtime.sendMessage { handler: "isEnabledForUrl", url: url }, (response) ->
isEnabledForUrl = response.isEnabledForUrl
if (isEnabledForUrl)
- initializeWhenEnabled()
+ initializeWhenEnabled(response.passKeys)
else if (HUD.isReady())
# Quickly hide any HUD we might already be showing, e.g. if we entered insert mode on page load.
HUD.hide()
diff --git a/icons/browser_action_partial.png b/icons/browser_action_partial.png
new file mode 100644
index 00000000..e713f005
--- /dev/null
+++ b/icons/browser_action_partial.png
Binary files differ
diff --git a/icons/icon48partial.png b/icons/icon48partial.png
new file mode 100644
index 00000000..088099b1
--- /dev/null
+++ b/icons/icon48partial.png
Binary files differ
diff --git a/pages/options.html b/pages/options.html
index b71625e8..07dcab1d 100644
--- a/pages/options.html
+++ b/pages/options.html
@@ -197,15 +197,30 @@
</tr>
<tr>
<td colspan="3">
- Excluded URLs<br/>
+ Excluded URLs and keys<br/>
<div class="help">
<div class="example">
- e.g. http*://mail.google.com/*<br/>
- This will disable Vimium on Gmail.<br/><br/>
- Enter one URL per line.<br/>
+ <p>
+ To disable Vimium on a site, use:<br/>
+ <tt>http*://mail.google.com/*</tt><br/>
+ This will <i>wholly disable</i> Vimium on Gmail.<br/><br/>
+ To use Vimium together with a website's own<br/>
+ key bindings, use:<br/>
+ <tt>http*://mail.google.com/* jknpc</tt><br/>
+ This will <i>enable</i> Vimium on Gmail, but pass<br/>
+ the five listed keys through to Gmail itself.<br/><br/>
+ One entry per line.<br/>
+ </p>
</div>
</div>
- <textarea id="excludedUrls"></textarea>
+ <!-- Hack: fix a minimum size for the text area (below) so that it is
+ not too much smaller than its help text (above). -->
+ <!-- FIXME:
+ This text area should really be broken out into an array
+ of separate inputs. However, the whole options page really
+ needs a workover, so I'm leaving it like this, for now
+ (Steve, 23 Aug, 14). -->
+ <textarea id="excludedUrls" style="min-height:180px"></textarea>
</td>
</tr>
<tbody id='advancedOptions'>
diff --git a/pages/popup.coffee b/pages/popup.coffee
index 6d7afafc..41fc17a9 100644
--- a/pages/popup.coffee
+++ b/pages/popup.coffee
@@ -1,10 +1,21 @@
onLoad = ->
document.getElementById("optionsLink").setAttribute "href", chrome.runtime.getURL("pages/options.html")
chrome.tabs.getSelected null, (tab) ->
- # The common use case is to disable Vimium at the domain level.
- # This regexp will match "http://www.example.com/" from "http://www.example.com/path/to/page.html".
- domain = tab.url.match(/[^\/]*\/\/[^\/]*\//) or tab.url
- document.getElementById("popupInput").value = domain + "*"
+ # Check if we have an existing exclusing rule for this page.
+ isEnabled = chrome.extension.getBackgroundPage().isEnabledForUrl(url: tab.url)
+ if isEnabled.matchingUrl
+ console.log isEnabled
+ # There is an existing rule for this page.
+ pattern = isEnabled.matchingUrl
+ if isEnabled.passKeys
+ pattern += " " + isEnabled.passKeys
+ document.getElementById("popupInput").value = pattern
+ else
+ # No existing exclusion rule.
+ # The common use case is to disable Vimium at the domain level.
+ # This regexp will match "http://www.example.com/" from "http://www.example.com/path/to/page.html".
+ domain = tab.url.match(/[^\/]*\/\/[^\/]*\//) or tab.url
+ document.getElementById("popupInput").value = domain + "*"
onExcludeUrl = (e) ->
url = document.getElementById("popupInput").value
diff --git a/pages/popup.html b/pages/popup.html
index 8ccf7126..89f1f02a 100644
--- a/pages/popup.html
+++ b/pages/popup.html
@@ -6,14 +6,14 @@
padding: 0px;
}
- #vimiumPopup { width: 300px; }
+ #vimiumPopup { width: 500px; }
#excludeControls {
padding: 10px;
}
#popupInput {
- width: 160px;
+ width: 330px;
}
#excludeConfirm {
@@ -54,7 +54,7 @@
<div id="excludeControls">
<input id="popupInput" type="text" />
<input id="popupButton" type="button" value="Exclude URL" />
- <span id="excludeConfirm">Saved exclude pattern.</span>
+ <span id="excludeConfirm">Saved.</span>
</div>
<div id="popupMenu">
diff --git a/tests/unit_tests/exclusion_test.coffee b/tests/unit_tests/exclusion_test.coffee
new file mode 100644
index 00000000..33dbccd3
--- /dev/null
+++ b/tests/unit_tests/exclusion_test.coffee
@@ -0,0 +1,71 @@
+
+require "./test_helper.js"
+require "./test_chrome_stubs.js"
+
+# FIXME:
+# Would like to do:
+# extend(global, require "../../background_scripts/marks.js")
+# But it looks like marks.coffee has never been included in a test before!
+# Temporary fix...
+root.Marks =
+ create: () -> true
+ goto:
+ bind: () -> true
+
+extend(global, require "../../lib/utils.js")
+Utils.getCurrentVersion = -> '1.44'
+extend(global,require "../../background_scripts/sync.js")
+extend(global,require "../../background_scripts/settings.js")
+Sync.init()
+extend(global, require "../../background_scripts/commands.js")
+extend(global, require "../../background_scripts/main.js")
+
+# These tests cover only the most basic aspects of excluded URLs and passKeys.
+#
+context "Excluded URLs and pass keys",
+ setup ->
+ Settings.set 'excludedUrls', 'http://mail.google.com/*\nhttp://www.facebook.com/* jk'
+
+ should "be disabled for excluded sites", ->
+ rule = isEnabledForUrl({ url: 'http://mail.google.com/u/0/inbox' })
+ assert.isFalse rule.isEnableForUrl
+ assert.isTrue rule.matchingUrl
+
+ should "be enabled, but with pass keys", ->
+ rule = isEnabledForUrl({ url: 'http://www.facebook.com/pages' })
+ assert.isTrue rule.isEnabledForUrl
+ assert.equal rule.passKeys, 'jk'
+ assert.isTrue rule.matchingUrl
+
+ should "be enabled", ->
+ rule = isEnabledForUrl({ url: 'http://www.twitter.com/pages' })
+ assert.isTrue rule.isEnabledForUrl
+ assert.isFalse rule.passKeys
+
+ should "add a new excluded URL", ->
+ rule = isEnabledForUrl({ url: 'http://www.example.com/page' })
+ assert.isTrue rule.isEnabledForUrl
+ addExcludedUrl("http://www.example.com*")
+ rule = isEnabledForUrl({ url: 'http://www.example.com/page' })
+ assert.isFalse rule.isEnabledForUrl
+ assert.isFalse rule.passKeys
+ assert.isTrue rule.matchingUrl
+
+ should "add a new excluded URL with passkeys", ->
+ rule = isEnabledForUrl({ url: 'http://www.example.com/page' })
+ assert.isTrue rule.isEnabledForUrl
+ addExcludedUrl("http://www.example.com/* jk")
+ rule = isEnabledForUrl({ url: 'http://www.example.com/page' })
+ assert.isTrue rule.isEnabledForUrl
+ assert.equal rule.passKeys, 'jk'
+ assert.isTrue rule.matchingUrl
+
+ should "update an existing excluded URL with passkeys", ->
+ rule = isEnabledForUrl({ url: 'http://www.facebook.com/page' })
+ assert.isTrue rule.isEnabledForUrl
+ addExcludedUrl("http://www.facebook.com/* jknp")
+ rule = isEnabledForUrl({ url: 'http://www.facebook.com/page' })
+ assert.isTrue rule.isEnabledForUrl
+ assert.equal rule.passKeys, 'jknp'
+ assert.isTrue rule.matchingUrl
+
diff --git a/tests/unit_tests/test_chrome_stubs.coffee b/tests/unit_tests/test_chrome_stubs.coffee
index e9c48f31..9622f85f 100644
--- a/tests/unit_tests/test_chrome_stubs.coffee
+++ b/tests/unit_tests/test_chrome_stubs.coffee
@@ -2,26 +2,55 @@
#
# This is a stub for chrome.strorage.sync for testing.
# It does what chrome.storage.sync should do (roughly), but does so synchronously.
+# It also provides stubs for a number of other chrome APIs.
#
+global.window = {}
+global.localStorage = {}
+
global.chrome =
- runtime: {}
+ runtime:
+ getManifest: () ->
+ version: "1.2.3"
+ onConnect:
+ addListener: () -> true
+ onMessage:
+ addListener: () -> true
- storage:
+ tabs:
+ onSelectionChanged:
+ addListener: () -> true
+ onUpdated:
+ addListener: () -> true
+ onAttached:
+ addListener: () -> true
+ onMoved:
+ addListener: () -> true
+ onRemoved:
+ addListener: () -> true
+ onActiveChanged:
+ addListener: () -> true
+ query: () -> true
+ windows:
+ onRemoved:
+ addListener: () -> true
+ getAll: () -> true
+
+ storage:
# chrome.storage.onChanged
onChanged:
addListener: (func) -> @func = func
# Fake a callback from chrome.storage.sync.
call: (key, value) ->
- chrome.runtime = { lastError: undefined }
+ chrome.runtime.lastError = undefined
key_value = {}
key_value[key] = { newValue: value }
@func(key_value,'synced storage stub') if @func
callEmpty: (key) ->
- chrome.runtime = { lastError: undefined }
+ chrome.runtime.lastError = undefined
if @func
items = {}
items[key] = {}
@@ -32,7 +61,7 @@ global.chrome =
store: {}
set: (items, callback) ->
- chrome.runtime = { lastError: undefined }
+ chrome.runtime.lastError = undefined
for own key, value of items
@store[key] = value
callback() if callback
@@ -41,7 +70,7 @@ global.chrome =
global.chrome.storage.onChanged.call(key,value)
get: (keys, callback) ->
- chrome.runtime = { lastError: undefined }
+ chrome.runtime.lastError = undefined
if keys == null
keys = []
for own key, value of @store
@@ -53,7 +82,7 @@ global.chrome =
callback items if callback
remove: (key, callback) ->
- chrome. runtime = { lastError: undefined }
+ chrome.runtime.lastError = undefined
if key of @store
delete @store[key]
callback() if callback