diff options
| author | Stephen Blott | 2014-12-29 14:22:53 +0000 | 
|---|---|---|
| committer | Stephen Blott | 2014-12-29 14:35:21 +0000 | 
| commit | 0524bdc3f76279e8930bfe4b1b42d93e0e9bf6e4 (patch) | |
| tree | 6f01a27708c2ba8c085ef093df58814715441b08 | |
| parent | a6dc63fd7c49926e6ad32174621b32eeb3fd9283 (diff) | |
| download | vimium-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.coffee | 5 | ||||
| -rw-r--r-- | content_scripts/ui_component.coffee | 79 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 10 | ||||
| -rw-r--r-- | pages/test_ui_component.coffee | 4 | ||||
| -rw-r--r-- | pages/ui_component_server.coffee | 44 | ||||
| -rw-r--r-- | tests/dom_tests/chrome.coffee | 1 | ||||
| -rw-r--r-- | tests/unit_tests/test_chrome_stubs.coffee | 4 | 
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 | 
