diff options
| -rw-r--r-- | README.markdown | 5 | ||||
| -rw-r--r-- | background_page.html | 10 | ||||
| -rw-r--r-- | commands.js | 2 | ||||
| -rw-r--r-- | linkHints.js | 6 | ||||
| -rw-r--r-- | test_harnesses/form.html | 2 | ||||
| -rw-r--r-- | test_harnesses/visibility_test.html | 175 | ||||
| -rw-r--r-- | vimiumFrontend.js | 24 |
7 files changed, 173 insertions, 51 deletions
diff --git a/README.markdown b/README.markdown index ba16e643..04e7375d 100644 --- a/README.markdown +++ b/README.markdown @@ -84,6 +84,11 @@ When you're done, send us a pull request on Github. Feel free to include a chang Release Notes ------------- +1.22 + +- Some sites are now excluded by default. +- Bugfixes. + 1.21 (10/24/2010) - Critical bugfix for an excluded URLs regression due to frame support. diff --git a/background_page.html b/background_page.html index 8f0cad23..10de4aca 100644 --- a/background_page.html +++ b/background_page.html @@ -20,7 +20,7 @@ // 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. - var namedKeyRegex = /^(<[amc-].|(?:[amc]-)?[a-z0-9]{2,5}>)(.*)$/; + var namedKeyRegex = /^(<(?:[amc]-.|(?:[amc]-)?[a-z0-9]{2,5})>)(.*)$/; var defaultSettings = { scrollStepSize: 60, @@ -29,7 +29,9 @@ narrowLinkHints: false, userDefinedLinkHintCss: ".vimiumHintMarker {\n\n}\n" + - ".vimiumHintMarker > .matchingCharacter {\n\n}" + ".vimiumHintMarker > .matchingCharacter {\n\n}", + excludedUrls: "http*://mail.google.com/*\n" + + "http*://www.google.com/reader/*\n" }; // This is the base internal link hints CSS. It's combined with the userDefinedLinkHintCss before @@ -137,7 +139,7 @@ */ function isEnabledForUrl(request) { // excludedUrls are stored as a series of URL expressions separated by newlines. - var excludedUrls = (localStorage["excludedUrls"] || "").split("\n"); + var excludedUrls = (localStorage["excludedUrls"] || defaultSettings.excludedUrls).split("\n"); var isEnabled = true; for (var i = 0; i < excludedUrls.length; i++) { // The user can add "*" to the URL which means ".*" @@ -551,7 +553,7 @@ // The second key might be a valid command by its self. if (keyToCommandRegistry[splitKey.second]) - newKeyQueue = checkKeyQueue(splitKey.second); + newKeyQueue = checkKeyQueue(splitKey.second, tabId, frameId); else newKeyQueue = (validFirstKeys[splitKey.second] ? splitKey.second : ""); } else { diff --git a/commands.js b/commands.js index 1518f113..e4cef0c2 100644 --- a/commands.js +++ b/commands.js @@ -170,7 +170,7 @@ addCommand('goBack', 'Go back in history'); addCommand('goForward', 'Go forward in history'); // Navigating the URL hierarchy -addCommand('goUp', 'Go up the URL hierarchy'); +addCommand('goUp', 'Go up the URL hierarchy', false, true); // Manipulating tabs: addCommand('nextTab', 'Go one tab right', true); diff --git a/linkHints.js b/linkHints.js index 8aed5fff..1367a827 100644 --- a/linkHints.js +++ b/linkHints.js @@ -30,11 +30,11 @@ var linkHintsPrototype = { * The final expression will be something like "//button | //xhtml:button | ..." */ clickableElementsXPath: (function() { - var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]"]; + var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]", + "*[@onclick or @tabindex or @role='link' or @role='button']"]; var xpath = []; for (var i in clickableElements) xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]); - xpath.push("//*[@onclick]"); return xpath.join(" | ") })(), @@ -90,7 +90,7 @@ var linkHintsPrototype = { this.hintMarkerContainingDiv.className = "internalVimiumHintMarker"; for (var i = 0; i < this.hintMarkers.length; i++) this.hintMarkerContainingDiv.appendChild(this.hintMarkers[i]); - document.body.appendChild(this.hintMarkerContainingDiv); + document.documentElement.appendChild(this.hintMarkerContainingDiv); }, hintStringGenerator: function() {}, diff --git a/test_harnesses/form.html b/test_harnesses/form.html index 740edb46..d7104ac2 100644 --- a/test_harnesses/form.html +++ b/test_harnesses/form.html @@ -10,6 +10,8 @@ <p> Text: <input type="text" name="text" value="" /> Text 2: <input type="text" name="text" value="" /> + Email: <input type="email" name="text" value="" /> + No Type: <input name="text" value="" /> </p> <p> Search: <input type="search" /> diff --git a/test_harnesses/visibility_test.html b/test_harnesses/visibility_test.html index 5fa4d728..7cfab412 100644 --- a/test_harnesses/visibility_test.html +++ b/test_harnesses/visibility_test.html @@ -7,65 +7,166 @@ <head> <title>Visibility test</title> <style type="text/css" media="screen"> + * { + font-family: sans; + font-size: 14px; + } span { display:block; width:30px; height:20px; background-color:blue; } + table { + border-collapse: separate; + } + tr, td { + border: 2px solid black; + margin: 0; + padding: 2px; + } </style> <script type="text/javascript" charset="utf-8"> + window.addEventListener("load", displayTests, false); window.addEventListener("load", checkDivsForVisibility, false); - - function checkDivsForVisibility() { - var divs = document.getElementsByTagName("span"); - for (var i = 0; i < divs.length; i++) { - console.log(divs[i].id, isVisible(divs[i])); + + var visibilityTests = [ + { + 'description': 'BoundingClientRect is inside viewport', + 'test': function(element) { + var rect = element.getBoundingClientRect(); + if (!rect || rect.top > window.innerHeight || rect.bottom < 0 || rect.left > window.innerWidth || rect.right < 0) + return false; + return true; + }, + }, + { + 'description': 'BoundingClientRect has nonzero dimensions', + 'test': function(element) { + var rect = element.getBoundingClientRect(); + if (!rect || rect.width == 0 || rect.height == 0) + return false; + return true; + }, + }, + { + 'description': 'Is visible and displayed', + 'test': function(element) { + var computedStyle = window.getComputedStyle(element, null); + if (computedStyle.getPropertyValue('visibility') != 'visible' || + computedStyle.getPropertyValue('display') == 'none') + return false; + return true; + }, + }, + { + 'description': 'Has ClientRect', + 'test': function(element) { + var clientRect = element.getClientRects()[0]; + if (!clientRect) + return false; + return true; + }, + }, + { + 'description': 'ClientRect has nonzero dimensions', + 'test': function(element) { + var clientRect = element.getClientRects()[0]; + if (!clientRect || clientRect.width == 0 || clientRect.height == 0) + return false; + return true; + }, + }, + ]; + + function displayTests() { + for (var i = 0; i < visibilityTests.length; i++) { + document.getElementById("testDescriptions").innerHTML += + "<b>Test " + (i+1) + ": </b>" + visibilityTests[i].description + "<br/>"; } } - /* - * Determine whether elements are visible. - * Inspired by Vimperator. - */ - function isVisible(element) { - // eliminate offscreen elements (case 4) - var rect = element.getBoundingClientRect(); - if (!rect || rect.top > window.innerHeight || rect.bottom < 0 || rect.left > window.innerWidth || rect.right < 0) - return false; - // this catches cases 2, 3, & 5 - var computedStyle = window.getComputedStyle(element, null); - if (computedStyle.getPropertyValue('visibility') != 'visible' || - computedStyle.getPropertyValue('display') == 'none') - return false; + function makeBoolTd(bool) { + var td = document.createElement("td"); + td.style.width = '15px'; + td.style.background = bool ? '#0f0' : "#f00"; + return td; + } - // this catches cases 3 & 6 - var clientRect = element.getClientRects()[0]; - if (!clientRect) - return false; + function makeTag(tag, text) { + var td = document.createElement(tag); + td.innerHTML = text; + return td; + } + + function checkDivsForVisibility() { + var table = document.getElementById("resultsDisplay"); + var tr = document.getElementsByTagName("tr")[0]; + for (var i = 0; i < visibilityTests.length; i++) + tr.appendChild(makeTag("th", i+1)); + tr.appendChild(makeTag("th", "Expected Result")); + tr.appendChild(makeTag("th", "Comments")); + + var divs = document.getElementsByClassName("testElement"); + for (var i = 0; i < divs.length; i++) { + var tr = document.createElement("tr"); + table.appendChild(tr); + table.appendChild(makeTag("td", i+1)); + var netResult = true; + for (var j = 0; j < visibilityTests.length; j++) { + result = visibilityTests[j].test(divs[i]); + table.appendChild(makeBoolTd(result)); + netResult = netResult && result; + } + var expectedResult = divs[i].getAttribute('data-expectedresult') == 1; + var td = makeTag("td", expectedResult); + td.style.background = netResult === expectedResult ? '#fff' : '#ccf'; + table.appendChild(td); + table.appendChild(makeTag("td", divs[i].getAttribute('data-comment'))); - return true; + // hide the test cases once we're done with them + divs[i].style.visibility = 'hidden'; + } } </script> </head> <body> - <span id="div1true"></span> - - <span id="div2false" style="visibility:hidden"></span> + <div id='testDescriptions' style='margin: 6px 0 6px 0;'></div> + <table id='resultsDisplay'> + <tr> + <th>Node/Test</th> + </tr> + </table> + + <div id='testContainer' style='position: absolute; top: 0; left:0'> + <span class='testElement' data-expectedresult=1 data-comment="default">test</span> + + <span class='testElement' data-expectedresult=0 style="visibility:hidden" data-comment="visibility: hidden">test</span> - <span id="div3false" style="display:none"></span> + <div style="visibility:hidden"> + <span class='testElement' data-expectedresult=0 data-comment="nested in an element that has visibility: hidden">test</span> + </div> - <span id="div4false" style="position:absolute;top:2000px"></span> + <span class='testElement' data-expectedresult=0 style="display:none" data-comment="display: none">test</span> - <div style="visibility:hidden"> - <span id="div5false"></span> - </div> - <div style="display:none"> - <span id="div6false"></span> - </div> - <div style="opacity:0"> - <span id="div7true" style="opacity:0"></span> + <div style="display:none"> + <span class='testElement' data-expectedresult=0 data-comment="nested in an element that has display: none">test</span> + </div> + + <span class='testElement' data-expectedresult=0 style="position:absolute;top:2000px" data-comment="outside viewport">test</span> + + <div style="opacity:0"> + <span class='testElement' data-expectedresult=1 data-comment="nested in an element that has opacity:0">test</span> + </div> + <div class='testElement' data-expectedresult=1 data-comment="Contains only a floated span. We must recurse into the div to find it."> + <span style="float: left">test</span> + </div> + <svg> + <a class='testElement' data-expectedresult=1 xlink:href='http://www.example.com/' data-comment="This link is contained within an SVG."> + <text x='0' y='68' >test</text> + </a> + </svg> </div> </html> diff --git a/vimiumFrontend.js b/vimiumFrontend.js index dac778cb..7289c135 100644 --- a/vimiumFrontend.js +++ b/vimiumFrontend.js @@ -26,6 +26,17 @@ var linkHintCss; // TODO(philc): This should be pulled from the extension's storage when the page loads. var currentZoomLevel = 100; +// 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. +// +// Should we include the HTML5 date pickers here? +var textInputTypes = ["text", "search", "email", "url", "number"]; +// The corresponding XPath for such elements. +var textInputXPath = '//input[' + + textInputTypes.map(function (type) { return '@type="' + type + '"'; }).join(" or ") + + ' or not(@type)]'; + /* * Give this frame a unique id. */ @@ -245,8 +256,8 @@ function scrollLeft() { window.scrollBy(-1 * settings["scrollStepSize"], 0); } function scrollRight() { window.scrollBy(settings["scrollStepSize"], 0); } function focusInput(count) { - var xpath = '//input[@type="text" or @type="search"]'; - var results = document.evaluate(xpath, document.documentElement, null, + var results = document.evaluate(textInputXPath, + document.documentElement, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); var lastInputBox; @@ -268,7 +279,7 @@ function reload() { window.location.reload(); } function goBack() { history.back(); } function goForward() { history.forward(); } -function goUp() { +function goUp(count) { var url = window.location.href; if (url[url.length-1] == '/') url = url.substring(0, url.length - 1); @@ -276,7 +287,7 @@ function goUp() { var urlsplit = url.split('/'); // make sure we haven't hit the base domain yet if (urlsplit.length > 3) { - delete urlsplit[urlsplit.length-1]; + urlsplit = urlsplit.slice(0, Math.max(3, urlsplit.length - count)); window.location.href = urlsplit.join('/'); } } @@ -638,6 +649,7 @@ HUD = { "bottom: 0px;" + "color: black;" + "height: 13px;" + + "width: auto;" + "max-width: 400px;" + "min-width: 150px;" + "text-align: left;" + @@ -800,8 +812,8 @@ Tween = { function addCssToPage(css) { var head = document.getElementsByTagName("head")[0]; if (!head) { - console.log("Warning: unable to add CSS to the page."); - return; + head = document.createElement("head"); + document.documentElement.appendChild(head); } var style = document.createElement("style"); style.type = "text/css"; |
