diff options
Diffstat (limited to 'content_scripts')
| -rw-r--r-- | content_scripts/hud.coffee | 20 | ||||
| -rw-r--r-- | content_scripts/ui_component.coffee | 136 | 
2 files changed, 78 insertions, 78 deletions
| diff --git a/content_scripts/hud.coffee b/content_scripts/hud.coffee index 62bcf03f..7ad13aaf 100644 --- a/content_scripts/hud.coffee +++ b/content_scripts/hud.coffee @@ -13,10 +13,8 @@ HUD =    # it doesn't sit on top of horizontal scrollbars like Chrome's HUD does.    init: -> -    unless @hudUI? -      @hudUI = new UIComponent "pages/hud.html", "vimiumHUDFrame", ({data}) => -        this[data.name]? data -      @tween = new Tween "iframe.vimiumHUDFrame.vimiumUIComponentVisible", @hudUI.shadowDOM +    @hudUI ?= new UIComponent "pages/hud.html", "vimiumHUDFrame", ({data}) => this[data.name]? data +    @tween ?= new Tween "iframe.vimiumHUDFrame.vimiumUIComponentVisible", @hudUI.shadowDOM    showForDuration: (text, duration) ->      @show(text) @@ -46,13 +44,13 @@ HUD =    # If :updateIndicator is truthy, then we also refresh the mode indicator.  The only time we don't update the    # mode indicator, is when hide() is called for the mode indicator itself.    hide: (immediate = false, updateIndicator = true) -> -    return unless @hudUI?.uiComponentIsReady -    clearTimeout(@_showForDurationTimerId) -    @tween.stop() -    if immediate -      if updateIndicator then Mode.setIndicator() else @hudUI.hide() -    else -      @tween.fade 0, 150, => @hide true, updateIndicator +    if @hudUI? and @tween? +      clearTimeout @_showForDurationTimerId +      @tween.stop() +      if immediate +        if updateIndicator then Mode.setIndicator() else @hudUI.hide() +      else +        @tween.fade 0, 150, => @hide true, updateIndicator    hideFindMode: (data) ->      @findMode.checkReturnToViewPort() diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index 0989bbc9..c7038f17 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -1,5 +1,4 @@  class UIComponent -  uiComponentIsReady: false    iframeElement: null    iframePort: null    showing: false @@ -13,64 +12,65 @@ class UIComponent      @iframeElement.classList.add addClass    constructor: (iframeUrl, className, @handleMessage) -> -    styleSheet = DomUtils.createElement "style" -    styleSheet.type = "text/css" -    # Default to everything hidden while the stylesheet loads. -    styleSheet.innerHTML = "iframe {display: none;}" +    DomUtils.documentReady => +      styleSheet = DomUtils.createElement "style" +      styleSheet.type = "text/css" +      # Default to everything hidden while the stylesheet loads. +      styleSheet.innerHTML = "iframe {display: none;}" -    # Use an XMLHttpRequest, possibly via the background page, to fetch the stylesheet. This allows us to -    # catch and recover from failures that we could not have caught when using CSS @include (eg. #1817). -    UIComponent::styleSheetGetter ?= new AsyncDataFetcher @fetchFileContents "content_scripts/vimium.css" -    @styleSheetGetter.use (styles) -> styleSheet.innerHTML = styles +      # Use an XMLHttpRequest, possibly via the background page, to fetch the stylesheet. This allows us to +      # catch and recover from failures that we could not have caught when using CSS @include (eg. #1817). +      UIComponent::styleSheetGetter ?= new AsyncDataFetcher @fetchFileContents "content_scripts/vimium.css" +      @styleSheetGetter.use (styles) -> styleSheet.innerHTML = styles -    @iframeElement = DomUtils.createElement "iframe" -    extend @iframeElement, -      className: className -      seamless: "seamless" -    shadowWrapper = DomUtils.createElement "div" -    # PhantomJS doesn't support createShadowRoot, so guard against its non-existance. -    @shadowDOM = shadowWrapper.createShadowRoot?() ? shadowWrapper -    @shadowDOM.appendChild styleSheet -    @shadowDOM.appendChild @iframeElement -    @toggleIframeElementClasses "vimiumUIComponentVisible", "vimiumUIComponentHidden" +      @iframeElement = DomUtils.createElement "iframe" +      extend @iframeElement, +        className: className +        seamless: "seamless" +      shadowWrapper = DomUtils.createElement "div" +      # PhantomJS doesn't support createShadowRoot, so guard against its non-existance. +      @shadowDOM = shadowWrapper.createShadowRoot?() ? shadowWrapper +      @shadowDOM.appendChild styleSheet +      @shadowDOM.appendChild @iframeElement +      @toggleIframeElementClasses "vimiumUIComponentVisible", "vimiumUIComponentHidden" -    # Open a port and pass it to the iframe via window.postMessage.  We use an AsyncDataFetcher to handle -    # requests which arrive before the iframe (and its message handlers) have completed initialization.  See -    # #1679. -    @iframePort = new AsyncDataFetcher (setIframePort) => -      # We set the iframe source and append the new element here (as opposed to above) to avoid a potential -      # race condition vis-a-vis the "load" event (because this callback runs on "nextTick"). -      @iframeElement.src = chrome.runtime.getURL iframeUrl -      document.documentElement.appendChild shadowWrapper +      # Open a port and pass it to the iframe via window.postMessage.  We use an AsyncDataFetcher to handle +      # requests which arrive before the iframe (and its message handlers) have completed initialization.  See +      # #1679. +      @iframePort = new AsyncDataFetcher (setIframePort) => +        # We set the iframe source and append the new element here (as opposed to above) to avoid a potential +        # race condition vis-a-vis the "load" event (because this callback runs on "nextTick"). +        @iframeElement.src = chrome.runtime.getURL iframeUrl +        document.documentElement.appendChild shadowWrapper -      @iframeElement.addEventListener "load", => -        # Get vimiumSecret so the iframe can determine that our message isn't the page impersonating us. -        chrome.storage.local.get "vimiumSecret", ({ vimiumSecret }) => -          { port1, port2 } = new MessageChannel -          @iframeElement.contentWindow.postMessage vimiumSecret, chrome.runtime.getURL(""), [ port2 ] -          port1.onmessage = (event) => -            switch event?.data?.name ? event?.data -              when "uiComponentIsReady" -                # If any other frame receives the focus, then hide the UI component. -                chrome.runtime.onMessage.addListener ({name, focusFrameId}) => -                  if name == "frameFocused" and @options?.focus and focusFrameId not in [frameId, @iframeFrameId] -                    @hide false -                  false # We will not be calling sendResponse. -                # If this frame receives the focus, then hide the UI component. -                window.addEventListener "focus", (event) => -                  if event.target == window and @options?.focus and not @options?.allowBlur -                    @hide false -                  true # Continue propagating the event. -                # Register the UI component as ready and post its iframe port. -                @uiComponentIsReady = true -                setIframePort port1 -              when "setIframeFrameId" then @iframeFrameId = event.data.iframeFrameId -              when "hide" then @hide() -              else @handleMessage event +        @iframeElement.addEventListener "load", => +          # Get vimiumSecret so the iframe can determine that our message isn't the page impersonating us. +          chrome.storage.local.get "vimiumSecret", ({ vimiumSecret }) => +            { port1, port2 } = new MessageChannel +            @iframeElement.contentWindow.postMessage vimiumSecret, chrome.runtime.getURL(""), [ port2 ] +            port1.onmessage = (event) => +              switch event?.data?.name ? event?.data +                when "uiComponentIsReady" +                  # If any other frame receives the focus, then hide the UI component. +                  chrome.runtime.onMessage.addListener ({name, focusFrameId}) => +                    if name == "frameFocused" and @options?.focus and focusFrameId not in [frameId, @iframeFrameId] +                      @hide false +                    false # We will not be calling sendResponse. +                  # If this frame receives the focus, then hide the UI component. +                  window.addEventListener "focus", (event) => +                    if event.target == window and @options?.focus and not @options?.allowBlur +                      @hide false +                    true # Continue propagating the event. +                  # Set the iframe's port, thereby rendering the UI component ready. +                  setIframePort port1 +                when "setIframeFrameId" then @iframeFrameId = event.data.iframeFrameId +                when "hide" then @hide() +                else @handleMessage event -  # Post a message (if provided), then call continuation (if provided). +  # Post a message (if provided), then call continuation (if provided).  Because the constructor waits for +  # documentReady(), @iframePort may not yet be ready.    postMessage: (message = null, continuation = null) -> -    @iframePort.use (port) => +    @iframePort?.use (port) =>        port.postMessage message if message?        continuation?() @@ -81,20 +81,22 @@ class UIComponent        @showing = true    hide: (shouldRefocusOriginalFrame = true) -> -    if @showing -      @showing = false -      @toggleIframeElementClasses "vimiumUIComponentVisible", "vimiumUIComponentHidden" -      if @options?.focus -        @iframeElement.blur() -        if shouldRefocusOriginalFrame -          if @options?.sourceFrameId? -            chrome.runtime.sendMessage -              handler: "sendMessageToFrames", -              message: name: "focusFrame", frameId: @options.sourceFrameId, forceFocusThisFrame: true -          else -            window.focus() -      @options = null -      @postMessage "hidden" # Inform the UI component that it is hidden. +    # We post a non-message (null) to ensure that hide() requests cannot overtake activate() requests. +    @postMessage null, => +      if @showing +        @showing = false +        @toggleIframeElementClasses "vimiumUIComponentVisible", "vimiumUIComponentHidden" +        if @options?.focus +          @iframeElement.blur() +          if shouldRefocusOriginalFrame +            if @options?.sourceFrameId? +              chrome.runtime.sendMessage +                handler: "sendMessageToFrames", +                message: name: "focusFrame", frameId: @options.sourceFrameId, forceFocusThisFrame: true +            else +              window.focus() +        @options = null +        @postMessage "hidden" # Inform the UI component that it is hidden.    # Fetch a Vimium file/resource (such as "content_scripts/vimium.css").    # We try making an XMLHttpRequest request.  That can fail (see #1817), in which case we fetch the | 
