aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.markdown5
-rw-r--r--background_page.html10
-rw-r--r--commands.js2
-rw-r--r--linkHints.js6
-rw-r--r--test_harnesses/form.html2
-rw-r--r--test_harnesses/visibility_test.html175
-rw-r--r--vimiumFrontend.js24
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";