diff options
| -rw-r--r-- | CREDITS | 1 | ||||
| -rw-r--r-- | README.markdown | 4 | ||||
| -rw-r--r-- | background_page.html | 85 | ||||
| -rw-r--r-- | commands.js | 8 | ||||
| -rw-r--r-- | manifest.json | 7 | ||||
| -rw-r--r-- | vimiumFrontend.js | 68 | 
6 files changed, 144 insertions, 29 deletions
| @@ -14,6 +14,7 @@ Contributors:    lack    markstos    rodimius +  Tim Morgan <tim@timmorgan.org> (github: seven1m)    tsigo  Feel free to add real names in addition to GitHub usernames. diff --git a/README.markdown b/README.markdown index 2e144110..b470da2a 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 @@ -84,6 +85,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 (`zH` and `zL`) to scroll to the left and right edges of the page.  -  A new command (`gi`) to focus the first (or n-th) text input box on the page. +-  Frame support.  -  Bug fixes.  1.19 (06/29/2010) diff --git a/background_page.html b/background_page.html index 94595b2b..b862a881 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 <a-b> (alt+b), <left> (the left arrow) or <f12>    // This regular expression captures two groups, the first is a named key, the second is the remainder of the string. @@ -66,7 +68,10 @@    var sendRequestHandlers = {      getCompletionKeys: getCompletionKeys,      getLinkHintCss: getLinkHintCss, +    openUrlInCurrentTab: openUrlInCurrentTab,      openOptionsPageInNewTab: openOptionsPageInNewTab, +    registerFrame: registerFrame, +    frameFocused: handleFrameFocused,      upgradeNotificationClosed: upgradeNotificationClosed,      updateScrollPosition: handleUpdateScrollPosition    }; @@ -90,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 });      } @@ -152,9 +158,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 });      });    } @@ -219,6 +225,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.     */ @@ -261,9 +276,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: @@ -362,6 +377,7 @@        tabQueue[openTabInfo.windowId] = [openTabInfo];      delete openTabs[tabId]; +    delete framesForTab[tabId];    });    chrome.windows.onRemoved.addListener(function(windowId) { @@ -480,19 +496,20 @@      return {count: count, command: command};    } -  function handleKeyDown(key, port) { +  function handleKeyDown(request, port) { +    var key = request.keyChar;      if (key == "<ESC>") {        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; @@ -508,6 +525,7 @@        if (!registryEntry.isBackgroundCommand) {          var port = chrome.tabs.connect(tabId, { name: "executePageCommand" });          port.postMessage({ command: registryEntry.command, +                           frameId: frameId,                             count: count,                             passCountToFunction: registryEntry.passCountToFunction,                             completionKeys: generateCompletionKeys("") @@ -515,7 +533,7 @@          refreshedCompletionKeys = true;        } else { -        repeatFunction(this[registryEntry.command], count, 0); +        repeatFunction(this[registryEntry.command], count, 0, frameId);        }        newKeyQueue = ""; @@ -585,6 +603,57 @@      });    } +  function registerFrame(request, sender) { +    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); +    } +  } + +  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) { +      var index; +      var frames = framesForTab[tab.id].frames; + +      for (index=0; index < frames.length; index++) { +        if (frames[index].id == focusedFrame) +            break; +      } + +      if (index >= frames.length-1) +        index = 0; +      else +        index++; + +      chrome.tabs.sendRequest(tab.id, { name: "focusFrame", frameId: frames[index].id, highlight: true }); +    }); +  } +    function init() {      clearKeyMappingsAndSetDefaults(); diff --git a/commands.js b/commands.js index a9fa0aba..635c5350 100644 --- a/commands.js +++ b/commands.js @@ -97,7 +97,7 @@ function clearKeyMappingsAndSetDefaults() {    mapKeyToCommand('<c-f>', 'scrollFullPageDown');    mapKeyToCommand('<c-b>', 'scrollFullPageUp');    mapKeyToCommand('r', 'reload'); -  mapKeyToCommand('gf', 'toggleViewSource'); +  mapKeyToCommand('gs', 'toggleViewSource');    mapKeyToCommand('i', 'enterInsertMode'); @@ -127,6 +127,8 @@ function clearKeyMappingsAndSetDefaults() {    mapKeyToCommand('t', 'createTab');    mapKeyToCommand('d', 'removeTab');    mapKeyToCommand('u', 'restoreTab'); + +  mapKeyToCommand('gf', 'nextFrame');  }  // Navigating the current page: @@ -175,6 +177,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. @@ -186,7 +190,7 @@ var commandGroups = {       "reload", "toggleViewSource", "zoomIn", "zoomOut", "copyCurrentUrl", "goUp",       "enterInsertMode", "focusInput",       "activateLinkHintsMode", "activateLinkHintsModeToOpenInNewTab", -     "enterFindMode", "performFind", "performBackwardsFind"], +     "enterFindMode", "performFind", "performBackwardsFind", "nextFrame"],    historyNavigation:      ["goBack", "goForward"],    tabManipulation: diff --git a/manifest.json b/manifest.json index 97966dd5..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": [      { @@ -18,7 +20,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 334e3a77..3b73144a 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) { @@ -66,7 +71,10 @@ function initializePreDomReady() {        if (isShowingHelpDialog)          hideHelpDialog();        else -        showHelpDialog(request.dialogHtml); +        showHelpDialog(request.dialogHtml, request.frameId); +    else if (request.name == "focusFrame") +      if(frameId == request.frameId) +        focusThisFrame(request.highlight);      else if (request.name == "refreshCompletionKeys")        refreshCompletionKeys(request.completionKeys);      sendResponse({}); // Free up the resources used by this open connection. @@ -75,7 +83,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) {            if (args.passCountToFunction) {              this[args.command].call(null, args.count);            } else { @@ -138,10 +146,35 @@ function initializeWhenEnabled() {    enterInsertModeIfElementIsFocused();  } + +/* + * The backend needs to know which frame has focus. + */ +window.addEventListener("focus", function(e){ +  chrome.extension.sendRequest({handler: "frameFocused", frameId: frameId}); +}); + +/* + * Called from the backend in order to change frame focus. + */ +function focusThisFrame(shouldHighlight) { +  window.focus(); +  if (document.body && shouldHighlight) { +    var borderWas = document.body.style.border; +    document.body.style.border = '5px solid yellow'; +    setTimeout(function(){document.body.style.border = borderWas}, 200); +  } +} +  /*   * 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(); @@ -149,6 +182,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.   */ @@ -257,9 +298,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});  }  /** @@ -348,10 +390,10 @@ function onKeydown(event) {          event.stopPropagation();        } -      keyPort.postMessage(keyChar); +      keyPort.postMessage({keyChar:keyChar, frameId:frameId});      }      else if (isEscape(event)) { -      keyPort.postMessage("<ESC>"); +      keyPort.postMessage({keyChar:"<ESC>", frameId:frameId});      }    }  } @@ -493,8 +535,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"); @@ -715,14 +757,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", | 
