aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--background_scripts/commands.coffee4
-rw-r--r--background_scripts/main.coffee34
-rw-r--r--content_scripts/link_hints.coffee5
-rw-r--r--content_scripts/vimium_frontend.coffee5
-rw-r--r--lib/dom_utils.coffee19
-rw-r--r--tests/dom_tests/chrome.coffee2
-rw-r--r--tests/unit_tests/exclusion_test.coffee12
7 files changed, 59 insertions, 22 deletions
diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee
index 79cb9ee0..9aa90c45 100644
--- a/background_scripts/commands.coffee
+++ b/background_scripts/commands.coffee
@@ -289,8 +289,8 @@ commandDescriptions =
openCopiedUrlInNewTab: ["Open the clipboard's URL in a new tab", { background: true, repeatLimit: 20 }]
enterInsertMode: ["Enter insert mode", { noRepeat: true }]
- enterVisualMode: ["Enter visual mode (not yet implemented)", { noRepeat: true }]
- enterVisualLineMode: ["Enter visual line mode (not yet implemented)", { noRepeat: true }]
+ enterVisualMode: ["Enter visual mode (beta feature)", { noRepeat: true }]
+ enterVisualLineMode: ["Enter visual line mode (beta feature)", { noRepeat: true }]
# enterEditMode: ["Enter vim-like edit mode (not yet implemented)", { noRepeat: true }]
focusInput: ["Focus the first text box on the page. Cycle between them using tab",
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index d034ffb0..23950e6e 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -77,11 +77,12 @@ getCurrentTabUrl = (request, sender) -> sender.tab.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.
#
-root.isEnabledForUrl = isEnabledForUrl = (request) ->
+root.isEnabledForUrl = isEnabledForUrl = (request, sender) ->
rule = Exclusions.getRule(request.url)
{
isEnabledForUrl: not rule or rule.passKeys
passKeys: rule?.passKeys or ""
+ incognito: sender.tab.incognito
}
# Retrieves the help dialog HTML template from a file, and populates it with the latest keybindings.
@@ -149,9 +150,17 @@ openUrlInCurrentTab = (request) ->
#
# Opens request.url in new tab and switches to it if request.selected is true.
#
-openUrlInNewTab = (request) ->
- chrome.tabs.getSelected(null, (tab) ->
- chrome.tabs.create({ url: Utils.convertToUrl(request.url), index: tab.index + 1, selected: true }))
+openUrlInNewTab = (request, callback) ->
+ chrome.tabs.getSelected null, (tab) ->
+ tabConfig =
+ url: Utils.convertToUrl request.url
+ index: tab.index + 1
+ selected: true
+ windowId: tab.windowId
+ # FIXME(smblott). openUrlInNewTab is being called in two different ways with different arguments. We
+ # should refactor it such that this check on callback isn't necessary.
+ callback = (->) unless typeof callback == "function"
+ chrome.tabs.create tabConfig, callback
openUrlInIncognito = (request) ->
chrome.windows.create({ url: Utils.convertToUrl(request.url), incognito: true})
@@ -219,7 +228,7 @@ moveTab = (callback, direction) ->
# These are commands which are bound to keystroke which must be handled by the background page. They are
# mapped in commands.coffee.
BackgroundCommands =
- createTab: (callback) -> chrome.tabs.create({url: Settings.get("newTabUrl")}, (tab) -> callback())
+ createTab: (callback) -> openUrlInNewTab { url: Settings.get("newTabUrl") }, callback
duplicateTab: (callback) ->
chrome.tabs.getSelected(null, (tab) ->
chrome.tabs.duplicate(tab.id)
@@ -348,14 +357,14 @@ chrome.browserAction.setBadgeBackgroundColor
setBadge = do ->
current = null
timer = null
- updateBadge = (badge) -> -> chrome.browserAction.setBadgeText text: badge
- (request) ->
+ updateBadge = (badge, tabId) -> -> chrome.browserAction.setBadgeText text: badge, tabId: tabId
+ (request, sender) ->
badge = request.badge
if badge? and badge != current
current = badge
clearTimeout timer if timer
# We wait a few moments. This avoids badge flicker when there are rapid changes.
- timer = setTimeout updateBadge(badge), 50
+ timer = setTimeout updateBadge(badge, sender.tab.id), 50
# 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.
@@ -366,14 +375,12 @@ root.updateActiveState = updateActiveState = (tabId) ->
disabledIcon = "icons/browser_action_disabled.png"
partialIcon = "icons/browser_action_partial.png"
chrome.tabs.get tabId, (tab) ->
- # Default to disabled state in case we can't connect to Vimium, primarily for the "New Tab" page.
- setBrowserActionIcon(tabId,disabledIcon)
- setBadge badge: ""
+ setBadge { badge: "" }, tab: { id: tabId }
chrome.tabs.sendMessage tabId, { name: "getActiveState" }, (response) ->
if response
isCurrentlyEnabled = response.enabled
currentPasskeys = response.passKeys
- config = isEnabledForUrl({url: tab.url})
+ config = isEnabledForUrl { url: tab.url }, { tab: tab }
enabled = config.isEnabledForUrl
passKeys = config.passKeys
if (enabled and passKeys)
@@ -385,7 +392,8 @@ root.updateActiveState = updateActiveState = (tabId) ->
# Propagate the new state only if it has changed.
if (isCurrentlyEnabled != enabled || currentPasskeys != passKeys)
chrome.tabs.sendMessage(tabId, { name: "setState", enabled: enabled, passKeys: passKeys, incognito: tab.incognito })
-
+ else
+ setBrowserActionIcon tabId, disabledIcon
handleUpdateScrollPosition = (request, sender) ->
updateScrollPosition(sender.tab, request.scrollX, request.scrollY)
diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee
index 2abfa001..f904c7d5 100644
--- a/content_scripts/link_hints.coffee
+++ b/content_scripts/link_hints.coffee
@@ -231,6 +231,8 @@ LinkHints =
# Remove rects from elements where another clickable element lies above it.
nonOverlappingElements = []
# Traverse the DOM from first to last, since later elements show above earlier elements.
+ # NOTE(smblott). filterHints.generateLinkText also assumes this order when generating the content text for
+ # each hint. Specifically, we consider descendents before we consider their ancestors.
visibleElements = visibleElements.reverse()
while visibleElement = visibleElements.pop()
rects = [visibleElement.rect]
@@ -469,7 +471,7 @@ filterHints =
linkText = element.firstElementChild.alt || element.firstElementChild.title
showLinkText = true if (linkText)
else
- linkText = element.textContent || element.innerHTML
+ linkText = DomUtils.textContent.get element
{ text: linkText, show: showLinkText }
@@ -479,6 +481,7 @@ filterHints =
fillInMarkers: (hintMarkers) ->
@generateLabelMap()
+ DomUtils.textContent.reset()
for marker, idx in hintMarkers
marker.hintString = @generateHintString(idx)
linkTextObject = @generateLinkText(marker.clickableItem)
diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee
index f996f753..836acecd 100644
--- a/content_scripts/vimium_frontend.coffee
+++ b/content_scripts/vimium_frontend.coffee
@@ -186,7 +186,7 @@ initializePreDomReady = ->
return if sender.tab and not sender.tab.url.startsWith 'chrome-extension://'
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"
+ return if request.handler in [ "registerFrame", "frameFocused", "unregisterFrame" ]
sendResponse requestHandlers[request.name](request, sender)
# Ensure the sendResponse callback is freed.
false
@@ -211,13 +211,13 @@ window.initializeWhenEnabled = ->
do (type) -> installListener window, type, (event) -> handlerStack.bubbleEvent type, event
installListener document, "DOMActivate", (event) -> handlerStack.bubbleEvent 'DOMActivate', event
installedListeners = true
+ FindModeHistory.init()
setState = (request) ->
isEnabledForUrl = request.enabled
passKeys = request.passKeys
isIncognitoMode = request.incognito
initializeWhenEnabled() if isEnabledForUrl
- FindModeHistory.init()
handlerStack.bubbleEvent "registerStateChange",
enabled: isEnabledForUrl
passKeys: passKeys
@@ -562,6 +562,7 @@ checkIfEnabledForUrl = ->
chrome.runtime.sendMessage { handler: "isEnabledForUrl", url: url }, (response) ->
isEnabledForUrl = response.isEnabledForUrl
passKeys = response.passKeys
+ isIncognitoMode = response.incognito
if isEnabledForUrl
initializeWhenEnabled()
else if (HUD.isReady())
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index 707906b4..0231f994 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -306,5 +306,24 @@ DomUtils =
document.body.removeChild div
coordinates
+ # Get the text content of an element (and its descendents), but omit the text content of previously-visited
+ # nodes. See #1514.
+ # NOTE(smblott). This is currently O(N^2) (when called on N elements). An alternative would be to mark
+ # each node visited, and then clear the marks when we're done.
+ textContent: do ->
+ visitedNodes = null
+ reset: -> visitedNodes = []
+ get: (element) ->
+ nodes = document.createTreeWalker element, NodeFilter.SHOW_TEXT
+ texts =
+ while node = nodes.nextNode()
+ continue unless node.nodeType == 3
+ continue if node in visitedNodes
+ text = node.data.trim()
+ continue unless 0 < text.length
+ visitedNodes.push node
+ text
+ texts.join " "
+
root = exports ? window
root.DomUtils = DomUtils
diff --git a/tests/dom_tests/chrome.coffee b/tests/dom_tests/chrome.coffee
index d6c03fc1..5f276649 100644
--- a/tests/dom_tests/chrome.coffee
+++ b/tests/dom_tests/chrome.coffee
@@ -27,3 +27,5 @@ root.chrome =
sync:
get: ->
set: ->
+ onChanged:
+ addListener: ->
diff --git a/tests/unit_tests/exclusion_test.coffee b/tests/unit_tests/exclusion_test.coffee
index b3ed7194..287d699d 100644
--- a/tests/unit_tests/exclusion_test.coffee
+++ b/tests/unit_tests/exclusion_test.coffee
@@ -21,6 +21,10 @@ extend(global, require "../../background_scripts/exclusions.js")
extend(global, require "../../background_scripts/commands.js")
extend(global, require "../../background_scripts/main.js")
+dummyTab =
+ tab:
+ incognito: false
+
# These tests cover only the most basic aspects of excluded URLs and passKeys.
#
context "Excluded URLs and pass keys",
@@ -36,22 +40,22 @@ context "Excluded URLs and pass keys",
])
should "be disabled for excluded sites", ->
- rule = isEnabledForUrl({ url: 'http://mail.google.com/calendar/page' })
+ rule = isEnabledForUrl({ url: 'http://mail.google.com/calendar/page' }, dummyTab)
assert.isFalse rule.isEnabledForUrl
assert.isFalse rule.passKeys
should "be disabled for excluded sites, one exclusion", ->
- rule = isEnabledForUrl({ url: 'http://www.bbc.com/calendar/page' })
+ rule = isEnabledForUrl({ url: 'http://www.bbc.com/calendar/page' }, dummyTab)
assert.isFalse rule.isEnabledForUrl
assert.isFalse rule.passKeys
should "be enabled, but with pass keys", ->
- rule = isEnabledForUrl({ url: 'https://www.facebook.com/something' })
+ rule = isEnabledForUrl({ url: 'https://www.facebook.com/something' }, dummyTab)
assert.isTrue rule.isEnabledForUrl
assert.equal rule.passKeys, 'abcd'
should "be enabled", ->
- rule = isEnabledForUrl({ url: 'http://www.twitter.com/pages' })
+ rule = isEnabledForUrl({ url: 'http://www.twitter.com/pages' }, dummyTab)
assert.isTrue rule.isEnabledForUrl
assert.isFalse rule.passKeys