am df467264: Merge "Final polish of WebView accessibility support for no JavaScript case." into honeycomb

* commit 'df4672643b92208b4ab7f9ab995dca8b69d3c458':
  Final polish of WebView accessibility support for no JavaScript case.
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index 6bb4320..db66305 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -113,6 +113,11 @@
      * @return True if the event was processed.
      */
     public boolean onKeyEvent(KeyEvent event) {
+        // We do not handle ENTER in any circumstances.
+        if (isEnterActionKey(event.getKeyCode())) {
+            return false;
+        }
+
         if (event.getAction() == KeyEvent.ACTION_UP) {
             return mLastDownEventHandled;
         }
@@ -367,6 +372,12 @@
         }
     }
 
+    private boolean isEnterActionKey(int keyCode) {
+        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
+                || keyCode == KeyEvent.KEYCODE_ENTER
+                || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
+    }
+
     /**
      * Represents a web content key-binding.
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1d5d08f..64039e8 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,9 +16,6 @@
 
 package android.webkit;
 
-import android.view.HardwareCanvas;
-import com.android.internal.R;
-
 import android.annotation.Widget;
 import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
@@ -31,7 +28,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -57,7 +53,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.os.SystemClock;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
 import android.text.Selection;
@@ -66,6 +61,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.view.Gravity;
+import android.view.HardwareCanvas;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -92,7 +88,6 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.CheckedTextView;
-import android.widget.EdgeGlow;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.OverScroller;
@@ -677,6 +672,8 @@
     static final int SET_AUTOFILLABLE                   = 133;
     static final int AUTOFILL_COMPLETE                  = 134;
 
+    static final int SELECT_AT                          = 135;
+
     private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
     private static final int LAST_PACKAGE_MSG_ID = SET_TOUCH_HIGHLIGHT_RECTS;
 
@@ -728,7 +725,8 @@
         "SET_TOUCH_HIGHLIGHT_RECTS", //      = 131;
         "SAVE_WEBARCHIVE_FINISHED", //       = 132;
         "SET_AUTOFILLABLE", //               = 133;
-        "AUTOFILL_COMPLETE" //               = 134;
+        "AUTOFILL_COMPLETE", //              = 134;
+        "SELECT_AT" //                       = 135;
     };
 
     // If the site doesn't use the viewport meta tag to specify the viewport,
@@ -7600,6 +7598,10 @@
                     }
                     break;
 
+                case SELECT_AT:
+                    nativeSelectAt(msg.arg1, msg.arg2);
+                    break;
+
                 default:
                     super.handleMessage(msg);
                     break;
@@ -7957,24 +7959,6 @@
                 cursorData());
     }
 
-    /*
-     * Called from JNI when the cursor has moved. This method
-     * sends a message to the WebCore requesting the given
-     * nodePtr in the given framePrt to be selected which will
-     * result in firing an accessibility event announing its
-     * content.
-     *
-     * Note: Accessibility support.
-     */
-    @SuppressWarnings("unused")
-    // called from JNI
-    private void sendMoveSelection(int framePtr, int nodePtr) {
-        if (AccessibilityManager.getInstance(mContext).isEnabled()
-                && mAccessibilityInjector != null) {
-            mWebViewCore.sendMessage(EventHub.MOVE_SELECTION, framePtr, nodePtr);
-        }
-    }
-
     /**
      * Called by JNI to send a message to the webcore thread that the user
      * touched the webpage.
@@ -8286,6 +8270,7 @@
     private native Point    nativeSelectableText();
     private native void     nativeSelectAll();
     private native void     nativeSelectBestAt(Rect rect);
+    private native void     nativeSelectAt(int x, int y);
     private native int      nativeSelectionX();
     private native int      nativeSelectionY();
     private native int      nativeFindIndex();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 069e8ef..27bf51c 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -636,18 +636,6 @@
      */
     private native String nativeModifySelection(int direction, int granularity);
 
-    /**
-     * Moves the selection to given node i.e. selects that node.
-     *
-     * Note: Accessibility support.
-     *
-     * @param framePtr Pointer to the frame containing the node to be selected.
-     * @param nodePtr Pointer to the node to be selected.
-     *
-     * @return The selection string.
-     */
-    private native String nativeMoveSelection(int framePtr, int nodePtr);
-
     // EventHub for processing messages
     private final EventHub mEventHub;
     // WebCore thread handler
@@ -1013,9 +1001,6 @@
 
         static final int PROXY_CHANGED = 193;
 
-        // accessibility support
-        static final int MOVE_SELECTION = 194;
-
         // private message ids
         private static final int DESTROY =     200;
 
@@ -1434,12 +1419,6 @@
                                     modifiedSelectionString).sendToTarget();
                             break;
 
