aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Blott2014-12-29 14:22:53 +0000
committerStephen Blott2014-12-29 14:35:21 +0000
commit0524bdc3f76279e8930bfe4b1b42d93e0e9bf6e4 (patch)
tree6f01a27708c2ba8c085ef093df58814715441b08
parenta6dc63fd7c49926e6ad32174621b32eeb3fd9283 (diff)
downloadvimium-0524bdc3f76279e8930bfe4b1b42d93e0e9bf6e4.tar.bz2
Refactor UIComponent, etc., and demo.
- Simplify component API. - Iframe flashes on re-focus. - Probably some other stuff which I've forgotten.
-rw-r--r--background_scripts/main.coffee5
-rw-r--r--content_scripts/ui_component.coffee79
-rw-r--r--content_scripts/vimium_frontend.coffee10
-rw-r--r--pages/test_ui_component.coffee4
-rw-r--r--pages/ui_component_server.coffee44
-rw-r--r--tests/dom_tests/chrome.coffee1
-rw-r--r--tests/unit_tests/test_chrome_stubs.coffee4
7 files changed, 56 insertions, 91 deletions
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index 647923c0..b85ea844 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -19,6 +19,11 @@ namedKeyRegex = /^(<(?:[amc]-.|(?:[amc]-)?[a-z0-9]{2,5})>)(.*)$/
selectionChangedHandlers = []
tabLoadedHandlers = {} # tabId -> function()
+# A secret, available only within the current instantiation of Vimium. The secret is big, likely unguessable
+# in practice, but less than 2^31.
+chrome.storage.local.set
+ vimiumSecret: Math.floor Math.random() * 2000000000
+
completionSources =
bookmarks: new BookmarkCompleter()
history: new HistoryCompleter()
diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee
index 10450778..a898d525 100644
--- a/content_scripts/ui_component.coffee
+++ b/content_scripts/ui_component.coffee
@@ -1,21 +1,19 @@
class UIComponent
iframeElement: null
iframePort: null
- messageEventListeners: []
+ showing: true
showStyle: "display: block;"
hideStyle: "display: none;"
- constructor: (iframeUrl, className, showStyle, hideStyle) ->
+ constructor: (iframeUrl, className, @handleMessage) ->
@iframeElement = document.createElement "iframe"
@iframeElement.className = className
@iframeElement.seamless = "seamless"
@iframeElement.src = chrome.runtime.getURL iframeUrl
@iframeElement.addEventListener "load", => @openPort()
document.documentElement.appendChild @iframeElement
-
- @setShowStyle showStyle if showStyle?
- @setHideStyle hideStyle if showStyle?
- @hide()
+ # Hide iframe, but don't interfere with the focus.
+ @hide false
# Open a port and pass it to the iframe via window.postMessage.
openPort: ->
@@ -23,63 +21,32 @@ class UIComponent
@iframePort = messageChannel.port1
@iframePort.onmessage = (event) => @handleMessage event
- # Get iframeMessageSecret so the iframe can determine that our message isn't the page impersonating us.
- chrome.storage.local.get "iframeMessageSecret", ({iframeMessageSecret: secret}) =>
+ # Get vimiumSecret so the iframe can determine that our message isn't the page impersonating us.
+ chrome.storage.local.get "vimiumSecret", ({vimiumSecret: secret}) =>
@iframeElement.contentWindow.postMessage secret, chrome.runtime.getURL(""), [messageChannel.port2]
- postMessage: (message) -> @iframePort.postMessage message
-
- # Execute each event listener on the current event until we get a non-null falsy return value.
- handleMessage: (event) ->
- for listener in @messageEventListeners
- retVal = listener.call this, event
- retVal ?= true
- return false unless retVal
- true
-
- addEventListener: (type, listener) ->
- if type == "message"
- @messageEventListeners.push listener
- undefined
-
- removeEventListener: (type, listener) ->
- if type == "message"
- @messageEventListeners = @messageEventListeners.filter (f) -> f != listener
- undefined
-
- setHideStyle: (@hideStyle) ->
- @hide() if @showing == false
-
- setShowStyle: (@showStyle) ->
- @show() if @showing == true
-
- setStyles: (@showStyle = @showStyle, @hideStyle = @hideStyle) ->
- if @showing
- @show()
- else
- @hide()
+ postMessage: (message) ->
+ @iframePort.postMessage message
activate: (message) ->
@postMessage message if message?
- @show() unless @showing
+ if @showing
+ # NOTE(smblott) Experimental. Not sure this is a great idea. If the iframe was already showing, then
+ # the user gets no visual feedback when it is re-focused. So flash its border.
+ borderWas = @iframeElement.style.border
+ @iframeElement.style.border = '5px solid yellow'
+ setTimeout((=> @iframeElement.style.border = borderWas), 200)
+ else
+ @iframeElement.setAttribute "style", @showStyle
+ @showing = true
@iframeElement.focus()
- show: (message) ->
- @postMessage message if message?
- @iframeElement.setAttribute "style", @showStyle
- @showing = true
-
- hide: ->
- @iframeElement.setAttribute "style", @hideStyle
- window.focus()
- @showing = false
-
-handleHideMessage = (event) ->
- if event.data == "hide"
- @hide()
- false
- else
- true
+ hide: (focusWindow=true)->
+ if @showing
+ @iframeElement.setAttribute "style", @hideStyle
+ # TODO(smblott) Is window always the right thing to focus, here?
+ window.focus() if focusWindow
+ @showing = false
root = exports ? window
root.UIComponent = UIComponent
diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee
index 3f898b74..8fcb16ed 100644
--- a/content_scripts/vimium_frontend.coffee
+++ b/content_scripts/vimium_frontend.coffee
@@ -1090,17 +1090,17 @@ Tween =
testUIComponent = null
testUIComponentSetup = ->
- testUIComponent = new UIComponent "pages/test_ui_component.html", "testUIComponent"
- testUIComponent.addEventListener "message", (event) ->
+ testUIComponent = new UIComponent "pages/test_ui_component.html", "testUIComponent", (event) ->
if event.data == "hide"
@hide()
window.focus()
- false
else
- true
+ # ... And we can get data back!
+ console.log event.data
window.activateTestUIComponent = ->
- testUIComponent.activate "version: #{chrome.runtime.getManifest().version}; random number: #{Math.random()}"
+ chrome.storage.local.get "vimiumSecret", ({vimiumSecret: secret}) ->
+ testUIComponent.activate [chrome.runtime.getManifest().version, secret, Math.random(), ].join "<br/>"
initializePreDomReady()
window.addEventListener("DOMContentLoaded", registerFrame)
diff --git a/pages/test_ui_component.coffee b/pages/test_ui_component.coffee
index 886a3470..e140fb14 100644
--- a/pages/test_ui_component.coffee
+++ b/pages/test_ui_component.coffee
@@ -1,6 +1,8 @@
-UIComponentServer.addEventListener "message", (event) ->
+UIComponentServer.registerHandler (event) ->
document.body.innerHTML = event.data
window.addEventListener "keydown", (event) ->
if KeyboardUtils.isEscape event
UIComponentServer.postMessage "hide"
+ else
+ UIComponentServer.postMessage event.keyCode
diff --git a/pages/ui_component_server.coffee b/pages/ui_component_server.coffee
index ccbb9e2b..8b43095b 100644
--- a/pages/ui_component_server.coffee
+++ b/pages/ui_component_server.coffee
@@ -1,41 +1,27 @@
-# Register the port recieved from the parent window, and stop listening for messages on the window object.
-window.addEventListener "message", (event) ->
- return unless event.source == window.parent
- currentFunction = arguments.callee
- # Check event.data against iframeMessageSecret so we can determine that this message hasn't been spoofed.
- chrome.storage.local.get "iframeMessageSecret", ({iframeMessageSecret: secret}) ->
- return unless event.data == secret
+# Fetch the Vimium secret, register the port recieved from the parent window, and stop listening for messages
+# on the window object. vimiumSecret is accessible only within the current instantion of Vimium. So a
+# malicious host page trying to register its own port can do no better than guessing.
+registerPort = (event) ->
+ chrome.storage.local.get "vimiumSecret", ({vimiumSecret: secret}) ->
+ return unless event.source == window.parent and event.data == secret
UIComponentServer.portOpen event.ports[0]
- window.removeEventListener "message", currentFunction # Stop listening for message events.
+ window.removeEventListener "message", registerPort
+
+window.addEventListener "message", registerPort
UIComponentServer =
ownerPagePort: null
- messageEventListeners: []
- exitOnEsc: true
+ handleMessage: null
portOpen: (@ownerPagePort) ->
- @ownerPagePort.onmessage = (event) => @handleMessage event
-
- postMessage: (message) -> @ownerPagePort.postMessage message
-
- # Execute each event listener on the current event until we get a non-null falsy return value.
- handleMessage: (event) ->
- for listener in @messageEventListeners
- retVal = listener.call this, event
- retVal ?= true
- return false unless retVal
- true
+ @ownerPagePort.onmessage = (event) =>
+ @handleMessage event if @handleMessage
- addEventListener: (type, listener) ->
- if type == "message"
- @messageEventListeners.push listener
- undefined
+ registerHandler: (@handleMessage) ->
- removeEventListener: (type, listener) ->
- if type == "message"
- @messageEventListeners = @messageEventListeners.filter (f) -> f != listener
- undefined
+ postMessage: (message) ->
+ @ownerPagePort.postMessage message if @ownerPagePort
root = exports ? window
root.UIComponentServer = UIComponentServer
diff --git a/tests/dom_tests/chrome.coffee b/tests/dom_tests/chrome.coffee
index 2695ef20..a49a565a 100644
--- a/tests/dom_tests/chrome.coffee
+++ b/tests/dom_tests/chrome.coffee
@@ -22,4 +22,5 @@ root.chrome = {
storage:
local:
get: ->
+ set: ->
}
diff --git a/tests/unit_tests/test_chrome_stubs.coffee b/tests/unit_tests/test_chrome_stubs.coffee
index 80750337..3258bcd6 100644
--- a/tests/unit_tests/test_chrome_stubs.coffee
+++ b/tests/unit_tests/test_chrome_stubs.coffee
@@ -42,6 +42,10 @@ exports.chrome =
getAll: () -> true
storage:
+ # chrome.storage.local
+ local:
+ set: ->
+
# chrome.storage.onChanged
onChanged:
addListener: (func) -> @func = func