diff options
Diffstat (limited to 'content_scripts/vimium_frontend.coffee')
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 66 |
1 files changed, 52 insertions, 14 deletions
diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index cc97b515..c41ca62f 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -19,6 +19,13 @@ keyQueue = null currentCompletionKeys = "" validFirstKeys = "" +# We track whther the current window has the focus or not. +windowIsFocused = do -> + windowHasFocus = document.hasFocus() + window.addEventListener "focus", (event) -> windowHasFocus = true if event.target == window; true + window.addEventListener "blur", (event) -> windowHasFocus = false if event.target == window; true + -> windowHasFocus + # The types in <input type="..."> that we consider for focusInput command. Right now this is recalculated in # each content script. Alternatively we could calculate it once in the background page and use a request to # fetch it each time. @@ -92,9 +99,14 @@ settings = @eventListeners[eventName].push(callback) # -# Give this frame a unique id. +# Give this frame a unique (non-zero) id. # -frameId = Math.floor(Math.random()*999999999) +frameId = 1 + Math.floor(Math.random()*999999999) + +# For debugging only. This logs to the console on the background page. +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). @@ -167,7 +179,7 @@ initializePreDomReady = -> requestHandlers = 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 @@ -175,16 +187,25 @@ initializePreDomReady = -> currentKeyQueue: (request) -> keyQueue = request.keyQueue handlerStack.bubbleEvent "registerKeyQueue", { keyQueue: keyQueue } + # A frame has received the focus. We don't care here (the Vomnibar/UI-component handles this). + frameFocused: -> updateEnabledForUrlState: updateEnabledForUrlState chrome.runtime.onMessage.addListener (request, sender, sendResponse) -> # In the options page, we will receive requests from both content and background scripts. ignore those # from the former. return if sender.tab and not sender.tab.url.startsWith 'chrome-extension://' - return unless isEnabledForUrl or request.name in ["updateEnabledForUrlState"] # These requests are delivered to the options page, but there are no handlers there. return if request.handler in [ "registerFrame", "frameFocused", "unregisterFrame" ] - sendResponse requestHandlers[request.name](request, sender) + shouldHandleRequest = isEnabledForUrl + # We always handle the message if it's one of these listed message types. + shouldHandleRequest ||= request.name in [ "executePageCommand", "updateEnabledForUrlState" ] + # Requests with a frameId of zero should always and only be handled in the main/top frame (regardless of + # whether Vimium is enabled there). + if request.frameId == 0 and DomUtils.isTopFrame() + request.frameId = frameId + shouldHandleRequest = true + sendResponse requestHandlers[request.name](request, sender) if shouldHandleRequest # Ensure the sendResponse callback is freed. false @@ -198,9 +219,11 @@ installListener = (element, event, callback) -> # Installing or uninstalling listeners is error prone. Instead we elect to check isEnabledForUrl each time so # we know whether the listener should run or not. # Run this as early as possible, so the page can't register any event handlers before us. +# Note: We install the listeners even if Vimium is disabled. See comment in commit +# 6446cf04c7b44c3d419dc450a73b60bcaf5cdf02. # installedListeners = false -window.initializeWithState = -> +window.installListeners = -> unless installedListeners # Key event handlers fire on window before they do on document. Prefer window for key events so the page # can't set handlers to grab the keys before us. @@ -234,7 +257,8 @@ initializeOnDomReady = -> # Tell the background page we're in the dom ready state. chrome.runtime.connect({ name: "domReady" }) CursorHider.init() - Vomnibar.init() + # We only initialize the vomnibar in the tab's main frame, because it's only ever opened there. + Vomnibar.init() if DomUtils.isTopFrame() registerFrame = -> # Don't register frameset containers; focusing them is no use. @@ -248,10 +272,21 @@ unregisterFrame = -> chrome.runtime.sendMessage handler: "unregisterFrame" frameId: frameId - tab_is_closing: window.top == window.self + tab_is_closing: DomUtils.isTopFrame() executePageCommand = (request) -> - return unless frameId == request.frameId + # Vomnibar commands are handled in the tab's main/top frame. They are handled even if Vimium is otherwise + # disabled in the 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 + return + + # All other commands are handled in their frame (but only if Vimium is enabled). + return unless frameId == request.frameId and isEnabledForUrl if (request.passCountToFunction) Utils.invokeCommandString(request.command, [request.count]) @@ -267,7 +302,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. @@ -275,7 +310,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) @@ -550,12 +587,11 @@ onKeyup = (event) -> checkIfEnabledForUrl = -> url = window.location.toString() - chrome.runtime.sendMessage { handler: "isEnabledForUrl", url: url }, updateEnabledForUrlState updateEnabledForUrlState = (response) -> - {isEnabledForUrl, passKeys} = response - initializeWithState() + { isEnabledForUrl, passKeys } = response + installListeners() if HUD.isReady() and not isEnabledForUrl # Quickly hide any HUD we might already be showing, e.g. if we entered insert mode on page load. HUD.hide() @@ -1193,3 +1229,5 @@ root.settings = settings root.HUD = HUD root.handlerStack = handlerStack root.frameId = frameId +root.windowIsFocused = windowIsFocused +root.bgLog = bgLog |