-                        case MOVE_SELECTION:
-                            String movedSelectionString = nativeMoveSelection(msg.arg1, msg.arg2);
-                            mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED,
-                                    movedSelectionString).sendToTarget();
-                            break;
-
                         case LISTBOX_CHOICES:
                             SparseBooleanArray choices = (SparseBooleanArray)
                                     msg.obj;
@@ -2709,6 +2688,14 @@
                 hMode, vMode).sendToTarget();
     }
 
+    // called by JNI
+    @SuppressWarnings("unused")
+    private void selectAt(int x, int y) {
+        if (mWebView != null) {
+            mWebView.mPrivateHandler.obtainMessage(WebView.SELECT_AT, x, y).sendToTarget();
+        }
+    }
+
     private void useMockDeviceOrientation() {
         mDeviceMotionAndOrientationManager.useMock();
     }
diff --git a/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java b/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java
index 4edd127..16108e6 100644
--- a/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java
+++ b/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java
@@ -51,7 +51,7 @@
     private static final long TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE = 1000;
 
     /** The count of tests to detect when to shut down the service. */
-    private static final int TEST_CASE_COUNT = 8;
+    private static final int TEST_CASE_COUNT = 16;
 
     /** The meta state for pressed left ALT. */
     private static final int META_STATE_ALT_LEFT_ON = KeyEvent.META_ALT_ON
@@ -140,7 +140,8 @@
                    "</p>" +
                    "<p>" +
                      "d" +
-                     "<input>e</input>" +
+                     "<p/>" +
+                     "e" +
                    "</p>" +
                "</body>" +
              "</html>";
@@ -179,6 +180,10 @@
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
         assertSelectionString(null);
 
+        // go to the fifth character (reverse)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("e");
+
         // go to the fourth character (reverse)
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
         assertSelectionString("d");
@@ -199,6 +204,10 @@
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
         assertSelectionString(null);
 
+        // go to the first character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("a");
+
         // go to the second character (reverse again)
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
         assertSelectionString("<b>b</b>");
@@ -223,11 +232,11 @@
                    "</p>" +
                    "<p>" +
                      " scattered " +
-                     "<input>all</input>" +
-                     " over " +
+                     "<p/>" +
+                     " all over " +
                    "</p>" +
                    "<div>" +
-                     "<button>the place.</button>" +
+                     "<p>the place.</p>" +
                    "</div>" +
                "</body>" +
              "</html>";
@@ -284,7 +293,7 @@
 
         // go to the last word (reverse)
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
-        assertSelectionString("place");
+        assertSelectionString("place.");
 
         // go to the eight word
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
@@ -322,6 +331,10 @@
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
         assertSelectionString(null);
 
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This");
+
         // go to the second word (reverse again)
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
         assertSelectionString("is");
@@ -384,6 +397,10 @@
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
         assertSelectionString(null);
 
+        // go to the fifth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("This is the second sentence of the second paragraph.");
+
         // go to the fourth sentence (reverse)
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
         assertSelectionString("This is the first sentence of the second paragraph.");
@@ -405,6 +422,11 @@
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
         assertSelectionString(null);
 
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This is the first sentence of the first paragraph and has an "
+                + "<b>inline bold tag</b>.");
+
         // go to the second sentence (reverse again)
         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
         assertSelectionString("This is the second sentence of the first paragraph.");
