Fixes bug 446106, 445518, 445407. Spatial Navigation improvements. r=gavin
authorDoug Turner <dougt@meer.net>
Wed, 23 Jul 2008 10:47:31 -0700
changeset 16153 fd1c0f6abf6d8cc0822e2012b34e0a37fa2c61d3
parent 16152 68b0caac284dd191e3ddb7a17161f4539666ec0c
child 16154 4e627e64d6509ef8efa3f81488912d35a757f9c4
push idunknown
push userunknown
push dateunknown
reviewersgavin
bugs446106, 445518, 445407
milestone1.9.1a2pre
Fixes bug 446106, 445518, 445407. Spatial Navigation improvements. r=gavin
toolkit/spatial-navigation/SpatialNavigation.js
toolkit/spatial-navigation/tests/Makefile.in
toolkit/spatial-navigation/tests/chrome/SpatialNavUtils.js
toolkit/spatial-navigation/tests/chrome/test_snav.xul
toolkit/spatial-navigation/tests/chrome/test_snav_disabledElement.xul
toolkit/spatial-navigation/tests/chrome/test_snav_selects.xul
toolkit/spatial-navigation/tests/chrome/test_snav_textFields.xul
toolkit/spatial-navigation/tests/chrome/test_snav_tightlinks.xul
--- a/toolkit/spatial-navigation/SpatialNavigation.js
+++ b/toolkit/spatial-navigation/SpatialNavigation.js
@@ -65,280 +65,339 @@ SpatialNavigation.prototype = {
 
 // Private stuff
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 function dump(msg)
 {
-    var console = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
-    console.logStringMessage("*** SNAV: " + msg);
+  var console = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
+  console.logStringMessage("*** SNAV: " + msg);
 }
 
 var gDirectionalBias = 10;
 var gRectFudge = 1;
 
 function _onInputKeyPress (event, callback) {
-    
-    // If it isn't an arrow key, bail.
-    if (event.keyCode != event.DOM_VK_LEFT  &&
-        event.keyCode != event.DOM_VK_RIGHT &&
-        event.keyCode != event.DOM_VK_UP    &&
-        event.keyCode != event.DOM_VK_DOWN  )
-        return;
+  
+  var target = event.target;
+
+  // If it isn't an arrow key, bail.
+  if (event.keyCode != event.DOM_VK_LEFT  &&
+      event.keyCode != event.DOM_VK_RIGHT &&
+      event.keyCode != event.DOM_VK_UP    &&
+      event.keyCode != event.DOM_VK_DOWN  )
+    return;
+  
+  // check to see if we are in a textarea or text input element, and if so,
+  // ensure that we let the arrow keys work properly.
+
+  if ((target instanceof Ci.nsIDOMHTMLInputElement && (target.type == "text" || target.type == "password")) ||
+      target instanceof Ci.nsIDOMHTMLTextAreaElement ) {
     
-    function snavfilter(node) {
-             
-        if (node instanceof Ci.nsIDOMHTMLLinkElement ||
-            node instanceof Ci.nsIDOMHTMLAnchorElement) {
-            // if a anchor doesn't have a href, don't target it.
-            if (node.href == "")
-                return Ci.nsIDOMNodeFilter.FILTER_SKIP;
-            return  Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
-        }
-        
-        if (node instanceof Ci.nsIDOMHTMLInputElement  ||
-            node instanceof Ci.nsIDOMHTMLSelectElement ||
-            node instanceof Ci.nsIDOMHTMLOptionElement)
-            return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
-        return Ci.nsIDOMNodeFilter.FILTER_SKIP;
+    // if there is any selection at all, just ignore
+    if (target.selectionEnd - target.selectionStart > 0)
+      return;
+    
+    // if there is no text, there is nothing special to do.
+    
+    if (target.textLength > 0) {
+
+      if (event.keyCode == event.DOM_VK_RIGHT || event.keyCode == event.DOM_VK_DOWN  ) {
+        // we are moving forward into the document
+        if (target.textLength != target.selectionEnd)
+          return;
+      }
+      else
+      {
+        // we are at the start of the text, okay to move 
+        if (target.selectionStart != 0)
+          return;
+      }
     }
-    var bestElementToFocus = null;
-    var distanceToBestElement = Infinity;
-    var focusedRect = _inflateRect(event.target.getBoundingClientRect(),
-                                   - gRectFudge);
-    var doc = event.target.ownerDocument;
-    
-    var treeWalker = doc.createTreeWalker(doc, Ci.nsIDOMNodeFilter.SHOW_ELEMENT, snavfilter, false);
-    var nextNode;
+  }
+
+  // Check to see if we are in a select
+  if (target instanceof Ci.nsIDOMHTMLSelectElement)
+  {
+    if (event.keyCode == event.DOM_VK_DOWN  ) {
+      if (target.selectedIndex + 1 < target.length)
+        return;
+    }
     
-    while ((nextNode = treeWalker.nextNode())) {
-        
-        if (nextNode == event.target)
-            continue;
-        
-        var nextRect = _inflateRect(nextNode.getBoundingClientRect(),
-                                    - gRectFudge);
-        
-        if (! _isRectInDirection(event, focusedRect, nextRect))
-            continue;
-        
-        var distance = _spatialDistance(event, focusedRect, nextRect);
-        
-        if (distance <= distanceToBestElement && distance > 0) {
-            distanceToBestElement = distance;
-            bestElementToFocus = nextNode;
-        }
+    if (event.keyCode == event.DOM_VK_UP) {
+      if (target.selectedIndex > 0)
+        return;
+    }
+  }
+
+  function snavfilter(node) {
+    
+    if (node instanceof Ci.nsIDOMHTMLLinkElement ||
+        node instanceof Ci.nsIDOMHTMLAnchorElement) {
+      // if a anchor doesn't have a href, don't target it.
+      if (node.href == "")
+        return Ci.nsIDOMNodeFilter.FILTER_SKIP;
+      return  Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
     }
     
-    if (bestElementToFocus != null) {
-        //        dump("focusing element  " + bestElementToFocus.nodeName + " " + bestElementToFocus);
-        // Wishing we could do element.focus()
-        doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).focus(bestElementToFocus);
-
-        if (callback != undefined)
-           callback(bestElementToFocus);
+    if ((node instanceof Ci.nsIDOMHTMLButtonElement ||
+         node instanceof Ci.nsIDOMHTMLInputElement ||
+         node instanceof Ci.nsIDOMHTMLLinkElement ||
+         node instanceof Ci.nsIDOMHTMLOptGroupElement ||
+         node instanceof Ci.nsIDOMHTMLSelectElement ||
+         node instanceof Ci.nsIDOMHTMLTextAreaElement) &&
+        node.disabled == false)
+      return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
+    
+    return Ci.nsIDOMNodeFilter.FILTER_SKIP;
+  }
+  var bestElementToFocus = null;
+  var distanceToBestElement = Infinity;
+  var focusedRect = _inflateRect(target.getBoundingClientRect(),
+                                 - gRectFudge);
+  var doc = target.ownerDocument;
+  
+  var treeWalker = doc.createTreeWalker(doc, Ci.nsIDOMNodeFilter.SHOW_ELEMENT, snavfilter, false);
+  var nextNode;
+  
+  while ((nextNode = treeWalker.nextNode())) {
+    
+    if (nextNode == target)
+      continue;
 
-    } else {
-        // couldn't find anything.  just advance and hope.
-        //        dump("advancing focus");
-        var windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
-        var window = windowMediator.getMostRecentWindow("navigator:browser");
-        window.document.commandDispatcher.advanceFocus();
+    var nextRect = _inflateRect(nextNode.getBoundingClientRect(),
+                                - gRectFudge);
+    
+    if (! _isRectInDirection(event, focusedRect, nextRect))
+      continue;
+    
+    var distance = _spatialDistance(event, focusedRect, nextRect);
+    
+    if (distance <= distanceToBestElement && distance > 0) {
+      distanceToBestElement = distance;
+      bestElementToFocus = nextNode;
+    }
+  }
+  
+  if (bestElementToFocus != null) {
+    dump("focusing element  " + bestElementToFocus.nodeName + " " + bestElementToFocus) + "id=" + bestElementToFocus.getAttribute("id");
+    // Wishing we could do element.focus()
+    doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).focus(bestElementToFocus);
 
-        if (callback != undefined)
-           callback(null);
+    // if it is a text element, select all.
+    if((bestElementToFocus instanceof Ci.nsIDOMHTMLInputElement && (bestElementToFocus.type == "text" || bestElementToFocus.type == "password")) ||
+       bestElementToFocus instanceof Ci.nsIDOMHTMLTextAreaElement ) {
+      bestElementToFocus.selectionStart = 0;
+      bestElementToFocus.selectionEnd = bestElementToFocus.textLength;
     }
+
+    if (callback != undefined)
+      callback(bestElementToFocus);
     
-    event.preventDefault();
-    event.stopPropagation();
+  } else {
+    // couldn't find anything.  just advance and hope.
+    var windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
+    var window = windowMediator.getMostRecentWindow("navigator:browser");
+    
+    if (event.keyCode == event.DOM_VK_RIGHT || event.keyCode != event.DOM_VK_DOWN  )
+      window.document.commandDispatcher.advanceFocus();
+    else
+      window.document.commandDispatcher.rewindFocus();
+    
+    if (callback != undefined)
+      callback(null);
+  }
+  
+  event.preventDefault();
+  event.stopPropagation();
 }
 
 function _isRectInDirection(event, focusedRect, anotherRect)
 {
     if (event.keyCode == event.DOM_VK_LEFT) {  
-        return (anotherRect.left < focusedRect.left);
+      return (anotherRect.left < focusedRect.left);
     }
     
     if (event.keyCode == event.DOM_VK_RIGHT) {
-        return (anotherRect.right > focusedRect.right);
+      return (anotherRect.right > focusedRect.right);
     }
     
     if (event.keyCode == event.DOM_VK_UP) {
-        return (anotherRect.top < focusedRect.top);
+      return (anotherRect.top < focusedRect.top);
     }
     
     if (event.keyCode == event.DOM_VK_DOWN) {
-        return (anotherRect.bottom > focusedRect.bottom);
+      return (anotherRect.bottom > focusedRect.bottom);
     }
     return false;
 }
 
 function _inflateRect(rect, value)
 {
-    var newRect = new Object();
-    
-    newRect.left   = rect.left - value;
-    newRect.top    = rect.top - value;
-    newRect.right  = rect.right  + value;
-    newRect.bottom = rect.bottom + value;
-    return newRect;
+  var newRect = new Object();
+  
+  newRect.left   = rect.left - value;
+  newRect.top    = rect.top - value;
+  newRect.right  = rect.right  + value;
+  newRect.bottom = rect.bottom + value;
+  return newRect;
 }
 
 function _containsRect(a, b)
 {
-    return ( (b.left  <= a.right) &&
-             (b.right >= a.left)  &&
-             (b.top  <= a.bottom) &&
-             (b.bottom >= a.top) );
+  return ( (b.left  <= a.right) &&
+           (b.right >= a.left)  &&
+           (b.top  <= a.bottom) &&
+           (b.bottom >= a.top) );
 }
 
 function _spatialDistance(event, a, b)
 {
-    var inlineNavigation = false;
-    var mx, my, nx, ny;
+  var inlineNavigation = false;
+  var mx, my, nx, ny;
+  
+  if (event.keyCode == event.DOM_VK_LEFT) {
+    
+    //  |---|
+    //  |---|
+    //
+    //  |---|  |---|
+    //  |---|  |---|
+    //
+    //  |---|
+    //  |---|
+    //
+    
+    if (a.top > b.bottom) {
+      // the b rect is above a.
+      mx = a.left;
+      my = a.top;
+      nx = b.right;
+      ny = b.bottom;
+    }
+    else if (a.bottom < b.top) {
+      // the b rect is below a.
+      mx = a.left;
+      my = a.bottom;
+      nx = b.right;
+      ny = b.top;       
+    }
+    else {
+      mx = a.left;
+      my = 0;
+      nx = b.right; 
+      ny = 0;    
+    }
+  } else if (event.keyCode == event.DOM_VK_RIGHT) {
+    
+    //         |---|
+    //         |---|
+    //
+    //  |---|  |---|
+    //  |---|  |---|
+    //
+    //         |---|
+    //         |---|
+    //
+    
+    if (a.top > b.bottom) {
+      // the b rect is above a.
+      mx = a.right;
+      my = a.top;
+      nx = b.left;
+      ny = b.bottom;
+    }
+    else if (a.bottom < b.top) {
+      // the b rect is below a.
+      mx = a.right;
+      my = a.bottom;
+      nx = b.left;
+      ny = b.top;       
+    } else {
+      mx = a.right;
+      my = 0;
+      nx = b.left; 
+      ny = 0;	    
+    }
+  } else if (event.keyCode == event.DOM_VK_UP) {
     
-    if (event.keyCode == event.DOM_VK_LEFT) {
-        
-        //  |---|
-        //  |---|
-        //
-        //  |---|  |---|
-        //  |---|  |---|
-        //
-        //  |---|
-        //  |---|
-        //
-        
-        if (a.top > b.bottom) {
-            // the b rect is above a.
-            mx = a.left;
-            my = a.top;
-            nx = b.right;
-            ny = b.bottom;
-        }
-        else if (a.bottom < b.top) {
-            // the b rect is below a.
-            mx = a.left;
-            my = a.bottom;
-            nx = b.right;
-            ny = b.top;       
-        }
-        else {
-            mx = a.left;
-            my = 0;
-            nx = b.right; 
-            ny = 0;    
-        }
-    } else if (event.keyCode == event.DOM_VK_RIGHT) {
-        
-        //         |---|
-        //         |---|
-        //
-        //  |---|  |---|
-        //  |---|  |---|
-        //
-        //         |---|
-        //         |---|
-        //
-        
-        if (a.top > b.bottom) {
-            // the b rect is above a.
-            mx = a.right;
-            my = a.top;
-            nx = b.left;
-            ny = b.bottom;
-        }
-        else if (a.bottom < b.top) {
-            // the b rect is below a.
-            mx = a.right;
-            my = a.bottom;
-            nx = b.left;
-            ny = b.top;       
-        } else {
-            mx = a.right;
-            my = 0;
-            nx = b.left; 
-            ny = 0;	    
-        }
-    } else if (event.keyCode == event.DOM_VK_UP) {
-        
-        //  |---|  |---|  |---|
-        //  |---|  |---|  |---|
-        //
-        //         |---|
-        //         |---|
-        //
-        
-        if (a.left > b.right) {
-            // the b rect is to the left of a.
-            mx = a.left;
-            my = a.top;
-            nx = b.right;
-            ny = b.bottom;
-        } else if (a.right < b.left) {
-            // the b rect is to the right of a
-            mx = a.right;
-            my = a.top;
-            nx = b.left;
-            ny = b.bottom;       
-        } else {
-            // both b and a share some common x's.
-            mx = 0;
-            my = a.top;
-            nx = 0;
-            ny = b.bottom;
-        }
-    } else if (event.keyCode == event.DOM_VK_DOWN) {
-        
-        //         |---|
-        //         |---|
-        //
-        //  |---|  |---|  |---|
-        //  |---|  |---|  |---|
-        //
-        
-        if (a.left > b.right) {
-            // the b rect is to the left of a.
-            mx = a.left;
-            my = a.bottom;
-            nx = b.right;
-            ny = b.top;
-        } else if (a.right < b.left) {
-            // the b rect is to the right of a
-            mx = a.right;
-            my = a.bottom;
-            nx = b.left;
-            ny = b.top;      
-        } else {
-            // both b and a share some common x's.
-            mx = 0;
-            my = a.bottom;
-            nx = 0;
-            ny = b.top;
-        }
+    //  |---|  |---|  |---|
+    //  |---|  |---|  |---|
+    //
+    //         |---|
+    //         |---|
+    //
+    
+    if (a.left > b.right) {
+      // the b rect is to the left of a.
+      mx = a.left;
+      my = a.top;
+      nx = b.right;
+      ny = b.bottom;
+    } else if (a.right < b.left) {
+      // the b rect is to the right of a
+      mx = a.right;
+      my = a.top;
+      nx = b.left;
+      ny = b.bottom;       
+    } else {
+      // both b and a share some common x's.
+      mx = 0;
+      my = a.top;
+      nx = 0;
+      ny = b.bottom;
     }
+  } else if (event.keyCode == event.DOM_VK_DOWN) {
     
-    var scopedRect = _inflateRect(a, gRectFudge);
+    //         |---|
+    //         |---|
+    //
+    //  |---|  |---|  |---|
+    //  |---|  |---|  |---|
+    //
     
-    if (event.keyCode == event.DOM_VK_LEFT || 
-        event.keyCode == event.DOM_VK_RIGHT) {
-        scopedRect.left = 0;
-        scopedRect.right = Infinity;
-        inlineNavigation = _containsRect(scopedRect, b);
+    if (a.left > b.right) {
+      // the b rect is to the left of a.
+      mx = a.left;
+      my = a.bottom;
+      nx = b.right;
+      ny = b.top;
+    } else if (a.right < b.left) {
+      // the b rect is to the right of a
+      mx = a.right;
+      my = a.bottom;
+      nx = b.left;
+      ny = b.top;      
+    } else {
+      // both b and a share some common x's.
+      mx = 0;
+      my = a.bottom;
+      nx = 0;
+      ny = b.top;
     }
-    else if (event.keyCode == event.DOM_VK_UP ||
-             event.keyCode == event.DOM_VK_DOWN) {
-        scopedRect.top = 0;
-        scopedRect.bottom = Infinity;
-        inlineNavigation = _containsRect(scopedRect, b);
-    }
-    
-    var d = Math.pow((mx-nx), 2) + Math.pow((my-ny), 2);
-    
-    // prefer elements directly aligned with the focused element
-    if (inlineNavigation)
-        d /= gDirectionalBias;
-    
-    return d;
+  }
+  
+  var scopedRect = _inflateRect(a, gRectFudge);
+  
+  if (event.keyCode == event.DOM_VK_LEFT || 
+      event.keyCode == event.DOM_VK_RIGHT) {
+    scopedRect.left = 0;
+    scopedRect.right = Infinity;
+    inlineNavigation = _containsRect(scopedRect, b);
+  }
+  else if (event.keyCode == event.DOM_VK_UP ||
+           event.keyCode == event.DOM_VK_DOWN) {
+    scopedRect.top = 0;
+    scopedRect.bottom = Infinity;
+    inlineNavigation = _containsRect(scopedRect, b);
+  }
+  
+  var d = Math.pow((mx-nx), 2) + Math.pow((my-ny), 2);
+  
+  // prefer elements directly aligned with the focused element
+  if (inlineNavigation)
+    d /= gDirectionalBias;
+  
+  return d;
 }
 
--- a/toolkit/spatial-navigation/tests/Makefile.in
+++ b/toolkit/spatial-navigation/tests/Makefile.in
@@ -41,14 +41,18 @@ VPATH		= @srcdir@
 relativesrcdir	= toolkit/spatial-navigation/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= test_snav
 
 MOCHI_TESTS = chrome/test_snav.xul \
               chrome/test_snav_tightlinks.xul \
+              chrome/test_snav_disabledElement.xul \
+              chrome/test_snav_textFields.xul \
+              chrome/test_snav_selects.xul \
+              chrome/SpatialNavUtils.js \
               $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 libs:: $(MOCHI_TESTS) $(MOCHI_CONTENT)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/toolkit/spatial-navigation/tests/chrome/SpatialNavUtils.js
@@ -0,0 +1,46 @@
+var _moveTable;
+var _moveTableIndex = 0;
+
+function testMoves(table) {
+    //    document.addEventListener("focus", _verifyAndAdvance, true);
+
+    _moveTable = table;
+    _moveTableIndex = 0;
+    _move();
+}
+
+function _nextMove()
+{
+    _moveTableIndex++;
+    
+    // When a table ends with "DONE", call finish.
+    if (_moveTable[_moveTableIndex][0] == "DONE") {
+        SimpleTest.finish();
+        return;
+    }
+    
+    // when a table has an empty elment, end the moves.
+    if (_moveTable[_moveTableIndex][0] == "") {
+        return;
+    }
+    
+    _move();
+}
+
+function _move()
+{
+    sendKey( _moveTable[_moveTableIndex][0], document.activeElement);
+    setTimeout( _verifyAndAdvance , 100);
+}
+
+function _verifyAndAdvance()
+{
+    var direction = _moveTable[_moveTableIndex][0];
+    var expectedID = _moveTable[_moveTableIndex][1];
+    
+    ok(document.activeElement.getAttribute("id") == expectedID,
+       "Move " + direction + " to " + expectedID + ". Found " + document.activeElement.getAttribute("id"));
+    
+    _nextMove();
+}
+
--- a/toolkit/spatial-navigation/tests/chrome/test_snav.xul
+++ b/toolkit/spatial-navigation/tests/chrome/test_snav.xul
@@ -8,17 +8,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 <window title="Mozilla Bug 288254"
   xmlns:html="http://www.w3.org/1999/xhtml"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   onload="onLoad();">
 
   <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>      
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript" src="SpatialNavUtils.js"></script>
 
 <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
 
 <a id="start" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436084">Mozilla Bug 436084</a>
 <table
  style="text-align: left; width: 100%; margin-left: auto; margin-right: auto;" border="1" cellpadding="2" cellspacing="2">
   <tbody>
     <tr>
@@ -40,40 +41,36 @@ https://bugzilla.mozilla.org/show_bug.cg
 </table>
 </body>
 
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 Components.utils.import("resource://gre/modules/SpatialNavigation.js");
 
+var moveTable = [
+["DOWN", "1"],
+["DOWN", "4"],
+["DOWN", "7"],
+["RIGHT", "8"],
+["RIGHT", "9"],
+["UP", "6"],
+["UP", "3"],
+["LEFT", "2"],
+["LEFT", "1"],
+["DONE", "DONE"]
+];
+
 function onLoad()
 {
     var x = document.getElementById("some-content");
     var snav = new SpatialNavigation(x);
 
-    function moveAndVerify(direction, value)
-    {
-        sendKey(direction, document.activeElement);
-        ok(document.activeElement.getAttribute("id") == value, "Move");
-    }
-    
     // get to a known place.
     document.getElementById("start").focus();
     
-    // from start.
-    moveAndVerify("DOWN", "1");
-    
-    for (var i = 1 ; i < 10 ; i ++) {
-        moveAndVerify("DOWN", "4");
-        moveAndVerify("DOWN", "7");
-        moveAndVerify("RIGHT", "8");
-        moveAndVerify("RIGHT", "9");
-        moveAndVerify("UP", "6");
-        moveAndVerify("UP", "3");
-        moveAndVerify("LEFT", "2");
-        moveAndVerify("LEFT", "1");
-    }
-    SimpleTest.finish();
+    testMoves(moveTable);
+
+    SimpleTest.waitForExplicitFinish();
 }
 
 ]]></script>
 </window>
new file mode 100644
--- /dev/null
+++ b/toolkit/spatial-navigation/tests/chrome/test_snav_disabledElement.xul
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css type="text/css"?>
+
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=436084
+-->
+
+<window title="Mozilla Bug 288254"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  onload="onLoad();">
+
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>      
+  <script type="text/javascript" src="SpatialNavUtils.js"></script>
+
+<body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
+
+<p>
+<a id="start" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436084">Mozilla Bug 436084</a>
+</p>
+
+<p>
+<input id="hi_mom" value="Some text" disabled="true" type="text"> </input>
+</p>
+
+<p>
+<a id="end" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436084">Mozilla Bug 436084</a>
+</p>
+
+</body>
+
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+Components.utils.import("resource://gre/modules/SpatialNavigation.js");
+
+var moveTable = [
+["DOWN", "end"],
+["DONE", "DONE"],
+];
+
+function onLoad()
+{
+    var x = document.getElementById("some-content");
+    var snav = new SpatialNavigation(x);
+
+    // get to a known place.
+    document.getElementById("start").focus();
+
+    testMoves(moveTable);
+    
+    SimpleTest.waitForExplicitFinish();
+}
+
+]]></script>
+</window>
new file mode 100644
--- /dev/null
+++ b/toolkit/spatial-navigation/tests/chrome/test_snav_selects.xul
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css type="text/css"?>
+
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=436084
+-->
+
+<window title="Mozilla Bug 288254"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  onload="onLoad();">
+
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>      
+  <script type="text/javascript" src="SpatialNavUtils.js"></script>
+
+<body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
+
+<p>
+<a id="start" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436084">Mozilla Bug 445518</a>
+</p>
+
+<form>
+<select id="select_element" name="dropdown" size="1">
+  <option value ="1">option 1</option>
+  <option value ="2">option 2</option>
+  <option value ="3">option 3</option>
+  <option value ="4">option 4</option>
+</select>
+</form>
+
+<p>
+<a id="end" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436084">Mozilla Bug 445518</a>
+</p>
+
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+var moveTable = [
+["DOWN", "select_element"],
+["UP", "start"],
+["DOWN", "select_element"],
+["DOWN", "select_element"],
+["DOWN", "select_element"],
+["DOWN", "select_element"],
+["DOWN", "end"],
+["DONE", "DONE"],
+];
+
+Components.utils.import("resource://gre/modules/SpatialNavigation.js");
+
+function onLoad()
+{
+    var x = document.getElementById("some-content");
+    var snav = new SpatialNavigation(x);
+    
+    // get to a known place.
+    document.getElementById("start").focus();
+    
+    testMoves(moveTable);
+
+    SimpleTest.waitForExplicitFinish();
+}
+
+]]></script>
+</window>
new file mode 100644
--- /dev/null
+++ b/toolkit/spatial-navigation/tests/chrome/test_snav_textFields.xul
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css type="text/css"?>
+
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=436084
+-->
+
+<window title="Mozilla Bug 288254"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  onload="onLoad();">
+
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>      
+  <script type="text/javascript" src="SpatialNavUtils.js"></script>
+
+<body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
+
+<p>
+<a id="start" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436084">Mozilla Bug 436084</a>
+</p>
+
+<p>
+<input id="textinput" value="abcdefghijklmnopqrstuvwxyz" type="text"></input>
+</p>
+
+<p>
+<textarea id="textarea" name="textarea" cols="30" rows="3" wrap="hard">The Book of Mozilla</textarea>
+</p>
+
+<p>
+<a id="end" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436084">Mozilla Bug 436084</a>
+</p>
+
+</body>
+
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+Components.utils.import("resource://gre/modules/SpatialNavigation.js");
+
+var moveTable = [
+["DOWN", "textinput"],
+["DOWN", "textinput"],
+["DOWN", "textarea"],
+["DOWN", "textarea"],
+["DOWN", "end"],
+["UP", "textarea"],
+["UP", "textarea"],
+["UP", "textinput"],
+["UP", "textinput"],
+["UP", "start"],
+["DOWN", "textinput"],
+["LEFT", "textinput"],
+
+
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+["RIGHT", "textinput"],
+
+["RIGHT", "textarea"],
+["LEFT", "textarea"],
+
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+["RIGHT", "textarea"],
+
+["RIGHT", "end"],
+["UP", "textarea"],
+["DONE", "DONE"],
+];
+
+function onLoad()
+{
+    var x = document.getElementById("some-content");
+    var snav = new SpatialNavigation(x);
+    
+    // get to a known place.
+    document.getElementById("start").focus();
+
+    testMoves(moveTable);
+
+    SimpleTest.waitForExplicitFinish();
+}
+
+]]></script>
+</window>
--- a/toolkit/spatial-navigation/tests/chrome/test_snav_tightlinks.xul
+++ b/toolkit/spatial-navigation/tests/chrome/test_snav_tightlinks.xul
@@ -9,16 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <window title="Mozilla Bug 288254"
   xmlns:html="http://www.w3.org/1999/xhtml"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   onload="onLoad();">
 
   <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>      
