From b489a9e4034e35c897083f9ce97866fd837680d8 Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Fri, 3 Sep 2010 06:31:09 -0500 Subject: Basic Support for Frames Only the focused frame will act on key commands. On some sites (such as Gmail), the main frame is already focused, so commands just work. For other sites, focusing the desired frame may be necessary, which can be done with the Tab key (not optimal) or by clicking with the mouse (even less optimal). An additional command to cycle through frames is desirable, which will likely come in a future commit. --- background_page.html | 9 +++++---- manifest.json | 3 ++- vimiumFrontend.js | 21 ++++++++++----------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/background_page.html b/background_page.html index 349212ef..4133a023 100644 --- a/background_page.html +++ b/background_page.html @@ -480,19 +480,20 @@ return {count: count, command: command}; } - function handleKeyDown(key, port) { + function handleKeyDown(request, port) { + var key = request.keyChar; if (key == "") { console.log("clearing keyQueue"); keyQueue = "" } else { console.log("checking keyQueue: [", keyQueue + key, "]"); - keyQueue = checkKeyQueue(keyQueue + key, port.tab.id); + keyQueue = checkKeyQueue(keyQueue + key, port.tab.id, request.frameId); console.log("new KeyQueue: " + keyQueue); } } - function checkKeyQueue(keysToCheck, tabId) { + function checkKeyQueue(keysToCheck, tabId, frameId) { var refreshedCompletionKeys = false; var splitHash = splitKeyQueue(keysToCheck); command = splitHash.command; @@ -507,7 +508,7 @@ if (!registryEntry.isBackgroundCommand) { var port = chrome.tabs.connect(tabId, { name: "executePageCommand" }); - port.postMessage({ command: registryEntry.command, count: count, + port.postMessage({ command: registryEntry.command, count: count, frameId: frameId, completionKeys: generateCompletionKeys("") }); refreshedCompletionKeys = true; diff --git a/manifest.json b/manifest.json index 97966dd5..703fd069 100644 --- a/manifest.json +++ b/manifest.json @@ -18,7 +18,8 @@ "linkHints.js", "vimiumFrontend.js" ], - "run_at": "document_start" + "run_at": "document_start", + "all_frames": true } ] } diff --git a/vimiumFrontend.js b/vimiumFrontend.js index e288714a..226c7953 100644 --- a/vimiumFrontend.js +++ b/vimiumFrontend.js @@ -75,7 +75,7 @@ function initializePreDomReady() { chrome.extension.onConnect.addListener(function(port, name) { if (port.name == "executePageCommand") { port.onMessage.addListener(function(args) { - if (this[args.command]) { + if (this[args.command] && frameId == args.frameId) { for (var i = 0; i < args.count; i++) { this[args.command].call(); } } @@ -134,6 +134,11 @@ function initializeWhenEnabled() { enterInsertModeIfElementIsFocused(); } +/* + * Give this frame a unique id. + */ +frameId = Math.floor(Math.random()*999999999) + /* * Initialization tasks that must wait for the document to be ready. */ @@ -324,10 +329,10 @@ function onKeydown(event) { event.stopPropagation(); } - keyPort.postMessage(keyChar); + keyPort.postMessage({keyChar:keyChar, frameId:frameId}); } else if (isEscape(event)) { - keyPort.postMessage(""); + keyPort.postMessage({keyChar:"", frameId:frameId}); } } } @@ -690,14 +695,8 @@ function addCssToPage(css) { head.appendChild(style); } -// Prevent our content script from being run on iframes -- only allow it to run on the top level DOM "window". -// TODO(philc): We don't want to process multiple keyhandlers etc. when embedded on a page containing IFrames. -// This should be revisited, because sometimes we *do* want to listen inside of the currently focused iframe. -var isIframe = (window.self != window.parent); -if (!isIframe) { - initializePreDomReady(); - window.addEventListener("DOMContentLoaded", initializeOnDomReady); -} +initializePreDomReady(); +window.addEventListener("DOMContentLoaded", initializeOnDomReady); window.onbeforeunload = function() { chrome.extension.sendRequest({ handler: "updateScrollPosition", -- cgit v1.2.3 From c1d255a662390027a5a61360d173f46b136478b8 Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Fri, 3 Sep 2010 19:59:36 -0500 Subject: Fix view source toggle when inside a frame. --- background_page.html | 10 ++++++++++ vimiumFrontend.js | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/background_page.html b/background_page.html index 4133a023..1efd6ae0 100644 --- a/background_page.html +++ b/background_page.html @@ -66,6 +66,7 @@ var sendRequestHandlers = { getCompletionKeys: getCompletionKeys, getLinkHintCss: getLinkHintCss, + openUrlInCurrentTab: openUrlInCurrentTab, openOptionsPageInNewTab: openOptionsPageInNewTab, upgradeNotificationClosed: upgradeNotificationClosed, updateScrollPosition: handleUpdateScrollPosition @@ -219,6 +220,15 @@ return {completionKeys: generateCompletionKeys()}; } + /** + * Opens the url in the current tab. + */ + function openUrlInCurrentTab(request) { + chrome.tabs.getSelected(null, function(tab) { + chrome.tabs.update(tab.id, {url: request.url}); + }); + } + /* * Returns the core CSS used for link hints, along with any user-provided overrides. */ diff --git a/vimiumFrontend.js b/vimiumFrontend.js index 226c7953..331e9afe 100644 --- a/vimiumFrontend.js +++ b/vimiumFrontend.js @@ -238,9 +238,10 @@ function copyCurrentUrl() { function toggleViewSourceCallback(url) { if (url.substr(0, 12) == "view-source:") { - window.location.href = url.substr(12, url.length - 12); + url = url.substr(12, url.length - 12); } - else { window.location.href = "view-source:" + url; } + else { url = "view-source:" + url; } + chrome.extension.sendRequest({handler: "openUrlInCurrentTab", url:url}); } /** -- cgit v1.2.3 From f9f5208f4d88873ee55a4c54062920981ad22eb8 Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Fri, 3 Sep 2010 20:15:59 -0500 Subject: Only show the Help Dialog on the focused frame. --- background_page.html | 10 +++++----- vimiumFrontend.js | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/background_page.html b/background_page.html index 1efd6ae0..8abe2f9e 100644 --- a/background_page.html +++ b/background_page.html @@ -153,9 +153,9 @@ returnPort.postMessage({ zoomLevel: zoomLevel }); } - function showHelp() { + function showHelp(callback, frameId) { chrome.tabs.getSelected(null, function(tab) { - chrome.tabs.sendRequest(tab.id, { name: "showHelpDialog", dialogHtml: helpDialogHtml() }); + chrome.tabs.sendRequest(tab.id, { name: "showHelpDialog", dialogHtml: helpDialogHtml(), frameId:frameId }); }); } @@ -271,9 +271,9 @@ if (selectionChangedHandlers.length > 0) { selectionChangedHandlers.pop().call(); } }); - function repeatFunction(func, totalCount, currentCount) { + function repeatFunction(func, totalCount, currentCount, frameId) { if (currentCount < totalCount) - func(function() { repeatFunction(func, totalCount, currentCount + 1); }); + func(function() { repeatFunction(func, totalCount, currentCount + 1, frameId); }, frameId); } // Returns the scroll coordinates of the given tab. Pass in a callback of the form: @@ -523,7 +523,7 @@ refreshedCompletionKeys = true; } else { - repeatFunction(this[registryEntry.command], count, 0); + repeatFunction(this[registryEntry.command], count, 0, frameId); } newKeyQueue = ""; diff --git a/vimiumFrontend.js b/vimiumFrontend.js index 331e9afe..829ded5e 100644 --- a/vimiumFrontend.js +++ b/vimiumFrontend.js @@ -66,7 +66,7 @@ function initializePreDomReady() { if (isShowingHelpDialog) hideHelpDialog(); else - showHelpDialog(request.dialogHtml); + showHelpDialog(request.dialogHtml, request.frameId); else if (request.name == "refreshCompletionKeys") refreshCompletionKeys(request.completionKeys); sendResponse({}); // Free up the resources used by this open connection. @@ -475,8 +475,8 @@ function exitFindMode() { HUD.hide(); } -function showHelpDialog(html) { - if (isShowingHelpDialog || !document.body) +function showHelpDialog(html, fid) { + if (isShowingHelpDialog || !document.body || fid != frameId) return; isShowingHelpDialog = true; var container = document.createElement("div"); -- cgit v1.2.3 From f146e5908bd79dcab4d4afa219af79d4a892b099 Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Fri, 3 Sep 2010 22:38:45 -0500 Subject: Command to go to next frame. I remapped toggleViewSource to 'gs' and set 'gf' to nextFrame. Sorry this is such a huge commit. This is really the simplest way I can find to allow the extension to track all available frames as well as the currently-focused frame. If Chrome would allow access to window.frames[i], then this could probably be simpler. --- background_page.html | 31 +++++++++++++++++++++++++++++++ commands.js | 8 ++++++-- vimiumFrontend.js | 28 +++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/background_page.html b/background_page.html index 8abe2f9e..6014d4c8 100644 --- a/background_page.html +++ b/background_page.html @@ -68,6 +68,8 @@ getLinkHintCss: getLinkHintCss, openUrlInCurrentTab: openUrlInCurrentTab, openOptionsPageInNewTab: openOptionsPageInNewTab, + registerFrame: registerFrame, + focusFrame: focusFrame, upgradeNotificationClosed: upgradeNotificationClosed, updateScrollPosition: handleUpdateScrollPosition }; @@ -593,6 +595,35 @@ }); } + var framesForTab = {}; + + function registerFrame(request, sender) { + if(request.top) + framesForTab[sender.tab.id] = []; + framesForTab[sender.tab.id].push(request.frameId); + } + + var focusedFrame = null; + function focusFrame(request, sender) { + focusedFrame = request.frameId; + } + + function nextFrame(callback, frameId) { + chrome.tabs.getSelected(null, function(tab) { + //chrome.tabs.sendRequest(tab.id, { name: "showHelpDialog", dialogHtml: helpDialogHtml(), frameId:frameId }); + var index; + var frames = framesForTab[tab.id]; + for(index=0; index= frames.length-1) + index = 0; + else + index++; + chrome.tabs.sendRequest(tab.id, { name: "focusFrame", frameId: frames[index] }); + }); + } + function init() { clearKeyMappingsAndSetDefaults(); diff --git a/commands.js b/commands.js index 477e5cd3..e49574e3 100644 --- a/commands.js +++ b/commands.js @@ -92,7 +92,7 @@ function clearKeyMappingsAndSetDefaults() { mapKeyToCommand('', 'scrollFullPageDown'); mapKeyToCommand('', 'scrollFullPageUp'); mapKeyToCommand('r', 'reload'); - mapKeyToCommand('gf', 'toggleViewSource'); + mapKeyToCommand('gs', 'toggleViewSource'); mapKeyToCommand('i', 'enterInsertMode'); @@ -120,6 +120,8 @@ function clearKeyMappingsAndSetDefaults() { mapKeyToCommand('t', 'createTab'); mapKeyToCommand('d', 'removeTab'); mapKeyToCommand('u', 'restoreTab'); + + mapKeyToCommand('gf', 'nextFrame'); } // Navigating the current page: @@ -166,6 +168,8 @@ addCommand('createTab', 'Create new tab', true); addCommand('removeTab', 'Close current tab', true); addCommand('restoreTab', "Restore closed tab", true); +addCommand('nextFrame', "Cycle forward to the next frame on the page", true); + // An ordered listing of all available commands, grouped by type. This is the order they will // be shown in the help page. @@ -175,7 +179,7 @@ var commandGroups = { "scrollToTop", "scrollToBottom", "scrollToLeft", "scrollToRight", "scrollPageDown", "scrollPageUp", "scrollFullPageDown", "reload", "toggleViewSource", "zoomIn", "zoomOut", "copyCurrentUrl", "goUp", "enterInsertMode", "activateLinkHintsMode", "activateLinkHintsModeToOpenInNewTab", - "enterFindMode", "performFind", "performBackwardsFind"], + "enterFindMode", "performFind", "performBackwardsFind", "nextFrame"], historyNavigation: ["goBack", "goForward"], tabManipulation: diff --git a/vimiumFrontend.js b/vimiumFrontend.js index 829ded5e..685e121f 100644 --- a/vimiumFrontend.js +++ b/vimiumFrontend.js @@ -67,6 +67,9 @@ function initializePreDomReady() { hideHelpDialog(); else showHelpDialog(request.dialogHtml, request.frameId); + else if (request.name == "focusFrame") + if(frameId == request.frameId) + focusThisFrame(); else if (request.name == "refreshCompletionKeys") refreshCompletionKeys(request.completionKeys); sendResponse({}); // Free up the resources used by this open connection. @@ -135,9 +138,32 @@ function initializeWhenEnabled() { } /* - * Give this frame a unique id. + * Give this frame a unique id and register with the backend. */ frameId = Math.floor(Math.random()*999999999) +if(window.top == window.self) + chrome.extension.sendRequest({handler: "registerFrame", frameId: frameId, top: true}); +else + chrome.extension.sendRequest({handler: "registerFrame", frameId: frameId}); + +/* + * The backend needs to know which frame has focus. + */ +window.addEventListener("focus", function(e){ + chrome.extension.sendRequest({handler: "focusFrame", frameId: frameId}); +}); + +/* + * Called from the backend in order to change frame focus. + */ +function focusThisFrame() { + window.focus(); + if(document.body) { + var borderWas = document.body.style.border; + document.body.style.border = '1px solid red'; + setTimeout(function(){document.body.style.border = borderWas}, 200); + } +} /* * Initialization tasks that must wait for the document to be ready. -- cgit v1.2.3 From f377565c3b5c3731623c24da96c8623860cec3f2 Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Fri, 3 Sep 2010 23:25:40 -0500 Subject: Update README with new/changed key mappings. --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 7e525141..f05aa3a8 100644 --- a/README.markdown +++ b/README.markdown @@ -37,7 +37,7 @@ Navigating the current page: f activate link hints mode to open in current tab F activate link hints mode to open in new tab r reload - gf view source + gs view source zi zoom in zo zoom out / enter find mode -- type your search query and hit enter to search or esc to cancel @@ -46,6 +46,7 @@ Navigating the current page: i enter insert mode -- all commands will be ignored until you hit esc to exit yy copy the current url to the clipboard gu go up one level in the URL hierarchy + gf cycle forward to the next frame Navigating your history: H go back in history -- cgit v1.2.3 From 64b550a4a7fa4af1d9c0996d6f66715dc479ccb5 Mon Sep 17 00:00:00 2001 From: Ilya Sukhar Date: Thu, 23 Sep 2010 23:27:56 -0700 Subject: A bunch of changes having to do with the frame support patch: - Focus the largest frame by default - Change the border styling to match link hints - Clean up framesForTab when tab is closed - Don't cycle through parent. This may break some sites. Needs more testing. - Fixed some naming and style - Added seven1m to CREDITS - Updated README--- CREDITS | 1 + README.markdown | 1 + background_page.html | 54 ++++++++++++++++++++++++++++++++++++++-------------- manifest.json | 4 +++- vimiumFrontend.js | 36 ++++++++++++++++++++++------------- 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/CREDITS b/CREDITS index 8c986adc..0f1c5797 100644 --- a/CREDITS +++ b/CREDITS @@ -14,6 +14,7 @@ Contributors: lack markstos rodimius + Tim Morgan (github: seven1m) tsigo Feel free to add real names in addition to GitHub usernames. diff --git a/README.markdown b/README.markdown index f05aa3a8..dff5f8da 100644 --- a/README.markdown +++ b/README.markdown @@ -84,6 +84,7 @@ Release Notes - In link hints mode, holding down the shift key will now toggle between opening in the current tab and opening in a new tab. - Two new commands to scroll to the left and right edges of the page bound to zH and zL respectively. +- Frame support. - Bug fixes. 1.19 (06/29/2010) diff --git a/background_page.html b/background_page.html index 6014d4c8..fb1eb48b 100644 --- a/background_page.html +++ b/background_page.html @@ -14,6 +14,8 @@ var keyQueue = ""; // Queue of keys typed var validFirstKeys = {}; var singleKeyCommands = []; + var focusedFrame = null; + var framesForTab = {}; // Keys are either literal characters, or "named" - for example (alt+b), (the left arrow) or // This regular expression captures two groups, the first is a named key, the second is the remainder of the string. @@ -69,7 +71,7 @@ openUrlInCurrentTab: openUrlInCurrentTab, openOptionsPageInNewTab: openOptionsPageInNewTab, registerFrame: registerFrame, - focusFrame: focusFrame, + frameFocused: handleFrameFocused, upgradeNotificationClosed: upgradeNotificationClosed, updateScrollPosition: handleUpdateScrollPosition }; @@ -93,6 +95,7 @@ } // domReady is the appropriate time to show the "vimium has been upgraded" message. + // TODO: This might be broken on pages with frames. if (shouldShowUpgradeMessage()) chrome.tabs.sendRequest(senderTabId, { name: "showUpgradeNotification", version: currentVersion }); } @@ -374,6 +377,7 @@ tabQueue[openTabInfo.windowId] = [openTabInfo]; delete openTabs[tabId]; + delete framesForTab[tabId]; }); chrome.windows.onRemoved.addListener(function(windowId) { @@ -595,32 +599,54 @@ }); } - var framesForTab = {}; - function registerFrame(request, sender) { - if(request.top) - framesForTab[sender.tab.id] = []; - framesForTab[sender.tab.id].push(request.frameId); + if (request.top) + framesForTab[sender.tab.id] = { total: request.total, frames: [] }; + else { + framesForTab[sender.tab.id].frames.push({ id: request.frameId, area: request.area }); + + // We've seen all the frames. Time to focus the largest one. + if (framesForTab[sender.tab.id].frames.length == framesForTab[sender.tab.id].total) + focusLargestFrame(sender.tab.id); + } } - var focusedFrame = null; - function focusFrame(request, sender) { + function focusLargestFrame(tabId) { + var mainFrameId = null; + var mainFrameArea = 0; + + for (var i = 0; i < framesForTab[tabId].frames.length; i++) { + var currentFrame = framesForTab[tabId].frames[i]; + + if (currentFrame.area > mainFrameArea) { + mainFrameId = currentFrame.id; + mainFrameArea = currentFrame.area; + } + } + + chrome.tabs.sendRequest(tabId, { name: "focusFrame", frameId: mainFrameId, highlight: false }); + } + + function handleFrameFocused(request, sender) { focusedFrame = request.frameId; } function nextFrame(callback, frameId) { chrome.tabs.getSelected(null, function(tab) { - //chrome.tabs.sendRequest(tab.id, { name: "showHelpDialog", dialogHtml: helpDialogHtml(), frameId:frameId }); var index; - var frames = framesForTab[tab.id]; - for(index=0; index= frames.length-1) + + if (index >= frames.length-1) index = 0; else index++; - chrome.tabs.sendRequest(tab.id, { name: "focusFrame", frameId: frames[index] }); + + chrome.tabs.sendRequest(tab.id, { name: "focusFrame", frameId: frames[index].id, highlight: true }); }); } diff --git a/manifest.json b/manifest.json index 703fd069..d49adfa2 100644 --- a/manifest.json +++ b/manifest.json @@ -8,7 +8,9 @@ "background_page": "background_page.html", "options_page": "options.html", "permissions": [ - "tabs" + "tabs", + "http://*/*", + "https://*/*" ], "content_scripts": [ { diff --git a/vimiumFrontend.js b/vimiumFrontend.js index 685e121f..ac7d366a 100644 --- a/vimiumFrontend.js +++ b/vimiumFrontend.js @@ -26,6 +26,11 @@ var linkHintCss; // TODO(philc): This should be pulled from the extension's storage when the page loads. var currentZoomLevel = 100; +/* + * Give this frame a unique id. + */ +frameId = Math.floor(Math.random()*999999999) + var hasModifiersRegex = /^<([amc]-)+.>/; function getSetting(key) { @@ -69,7 +74,7 @@ function initializePreDomReady() { showHelpDialog(request.dialogHtml, request.frameId); else if (request.name == "focusFrame") if(frameId == request.frameId) - focusThisFrame(); + focusThisFrame(request.highlight); else if (request.name == "refreshCompletionKeys") refreshCompletionKeys(request.completionKeys); sendResponse({}); // Free up the resources used by this open connection. @@ -137,30 +142,22 @@ function initializeWhenEnabled() { enterInsertModeIfElementIsFocused(); } -/* - * Give this frame a unique id and register with the backend. - */ -frameId = Math.floor(Math.random()*999999999) -if(window.top == window.self) - chrome.extension.sendRequest({handler: "registerFrame", frameId: frameId, top: true}); -else - chrome.extension.sendRequest({handler: "registerFrame", frameId: frameId}); /* * The backend needs to know which frame has focus. */ window.addEventListener("focus", function(e){ - chrome.extension.sendRequest({handler: "focusFrame", frameId: frameId}); + chrome.extension.sendRequest({handler: "frameFocused", frameId: frameId}); }); /* * Called from the backend in order to change frame focus. */ -function focusThisFrame() { +function focusThisFrame(shouldHighlight) { window.focus(); - if(document.body) { + if (document.body && shouldHighlight) { var borderWas = document.body.style.border; - document.body.style.border = '1px solid red'; + document.body.style.border = '5px solid yellow'; setTimeout(function(){document.body.style.border = borderWas}, 200); } } @@ -169,6 +166,11 @@ function focusThisFrame() { * Initialization tasks that must wait for the document to be ready. */ function initializeOnDomReady() { + if (window.top == window.self) + chrome.extension.sendRequest({ handler: "registerFrame", frameId: frameId, top: true, total: frames.length }); + else + registerFrameIfSizeAvailable(); + if (isEnabledForUrl) enterInsertModeIfElementIsFocused(); @@ -176,6 +178,14 @@ function initializeOnDomReady() { chrome.extension.connect({ name: "domReady" }); }; +// This is a little hacky but sometimes the size wasn't available on domReady? +function registerFrameIfSizeAvailable () { + if (innerWidth != undefined && innerWidth != 0 && innerHeight != undefined && innerHeight != 0) + chrome.extension.sendRequest({ handler: "registerFrame", frameId: frameId, area: innerWidth * innerHeight }); + else + setTimeout(registerFrameIfSizeAvailable, 100); +} + /* * Checks the currently focused element of the document and will enter insert mode if that element is focusable. */ -- cgit v1.2.3