@@ -747,6 +769,539 @@
     }
 
     /**
+     * Tests that the selection does not cross anchor boundaries. This is a
+     * workaround for the asymmetric and inconsistent handling of text with
+     * links by WebKit while traversing by sentence.
+     */
+    @LargeTest
+    public void testEnforceSelectionDoesNotCrossAnchorBoundary1() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<div>First</div>" +
+                "<p>" +
+                  "<a href=\"\">Second</a> Third" +
+                "</p>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<a href=\"\">Second</a>");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Third");
+
+        // go to past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("Third");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<a href=\"\">Second</a>");
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("First");
+
+        // go to before the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+    }
+
+    /**
+     * Tests that the selection does not cross anchor boundaries. This is a
+     * workaround for the asymmetric and inconsistent handling of text with
+     * links by WebKit while traversing by sentence.
+     */
+    @LargeTest
+    public void testEnforceSelectionDoesNotCrossAnchorBoundary2() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<div>First</div>" +
+                "<a href=\"#\">Second</a>" +
+                "&nbsp;" +
+                "<a href=\"#\">Third</a>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<a href=\"#\">Second</a>");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("&nbsp;");
+
+        // go to the fourth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<a href=\"#\">Third</a>");
+
+        // go to past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the fourth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<a href=\"#\">Third</a>");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("&nbsp;");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<a href=\"#\">Second</a>");
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("First");
+
+        // go to before the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+    }
+
+    /**
+     * Tests that the selection does not cross anchor boundaries. This is a
+     * workaround for the asymmetric and inconsistent handling of text with
+     * links by WebKit while traversing by sentence.
+     */
+    @LargeTest
+    public void testEnforceSelectionDoesNotCrossAnchorBoundary3() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<div>" +
+                  "First" +
+                "<div>" +
+                "<div>" +
+                  "<a href=\"#\">Second</a>" +
+                "</div>" +
+                "<div>" +
+                  "Third" +
+                "</div>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<a href=\"#\">Second</a>");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Third");
+
+        // go to past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("Third");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<a href=\"#\">Second</a>");
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("First");
+
+        // go to before the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+    }
+
+    /**
+     * Tests skipping of content with hidden visibility.
+     */
+    @LargeTest
+    public void testSkipVisibilityHidden() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<div>First </div>" +
+                "<div style=\"visibility:hidden;\">Second</div>" +
+                "<div> Third</div>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("1"); // expect the word navigation axis
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+
+        // go to the third word (the second is invisible)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Third");
+
+        // go to past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the third word (the second is invisible)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("Third");
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("First");
+
+        // go to before the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+    }
+
+    /**
+     * Tests skipping of content with display none.
+     */
+    @LargeTest
+    public void testSkipDisplayNone() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<div>First</div>" +
+                "<div style=\"display: none;\">Second</div>" +
+                "<div>Third</div>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("1"); // expect the word navigation axis
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+
+        // go to the third word (the second is invisible)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Third");
+
+        // go to past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the third word (the second is invisible)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("Third");
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("First");
+
+        // go to before the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+    }
+
+    /**
+     * Tests for the selection not getting stuck.
+     *
+     * Note: The selection always proceeds but if it can
+     * be selecting the same content i.e. between the start
+     * and end are contained the same text nodes.
+     */
+    @LargeTest
+    public void testSelectionTextProceed() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<a href=\"#\">First</a>" +
+                "<span><a href=\"#\"><span>Second</span>&nbsp;<small>a</small></a>" +
+                "</span>&nbsp;<a href=\"#\">Third</a>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<a href=\"#\">First</a>");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<a href=\"#\"><span>Second&nbsp;<small>a</small></a>");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("&nbsp;");
+
+        // go to the fourth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<a href=\"#\">Third</a>");
+
+        // go to past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<a href=\"#\">Third</a>");
+
+        // NOTE: Here we are a bit asymmetric around whitespace but we can live with it
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("&nbsp;");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<a href=\"#\"><span>Second&nbsp;<small>a</small></a>");
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<a href=\"#\">First</a>");
+
+        // go to before the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<a href=\"#\">First</a>");
+    }
+
+    /**
+     * Tests if input elements are selected rather skipped.
+     */
+    @LargeTest
+    public void testSelectionOfInputElements() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<p>" +
+                  "First" +
+                "</p>" +
+                "<input type=\"text\"/>" +
+                "<p>" +
+                  "Second" +
+                "</p>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<input type=\"text\">");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Second");
+
+        // go to past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("Second");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<input type=\"text\">");
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("First");
+
+        // go to before the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+    }
+
+    /**
+     * Tests traversing of input controls.
+     */
+    @LargeTest
+    public void testSelectionOfInputElements2() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<div>" +
+                  "First" +
+                  "<input type=\"text\"/>" +
+                  "<span>" +
+                    "<input type=\"text\"/>" +
+                  "</span>" +
+                  "<button type=\"button\">Click Me!</button>" +
+                  "<div>" +
+                    "<input type=\"submit\"/>" +
+                  "</div>" +
+                  "<p>" +
+                    "Second" +
+                  "</p>" +
+                "</div>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<input type=\"text\">");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<input type=\"text\">");
+
+        // go to the fourth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<button type=\"button\">Click Me!</button>");
+
+        // go to the fifth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<input type=\"submit\">");
+
+        // go to the sixth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Second");
+
+        // go to past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the sixth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("Second");
+
+        // go to the fifth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<input type=\"submit\">");
+
+        // go to the fourth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<button type=\"button\">Click Me!</button>");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<input type=\"text\">");
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("First");
+    }
+
+    /**
      * Enable accessibility and the mock accessibility service.
      */
     private void enableAccessibilityAndMockAccessibilityService() {
@@ -887,7 +1442,8 @@
      */
     private void restoreDefaultWebContentKeyBindings() {
         Settings.Secure.putString(getContext().getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS, mDefaultKeyBindings);
+                Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
+                mDefaultKeyBindings);
     }
 
     /**