aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--background_scripts/exclusions.coffee5
-rw-r--r--background_scripts/main.coffee17
-rw-r--r--background_scripts/settings.coffee2
-rw-r--r--content_scripts/mode_insert.coffee20
-rw-r--r--content_scripts/ui_component.coffee7
-rw-r--r--content_scripts/vimium.css14
-rw-r--r--content_scripts/vimium_frontend.coffee43
-rw-r--r--lib/utils.coffee16
-rw-r--r--manifest.json2
-rw-r--r--tests/unit_tests/utils_test.coffee18
11 files changed, 108 insertions, 46 deletions
diff --git a/README.md b/README.md
index 6207459e..fc566c58 100644
--- a/README.md
+++ b/README.md
@@ -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"