+  <script type="application/javascript" src="SpatialNavUtils.js"></script>
 
 <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
 
 <a id="start" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436084">Mozilla Bug 436084 (roxama test case)</a>
 <table>
 
 <tbody>
 
@@ -54,65 +55,54 @@ https://bugzilla.mozilla.org/show_bug.cg
 </table>
 </body>
 
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 Components.utils.import("resource://gre/modules/SpatialNavigation.js");
 
+var moveTable = [
+["DOWN", "5"],
+["DOWN", "9"],
+["DOWN", "13"],
+["UP", "9"],
+["UP", "5"],
+["UP", "1"],
+["RIGHT", "2"],
+["DOWN", "6"],
+["DOWN", "10"],
+["DOWN", "14"],
+["UP", "10"],
+["UP", "6"],
+["UP", "2"],
+["RIGHT", "3"],
+["DOWN", "7"],
+["DOWN", "11"],
+["DOWN", "15"],
+["UP", "11"],
+["UP", "7"],
+["UP", "3"],
+["RIGHT", "4"],
+["DOWN", "8"],
+["DOWN", "12"],
+["DOWN", "16"],
+["UP", "12"],
+["UP", "8"],
+["UP", "4"],
+["DONE", "DONE"]
+];
+
 function onLoad()
 {
     var x = document.getElementById("some-content");
     var snav = new SpatialNavigation(x);
 
-    function moveAndVerify(direction, value)
-    {
-        sendKey(direction, document.activeElement);
-        ok(document.activeElement.getAttribute("id") == value, "Move");
-    }
-    
     // get to a known place.
     document.getElementById("1").focus();
-    ok(document.activeElement.getAttribute("id") == "1", "Were we able to focus the starting point?");
-
-    moveAndVerify("DOWN", "5");
-    moveAndVerify("DOWN", "9");
-    moveAndVerify("DOWN", "13");
-    moveAndVerify("UP", "9");
-    moveAndVerify("UP", "5");
-    moveAndVerify("UP", "1");
-
-
-    moveAndVerify("RIGHT", "2");
-
-    moveAndVerify("DOWN", "6");
-    moveAndVerify("DOWN", "10");
-    moveAndVerify("DOWN", "14");
-    moveAndVerify("UP", "10");
-    moveAndVerify("UP", "6");
-    moveAndVerify("UP", "2");
-
 
-    moveAndVerify("RIGHT", "3");
-
-    moveAndVerify("DOWN", "7");
-    moveAndVerify("DOWN", "11");
-    moveAndVerify("DOWN", "15");
-    moveAndVerify("UP", "11");
-    moveAndVerify("UP", "7");
-    moveAndVerify("UP", "3");
-
+    testMoves(moveTable);
 
-    moveAndVerify("RIGHT", "4");
-
-    moveAndVerify("DOWN", "8");
-    moveAndVerify("DOWN", "12");
-    moveAndVerify("DOWN", "16");
-    moveAndVerify("UP", "12");
-    moveAndVerify("UP", "8");
-    moveAndVerify("UP", "4");
-
-    SimpleTest.finish();
+    SimpleTest.waitForExplicitFinish();
 }
 
 ]]></script>
 </window>