diff options
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | background_scripts/exclusions.coffee | 5 | ||||
| -rw-r--r-- | background_scripts/main.coffee | 17 | ||||
| -rw-r--r-- | background_scripts/settings.coffee | 2 | ||||
| -rw-r--r-- | content_scripts/mode_insert.coffee | 20 | ||||
| -rw-r--r-- | content_scripts/ui_component.coffee | 7 | ||||
| -rw-r--r-- | content_scripts/vimium.css | 14 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 43 | ||||
| -rw-r--r-- | lib/utils.coffee | 16 | ||||
| -rw-r--r-- | manifest.json | 2 | ||||
| -rw-r--r-- | tests/unit_tests/utils_test.coffee | 18 |
11 files changed, 108 insertions, 46 deletions
@@ -141,9 +141,13 @@ Please see [CONTRIBUTING.md](https://github.com/philc/vimium/blob/master/CONTRIB Release Notes ------------- -1.51 (not yet released) -- Bug fixes for 1.50 (see [here](https://github.com/philc/vimium/pulls?utf8=%E2%9C%93&q=is%3Apr+sort%3Aupdated-desc+is%3Aclosed+updated%3A%3E%3D2015-04-26+state%3Amerged)). -- 1.51 is expected to be released mid-to-end May, 2015. +Changes since 1.51 (these have not yet been released) + +- Bug fixes: bookmarklets accessed from the vomnibar. + +1.51 (2015-05-02) + +- Bug [fixes](https://github.com/philc/vimium/pulls?utf8=%E2%9C%93&q=is%3Apr+sort%3Aupdated-desc+is%3Aclosed+merged%3A%3E%3D2015-04-26+merged%3A%3C2015-05-02+state%3Amerged). 1.50 (2015-04-26) diff --git a/background_scripts/exclusions.coffee b/background_scripts/exclusions.coffee index b3a3960d..5ec76e2a 100644 --- a/background_scripts/exclusions.coffee +++ b/background_scripts/exclusions.coffee @@ -2,6 +2,7 @@ root = exports ? window RegexpCache = cache: {} + clear: -> @cache = {} get: (pattern) -> if regexp = @cache[pattern] regexp @@ -44,8 +45,8 @@ root.Exclusions = Exclusions = @rules = rules.filter (rule) -> rule and rule.pattern Settings.set("exclusionRules", @rules) - postUpdateHook: (rules) -> - @rules = rules + postUpdateHook: (@rules) -> + RegexpCache.clear() # Development and debug only. # Enable this (temporarily) to restore legacy exclusion rules from backup. diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index d0411f6e..913f1de5 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -16,7 +16,7 @@ chrome.runtime.onInstalled.addListener ({ reason }) -> for tab in tabs for [ func, files ] in jobs for file in files - func tab.id, { file: file, allFrames: contentScripts.allFrames }, checkLastRuntimeError + func tab.id, { file: file, allFrames: contentScripts.all_frames }, checkLastRuntimeError currentVersion = Utils.getCurrentVersion() tabQueue = {} # windowId -> Array @@ -394,7 +394,8 @@ setIcon = (request, sender) -> chrome.browserAction.setIcon tabId: sender.tab.id, path: path handleUpdateScrollPosition = (request, sender) -> - updateScrollPosition(sender.tab, request.scrollX, request.scrollY) + # See note regarding sender.tab at unregisterFrame. + updateScrollPosition sender.tab, request.scrollX, request.scrollY if sender.tab? updateScrollPosition = (tab, scrollX, scrollY) -> tabInfoMap[tab.id].scrollX = scrollX @@ -607,7 +608,10 @@ registerFrame = (request, sender) -> (frameIdsForTab[sender.tab.id] ?= []).push request.frameId unregisterFrame = (request, sender) -> - tabId = sender.tab.id + # When a tab is closing, Chrome sometimes passes messages without sender.tab. Therefore, we guard against + # this. + tabId = sender.tab?.id + return unless tabId? if frameIdsForTab[tabId]? if request.tab_is_closing updateOpenTabs sender.tab, true @@ -683,6 +687,13 @@ chrome.tabs.onRemoved.addListener (tabId) -> # There are no remaining incognito-mode tabs, and findModeRawQueryListIncognito is set. chrome.storage.local.remove "findModeRawQueryListIncognito" +# Tidy up tab caches when tabs are removed. We cannot rely on unregisterFrame because Chrome does not always +# provide sender.tab there. +# NOTE(smblott) (2015-05-05) This may break restoreTab on legacy Chrome versions, but we'll be moving to +# chrome.sessions support only soon anyway. +chrome.tabs.onRemoved.addListener (tabId) -> + delete cache[tabId] for cache in [ frameIdsForTab, urlForTab, tabInfoMap ] + # Convenience function for development use. window.runTests = -> open(chrome.runtime.getURL('tests/dom_tests/dom_tests.html')) diff --git a/background_scripts/settings.coffee b/background_scripts/settings.coffee index e042eded..afc270fd 100644 --- a/background_scripts/settings.coffee +++ b/background_scripts/settings.coffee @@ -86,7 +86,7 @@ root.Settings = Settings = # "\bnext\b,\bmore\b,>,→,»,≫,>>" nextPatterns: "next,more,>,\u2192,\xbb,\u226b,>>" # default/fall back search engine - searchUrl: "http://www.google.com/search?q=" + searchUrl: "https://www.google.com/search?q=" # put in an example search engine searchEngines: [ "w: http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s Wikipedia" diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee index 4d68b12d..4e03cdd5 100644 --- a/content_scripts/mode_insert.coffee +++ b/content_scripts/mode_insert.coffee @@ -19,7 +19,7 @@ class InsertMode extends Mode # the right thing to do for most common use cases. However, it could also cripple flash-based sites and # games. See discussion in #1211 and #1194. target.blur() - else if target.shadowRoot and @insertModeLock + else if target?.shadowRoot and @insertModeLock # An editable element in a shadow DOM is focused; blur it. @insertModeLock.blur() @exit event, event.srcElement @@ -61,17 +61,19 @@ class InsertMode extends Mode # event inside the shadow DOM. This fixes #853. shadowRoot = event.target.shadowRoot eventListeners = {} - for type in ["focus", "blur"] + for type in [ "focus", "blur" ] eventListeners[type] = do (type) -> - (event) -> - handlerStack.bubbleEvent type, event - if type == "blur" - # Unregister the event listeners. - for ltype, listener of eventListeners - shadowRoot.removeEventListener ltype, listener, true - + (event) -> handlerStack.bubbleEvent type, event shadowRoot.addEventListener type, eventListeners[type], true + handlerStack.push + _name: "shadow-DOM-input-mode" + blur: (event) -> + if event.target.shadowRoot == shadowRoot + handlerStack.remove() + for type, listener of eventListeners + shadowRoot.removeEventListener type, listener, true + # Only for tests. This gives us a hook to test the status of the permanently-installed instance. InsertMode.permanentInstance = @ if @permanent diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index 61c45136..5e0bd5d5 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -8,12 +8,7 @@ class UIComponent styleSheet = document.createElement "style" styleSheet.type = "text/css" # Default to everything hidden while the stylesheet loads. - styleSheet.innerHTML = "* {display: none !important;}" - # Load stylesheet. - xhr = new XMLHttpRequest() - xhr.onload = (e) -> styleSheet.innerHTML = xhr.responseText - xhr.open "GET", chrome.runtime.getURL("content_scripts/vimium.css"), true - xhr.send() + styleSheet.innerHTML = "@import url(\"#{chrome.runtime.getURL("content_scripts/vimium.css")}\");" @iframeElement = document.createElement "iframe" extend @iframeElement, diff --git a/content_scripts/vimium.css b/content_scripts/vimium.css index fb8824c2..5180fb22 100644 --- a/content_scripts/vimium.css +++ b/content_scripts/vimium.css @@ -109,6 +109,20 @@ div.internalVimiumSelectedInputHint span { color: white !important; } +/* Frame Highlight Marker CSS*/ +div.vimiumHighlightedFrame { + position: fixed; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + padding: 0px; + margin: 0px; + border: 5px solid yellow; + box-sizing: border-box; + pointer-events: none; +} + /* Help Dialog CSS */ div#vimiumHelpDialog { diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index e1d7b581..d6cc835d 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -327,20 +327,35 @@ setScrollPosition = (scrollX, scrollY) -> # # Called from the backend in order to change frame focus. # -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. - # Here we're assuming that there is at least one frame large enough to focus. - chrome.runtime.sendMessage({ handler: "nextFrame", frameId: frameId }) - return - window.focus() - shouldHighlight = request.highlight - shouldHighlight ||= request.highlightOnlyIfNotTop and not DomUtils.isTopFrame() - if document.body and shouldHighlight - borderWas = document.body.style.border - document.body.style.border = '5px solid yellow' - setTimeout((-> document.body.style.border = borderWas), 200) +window.focusThisFrame = do -> + # Create a shadow DOM wrapping the frame so the page's styles don't interfere with ours. + highlightedFrameElement = document.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 + + _frameEl = document.createElement "div" + _frameEl.className = "vimiumReset vimiumHighlightedFrame" + _shadowDOM.appendChild _frameEl + + (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. + # Here we're assuming that there is at least one frame large enough to focus. + chrome.runtime.sendMessage({ handler: "nextFrame", frameId: frameId }) + return + window.focus() + shouldHighlight = request.highlight + shouldHighlight ||= request.highlightOnlyIfNotTop and not DomUtils.isTopFrame() + if shouldHighlight + document.documentElement.appendChild highlightedFrameElement + setTimeout (-> highlightedFrameElement.remove()), 200 extend window, scrollToBottom: -> Scroller.scrollTo "y", "max" diff --git a/lib/utils.coffee b/lib/utils.coffee index 1ff33300..a56340f5 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -39,6 +39,17 @@ Utils = urlPrefix = new RegExp "^[a-z]{3,}://." (url) -> urlPrefix.test url + # Decode valid escape sequences in a Javascript URI. This is intended to mimic the best-effort decoding + # Chrome itself seems to apply when a Javascript URI is enetered into the omnibox (or clicked). + # See https://code.google.com/p/chromium/issues/detail?id=483000, #1611 and #1636. + decodeJavascriptURI: (uri) -> + uri.split(/(?=%)/).map((uriComponent) -> + try + decodeURIComponent uriComponent + catch + uriComponent + ).join "" + # Completes a partial URL (without scheme) createFullUrl: (partialUrl) -> if @hasFullUrlPrefix(partialUrl) then partialUrl else ("http://" + partialUrl) @@ -112,10 +123,7 @@ Utils = if Utils.hasChromePrefix string string else if Utils.hasJavascriptPrefix string - # We blindly URL decode javascript: URLs. That's what Chrome does when they're clicked, or entered into - # the omnibox. However, Chrome does not URL decode such URLs in chrome.tabs.update. - # This is arguably a Chrome bug. See https://code.google.com/p/chromium/issues/detail?id=483000. - decodeURI string + Utils.decodeJavascriptURI string else if Utils.isUrl string Utils.createFullUrl string else diff --git a/manifest.json b/manifest.json index 7abd7738..6445548a 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Vimium", - "version": "1.50", + "version": "1.51", "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", diff --git a/tests/unit_tests/utils_test.coffee b/tests/unit_tests/utils_test.coffee index 88e9a15b..829c7042 100644 --- a/tests/unit_tests/utils_test.coffee +++ b/tests/unit_tests/utils_test.coffee @@ -42,11 +42,12 @@ context "convertToUrl", assert.equal "http://127.0.0.1:8080", Utils.convertToUrl("127.0.0.1:8080") assert.equal "http://[::]:8080", Utils.convertToUrl("[::]:8080") assert.equal "view-source: 0.0.0.0", Utils.convertToUrl("view-source: 0.0.0.0") + assert.equal "javascript:alert('25 % 20 * 25 ');", Utils.convertToUrl "javascript:alert('25 % 20 * 25%20');" should "convert non-URL terms into search queries", -> - assert.equal "http://www.google.com/search?q=google", Utils.convertToUrl("google") - assert.equal "http://www.google.com/search?q=go+ogle.com", Utils.convertToUrl("go ogle.com") - assert.equal "http://www.google.com/search?q=%40twitter", Utils.convertToUrl("@twitter") + assert.equal "https://www.google.com/search?q=google", Utils.convertToUrl("google") + assert.equal "https://www.google.com/search?q=go+ogle.com", Utils.convertToUrl("go ogle.com") + assert.equal "https://www.google.com/search?q=%40twitter", Utils.convertToUrl("@twitter") context "hasChromePrefix", should "detect chrome prefixes of URLs", -> @@ -62,6 +63,17 @@ context "hasChromePrefix", assert.isFalse Utils.hasChromePrefix "data" assert.isFalse Utils.hasChromePrefix "data :foobar" +context "hasJavascriptPrefix", + should "detect javascript: URLs", -> + assert.isTrue Utils.hasJavascriptPrefix "javascript:foobar" + assert.isFalse Utils.hasJavascriptPrefix "http:foobar" + +context "decodeJavascriptURI", + should "decode javascript: URLs", -> + assert.equal "foobar", Utils.decodeJavascriptURI "foobar" + assert.equal " ", Utils.decodeJavascriptURI "%20" + assert.equal "25 % 20 25 ", Utils.decodeJavascriptURI "25 % 20 25%20" + context "isUrl", should "identify URLs as URLs", -> assert.isTrue Utils.isUrl "http://www.example.com/blah" |
