From 1ae621489da091e6d9430da248d0e6eaab606f35 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 16 Mar 2015 09:35:24 +0000 Subject: Activate vomnibar in window.top. This changes vomnibar commands to activate not in the current frame, but in window.top. Consequently, the vomnibar always appears in the same position, and we don't get odd looking vomnibars in small frames. Apart from the better UX, this seems to be the right thing to do. Vomnibar commands apply to tabs (not frames). Currently incomplete: - On exit, the focus is not returned to the frame which originally had the focus. (It's returned to window.top). - The vomnibar can lose the focus and not hide itself for various frame/click combinations. --- content_scripts/ui_component.coffee | 3 +++ content_scripts/vimium_frontend.coffee | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index dadc84b5..b582cb5d 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -36,6 +36,9 @@ class UIComponent @postMessage message if message? @iframeElement.classList.remove "vimiumUIComponentHidden" @iframeElement.classList.add "vimiumUIComponentShowing" + # The window may not have the focus. We focus it now so that the focus listener below isn't triggered + # immediately. + window.focus() window.addEventListener "focus", @onFocus = (event) => if event.target == window window.removeEventListener "focus", @onFocus diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 429657fc..0aaee3db 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -241,7 +241,7 @@ initializeOnDomReady = -> # Tell the background page we're in the dom ready state. chrome.runtime.connect({ name: "domReady" }) CursorHider.init() - Vomnibar.init() + Vomnibar.init() if window.top == window registerFrame = -> # Don't register frameset containers; focusing them is no use. @@ -258,6 +258,14 @@ unregisterFrame = -> tab_is_closing: window.top == window.self executePageCommand = (request) -> + # Vomnibar commands are handled in the tab's main frame. + if request.command.split(".")[0] == "Vomnibar" + if window.top == window + Utils.invokeCommandString request.command + refreshCompletionKeys request + return + + # All other commands are handled in their frame. return unless frameId == request.frameId if (request.passCountToFunction) -- cgit v1.2.3 From efada88f419933c5bd1478faada3b4eff3082103 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 09:18:52 +0000 Subject: Activate vomnibar in window.top; refocus original frame. --- background_scripts/main.coffee | 10 ++++++++++ content_scripts/ui_component.coffee | 14 +++++++++++--- content_scripts/vimium_frontend.coffee | 9 +++++++-- content_scripts/vomnibar.coffee | 19 +++++++++++-------- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index fe6cc70b..f031eeaf 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -658,6 +658,15 @@ handleFrameFocused = (request, sender) -> frameIdsForTab[tabId] = [request.frameId, (frameIdsForTab[tabId].filter (id) -> id != request.frameId)...] +# Send a message to a frame (in the sender's tab). The sender should set: +# - request.targetFrameId (the target frame) +# - request.name (the name of the handler in the target frame, e.g. "focusFrame") +# In addition, request.senderFrameId will be set to the frameId of the sender. +sendMessageToFrame = (request, sender) -> + request.senderFrameId = request.frameId + request.frameId = request.targetFrameId + chrome.tabs.sendMessage sender.tab.id, request + # Port handler mapping portHandlers = keyDown: handleKeyDown, @@ -685,6 +694,7 @@ sendRequestHandlers = createMark: Marks.create.bind(Marks) gotoMark: Marks.goto.bind(Marks) setBadge: setBadge + sendMessageToFrame: sendMessageToFrame # We always remove chrome.storage.local/findModeRawQueryListIncognito on startup. chrome.storage.local.remove "findModeRawQueryListIncognito" diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index b582cb5d..3a4af6b5 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -2,6 +2,7 @@ class UIComponent iframeElement: null iframePort: null showing: null + options: null constructor: (iframeUrl, className, @handleMessage) -> @iframeElement = document.createElement "iframe" @@ -27,8 +28,8 @@ class UIComponent postMessage: (message) -> @iframePort.postMessage message - activate: (message) -> - @postMessage message if message? + activate: (@options) -> + @postMessage @options if @options? @show() unless @showing @iframeElement.focus() @@ -51,7 +52,14 @@ class UIComponent @iframeElement.classList.add "vimiumUIComponentHidden" window.removeEventListener "focus", @onFocus if @onFocus @onFocus = null - window.focus() if focusWindow + if focusWindow and @options?.frameId? + chrome.runtime.sendMessage + handler: "sendMessageToFrame" + frameId: frameId + targetFrameId: @options.frameId + name: "focusFrame" + highlight: true # true for debugging; should be false when live. + @options = null @showing = false root = exports ? window diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 0aaee3db..cdb47613 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -232,7 +232,9 @@ getActiveState = -> registerFocus = -> # settings may have changed since the frame last had focus settings.load() - chrome.runtime.sendMessage({ handler: "frameFocused", frameId: frameId }) + # Don't register frameset containers; focusing them is no use. + unless document.body?.tagName.toLowerCase() == "frameset" + chrome.runtime.sendMessage({ handler: "frameFocused", frameId: frameId }) # # Initialization tasks that must wait for the document to be ready. @@ -261,7 +263,9 @@ executePageCommand = (request) -> # Vomnibar commands are handled in the tab's main frame. if request.command.split(".")[0] == "Vomnibar" if window.top == window - Utils.invokeCommandString request.command + # We pass the frameId from request. That's the frame which originated the request, so that's the frame + # that needs to receive the focus when we're done. + Utils.invokeCommandString request.command, [ request.frameId ] refreshCompletionKeys request return @@ -283,6 +287,7 @@ setScrollPosition = (scrollX, scrollY) -> # Called from the backend in order to change frame focus. # window.focusThisFrame = (shouldHighlight) -> + console.log "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 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. diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index c4cfc8b9..c2b7fc5b 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -4,31 +4,33 @@ Vomnibar = vomnibarUI: null - activate: -> @open {completer:"omni"} - activateInNewTab: -> @open { + # frameId here (and below) is the ID of the frame from which this request originates, which may be different + # from the current frame. + activate: (frameId) -> @open frameId, {completer:"omni"} + activateInNewTab: (frameId) -> @open frameId, { completer: "omni" selectFirst: false newTab: true } - activateTabSelection: -> @open { + activateTabSelection: (frameId) -> @open frameId, { completer: "tabs" selectFirst: true } - activateBookmarks: -> @open { + activateBookmarks: (frameId) -> @open frameId, { completer: "bookmarks" selectFirst: true } - activateBookmarksInNewTab: -> @open { + activateBookmarksInNewTab: (frameId) -> @open frameId, { completer: "bookmarks" selectFirst: true newTab: true } - activateEditUrl: -> @open { + activateEditUrl: (frameId) -> @open frameId, { completer: "omni" selectFirst: false query: window.location.href } - activateEditUrlInNewTab: -> @open { + activateEditUrlInNewTab: (frameId) -> @open frameId, { completer: "omni" selectFirst: false query: window.location.href @@ -48,7 +50,8 @@ Vomnibar = # query - Optional. Text to prefill the Vomnibar with. # selectFirst - Optional, boolean. Whether to select the first entry. # newTab - Optional, boolean. Whether to open the result in a new tab. - open: (options) -> @vomnibarUI.activate options + open: (frameId, options) -> + @vomnibarUI.activate extend options, { frameId } root = exports ? window root.Vomnibar = Vomnibar -- cgit v1.2.3 From e65f4d4e8a498e34a18c64898cb7425caee9fdc0 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 09:34:43 +0000 Subject: Activate vomnibar in window.top; minor clean up. --- content_scripts/vimium_frontend.coffee | 1 - content_scripts/vomnibar.coffee | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index cdb47613..eb951c33 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -287,7 +287,6 @@ setScrollPosition = (scrollX, scrollY) -> # Called from the backend in order to change frame focus. # window.focusThisFrame = (shouldHighlight) -> - console.log "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 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. diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index c2b7fc5b..823dcd01 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -50,8 +50,7 @@ Vomnibar = # query - Optional. Text to prefill the Vomnibar with. # selectFirst - Optional, boolean. Whether to select the first entry. # newTab - Optional, boolean. Whether to open the result in a new tab. - open: (frameId, options) -> - @vomnibarUI.activate extend options, { frameId } + open: (frameId, options) -> @vomnibarUI.activate extend options, { frameId } root = exports ? window root.Vomnibar = Vomnibar -- cgit v1.2.3 From aa8d00fd9779d4061431dbdcb7a70b9c39e8049a Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 09:47:46 +0000 Subject: Activate vomnibar in window.top; simplify messaging. --- background_scripts/main.coffee | 13 ++++--------- content_scripts/ui_component.coffee | 10 +++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index f031eeaf..b8c015d7 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -658,14 +658,9 @@ handleFrameFocused = (request, sender) -> frameIdsForTab[tabId] = [request.frameId, (frameIdsForTab[tabId].filter (id) -> id != request.frameId)...] -# Send a message to a frame (in the sender's tab). The sender should set: -# - request.targetFrameId (the target frame) -# - request.name (the name of the handler in the target frame, e.g. "focusFrame") -# In addition, request.senderFrameId will be set to the frameId of the sender. -sendMessageToFrame = (request, sender) -> - request.senderFrameId = request.frameId - request.frameId = request.targetFrameId - chrome.tabs.sendMessage sender.tab.id, request +# Send a message to a all frames in the current tab. +sendMessageToFrames = (request, sender) -> + chrome.tabs.sendMessage sender.tab.id, request.message # Port handler mapping portHandlers = @@ -694,7 +689,7 @@ sendRequestHandlers = createMark: Marks.create.bind(Marks) gotoMark: Marks.goto.bind(Marks) setBadge: setBadge - sendMessageToFrame: sendMessageToFrame + sendMessageToFrames: sendMessageToFrames # We always remove chrome.storage.local/findModeRawQueryListIncognito on startup. chrome.storage.local.remove "findModeRawQueryListIncognito" diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index 3a4af6b5..82398727 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -54,11 +54,11 @@ class UIComponent @onFocus = null if focusWindow and @options?.frameId? chrome.runtime.sendMessage - handler: "sendMessageToFrame" - frameId: frameId - targetFrameId: @options.frameId - name: "focusFrame" - highlight: true # true for debugging; should be false when live. + handler: "sendMessageToFrames" + message: + name: "focusFrame" + frameId: @options.frameId + highlight: true # true for debugging; should be false when live. @options = null @showing = false -- cgit v1.2.3 From 9461d30b0d19fd65dc43e18bebec1fe0fd3ee818 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 10:32:40 +0000 Subject: Activate vomnibar in window.top; hide on focus. --- background_scripts/main.coffee | 4 ++++ content_scripts/ui_component.coffee | 15 +++++---------- content_scripts/vimium_frontend.coffee | 1 + 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index b8c015d7..0b19ce4a 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -657,6 +657,10 @@ handleFrameFocused = (request, sender) -> if frameIdsForTab[tabId]? frameIdsForTab[tabId] = [request.frameId, (frameIdsForTab[tabId].filter (id) -> id != request.frameId)...] + # Inform all frames that a frame has received the focus. + chrome.tabs.sendMessage sender.tab.id, + name: "frameFocused" + frameId: request.frameId # Send a message to a all frames in the current tab. sendMessageToFrames = (request, sender) -> diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index 82398727..64dddf67 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -15,6 +15,11 @@ class UIComponent # Hide the iframe, but don't interfere with the focus. @hide false + # If any frame in the current tab receives the focus, then we hide the vomnibar. + chrome.runtime.onMessage.addListener (request) => + @hide false if @showing and request.name == "frameFocused" + false # Free up response handler. + # Open a port and pass it to the iframe via window.postMessage. openPort: -> messageChannel = new MessageChannel() @@ -37,21 +42,11 @@ class UIComponent @postMessage message if message? @iframeElement.classList.remove "vimiumUIComponentHidden" @iframeElement.classList.add "vimiumUIComponentShowing" - # The window may not have the focus. We focus it now so that the focus listener below isn't triggered - # immediately. - window.focus() - window.addEventListener "focus", @onFocus = (event) => - if event.target == window - window.removeEventListener "focus", @onFocus - @onFocus = null - @postMessage "hide" @showing = true hide: (focusWindow = true)-> @iframeElement.classList.remove "vimiumUIComponentShowing" @iframeElement.classList.add "vimiumUIComponentHidden" - window.removeEventListener "focus", @onFocus if @onFocus - @onFocus = null if focusWindow and @options?.frameId? chrome.runtime.sendMessage handler: "sendMessageToFrames" diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index eb951c33..941b2bc2 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -178,6 +178,7 @@ initializePreDomReady = -> currentKeyQueue: (request) -> keyQueue = request.keyQueue handlerStack.bubbleEvent "registerKeyQueue", { keyQueue: keyQueue } + frameFocused: -> # A frame has received the focus. We don't care, here. The Vomnibar/UI-component cares. chrome.runtime.onMessage.addListener (request, sender, sendResponse) -> # In the options page, we will receive requests from both content and background scripts. ignore those -- cgit v1.2.3 From 8bc811aae1118e28faa7c17e67b039c67f637274 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 11:42:58 +0000 Subject: Activate vomnibar in window.top; hide on focus, fixed. --- background_scripts/main.coffee | 5 +++++ content_scripts/ui_component.coffee | 14 ++++++++++++-- content_scripts/vimium_frontend.coffee | 20 +++++++++++++------- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 0b19ce4a..07e89e08 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -666,6 +666,10 @@ handleFrameFocused = (request, sender) -> sendMessageToFrames = (request, sender) -> chrome.tabs.sendMessage sender.tab.id, request.message +# For debugging only. This allows content scripts to log messages to the background page's console. +bgLog = (request, sender) -> + console.log "#{sender.tab.id}/#{request.frameId}", request.message + # Port handler mapping portHandlers = keyDown: handleKeyDown, @@ -694,6 +698,7 @@ sendRequestHandlers = gotoMark: Marks.goto.bind(Marks) setBadge: setBadge sendMessageToFrames: sendMessageToFrames + log: bgLog # We always remove chrome.storage.local/findModeRawQueryListIncognito on startup. chrome.storage.local.remove "findModeRawQueryListIncognito" diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index 64dddf67..3d8cef29 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -15,9 +15,9 @@ class UIComponent # Hide the iframe, but don't interfere with the focus. @hide false - # If any frame in the current tab receives the focus, then we hide the vomnibar. + # If any other frame in the current tab receives the focus, then we hide the vomnibar. chrome.runtime.onMessage.addListener (request) => - @hide false if @showing and request.name == "frameFocused" + @hide false if @showing and request.name == "frameFocused" and request.frameId != frameId false # Free up response handler. # Open a port and pass it to the iframe via window.postMessage. @@ -42,11 +42,21 @@ class UIComponent @postMessage message if message? @iframeElement.classList.remove "vimiumUIComponentHidden" @iframeElement.classList.add "vimiumUIComponentShowing" + # The window may not have the focus. We focus it now, to prevent the "focus" listener below from firing + # immediately. + window.focus() + window.addEventListener "focus", @onFocus = (event) => + if event.target == window + window.removeEventListener "focus", @onFocus + @onFocus = null + @postMessage "hide" @showing = true hide: (focusWindow = true)-> @iframeElement.classList.remove "vimiumUIComponentShowing" @iframeElement.classList.add "vimiumUIComponentHidden" + window.removeEventListener "focus", @onFocus if @onFocus + @onFocus = null if focusWindow and @options?.frameId? chrome.runtime.sendMessage handler: "sendMessageToFrames" diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 941b2bc2..90b25a40 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -94,7 +94,12 @@ settings = # # Give this frame a unique id. # -frameId = Math.floor(Math.random()*999999999) +window.frameId = Math.floor(Math.random()*999999999) + +# For debugging only. This logs to the console on the background page. +window.bgLog = (args...) -> + args = (arg.toString() for arg in args) + chrome.runtime.sendMessage handler: "log", frameId: frameId, message: args.join " " # If an input grabs the focus before the user has interacted with the page, then grab it back (if the # grabBackFocus option is set). @@ -230,12 +235,13 @@ getActiveState = -> # # The backend needs to know which frame has focus. # -registerFocus = -> - # settings may have changed since the frame last had focus - settings.load() - # Don't register frameset containers; focusing them is no use. - unless document.body?.tagName.toLowerCase() == "frameset" - chrome.runtime.sendMessage({ handler: "frameFocused", frameId: frameId }) +registerFocus = (event) -> + if event.target == window + # settings may have changed since the frame last had focus + settings.load() + # Don't register frameset containers; focusing them is no use. + unless document.body?.tagName.toLowerCase() == "frameset" + chrome.runtime.sendMessage({ handler: "frameFocused", frameId: frameId }) # # Initialization tasks that must wait for the document to be ready. -- cgit v1.2.3 From 4399e2e4fffc4faba9f1505dfc0ad3150a948632 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 12:06:15 +0000 Subject: Activate vomnibar in window.top; more clean up. --- background_scripts/main.coffee | 4 ++-- content_scripts/ui_component.coffee | 10 ++++++---- content_scripts/vimium_frontend.coffee | 12 ++++++------ content_scripts/vomnibar.coffee | 18 +++++++++--------- lib/dom_utils.coffee | 6 ++++++ 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 07e89e08..a64c7e37 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -660,9 +660,9 @@ handleFrameFocused = (request, sender) -> # Inform all frames that a frame has received the focus. chrome.tabs.sendMessage sender.tab.id, name: "frameFocused" - frameId: request.frameId + focusFrameId: request.frameId -# Send a message to a all frames in the current tab. +# Send a message to all frames in the current tab. sendMessageToFrames = (request, sender) -> chrome.tabs.sendMessage sender.tab.id, request.message diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index 3d8cef29..185d525a 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -15,9 +15,11 @@ class UIComponent # Hide the iframe, but don't interfere with the focus. @hide false - # If any other frame in the current tab receives the focus, then we hide the vomnibar. + # If any other frame in the current tab receives the focus, then we hide the UI component. + # NOTE(smblott) This is correct for the vomnibar, but might be incorrect (and need to be revisited) for + # other UI components. chrome.runtime.onMessage.addListener (request) => - @hide false if @showing and request.name == "frameFocused" and request.frameId != frameId + @hide false if @showing and request.name == "frameFocused" and request.focusFrameId != frameId false # Free up response handler. # Open a port and pass it to the iframe via window.postMessage. @@ -57,12 +59,12 @@ class UIComponent @iframeElement.classList.add "vimiumUIComponentHidden" window.removeEventListener "focus", @onFocus if @onFocus @onFocus = null - if focusWindow and @options?.frameId? + if focusWindow and @options?.sourceFrameId? chrome.runtime.sendMessage handler: "sendMessageToFrames" message: name: "focusFrame" - frameId: @options.frameId + frameId: @options.sourceFrameId highlight: true # true for debugging; should be false when live. @options = null @showing = false diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 90b25a40..072789a4 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -237,11 +237,11 @@ getActiveState = -> # registerFocus = (event) -> if event.target == window - # settings may have changed since the frame last had focus + # Settings may have changed since the frame last had focus. settings.load() # Don't register frameset containers; focusing them is no use. unless document.body?.tagName.toLowerCase() == "frameset" - chrome.runtime.sendMessage({ handler: "frameFocused", frameId: frameId }) + chrome.runtime.sendMessage handler: "frameFocused", frameId: frameId # # Initialization tasks that must wait for the document to be ready. @@ -250,7 +250,7 @@ initializeOnDomReady = -> # Tell the background page we're in the dom ready state. chrome.runtime.connect({ name: "domReady" }) CursorHider.init() - Vomnibar.init() if window.top == window + Vomnibar.init() if DomUtils.isTopFrame() registerFrame = -> # Don't register frameset containers; focusing them is no use. @@ -264,12 +264,12 @@ unregisterFrame = -> chrome.runtime.sendMessage handler: "unregisterFrame" frameId: frameId - tab_is_closing: window.top == window.self + tab_is_closing: DomUtils.isTopFrame() executePageCommand = (request) -> - # Vomnibar commands are handled in the tab's main frame. + # Vomnibar commands are handled in the tab's main/top frame. if request.command.split(".")[0] == "Vomnibar" - if window.top == window + if DomUtils.isTopFrame() # We pass the frameId from request. That's the frame which originated the request, so that's the frame # that needs to receive the focus when we're done. Utils.invokeCommandString request.command, [ request.frameId ] diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index 823dcd01..3d54e643 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -4,33 +4,33 @@ Vomnibar = vomnibarUI: null - # frameId here (and below) is the ID of the frame from which this request originates, which may be different + # sourceFrameId here (and below) is the ID of the frame from which this request originates, which may be different # from the current frame. - activate: (frameId) -> @open frameId, {completer:"omni"} - activateInNewTab: (frameId) -> @open frameId, { + activate: (sourceFrameId) -> @open sourceFrameId, {completer:"omni"} + activateInNewTab: (sourceFrameId) -> @open sourceFrameId, { completer: "omni" selectFirst: false newTab: true } - activateTabSelection: (frameId) -> @open frameId, { + activateTabSelection: (sourceFrameId) -> @open sourceFrameId, { completer: "tabs" selectFirst: true } - activateBookmarks: (frameId) -> @open frameId, { + activateBookmarks: (sourceFrameId) -> @open sourceFrameId, { completer: "bookmarks" selectFirst: true } - activateBookmarksInNewTab: (frameId) -> @open frameId, { + activateBookmarksInNewTab: (sourceFrameId) -> @open sourceFrameId, { completer: "bookmarks" selectFirst: true newTab: true } - activateEditUrl: (frameId) -> @open frameId, { + activateEditUrl: (sourceFrameId) -> @open sourceFrameId, { completer: "omni" selectFirst: false query: window.location.href } - activateEditUrlInNewTab: (frameId) -> @open frameId, { + activateEditUrlInNewTab: (sourceFrameId) -> @open sourceFrameId, { completer: "omni" selectFirst: false query: window.location.href @@ -50,7 +50,7 @@ Vomnibar = # query - Optional. Text to prefill the Vomnibar with. # selectFirst - Optional, boolean. Whether to select the first entry. # newTab - Optional, boolean. Whether to open the result in a new tab. - open: (frameId, options) -> @vomnibarUI.activate extend options, { frameId } + open: (sourceFrameId, options) -> @vomnibarUI.activate extend options, { sourceFrameId } root = exports ? window root.Vomnibar = Vomnibar diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee index 0231f994..efb125f6 100644 --- a/lib/dom_utils.coffee +++ b/lib/dom_utils.coffee @@ -26,6 +26,12 @@ DomUtils = # removeElement: (el) -> el.parentNode.removeChild el + # + # Test whether the current frame is the top/main frame. + # + isTopFrame: -> + window.top == window.self + # # Takes an array of XPath selectors, adds the necessary namespaces (currently only XHTML), and applies them # to the document root. The namespaceResolver in evaluateXPath should be kept in sync with the namespaces -- cgit v1.2.3 From 71d33fdeaa5c081de5041a6a0909d452e1564633 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 13:21:02 +0000 Subject: Activate vomnibar in window.top; fix race condition on close. --- background_scripts/main.coffee | 1 + content_scripts/ui_component.coffee | 30 ++++++++++++++++++++---------- content_scripts/vimium_frontend.coffee | 8 +++++--- pages/vomnibar.coffee | 3 +-- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index a64c7e37..05fad941 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -657,6 +657,7 @@ handleFrameFocused = (request, sender) -> if frameIdsForTab[tabId]? frameIdsForTab[tabId] = [request.frameId, (frameIdsForTab[tabId].filter (id) -> id != request.frameId)...] + console.log frameIdsForTab[tabId] # Inform all frames that a frame has received the focus. chrome.tabs.sendMessage sender.tab.id, name: "frameFocused" diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index 185d525a..727f6c27 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -19,7 +19,7 @@ class UIComponent # NOTE(smblott) This is correct for the vomnibar, but might be incorrect (and need to be revisited) for # other UI components. chrome.runtime.onMessage.addListener (request) => - @hide false if @showing and request.name == "frameFocused" and request.focusFrameId != frameId + @postMessage "hide" if @showing and request.name == "frameFocused" and request.focusFrameId != frameId false # Free up response handler. # Open a port and pass it to the iframe via window.postMessage. @@ -55,19 +55,29 @@ class UIComponent @showing = true hide: (focusWindow = true)-> - @iframeElement.classList.remove "vimiumUIComponentShowing" - @iframeElement.classList.add "vimiumUIComponentHidden" + @refocusSourceFrame @options?.sourceFrameId if focusWindow and @options?.sourceFrameId? window.removeEventListener "focus", @onFocus if @onFocus @onFocus = null - if focusWindow and @options?.sourceFrameId? - chrome.runtime.sendMessage - handler: "sendMessageToFrames" - message: - name: "focusFrame" - frameId: @options.sourceFrameId - highlight: true # true for debugging; should be false when live. + @iframeElement.classList.remove "vimiumUIComponentShowing" + @iframeElement.classList.add "vimiumUIComponentHidden" @options = null @showing = false + # Refocus the frame from which the UI component was opened. + # After hiding the UI component, Chrome refocuses the containing frame. To avoid a race condition, we need + # to wait until that frame receives the focus, before then focusing the frame which should now have the + # focus. + refocusSourceFrame: (sourceFrameId) -> + window.addEventListener "focus", handler = (event) -> + if event.target == window + window.removeEventListener "focus", handler + chrome.runtime.sendMessage + handler: "sendMessageToFrames" + message: + name: "focusFrame" + frameId: sourceFrameId + highlight: false + highlightOnlyIfNotTop: true + root = exports ? window root.UIComponent = UIComponent diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 072789a4..dccdfe76 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -173,7 +173,7 @@ initializePreDomReady = -> showUpgradeNotification: (request) -> HUD.showUpgradeNotification(request.version) showHUDforDuration: (request) -> HUD.showForDuration request.text, request.duration toggleHelpDialog: (request) -> toggleHelpDialog(request.dialogHtml, request.frameId) - focusFrame: (request) -> if (frameId == request.frameId) then focusThisFrame(request.highlight) + focusFrame: (request) -> if (frameId == request.frameId) then focusThisFrame request refreshCompletionKeys: refreshCompletionKeys getScrollPosition: -> scrollX: window.scrollX, scrollY: window.scrollY setScrollPosition: (request) -> setScrollPosition request.scrollX, request.scrollY @@ -293,7 +293,7 @@ setScrollPosition = (scrollX, scrollY) -> # # Called from the backend in order to change frame focus. # -window.focusThisFrame = (shouldHighlight) -> +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. @@ -301,7 +301,9 @@ window.focusThisFrame = (shouldHighlight) -> chrome.runtime.sendMessage({ handler: "nextFrame", frameId: frameId }) return window.focus() - if (document.body && shouldHighlight) + 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) diff --git a/pages/vomnibar.coffee b/pages/vomnibar.coffee index 06ec9ee9..dbb37ebf 100644 --- a/pages/vomnibar.coffee +++ b/pages/vomnibar.coffee @@ -67,10 +67,9 @@ class VomnibarUI # 3. Only once "hidden" message is received here is any required action (callback) invoked (in onHidden). # This ensures that the vomnibar is actually hidden, and avoids flicker after opening a link in a new tab # (see #1485). - hide: (callback = null) -> + hide: (@postHideCallback = null) -> UIComponentServer.postMessage "hide" @reset() - @postHideCallback = callback onHidden: -> @postHideCallback?() -- cgit v1.2.3 From b65f285aa65b2cfbdaefb4d7c445dbd5e684fbbf Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 14:29:12 +0000 Subject: Activate vomnibar in window.top; more clean up. Clean up, and fixes following code review from @mrmr1993. --- background_scripts/main.coffee | 1 - content_scripts/ui_component.coffee | 2 +- content_scripts/vimium_frontend.coffee | 7 ++++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index 05fad941..a64c7e37 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -657,7 +657,6 @@ handleFrameFocused = (request, sender) -> if frameIdsForTab[tabId]? frameIdsForTab[tabId] = [request.frameId, (frameIdsForTab[tabId].filter (id) -> id != request.frameId)...] - console.log frameIdsForTab[tabId] # Inform all frames that a frame has received the focus. chrome.tabs.sendMessage sender.tab.id, name: "frameFocused" diff --git a/content_scripts/ui_component.coffee b/content_scripts/ui_component.coffee index 727f6c27..ee74c3a8 100644 --- a/content_scripts/ui_component.coffee +++ b/content_scripts/ui_component.coffee @@ -20,7 +20,7 @@ class UIComponent # other UI components. chrome.runtime.onMessage.addListener (request) => @postMessage "hide" if @showing and request.name == "frameFocused" and request.focusFrameId != frameId - false # Free up response handler. + false # Free up the sendResponse handler. # Open a port and pass it to the iframe via window.postMessage. openPort: -> diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index dccdfe76..11ef30cc 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -94,10 +94,10 @@ settings = # # Give this frame a unique id. # -window.frameId = Math.floor(Math.random()*999999999) +frameId = Math.floor(Math.random()*999999999) # For debugging only. This logs to the console on the background page. -window.bgLog = (args...) -> +bgLog = (args...) -> args = (arg.toString() for arg in args) chrome.runtime.sendMessage handler: "log", frameId: frameId, message: args.join " " @@ -271,7 +271,7 @@ executePageCommand = (request) -> if request.command.split(".")[0] == "Vomnibar" if DomUtils.isTopFrame() # We pass the frameId from request. That's the frame which originated the request, so that's the frame - # that needs to receive the focus when we're done. + # which whould receive the focus when the vomnibar closes. Utils.invokeCommandString request.command, [ request.frameId ] refreshCompletionKeys request return @@ -1238,3 +1238,4 @@ root.settings = settings root.HUD = HUD root.handlerStack = handlerStack root.frameId = frameId +root.bgLog = bgLog -- cgit v1.2.3 From 8d09dc19c3e1afb629becef02d7d6cc58d13eaad Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 15:08:21 +0000 Subject: Activate vomnibar in window.top; yet more clean up. --- content_scripts/vimium_frontend.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 11ef30cc..a057d654 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -192,6 +192,9 @@ initializePreDomReady = -> return unless isEnabledForUrl or request.name == 'getActiveState' or request.name == 'setState' # These requests are delivered to the options page, but there are no handlers there. return if request.handler in [ "registerFrame", "frameFocused", "unregisterFrame" ] + # We don't handle these here. They're handled elsewhere (e.g. in the vomnibar/UI component). + return if request.name in [ "frameFocused" ] + # Handle the request. sendResponse requestHandlers[request.name](request, sender) # Ensure the sendResponse callback is freed. false @@ -271,7 +274,7 @@ executePageCommand = (request) -> if request.command.split(".")[0] == "Vomnibar" if DomUtils.isTopFrame() # We pass the frameId from request. That's the frame which originated the request, so that's the frame - # which whould receive the focus when the vomnibar closes. + # which should receive the focus when the vomnibar closes. Utils.invokeCommandString request.command, [ request.frameId ] refreshCompletionKeys request return -- cgit v1.2.3 From 49c52049f8e54c7a8a4e4432bc147d781a481c1a Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Tue, 17 Mar 2015 15:43:48 +0000 Subject: Activate vomnibar in window.top; fix dropped line. --- content_scripts/vimium_frontend.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index a057d654..81e0e3b2 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -242,6 +242,7 @@ registerFocus = (event) -> if event.target == window # Settings may have changed since the frame last had focus. settings.load() + checkIfEnabledForUrl() # Don't register frameset containers; focusing them is no use. unless document.body?.tagName.toLowerCase() == "frameset" chrome.runtime.sendMessage handler: "frameFocused", frameId: frameId -- cgit v1.2.3 From a469e8265ab5121bdb83935e977c8a89777572b0 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 18 Apr 2015 15:47:38 +0100 Subject: Activate vomnibar in window.top; add "labs" option. --- background_scripts/settings.coffee | 2 ++ content_scripts/vimium_frontend.coffee | 15 ++++++++------- pages/options.coffee | 1 + pages/options.html | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/background_scripts/settings.coffee b/background_scripts/settings.coffee index 3528e8a9..50a6a9f4 100644 --- a/background_scripts/settings.coffee +++ b/background_scripts/settings.coffee @@ -114,6 +114,8 @@ root.Settings = Settings = searchEngines: "w: http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s wikipedia" newTabUrl: "chrome://newtab" grabBackFocus: false + # Vimium Labs settings + vomnibarInTopFrame: false settingsVersion: Utils.getCurrentVersion() diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 81e0e3b2..95c508ef 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -44,7 +44,7 @@ settings = loadedValues: 0 valuesToLoad: [ "scrollStepSize", "linkHintCharacters", "linkHintNumbers", "filterLinkHints", "hideHud", "previousPatterns", "nextPatterns", "regexFindMode", "userDefinedLinkHintCss", - "helpDialog_showAdvancedCommands", "smoothScroll", "grabBackFocus" ] + "helpDialog_showAdvancedCommands", "smoothScroll", "grabBackFocus", "vomnibarInTopFrame" ] isLoaded: false eventListeners: {} @@ -254,7 +254,7 @@ initializeOnDomReady = -> # Tell the background page we're in the dom ready state. chrome.runtime.connect({ name: "domReady" }) CursorHider.init() - Vomnibar.init() if DomUtils.isTopFrame() + Vomnibar.init() # if DomUtils.isTopFrame() registerFrame = -> # Don't register frameset containers; focusing them is no use. @@ -273,11 +273,12 @@ unregisterFrame = -> executePageCommand = (request) -> # Vomnibar commands are handled in the tab's main/top frame. if request.command.split(".")[0] == "Vomnibar" - if DomUtils.isTopFrame() - # We pass the frameId from request. That's the frame which originated the request, so that's the frame - # which should receive the focus when the vomnibar closes. - Utils.invokeCommandString request.command, [ request.frameId ] - refreshCompletionKeys request + if (DomUtils.isTopFrame() and settings.get "vomnibarInTopFrame") or + (frameId == request.frameId and not settings.get "vomnibarInTopFrame") + # We pass the frameId from request. That's the frame which originated the request, so that's the frame + # which should receive the focus when the vomnibar closes. + Utils.invokeCommandString request.command, [ request.frameId ] + refreshCompletionKeys request return # All other commands are handled in their frame. diff --git a/pages/options.coffee b/pages/options.coffee index d2950348..55b5b63a 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -261,6 +261,7 @@ initOptionsPage = -> searchEngines: TextOption searchUrl: NonEmptyTextOption userDefinedLinkHintCss: TextOption + vomnibarInTopFrame: CheckBoxOption # Populate options. The constructor adds each new object to "Option.all". for name, type of options diff --git a/pages/options.html b/pages/options.html index f89ddcbb..19298189 100644 --- a/pages/options.html +++ b/pages/options.html @@ -70,6 +70,11 @@ b: http://b.com/?q=%s description
+ These features are awesome! However, they're also not yet fully baked. They might + not always work, may be subject to substantial change in a future release, and could even be + withdrawn entirely. +
+ ++ Please provide feedback or suggestions on + github. +
+- These features are awesome! However, they're also not yet fully baked. They might - not always work, may be subject to substantial change in a future release, and could even be - withdrawn entirely. -
- -- Please provide feedback or suggestions on - github. -
-