diff options
| -rw-r--r-- | README.md | 31 | ||||
| -rw-r--r-- | content_scripts/hud.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/link_hints.coffee | 19 | ||||
| -rw-r--r-- | content_scripts/mode_passkeys.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/scroller.coffee | 12 | ||||
| -rw-r--r-- | content_scripts/ui_component.coffee | 18 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 45 | ||||
| -rw-r--r-- | lib/clipboard.coffee | 4 | ||||
| -rw-r--r-- | lib/dom_utils.coffee | 29 | ||||
| -rw-r--r-- | lib/utils.coffee | 6 | ||||
| -rw-r--r-- | manifest.json | 3 | ||||
| -rw-r--r-- | pages/options.html | 2 | ||||
| -rw-r--r-- | pages/vomnibar.coffee | 2 | 
13 files changed, 100 insertions, 75 deletions
| @@ -153,22 +153,21 @@ Please see [CONTRIBUTING.md](https://github.com/philc/vimium/blob/master/CONTRIB  Release Notes  ------------- -1.52 (not yet released) - -- Search completion for selected popular search engines; for details, see the wiki -  ([here](https://github.com/philc/vimium/wiki/Search-Completion) and -  [here](https://github.com/philc/vimium/wiki/Tips-and-Tricks#repeat-recent-queries)). -- A much improved user interface for custom search engines. -- `Tab` on an empty Vomnibar now expands the Vomnibar with your recent history. -- Added <tt>\`\`</tt> to jump back to the previous position after selected jump-like movements: <br/> -    (`gg`, `G`, `n`, `N`, `/` and local mark movements). -- Global marks are now persistent (across tab closes and browser sessions) and synced. -- For filtered link hints (not the default), you can now use `Tab` and `Enter` -  to select hints and hints are ordered by best match. -- Bug fixes, including: -    - Bookmarklets accessed from the Vomnibar. -    - Global marks on non-Windows platforms. -    - Link-hints for non-Latin keyboards. +1.52 (2015-09-09) + +- Search completion for selected custom search engines +  (details on the [wiki](https://github.com/philc/vimium/wiki/Search-Completion)). +- Use `Tab` on an empty Vomnibar to repeat or edit recent queries +  (details on the [wiki](https://github.com/philc/vimium/wiki/Tips-and-Tricks#repeat-recent-vomnibar-queries)). +- Marks: +    - Use <tt>\`\`</tt> to jump back to the previous position after jump-like movements: <br/> +        (`gg`, `G`, `n`, `N`, `/` and local mark movements). +    - Global marks are now persistent and synced. +- For numeric link hints, you can now use `Tab` and `Enter` to select hints, and hints are ordered by the best +  match. +- The Find Mode text entry box now supports editing, pasting, and better handles non-latin characters. +- Vimium now works on XML pages. +- Bug fixes.  1.51 (2015-05-02) diff --git a/content_scripts/hud.coffee b/content_scripts/hud.coffee index bfad71b7..5a3d9b79 100644 --- a/content_scripts/hud.coffee +++ b/content_scripts/hud.coffee @@ -96,7 +96,7 @@ class Tween    styleElement: null    constructor: (@cssSelector, insertionPoint = document.documentElement) -> -    @styleElement = document.createElement "style" +    @styleElement = DomUtils.createElement "style"      unless @styleElement.style        # We're in an XML document, so we shouldn't inject any elements. See the comment in UIComponent. diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee index 15af15c5..54c10284 100644 --- a/content_scripts/link_hints.coffee +++ b/content_scripts/link_hints.coffee @@ -127,7 +127,7 @@ class LinkHintsMode    # Creates a link marker for the given link.    #    createMarkerFor: (link) -> -    marker = document.createElement("div") +    marker = DomUtils.createElement "div"      marker.className = "vimiumReset internalVimiumHintMarker vimiumHintMarker"      marker.clickableItem = link.element @@ -167,6 +167,23 @@ class LinkHintsMode          element.getAttribute("aria-disabled")?.toLowerCase() in ["", "true"])        return [] # This element should never have a link hint. +    # Check for AngularJS listeners on the element. +    @checkForAngularJs ?= do -> +      angularElements = document.getElementsByClassName "ng-scope" +      if angularElements.length == 0 +        -> false +      else +        ngAttributes = [] +        for prefix in [ '', 'data-', 'x-' ] +          for separator in [ '-', ':', '_' ] +            ngAttributes.push "#{prefix}ng#{separator}click" +        (element) -> +          for attribute in ngAttributes +            return true if element.hasAttribute attribute +          false + +    isClickable ||= @checkForAngularJs element +      # Check for attributes that make an element clickable regardless of its tagName.      if (element.hasAttribute("onclick") or          element.getAttribute("role")?.toLowerCase() in ["button", "link"] or diff --git a/content_scripts/mode_passkeys.coffee b/content_scripts/mode_passkeys.coffee index 1ed69ac2..631eb621 100644 --- a/content_scripts/mode_passkeys.coffee +++ b/content_scripts/mode_passkeys.coffee @@ -12,7 +12,7 @@ class PassKeysMode extends Mode    # passKey, then 'gt' and '99t' will neverthless be handled by Vimium.    handleKeyChar: (event, keyChar) ->      return @continueBubbling if event.altKey or event.ctrlKey or event.metaKey -    if keyChar and not @keyQueue and 0 <= @passKeys.indexOf keyChar +    if keyChar and not @keyQueue and keyChar.length == 1 and 0 <= @passKeys.indexOf keyChar        @stopBubblingAndTrue      else        @continueBubbling diff --git a/content_scripts/scroller.coffee b/content_scripts/scroller.coffee index 81c71fcd..5f10ab65 100644 --- a/content_scripts/scroller.coffee +++ b/content_scripts/scroller.coffee @@ -81,7 +81,7 @@ doesScroll = (element, direction, amount, factor) ->  findScrollableElement = (element, direction, amount, factor) ->    while element != document.body and      not (doesScroll(element, direction, amount, factor) and shouldScroll(element, direction)) -      element = element.parentElement || document.body +      element = (DomUtils.getContainingElement element) ? document.body    element  # On some pages, document.body is not scrollable.  Here, we search the document for the largest visible @@ -136,6 +136,9 @@ CoreScroller =          handlerStack.alwaysContinueBubbling =>            @keyIsDown = false            @time += 1 +      blur: => +        handlerStack.alwaysContinueBubbling => +          @time += 1 if event.target == window    # Return true if CoreScroller would not initiate a new scroll right now.    wouldNotInitiateScroll: -> @lastEvent?.repeat and Settings.get "smoothScroll" @@ -217,7 +220,12 @@ Scroller =    init: ->      handlerStack.push        _name: 'scroller/active-element' -      DOMActivate: (event) -> handlerStack.alwaysContinueBubbling -> activatedElement = event.target +      DOMActivate: (event) -> handlerStack.alwaysContinueBubbling -> +        # If event.path is present, the true event taget (potentially inside a Shadow DOM inside +        # event.target) can be found as its first element. +        # NOTE(mrmr1993): event.path has been renamed to event.deepPath in the spec, but this change is not +        # yet implemented by Chrome. +        activatedElement = event.deepPath?[0] ? event.path?[0] ? event.target      CoreScroller.init()    # scroll the active element in :direction by :amount * :factor. diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index e4cfc293..d935079d 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -6,28 +6,16 @@ class UIComponent    shadowDOM: null    constructor: (iframeUrl, className, @handleMessage) -> -    styleSheet = document.createElement "style" - -    unless styleSheet.style -      # If this is an XML document, nothing we do here works: -      # * <style> elements show their contents inline, -      # * <iframe> elements don't load any content, -      # * document.createElement generates elements that have style == null and ignore CSS. -      # If this is the case we don't want to pollute the DOM to no or negative effect.  So we bail -      # immediately, and disable all externally-called methods. -      @postMessage = @activate = @show = @hide = -> -        console.log "This vimium feature is disabled because it is incompatible with this page." -      return - +    styleSheet = DomUtils.createElement "style"      styleSheet.type = "text/css"      # Default to everything hidden while the stylesheet loads.      styleSheet.innerHTML = "@import url(\"#{chrome.runtime.getURL("content_scripts/vimium.css")}\");" -    @iframeElement = document.createElement "iframe" +    @iframeElement = DomUtils.createElement "iframe"      extend @iframeElement,        className: className        seamless: "seamless" -    shadowWrapper = document.createElement "div" +    shadowWrapper = DomUtils.createElement "div"      # PhantomJS doesn't support createShadowRoot, so guard against its non-existance.      @shadowDOM = shadowWrapper.createShadowRoot?() ? shadowWrapper      @shadowDOM.appendChild styleSheet diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 8f4c7e82..781223b1 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -274,23 +274,22 @@ setScrollPosition = ({ scrollX, scrollY }) ->  #  # Called from the backend in order to change frame focus.  # -window.focusThisFrame = do -> +DomUtils.documentReady ->    # Create a shadow DOM wrapping the frame so the page's styles don't interfere with ours. -  highlightedFrameElement = document.createElement "div" +  highlightedFrameElement = DomUtils.createElement "div"    # PhantomJS doesn't support createShadowRoot, so guard against its non-existance.    _shadowDOM = highlightedFrameElement.createShadowRoot?() ? highlightedFrameElement    # Inject stylesheet. -  _styleSheet = document.createElement "style" -  if _styleSheet.style? -    _styleSheet.innerHTML = "@import url(\"#{chrome.runtime.getURL("content_scripts/vimium.css")}\");" -    _shadowDOM.appendChild _styleSheet +  _styleSheet = DomUtils.createElement "style" +  _styleSheet.innerHTML = "@import url(\"#{chrome.runtime.getURL("content_scripts/vimium.css")}\");" +  _shadowDOM.appendChild _styleSheet -  _frameEl = document.createElement "div" +  _frameEl = DomUtils.createElement "div"    _frameEl.className = "vimiumReset vimiumHighlightedFrame"    _shadowDOM.appendChild _frameEl -  (request) -> +  window.focusThisFrame = (request) ->      if window.innerWidth < 3 or window.innerHeight < 3        # This frame is too small to focus. Cancel and tell the background frame to focus the next one instead.        # This affects sites like Google Inbox, which have many tiny iframes. See #1317. @@ -304,6 +303,8 @@ window.focusThisFrame = do ->        document.documentElement.appendChild highlightedFrameElement        setTimeout (-> highlightedFrameElement.remove()), 200 +window.focusThisFrame = -> +  extend window,    scrollToBottom: ->      Marks.setPreviousPosition() @@ -405,7 +406,7 @@ extend window,            Math.min(count, visibleInputs.length) - 1        hints = for tuple in visibleInputs -        hint = document.createElement "div" +        hint = DomUtils.createElement "div"          hint.className = "vimiumReset internalVimiumInputHint vimiumInputHint"          # minus 1 for the border @@ -429,7 +430,7 @@ extend window,                  hints[selectedInputIndex].classList.add 'internalVimiumSelectedInputHint'                  # Deactivate any active modes on this element (PostFindMode, or a suspended edit mode).                  @deactivateSingleton visibleInputs[selectedInputIndex].element -                visibleInputs[selectedInputIndex].element.focus() +                DomUtils.simulateSelect visibleInputs[selectedInputIndex].element                  @suppressEvent                else unless event.keyCode == KeyboardUtils.keyCodes.shiftKey                  @exit() @@ -442,7 +443,7 @@ extend window,            # Deactivate any active modes on this element (PostFindMode, or a suspended edit mode).            @deactivateSingleton visibleInputs[selectedInputIndex].element -          visibleInputs[selectedInputIndex].element.focus() +          DomUtils.simulateSelect visibleInputs[selectedInputIndex].element            if visibleInputs.length == 1              @exit()              return @@ -463,25 +464,25 @@ extend window,  KeydownEvents =    handledEvents: {} -  stringify: (event) -> -    JSON.stringify -      metaKey: event.metaKey -      altKey: event.altKey -      ctrlKey: event.ctrlKey -      keyIdentifier: event.keyIdentifier -      keyCode: event.keyCode +  getEventCode: (event) -> event.keyCode    push: (event) -> -    @handledEvents[@stringify event] = true +    @handledEvents[@getEventCode event] = true    # Yields truthy or falsy depending upon whether a corresponding keydown event is present (and removes that    # event).    pop: (event) -> -    detailString = @stringify event +    detailString = @getEventCode event      value = @handledEvents[detailString]      delete @handledEvents[detailString]      value +  clear: -> @handledEvents = {} + +handlerStack.push +  _name: "KeydownEvents-cleanup" +  blur: (event) -> KeydownEvents.clear() if event.target == window; true +  #  # Sends everything except i & ESC to the handler in background_page. i & ESC are special because they control  # insert mode which is local state to the page. The key will be are either a single ascii letter or a @@ -775,7 +776,7 @@ window.enterFindMode = ->  window.showHelpDialog = (html, fid) ->    return if (isShowingHelpDialog || !document.body || fid != frameId)    isShowingHelpDialog = true -  container = document.createElement("div") +  container = DomUtils.createElement "div"    container.id = "vimiumHelpDialogContainer"    container.className = "vimiumReset" @@ -861,7 +862,7 @@ CursorHider =      # See #1345 and #1348.      return unless Utils.haveChromeVersion "39.0.2171.71" -    @cursorHideStyle = document.createElement("style") +    @cursorHideStyle = DomUtils.createElement "style"      @cursorHideStyle.innerHTML = """        body * {pointer-events: none !important; cursor: none !important;}        body, html {cursor: none !important;} diff --git a/lib/clipboard.coffee b/lib/clipboard.coffee index 2b28df70..f0fb83b1 100644 --- a/lib/clipboard.coffee +++ b/lib/clipboard.coffee @@ -1,6 +1,6 @@  Clipboard =    _createTextArea: -> -    textArea = document.createElement("textarea") +    textArea = document.createElement "textarea"      textArea.style.position = "absolute"      textArea.style.left = "-100%"      textArea @@ -16,7 +16,7 @@ Clipboard =      document.body.removeChild(textArea)    paste: -> -    textArea = @._createTextArea() +    textArea = @_createTextArea()      document.body.appendChild(textArea)      textArea.focus()      document.execCommand("Paste") diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee index 9658df2b..ee7d415f 100644 --- a/lib/dom_utils.coffee +++ b/lib/dom_utils.coffee @@ -8,12 +8,25 @@ DomUtils =      else        func() +  createElement: (tagName) -> +    element = document.createElement tagName +    if element instanceof HTMLElement +      # The document namespace provides (X)HTML elements, so we can use them directly. +      @createElement = (tagName) -> document.createElement tagName +      element +    else +      # The document namespace doesn't give (X)HTML elements, so we create them with the correct namespace +      # manually. +      @createElement = (tagName) -> +        document.createElementNS "http://www.w3.org/1999/xhtml", tagName +      @createElement(tagName) +    #    # Adds a list of elements to a page.    # Note that adding these nodes all at once (via the parent div) is significantly faster than one-by-one.    #    addElementList: (els, overlayOptions) -> -    parent = document.createElement("div") +    parent = @createElement "div"      parent.id = overlayOptions.id if overlayOptions.id?      parent.className = overlayOptions.className if overlayOptions.className?      parent.appendChild(el) for el in els @@ -82,7 +95,7 @@ DomUtils =            # NOTE(mrmr1993): This ignores floated/absolutely positioned descendants nested within inline            # children.            continue if (computedStyle.getPropertyValue("float") == "none" and -            computedStyle.getPropertyValue("position") != "absolute" and +            not (computedStyle.getPropertyValue("position") in ["absolute", "fixed"]) and              not (clientRect.height == 0 and isInlineZeroHeight() and                0 == computedStyle.getPropertyValue("display").indexOf "inline"))            childClientRect = @getVisibleClientRect child, true @@ -236,7 +249,7 @@ DomUtils =    # momentarily flash a rectangular border to give user some visual feedback    flashRect: (rect) -> -    flashEl = document.createElement("div") +    flashEl = @createElement "div"      flashEl.id = "vimiumFlash"      flashEl.className = "vimiumReset"      flashEl.style.left = rect.left + window.scrollX + "px" @@ -297,7 +310,7 @@ DomUtils =        'letterSpacing', 'wordSpacing' ]      (element, position) -> -      div = document.createElement "div" +      div = @createElement "div"        div.id = "vimium-input-textarea-caret-position-mirror-div"        document.body.appendChild div @@ -315,7 +328,7 @@ DomUtils =        if element.nodeName.toLowerCase() == "input"          div.textContent = div.textContent.replace /\s/g, "\u00a0" -      span = document.createElement "span" +      span = @createElement "span"        span.textContent = element.value.substring(position) || "."        div.appendChild span @@ -357,5 +370,11 @@ DomUtils =            text        texts.join " " +  # Get the element in the DOM hierachy that contains `element`. +  # If the element is rendered in a shadow DOM via a <content> element, the <content> element will be +  # returned, so the shadow DOM is traversed rather than passed over. +  getContainingElement: (element) -> +    element.getDestinationInsertionPoints()[0] or element.parentElement +  root = exports ? window  root.DomUtils = DomUtils diff --git a/lib/utils.coffee b/lib/utils.coffee index d4beff03..90469fad 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -19,12 +19,6 @@ Utils =      func = obj[components.pop()]      func.apply(obj, argArray) -  # Creates a single DOM element from :html -  createElementFromHtml: (html) -> -    tmp = document.createElement("div") -    tmp.innerHTML = html -    tmp.firstChild -    escapeHtml: (string) -> string.replace(/</g, "<").replace(/>/g, ">")    # Generates a unique ID diff --git a/manifest.json b/manifest.json index 4ef5edfe..6d971db2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@  {    "manifest_version": 2,    "name": "Vimium", -  "version": "1.51", +  "version": "1.52",    "description": "The Hacker's Browser. Vimium provides keyboard shortcuts for navigation and control in the spirit of Vim.",    "icons": {  "16": "icons/icon16.png",                "48": "icons/icon48.png", @@ -40,7 +40,6 @@               "lib/dom_utils.js",               "lib/rect.js",               "lib/handler_stack.js", -             "lib/clipboard.js",               "lib/settings.js",               "lib/find_mode_history.js",               "content_scripts/ui_component.js", diff --git a/pages/options.html b/pages/options.html index 22b041b7..b5aa5936 100644 --- a/pages/options.html +++ b/pages/options.html @@ -120,7 +120,7 @@ b: http://b.com/?q=%s description              <td verticalAlign="top" class="booleanOption">                <div class="help">                  <div class="example"> -                  In link-hint mode, this option lets you also select a link by typing its text. +                  In link-hint mode, this option lets you select a link by typing its text.                  </div>                </div>                <label> diff --git a/pages/vomnibar.coffee b/pages/vomnibar.coffee index cf9ed7b0..742dba10 100644 --- a/pages/vomnibar.coffee +++ b/pages/vomnibar.coffee @@ -125,7 +125,7 @@ class VomnibarUI      return true unless action # pass through      openInNewTab = @forceNewTab || -      (event.shiftKey || event.ctrlKey || KeyboardUtils.isPrimaryModifierKey(event)) +      (event.shiftKey || event.ctrlKey || event.altKey || KeyboardUtils.isPrimaryModifierKey(event))      if (action == "dismiss")        @hide()      else if action in [ "tab", "down" ] | 
