diff options
| -rw-r--r-- | background_scripts/commands.coffee | 4 | ||||
| -rw-r--r-- | background_scripts/main.coffee | 34 | ||||
| -rw-r--r-- | content_scripts/link_hints.coffee | 5 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 5 | ||||
| -rw-r--r-- | lib/dom_utils.coffee | 19 | ||||
| -rw-r--r-- | tests/dom_tests/chrome.coffee | 2 | ||||
| -rw-r--r-- | tests/unit_tests/exclusion_test.coffee | 12 |
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 |
