Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit;
     18 
     19 import android.accessibilityservice.AccessibilityService;
     20 import android.accessibilityservice.AccessibilityServiceInfo;
     21 import android.content.ComponentName;
     22 import android.content.ContentResolver;
     23 import android.content.Intent;
     24 import android.os.Handler;
     25 import android.os.Looper;
     26 import android.os.SystemClock;
     27 import android.provider.Settings;
     28 import android.test.ActivityInstrumentationTestCase2;
     29 import android.test.suitebuilder.annotation.LargeTest;
     30 import android.view.KeyEvent;
     31 import android.view.accessibility.AccessibilityEvent;
     32 import android.view.accessibility.AccessibilityManager;
     33 
     34 /**
     35  * This is a test for the behavior of the {@link AccessibilityInjector}
     36  * which is used by {@link WebView} to provide basic accessibility support
     37  * in case JavaScript is disabled.
     38  * </p>
     39  * Note: This test works against the generated {@link AccessibilityEvent}s
     40  *       to so it also checks if the test for announcing navigation axis and
     41  *       status messages as appropriate.
     42  */
     43 public class AccessibilityInjectorTest
     44     extends ActivityInstrumentationTestCase2<AccessibilityInjectorTestActivity> {
     45 
     46     /** The timeout to wait for the expected selection. */
     47     private static final long TIMEOUT_WAIT_FOR_SELECTION_STRING = 1000;
     48 
     49     /** The timeout to wait for accessibility and the mock service to be enabled. */
     50     private static final long TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE = 1000;
     51 
     52     /** The count of tests to detect when to shut down the service. */
     53     private static final int TEST_CASE_COUNT = 19;
     54 
     55     /** The meta state for pressed left ALT. */
     56     private static final int META_STATE_ALT_LEFT_ON = KeyEvent.META_ALT_ON
     57             | KeyEvent.META_ALT_LEFT_ON;
     58 
     59     /** Prefix for the CSS style span appended by WebKit. */
     60     private static final String APPLE_SPAN_PREFIX = "<span class=\"Apple-style-span\"";
     61 
     62     /** Suffix for the CSS style span appended by WebKit. */
     63     private static final String APPLE_SPAN_SUFFIX = "</span>";
     64 
     65     /** The value for not specified selection string since null is a valid value. */
     66     private static final String SELECTION_STRING_UNKNOWN = "Unknown";
     67 
     68     /** Lock for locking the test. */
     69     private static final Object sTestLock = new Object();
     70 
     71     /** Key bindings used for testing. */
     72     private static final String TEST_KEY_DINDINGS =
     73         "0x13=0x01000100;" +
     74         "0x14=0x01010100;" +
     75         "0x15=0x04000000;" +
     76         "0x16=0x04000000;" +
     77         "0x200000013=0x03020701:0x03010201:0x03000101:0x03030001:0x03040001:0x03050001:0x03060001;" +
     78         "0x200000014=0x03010001:0x03020101:0x03070201:0x03030701:0x03040701:0x03050701:0x03060701;" +
     79         "0x200000015=0x03040301:0x03050401:0x03060501:0x03000601:0x03010601:0x03020601:0x03070601;" +
     80         "0x200000016=0x03050601:0x03040501:0x03030401:0x03020301:0x03070301:0x03010301:0x03000301;";
     81 
     82     /** Handle to the test for use by the mock service. */
     83     private static AccessibilityInjectorTest sInstance;
     84 
     85     /** Flag indicating if the accessibility service is ready to receive events. */
     86     private static boolean sIsAccessibilityServiceReady;
     87 
     88     /** The count of executed tests to detect when to toggle accessibility and the service. */
     89     private static int sExecutedTestCount;
     90 
     91     /** Worker thread with a handler to perform non test thread processing. */
     92     private Worker mWorker;
     93 
     94     /** Handle to the {@link WebView} to load data in. */
     95     private WebView mWebView;
     96 
     97     /** Used for caching the default bindings so they can be restored. */
     98     private static String sDefaultKeyBindings;
     99 
    100     /** The received selection string for assertion checking. */
    101     private static String sReceivedSelectionString = SELECTION_STRING_UNKNOWN;
    102 
    103     public AccessibilityInjectorTest() {
    104         super(AccessibilityInjectorTestActivity.class);
    105     }
    106 
    107     @Override
    108     protected void setUp() throws Exception {
    109         super.setUp();
    110         mWorker = new Worker();
    111         sInstance = this;
    112         if (sExecutedTestCount == 0) {
    113             // until JUnit4 comes to play with @BeforeTest
    114             disableAccessibilityAndMockAccessibilityService();
    115             enableAccessibilityAndMockAccessibilityService();
    116             injectTestWebContentKeyBindings();
    117         }
    118     }
    119 
    120     @Override
    121     protected void tearDown() throws Exception {
    122         if (mWorker != null) {
    123             mWorker.stop();
    124         }
    125         if (sExecutedTestCount == TEST_CASE_COUNT) {
    126             // until JUnit4 comes to play with @AfterTest
    127             disableAccessibilityAndMockAccessibilityService();
    128             restoreDefaultWebContentKeyBindings();
    129         }
    130         super.tearDown();
    131     }
    132 
    133     /**
    134      * Tests navigation by character.
    135      */
    136     @LargeTest
    137     public void testNavigationByCharacter() throws Exception {
    138         // a bit ugly but helps detect beginning and end of all tests so accessibility
    139         // and the mock service are not toggled on every test (expensive)
    140         sExecutedTestCount++;
    141 
    142         String html =
    143             "<html>" +
    144                "<head>" +
    145                "</head>" +
    146                "<body>" +
    147                    "<p>" +
    148                       "a<b>b</b>c" +
    149                    "</p>" +
    150                    "<p>" +
    151                      "d" +
    152                    "<p/>" +
    153                    "e" +
    154                "</body>" +
    155              "</html>";
    156 
    157         WebView webView = loadHTML(html);
    158 
    159         // change navigation axis to word
    160         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
    161         assertSelectionString("1"); // expect the word navigation axis
    162 
    163         // change navigation axis to character
    164         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
    165         assertSelectionString("0"); // expect the character navigation axis
    166 
    167         // go to the first character
    168         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    169         assertSelectionString("a");
    170 
    171         // go to the second character
    172         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    173         assertSelectionString("<b>b</b>");
    174 
    175         // go to the third character
    176         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    177         assertSelectionString("c");
    178 
    179         // go to the fourth character
    180         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    181         assertSelectionString("d");
    182 
    183         // go to the fifth character
    184         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    185         assertSelectionString("e");
    186 
    187         // try to go past the last character
    188         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    189         assertSelectionString(null);
    190 
    191         // go to the fifth character (reverse)
    192         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    193         assertSelectionString("e");
    194 
    195         // go to the fourth character (reverse)
    196         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    197         assertSelectionString("d");
    198 
    199         // go to the third character
    200         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    201         assertSelectionString("c");
    202 
    203         // go to the second character
    204         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    205         assertSelectionString("<b>b</b>");
    206 
    207         // go to the first character
    208         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    209         assertSelectionString("a");
    210 
    211         // try to go before the first character
    212         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    213         assertSelectionString(null);
    214 
    215         // go to the first character
    216         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    217         assertSelectionString("a");
    218 
    219         // go to the second character (reverse again)
    220         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    221         assertSelectionString("<b>b</b>");
    222     }
    223 
    224     /**
    225      * Tests navigation by word.
    226      */
    227     @LargeTest
    228     public void testNavigationByWord() throws Exception {
    229         // a bit ugly but helps detect beginning and end of all tests so accessibility
    230         // and the mock service are not toggled on every test (expensive)
    231         sExecutedTestCount++;
    232 
    233         String html =
    234             "<html>" +
    235                "<head>" +
    236                "</head>" +
    237                "<body>" +
    238                    "<p>" +
    239                       "This is <b>a</b> sentence" +
    240                    "</p>" +
    241                    "<p>" +
    242                      " scattered " +
    243                      "<p/>" +
    244                      " all over " +
    245                    "</p>" +
    246                    "<div>" +
    247                      "<p>the place.</p>" +
    248                    "</div>" +
    249                "</body>" +
    250              "</html>";
    251 
    252         WebView webView = loadHTML(html);
    253 
    254         // change navigation axis to word
    255         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
    256         assertSelectionString("1"); // expect the word navigation axis
    257 
    258         // go to the first word
    259         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    260         assertSelectionString("This");
    261 
    262         // go to the second word
    263         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    264         assertSelectionString("is");
    265 
    266         // go to the third word
    267         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    268         assertSelectionString("<b>a</b>");
    269 
    270         // go to the fourth word
    271         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    272         assertSelectionString("sentence");
    273 
    274         // go to the fifth word
    275         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    276         assertSelectionString("scattered");
    277 
    278         // go to the sixth word
    279         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    280         assertSelectionString("all");
    281 
    282         // go to the seventh word
    283         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    284         assertSelectionString("over");
    285 
    286         // go to the eight word
    287         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    288         assertSelectionString("the");
    289 
    290         // go to the ninth word
    291         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    292         assertSelectionString("place");
    293 
    294         // NOTE: WebKit selection returns the dot as a word
    295         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    296         assertSelectionString(".");
    297 
    298         // try to go past the last word
    299         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    300         assertSelectionString(null);
    301 
    302         // go to the last word (reverse)
    303         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    304         assertSelectionString("place.");
    305 
    306         // go to the eight word
    307         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    308         assertSelectionString("the");
    309 
    310         // go to the seventh word
    311         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    312         assertSelectionString("over");
    313 
    314         // go to the sixth word
    315         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    316         assertSelectionString("all");
    317 
    318         // go to the fifth word
    319         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    320         assertSelectionString("scattered");
    321 
    322         // go to the fourth word
    323         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    324         assertSelectionString("sentence");
    325 
    326         // go to the third word
    327         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    328         assertSelectionString("<b>a</b>");
    329 
    330         // go to the second word
    331         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    332         assertSelectionString("is");
    333 
    334         // go to the first word
    335         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    336         assertSelectionString("This");
    337 
    338         // try to go before the first word
    339         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    340         assertSelectionString(null);
    341 
    342         // go to the first word
    343         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    344         assertSelectionString("This");
    345 
    346         // go to the second word (reverse again)
    347         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    348         assertSelectionString("is");
    349     }
    350 
    351     /**
    352      * Tests navigation by sentence.
    353      */
    354     @LargeTest
    355     public void testNavigationBySentence() throws Exception {
    356         // a bit ugly but helps detect beginning and end of all tests so accessibility
    357         // and the mock service are not toggled on every test (expensive)
    358         sExecutedTestCount++;
    359 
    360         String html =
    361             "<html>" +
    362               "<head>" +
    363               "</head>" +
    364               "<body>" +
    365                 "<div>" +
    366                   "<p>" +
    367                     "This is the first sentence of the first paragraph and has an <b>inline bold tag</b>." +
    368                     "This is the second sentence of the first paragraph." +
    369                   "</p>" +
    370                   "<h1>This is a heading</h1>" +
    371                   "<p>" +
    372                     "This is the first sentence of the second paragraph." +
    373                     "This is the second sentence of the second paragraph." +
    374                   "</p>" +
    375                 "</div>" +
    376               "</body>" +
    377             "</html>";
    378 
    379         WebView webView = loadHTML(html);
    380 
    381         // Sentence axis is the default
    382 
    383         // go to the first sentence
    384         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    385         assertSelectionString("This is the first sentence of the first paragraph and has an "
    386                 + "<b>inline bold tag</b>.");
    387 
    388         // go to the second sentence
    389         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    390         assertSelectionString("This is the second sentence of the first paragraph.");
    391 
    392         // go to the third sentence
    393         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    394         assertSelectionString("This is a heading");
    395 
    396         // go to the fourth sentence
    397         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    398         assertSelectionString("This is the first sentence of the second paragraph.");
    399 
    400         // go to the fifth sentence
    401         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    402         assertSelectionString("This is the second sentence of the second paragraph.");
    403 
    404         // try to go past the last sentence
    405         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    406         assertSelectionString(null);
    407 
    408         // go to the fifth sentence
    409         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    410         assertSelectionString("This is the second sentence of the second paragraph.");
    411 
    412         // go to the fourth sentence (reverse)
    413         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    414         assertSelectionString("This is the first sentence of the second paragraph.");
    415 
    416         // go to the third sentence
    417         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    418         assertSelectionString("This is a heading");
    419 
    420         // go to the second sentence
    421         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    422         assertSelectionString("This is the second sentence of the first paragraph.");
    423 
    424         // go to the first sentence
    425         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    426         assertSelectionString("This is the first sentence of the first paragraph and has an "
    427                 + "<b>inline bold tag</b>.");
    428 
    429         // try to go before the first sentence
    430         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    431         assertSelectionString(null);
    432 
    433         // go to the first sentence
    434         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    435         assertSelectionString("This is the first sentence of the first paragraph and has an "
    436                 + "<b>inline bold tag</b>.");
    437 
    438         // go to the second sentence (reverse again)
    439         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    440         assertSelectionString("This is the second sentence of the first paragraph.");
    441     }
    442 
    443     /**
    444      * Tests navigation by heading.
    445      */
    446     @LargeTest
    447     public void testNavigationByHeading() throws Exception {
    448         // a bit ugly but helps detect beginning and end of all tests so accessibility
    449         // and the mock service are not toggled on every test (expensive)
    450         sExecutedTestCount++;
    451 
    452         String html =
    453             "<!DOCTYPE html>" +
    454             "<html>" +
    455               "<head>" +
    456               "</head>" +
    457               "<body>" +
    458                 "<h1>Heading one</h1>" +
    459                 "<p>" +
    460                   "This is some text" +
    461                 "</p>" +
    462                 "<h2>Heading two</h2>" +
    463                 "<p>" +
    464                   "This is some text" +
    465                 "</p>" +
    466                 "<h3>Heading three</h3>" +
    467                 "<p>" +
    468                   "This is some text" +
    469                 "</p>" +
    470                 "<h4>Heading four</h4>" +
    471                 "<p>" +
    472                   "This is some text" +
    473                 "</p>" +
    474                 "<h5>Heading five</h5>" +
    475                 "<p>" +
    476                   "This is some text" +
    477                 "</p>" +
    478                 "<h6>Heading six</h6>" +
    479               "</body>" +
    480             "</html>";
    481 
    482         WebView webView = loadHTML(html);
    483 
    484         // change navigation axis to heading
    485         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
    486         assertSelectionString("3"); // expect the heading navigation axis
    487 
    488         // go to the first heading
    489         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    490         assertSelectionString("<h1>Heading one</h1>");
    491 
    492         // go to the second heading
    493         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    494         assertSelectionString("<h2>Heading two</h2>");
    495 
    496         // go to the third heading
    497         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    498         assertSelectionString("<h3>Heading three</h3>");
    499 
    500         // go to the fourth heading
    501         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    502         assertSelectionString("<h4>Heading four</h4>");
    503 
    504         // go to the fifth heading
    505         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    506         assertSelectionString("<h5>Heading five</h5>");
    507 
    508         // go to the sixth heading
    509         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    510         assertSelectionString("<h6>Heading six</h6>");
    511 
    512         // try to go past the last heading
    513         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    514         assertSelectionString(null);
    515 
    516         // go to the fifth heading (reverse)
    517         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    518         assertSelectionString("<h5>Heading five</h5>");
    519 
    520         // go to the fourth heading
    521         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    522         assertSelectionString("<h4>Heading four</h4>");
    523 
    524         // go to the third heading
    525         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    526         assertSelectionString("<h3>Heading three</h3>");
    527 
    528         // go to the second heading
    529         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    530         assertSelectionString("<h2>Heading two</h2>");
    531 
    532         // go to the first heading
    533         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    534         assertSelectionString("<h1>Heading one</h1>");
    535 
    536         // try to go before the first heading
    537         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    538         assertSelectionString(null);
    539 
    540         // go to the second heading (reverse again)
    541         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    542         assertSelectionString("<h2>Heading two</h2>");
    543     }
    544 
    545     /**
    546      * Tests navigation by sibling.
    547      */
    548     @LargeTest
    549     public void testNavigationBySibing() throws Exception {
    550         // a bit ugly but helps detect beginning and end of all tests so accessibility
    551         // and the mock service are not toggled on every test (expensive)
    552         sExecutedTestCount++;
    553 
    554         String html =
    555             "<!DOCTYPE html>" +
    556             "<html>" +
    557               "<head>" +
    558               "</head>" +
    559               "<body>" +
    560                 "<h1>Heading one</h1>" +
    561                 "<p>" +
    562                   "This is some text" +
    563                 "</p>" +
    564                 "<div>" +
    565                   "<button>Input</button>" +
    566                 "</div>" +
    567               "</body>" +
    568             "</html>";
    569 
    570         WebView webView = loadHTML(html);
    571 
    572         // change navigation axis to heading
    573         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
    574         assertSelectionString("3"); // expect the heading navigation axis
    575 
    576         // change navigation axis to sibling
    577         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
    578         assertSelectionString("4"); // expect the sibling navigation axis
    579 
    580         // change navigation axis to parent/first child
    581         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
    582         assertSelectionString("5"); // expect the parent/first child navigation axis
    583 
    584         // go to the first child of the body
    585         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    586         assertSelectionString("<h1>Heading one</h1>");
    587 
    588         // change navigation axis to sibling
    589         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON);
    590         assertSelectionString("4"); // expect the sibling navigation axis
    591 
    592         // go to the next sibling
    593         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    594         assertSelectionString("<p>This is some text</p>");
    595 
    596         // go to the next sibling
    597         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    598         assertSelectionString("<div><button>Input</button></div>");
    599 
    600         // try to go past the last sibling
    601         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    602         assertSelectionString(null);
    603 
    604         // go to the previous sibling (reverse)
    605         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    606         assertSelectionString("<p>This is some text</p>");
    607 
    608         // go to the previous sibling
    609         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    610         assertSelectionString("<h1>Heading one</h1>");
    611 
    612         // try to go before the previous sibling
    613         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    614         assertSelectionString(null);
    615 
    616         // go to the next sibling (reverse again)
    617         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    618         assertSelectionString("<p>This is some text</p>");
    619     }
    620 
    621     /**
    622      * Tests navigation by parent/first child.
    623      */
    624     @LargeTest
    625     public void testNavigationByParentFirstChild() throws Exception {
    626         // a bit ugly but helps detect beginning and end of all tests so accessibility
    627         // and the mock service are not toggled on every test (expensive)
    628         sExecutedTestCount++;
    629 
    630         String html =
    631             "<!DOCTYPE html>" +
    632             "<html>" +
    633               "<head>" +
    634               "</head>" +
    635               "<body>" +
    636                 "<div>" +
    637                   "<button>Input</button>" +
    638                 "</div>" +
    639               "</body>" +
    640             "</html>";
    641 
    642         WebView webView = loadHTML(html);
    643 
    644         // change navigation axis to document
    645         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON);
    646         assertSelectionString("6"); // expect the document navigation axis
    647 
    648         // change navigation axis to parent/first child
    649         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON);
    650         assertSelectionString("5"); // expect the parent/first child navigation axis
    651 
    652         // go to the first child
    653         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    654         assertSelectionString("<div><button>Input</button></div>");
    655 
    656         // go to the first child
    657         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    658         assertSelectionString("<button>Input</button>");
    659 
    660         // try to go to the first child of a leaf element
    661         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    662         assertSelectionString(null);
    663 
    664         // go to the parent (reverse)
    665         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    666         assertSelectionString("<div><button>Input</button></div>");
    667 
    668         // go to the parent
    669         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    670         assertSelectionString("<body><div><button>Input</button></div></body>");
    671 
    672         // try to go to the body parent
    673         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    674         assertSelectionString(null);
    675 
    676         // go to the first child (reverse again)
    677         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    678         assertSelectionString("<div><button>Input</button></div>");
    679     }
    680 
    681     /**
    682      * Tests navigation by document.
    683      */
    684     @LargeTest
    685     public void testNavigationByDocument() throws Exception {
    686         // a bit ugly but helps detect beginning and end of all tests so accessibility
    687         // and the mock service are not toggled on every test (expensive)
    688         sExecutedTestCount++;
    689 
    690         String html =
    691             "<!DOCTYPE html>" +
    692             "<html>" +
    693               "<head>" +
    694               "</head>" +
    695               "<body>" +
    696                 "<button>Click</button>" +
    697               "</body>" +
    698             "</html>";
    699 
    700         WebView webView = loadHTML(html);
    701 
    702         // change navigation axis to document
    703         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON);
    704         assertSelectionString("6"); // expect the document navigation axis
    705 
    706         // go to the bottom of the document
    707         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    708         assertSelectionString("Click");
    709 
    710         // go to the top of the document (reverse)
    711         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    712         assertSelectionString("<body><button>Click</button></body>");
    713 
    714         // go to the bottom of the document (reverse again)
    715         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    716         assertSelectionString("Click");
    717     }
    718 
    719     /**
    720      * Tests the sync between the text navigation and navigation by DOM elements.
    721      */
    722     @LargeTest
    723     public void testSyncBetweenTextAndDomNodeNavigation() throws Exception {
    724         // a bit ugly but helps detect beginning and end of all tests so accessibility
    725         // and the mock service are not toggled on every test (expensive)
    726         sExecutedTestCount++;
    727 
    728         String html =
    729             "<!DOCTYPE html>" +
    730             "<html>" +
    731               "<head>" +
    732               "</head>" +
    733               "<body>" +
    734                 "<p>" +
    735                   "First" +
    736                 "</p>" +
    737                 "<button>Second</button>" +
    738                 "<p>" +
    739                   "Third" +
    740                 "</p>" +
    741               "</body>" +
    742             "</html>";
    743 
    744         WebView webView = loadHTML(html);
    745 
    746         // change navigation axis to word
    747         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
    748         assertSelectionString("1"); // expect the word navigation axis
    749 
    750         // go to the first word
    751         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    752         assertSelectionString("First");
    753 
    754         // change navigation axis to heading
    755         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
    756         assertSelectionString("3"); // expect the heading navigation axis
    757 
    758         // change navigation axis to sibling
    759         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
    760         assertSelectionString("4"); // expect the sibling navigation axis
    761 
    762         // go to the next sibling
    763         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    764         assertSelectionString("<button>Second</button>");
    765 
    766         // change navigation axis to character
    767         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, META_STATE_ALT_LEFT_ON);
    768         assertSelectionString("0"); // expect the character navigation axis
    769 
    770         // change navigation axis to word
    771         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, META_STATE_ALT_LEFT_ON);
    772         assertSelectionString("1"); // expect the word navigation axis
    773 
    774         // go to the next word
    775         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    776         assertSelectionString("Third");
    777     }
    778 
    779     /**
    780      * Tests that the selection does not cross anchor boundaries. This is a
    781      * workaround for the asymmetric and inconsistent handling of text with
    782      * links by WebKit while traversing by sentence.
    783      */
    784     @LargeTest
    785     public void testEnforceSelectionDoesNotCrossAnchorBoundary1() throws Exception {
    786         // a bit ugly but helps detect beginning and end of all tests so accessibility
    787         // and the mock service are not toggled on every test (expensive)
    788         sExecutedTestCount++;
    789 
    790         String html =
    791             "<!DOCTYPE html>" +
    792             "<html>" +
    793               "<head>" +
    794               "</head>" +
    795               "<body>" +
    796                 "<div>First</div>" +
    797                 "<p>" +
    798                   "<a href=\"\">Second</a> Third" +
    799                 "</p>" +
    800               "</body>" +
    801             "</html>";
    802 
    803         WebView webView = loadHTML(html);
    804 
    805         // go to the first sentence
    806         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    807         assertSelectionString("<div>First</div>");
    808 
    809         // go to the second sentence
    810         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    811         assertSelectionString("<a href=\"\">Second</a>");
    812 
    813         // go to the third sentence
    814         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    815         assertSelectionString("Third");
    816 
    817         // go to past the last sentence
    818         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    819         assertSelectionString(null);
    820 
    821         // go to the third sentence
    822         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    823         assertSelectionString("Third");
    824 
    825         // go to the second sentence
    826         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    827         assertSelectionString("<a href=\"\">Second</a>");
    828 
    829         // go to the first sentence
    830         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    831         assertSelectionString("First");
    832 
    833         // go to before the first sentence
    834         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    835         assertSelectionString(null);
    836 
    837         // go to the first sentence
    838         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    839         assertSelectionString("<div>First</div>");
    840     }
    841 
    842     /**
    843      * Tests that the selection does not cross anchor boundaries. This is a
    844      * workaround for the asymmetric and inconsistent handling of text with
    845      * links by WebKit while traversing by sentence.
    846      */
    847     @LargeTest
    848     public void testEnforceSelectionDoesNotCrossAnchorBoundary2() throws Exception {
    849         // a bit ugly but helps detect beginning and end of all tests so accessibility
    850         // and the mock service are not toggled on every test (expensive)
    851         sExecutedTestCount++;
    852 
    853         String html =
    854             "<!DOCTYPE html>" +
    855             "<html>" +
    856               "<head>" +
    857               "</head>" +
    858               "<body>" +
    859                 "<div>First</div>" +
    860                 "<a href=\"#\">Second</a>" +
    861                 "&nbsp;" +
    862                 "<a href=\"#\">Third</a>" +
    863               "</body>" +
    864             "</html>";
    865 
    866         WebView webView = loadHTML(html);
    867 
    868         // go to the first sentence
    869         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    870         assertSelectionString("First");
    871 
    872         // go to the second sentence
    873         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    874         assertSelectionString("<a href=\"#\">Second</a>");
    875 
    876         // go to the third sentence
    877         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    878         assertSelectionString("&nbsp;");
    879 
    880         // go to the fourth sentence
    881         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    882         assertSelectionString("<a href=\"#\">Third</a>");
    883 
    884         // go to past the last sentence
    885         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    886         assertSelectionString(null);
    887 
    888         // go to the fourth sentence
    889         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    890         assertSelectionString("<a href=\"#\">Third</a>");
    891 
    892         // go to the third sentence
    893         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    894         assertSelectionString("&nbsp;");
    895 
    896         // go to the second sentence
    897         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    898         assertSelectionString("<a href=\"#\">Second</a>");
    899 
    900         // go to the first sentence
    901         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    902         assertSelectionString("First");
    903 
    904         // go to before the first sentence
    905         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    906         assertSelectionString(null);
    907 
    908         // go to the first sentence
    909         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    910         assertSelectionString("First");
    911     }
    912 
    913     /**
    914      * Tests that the selection does not cross anchor boundaries. This is a
    915      * workaround for the asymmetric and inconsistent handling of text with
    916      * links by WebKit while traversing by sentence.
    917      */
    918     @LargeTest
    919     public void testEnforceSelectionDoesNotCrossAnchorBoundary3() throws Exception {
    920         // a bit ugly but helps detect beginning and end of all tests so accessibility
    921         // and the mock service are not toggled on every test (expensive)
    922         sExecutedTestCount++;
    923 
    924         String html =
    925             "<!DOCTYPE html>" +
    926             "<html>" +
    927               "<head>" +
    928               "</head>" +
    929               "<body>" +
    930                 "<div>" +
    931                   "First" +
    932                 "<div>" +
    933                 "<div>" +
    934                   "<a href=\"#\">Second</a>" +
    935                 "</div>" +
    936                 "<div>" +
    937                   "Third" +
    938                 "</div>" +
    939               "</body>" +
    940             "</html>";
    941 
    942         WebView webView = loadHTML(html);
    943 
    944         // go to the first sentence
    945         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    946         assertSelectionString("First");
    947 
    948         // go to the second sentence
    949         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    950         assertSelectionString("<a href=\"#\">Second</a>");
    951 
    952         // go to the third sentence
    953         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    954         assertSelectionString("Third");
    955 
    956         // go to past the last sentence
    957         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    958         assertSelectionString(null);
    959 
    960         // go to the third sentence
    961         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    962         assertSelectionString("Third");
    963 
    964         // go to the second sentence
    965         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    966         assertSelectionString("<a href=\"#\">Second</a>");
    967 
    968         // go to the first sentence
    969         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    970         assertSelectionString("First");
    971 
    972         // go to before the first sentence
    973         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
    974         assertSelectionString(null);
    975 
    976         // go to the first sentence
    977         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
    978         assertSelectionString("First");
    979     }
    980 
    981     /**
    982      * Tests skipping of content with hidden visibility.
    983      */
    984     @LargeTest
    985     public void testSkipVisibilityHidden() throws Exception {
    986         // a bit ugly but helps detect beginning and end of all tests so accessibility
    987         // and the mock service are not toggled on every test (expensive)
    988         sExecutedTestCount++;
    989 
    990         String html =
    991             "<!DOCTYPE html>" +
    992             "<html>" +
    993               "<head>" +
    994               "</head>" +
    995               "<body>" +
    996                 "<div>First </div>" +
    997                 "<div style=\"visibility:hidden;\">Second</div>" +
    998                 "<div> Third</div>" +
    999               "</body>" +
   1000             "</html>";
   1001 
   1002         WebView webView = loadHTML(html);
   1003 
   1004         // change navigation axis to word
   1005         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
   1006         assertSelectionString("1"); // expect the word navigation axis
   1007 
   1008         // go to the first word
   1009         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1010         assertSelectionString("First");
   1011 
   1012         // go to the third word (the second is invisible)
   1013         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1014         assertSelectionString("Third");
   1015 
   1016         // go to past the last sentence
   1017         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1018         assertSelectionString(null);
   1019 
   1020         // go to the third word (the second is invisible)
   1021         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1022         assertSelectionString("Third");
   1023 
   1024         // go to the first word
   1025         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1026         assertSelectionString("First");
   1027 
   1028         // go to before the first word
   1029         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1030         assertSelectionString(null);
   1031 
   1032         // go to the first word
   1033         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1034         assertSelectionString("First");
   1035     }
   1036 
   1037     /**
   1038      * Tests skipping of content with display none.
   1039      */
   1040     @LargeTest
   1041     public void testSkipDisplayNone() throws Exception {
   1042         // a bit ugly but helps detect beginning and end of all tests so accessibility
   1043         // and the mock service are not toggled on every test (expensive)
   1044         sExecutedTestCount++;
   1045 
   1046         String html =
   1047             "<!DOCTYPE html>" +
   1048             "<html>" +
   1049               "<head>" +
   1050               "</head>" +
   1051               "<body>" +
   1052                 "<div>First</div>" +
   1053                 "<div style=\"display: none;\">Second</div>" +
   1054                 "<div>Third</div>" +
   1055               "</body>" +
   1056             "</html>";
   1057 
   1058         WebView webView = loadHTML(html);
   1059 
   1060         // change navigation axis to word
   1061         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
   1062         assertSelectionString("1"); // expect the word navigation axis
   1063 
   1064         // go to the first word
   1065         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1066         assertSelectionString("First");
   1067 
   1068         // go to the third word (the second is invisible)
   1069         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1070         assertSelectionString("Third");
   1071 
   1072         // go to past the last sentence
   1073         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1074         assertSelectionString(null);
   1075 
   1076         // go to the third word (the second is invisible)
   1077         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1078         assertSelectionString("Third");
   1079 
   1080         // go to the first word
   1081         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1082         assertSelectionString("First");
   1083 
   1084         // go to before the first word
   1085         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1086         assertSelectionString(null);
   1087 
   1088         // go to the first word
   1089         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1090         assertSelectionString("First");
   1091     }
   1092 
   1093     /**
   1094      * Tests for the selection not getting stuck.
   1095      *
   1096      * Note: The selection always proceeds but if it can
   1097      * be selecting the same content i.e. between the start
   1098      * and end are contained the same text nodes.
   1099      */
   1100     @LargeTest
   1101     public void testSelectionTextProceed() throws Exception {
   1102         // a bit ugly but helps detect beginning and end of all tests so accessibility
   1103         // and the mock service are not toggled on every test (expensive)
   1104         sExecutedTestCount++;
   1105 
   1106         String html =
   1107             "<!DOCTYPE html>" +
   1108             "<html>" +
   1109               "<head>" +
   1110               "</head>" +
   1111               "<body>" +
   1112                 "<a href=\"#\">First</a>" +
   1113                 "<span><a href=\"#\"><span>Second</span>&nbsp;<small>a</small></a>" +
   1114                 "</span>&nbsp;<a href=\"#\">Third</a>" +
   1115               "</body>" +
   1116             "</html>";
   1117 
   1118         WebView webView = loadHTML(html);
   1119 
   1120         // go to the first sentence
   1121         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1122         assertSelectionString("<a href=\"#\">First</a>");
   1123 
   1124         // go to the second sentence
   1125         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1126         assertSelectionString("<a href=\"#\"><span>Second&nbsp;<small>a</small></a>");
   1127 
   1128         // go to the third sentence
   1129         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1130         assertSelectionString("&nbsp;");
   1131 
   1132         // go to the fourth sentence
   1133         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1134         assertSelectionString("<a href=\"#\">Third</a>");
   1135 
   1136         // go to past the last sentence
   1137         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1138         assertSelectionString(null);
   1139 
   1140         // go to the third sentence
   1141         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1142         assertSelectionString("<a href=\"#\">Third</a>");
   1143 
   1144         // NOTE: Here we are a bit asymmetric around whitespace but we can live with it
   1145         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1146         assertSelectionString("&nbsp;");
   1147 
   1148         // go to the second sentence
   1149         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1150         assertSelectionString("<a href=\"#\"><span>Second&nbsp;<small>a</small></a>");
   1151 
   1152         // go to the first sentence
   1153         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1154         assertSelectionString("<a href=\"#\">First</a>");
   1155 
   1156         // go to before the first sentence
   1157         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1158         assertSelectionString(null);
   1159 
   1160         // go to the first sentence
   1161         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1162         assertSelectionString("<a href=\"#\">First</a>");
   1163     }
   1164 
   1165     /**
   1166      * Tests if input elements are selected rather skipped.
   1167      */
   1168     @LargeTest
   1169     public void testSelectionOfInputElements() throws Exception {
   1170         // a bit ugly but helps detect beginning and end of all tests so accessibility
   1171         // and the mock service are not toggled on every test (expensive)
   1172         sExecutedTestCount++;
   1173 
   1174         String html =
   1175             "<!DOCTYPE html>" +
   1176             "<html>" +
   1177               "<head>" +
   1178               "</head>" +
   1179               "<body>" +
   1180                 "<p>" +
   1181                   "First" +
   1182                 "</p>" +
   1183                 "<input type=\"text\"/>" +
   1184                 "<p>" +
   1185                   "Second" +
   1186                 "</p>" +
   1187               "</body>" +
   1188             "</html>";
   1189 
   1190         WebView webView = loadHTML(html);
   1191 
   1192         // go to the first sentence
   1193         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1194         assertSelectionString("First");
   1195 
   1196         // go to the second sentence
   1197         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1198         assertSelectionString("<input type=\"text\">");
   1199 
   1200         // go to the third sentence
   1201         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1202         assertSelectionString("Second");
   1203 
   1204         // go to past the last sentence
   1205         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1206         assertSelectionString(null);
   1207 
   1208         // go to the third sentence
   1209         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1210         assertSelectionString("Second");
   1211 
   1212         // go to the second sentence
   1213         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1214         assertSelectionString("<input type=\"text\">");
   1215 
   1216         // go to the first sentence
   1217         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1218         assertSelectionString("First");
   1219 
   1220         // go to before the first sentence
   1221         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1222         assertSelectionString(null);
   1223 
   1224         // go to the first sentence
   1225         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1226         assertSelectionString("First");
   1227     }
   1228 
   1229     /**
   1230      * Tests traversing of input controls.
   1231      */
   1232     @LargeTest
   1233     public void testSelectionOfInputElements2() throws Exception {
   1234         // a bit ugly but helps detect beginning and end of all tests so accessibility
   1235         // and the mock service are not toggled on every test (expensive)
   1236         sExecutedTestCount++;
   1237 
   1238         String html =
   1239             "<!DOCTYPE html>" +
   1240             "<html>" +
   1241               "<head>" +
   1242               "</head>" +
   1243               "<body>" +
   1244                 "<div>" +
   1245                   "First" +
   1246                   "<input type=\"text\"/>" +
   1247                   "<span>" +
   1248                     "<input type=\"text\"/>" +
   1249                   "</span>" +
   1250                   "<button type=\"button\">Click Me!</button>" +
   1251                   "<div>" +
   1252                     "<input type=\"submit\"/>" +
   1253                   "</div>" +
   1254                   "<p>" +
   1255                     "Second" +
   1256                   "</p>" +
   1257                 "</div>" +
   1258               "</body>" +
   1259             "</html>";
   1260 
   1261         WebView webView = loadHTML(html);
   1262 
   1263         // go to the first sentence
   1264         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1265         assertSelectionString("First");
   1266 
   1267         // go to the second sentence
   1268         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1269         assertSelectionString("<input type=\"text\">");
   1270 
   1271         // go to the third sentence
   1272         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1273         assertSelectionString("<input type=\"text\">");
   1274 
   1275         // go to the fourth sentence
   1276         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1277         assertSelectionString("<button type=\"button\">Click Me!</button>");
   1278 
   1279         // go to the fifth sentence
   1280         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1281         assertSelectionString("<input type=\"submit\">");
   1282 
   1283         // go to the sixth sentence
   1284         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1285         assertSelectionString("Second");
   1286 
   1287         // go to past the last sentence
   1288         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1289         assertSelectionString(null);
   1290 
   1291         // go to the sixth sentence
   1292         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1293         assertSelectionString("Second");
   1294 
   1295         // go to the fifth sentence
   1296         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1297         assertSelectionString("<input type=\"submit\">");
   1298 
   1299         // go to the fourth sentence
   1300         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1301         assertSelectionString("<button type=\"button\">Click Me!</button>");
   1302 
   1303         // go to the third sentence
   1304         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1305         assertSelectionString("<input type=\"text\">");
   1306 
   1307         // go to the second sentence
   1308         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1309         assertSelectionString("<input type=\"text\">");
   1310 
   1311         // go to the first sentence
   1312         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1313         assertSelectionString("First");
   1314     }
   1315 
   1316     /**
   1317      * Tests traversing of input controls.
   1318      */
   1319     @LargeTest
   1320     public void testSelectionOfInputElements3() throws Exception {
   1321         // a bit ugly but helps detect beginning and end of all tests so accessibility
   1322         // and the mock service are not toggled on every test (expensive)
   1323         sExecutedTestCount++;
   1324 
   1325         String html =
   1326             "<!DOCTYPE html>" +
   1327             "<html>" +
   1328               "<head>" +
   1329               "</head>" +
   1330               "<body>" +
   1331                 "<input type=\"text\"/>" +
   1332                 "<button type=\"button\">Click Me!</button>" +
   1333                 "<select>" +
   1334                   "<option value=\"volvo\">Volvo</option>" +
   1335                   "<option value=\"saab\">Saab</option>" +
   1336                 "</select>" +
   1337               "</body>" +
   1338             "</html>";
   1339 
   1340         WebView webView = loadHTML(html);
   1341 
   1342         // go to the first sentence
   1343         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1344         assertSelectionString("<input type=\"text\">");
   1345 
   1346         // go to the second sentence
   1347         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1348         assertSelectionString("<button type=\"button\">Click Me!</button>");
   1349 
   1350         // go to the third sentence
   1351         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1352         assertSelectionString("<select><option value=\"volvo\">Volvo</option>" +
   1353                 "<option value=\"saab\">Saab</option></select>");
   1354 
   1355         // go to past the last sentence
   1356         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1357         assertSelectionString(null);
   1358 
   1359         // go to the third sentence
   1360         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1361         assertSelectionString("<select><option value=\"volvo\">Volvo</option>" +
   1362                 "<option value=\"saab\">Saab</option></select>");
   1363 
   1364         // go to the second sentence
   1365         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1366         assertSelectionString("<button type=\"button\">Click Me!</button>");
   1367 
   1368         // go to the first sentence
   1369         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1370         assertSelectionString("<input type=\"text\">");
   1371 
   1372         // go to before the first sentence
   1373         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1374         assertSelectionString(null);
   1375 
   1376         // go to the first sentence
   1377         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1378         assertSelectionString("<input type=\"text\">");
   1379     }
   1380 
   1381     /**
   1382      * Tests traversing of input controls.
   1383      */
   1384     @LargeTest
   1385     public void testSelectionOfInputElements4() throws Exception {
   1386         // a bit ugly but helps detect beginning and end of all tests so accessibility
   1387         // and the mock service are not toggled on every test (expensive)
   1388         sExecutedTestCount++;
   1389 
   1390         String html =
   1391             "<!DOCTYPE html>" +
   1392             "<html>" +
   1393               "<head>" +
   1394               "</head>" +
   1395               "<body>" +
   1396                 "Start" +
   1397                 "<span>" +
   1398                   "<span>" +
   1399                     "<input type=\"submit\">" +
   1400                   "</span>" +
   1401                 "</span>" +
   1402                 "<input type=\"text\" size=\"30\">" +
   1403                 "<span>" +
   1404                   "<span>" +
   1405                     "<input type=\"submit\" size=\"30\">" +
   1406                   "</span>" +
   1407                 "</span>" +
   1408                 "End" +
   1409               "</body>" +
   1410             "</html>";
   1411 
   1412         WebView webView = loadHTML(html);
   1413 
   1414         // go to the first sentence
   1415         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1416         assertSelectionString("Start");
   1417 
   1418         // go to the second sentence
   1419         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1420         assertSelectionString("<input type=\"submit\">");
   1421 
   1422         // go to the third sentence
   1423         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1424         assertSelectionString("<input type=\"text\" size=\"30\">");
   1425 
   1426         // go to the fourth sentence
   1427         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1428         assertSelectionString("<input type=\"submit\" size=\"30\">");
   1429 
   1430         // go to the fifth sentence
   1431         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1432         assertSelectionString("End");
   1433 
   1434         // go to past the last sentence
   1435         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1436         assertSelectionString(null);
   1437 
   1438         // go to the fifth sentence
   1439         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1440         assertSelectionString("End");
   1441 
   1442         // go to the fourth sentence
   1443         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1444         assertSelectionString("<input type=\"submit\" size=\"30\">");
   1445 
   1446         // go to the third sentence
   1447         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1448         assertSelectionString("<input type=\"text\" size=\"30\">");
   1449 
   1450         // go to the second sentence
   1451         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1452         assertSelectionString("<input type=\"submit\">");
   1453 
   1454         // go to the first sentence
   1455         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1456         assertSelectionString("Start");
   1457 
   1458         // go to before the first sentence
   1459         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1460         assertSelectionString(null);
   1461 
   1462         // go to the first sentence
   1463         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1464         assertSelectionString("Start");
   1465     }
   1466 
   1467     /**
   1468      * Tests traversing of input controls.
   1469      */
   1470     @LargeTest
   1471     public void testSelectionOfInputElements5() throws Exception {
   1472         // a bit ugly but helps detect beginning and end of all tests so accessibility
   1473         // and the mock service are not toggled on every test (expensive)
   1474         sExecutedTestCount++;
   1475 
   1476         String html =
   1477             "<!DOCTYPE html>" +
   1478             "<html>" +
   1479               "<head>" +
   1480               "</head>" +
   1481               "<body>" +
   1482                 "<div>" +
   1483                   "First" +
   1484                   "<input type=\"hidden\">" +
   1485                   "<input type=\"hidden\">" +
   1486                   "<input type=\"hidden\">" +
   1487                   "<input type=\"hidden\">" +
   1488                   "<input type=\"text\">" +
   1489                   "<span>" +
   1490                     "<span>" +
   1491                       "<input type=\"submit\">" +
   1492                     "</span>" +
   1493                   "</span>" +
   1494                 "</div>" +
   1495               "</body>" +
   1496               "Second" +
   1497             "</html>";
   1498 
   1499         WebView webView = loadHTML(html);
   1500 
   1501         // go to the first sentence
   1502         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1503         assertSelectionString("First");
   1504 
   1505         // go to the second sentence
   1506         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1507         assertSelectionString("<input type=\"text\">");
   1508 
   1509         // go to the third sentence
   1510         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1511         assertSelectionString("<input type=\"submit\">");
   1512 
   1513         // go to the fourth sentence
   1514         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1515         assertSelectionString("Second");
   1516 
   1517         // go to past the last sentence
   1518         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1519         assertSelectionString(null);
   1520 
   1521         // go to the fourth sentence
   1522         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1523         assertSelectionString("Second");
   1524 
   1525         // go to the third sentence
   1526         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1527         assertSelectionString("<input type=\"submit\">");
   1528 
   1529         // go to the second sentence
   1530         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1531         assertSelectionString("<input type=\"text\">");
   1532 
   1533         // go to the first sentence
   1534         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1535         assertSelectionString("First");
   1536 
   1537         // go to before the first sentence
   1538         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
   1539         assertSelectionString(null);
   1540 
   1541         // go to the first sentence
   1542         sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
   1543         assertSelectionString("First");
   1544     }
   1545 
   1546     /**
   1547      * Enable accessibility and the mock accessibility service.
   1548      */
   1549     private void enableAccessibilityAndMockAccessibilityService() {
   1550         // make sure the manager is instantiated so the system initializes it
   1551         AccessibilityManager.getInstance(getActivity());
   1552 
   1553         // enable accessibility and the mock accessibility service
   1554         Settings.Secure.putInt(getActivity().getContentResolver(),
   1555                 Settings.Secure.ACCESSIBILITY_ENABLED, 1);
   1556         String enabledServices = new ComponentName(getActivity().getPackageName(),
   1557                 MockAccessibilityService.class.getName()).flattenToShortString();
   1558         Settings.Secure.putString(getActivity().getContentResolver(),
   1559                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices);
   1560 
   1561         // poll within a timeout and let be interrupted in case of success
   1562         long incrementStep = TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE / 5;
   1563         long start = SystemClock.uptimeMillis();
   1564         while (SystemClock.uptimeMillis() - start < TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE &&
   1565                 !sIsAccessibilityServiceReady) {
   1566             synchronized (sTestLock) {
   1567                 try {
   1568                     sTestLock.wait(incrementStep);
   1569                 } catch (InterruptedException ie) {
   1570                     /* ignore */
   1571                 }
   1572             }
   1573         }
   1574 
   1575         if (!sIsAccessibilityServiceReady) {
   1576             throw new IllegalStateException("MockAccessibilityService not ready. Did you add " +
   1577                     "tests and forgot to update AccessibilityInjectorTest#TEST_CASE_COUNT?");
   1578         }
   1579     }
   1580 
   1581     @Override
   1582     protected void scrubClass(Class<?> testCaseClass) {
   1583         /* do nothing - avoid superclass behavior */
   1584     }
   1585 
   1586     /**
   1587      * Strips the apple span appended by WebKit while generating
   1588      * the selection markup.
   1589      *
   1590      * @param markup The markup.
   1591      * @return Stripped from apple spans markup.
   1592      */
   1593     private static String stripAppleSpanFromMarkup(String markup) {
   1594         StringBuilder stripped = new StringBuilder(markup);
   1595         int prefixBegIdx = stripped.indexOf(APPLE_SPAN_PREFIX);
   1596         while (prefixBegIdx >= 0) {
   1597             int prefixEndIdx = stripped.indexOf(">", prefixBegIdx) + 1;
   1598             stripped.replace(prefixBegIdx, prefixEndIdx, "");
   1599             int suffixBegIdx = stripped.lastIndexOf(APPLE_SPAN_SUFFIX);
   1600             int suffixEndIdx = suffixBegIdx + APPLE_SPAN_SUFFIX.length();
   1601             stripped.replace(suffixBegIdx, suffixEndIdx, "");
   1602             prefixBegIdx = stripped.indexOf(APPLE_SPAN_PREFIX);
   1603         }
   1604         return stripped.toString();
   1605     }
   1606 
   1607     /**
   1608      * Disables accessibility and the mock accessibility service.
   1609      */
   1610     private void disableAccessibilityAndMockAccessibilityService() {
   1611         // disable accessibility and the mock accessibility service
   1612         Settings.Secure.putInt(getActivity().getContentResolver(),
   1613                 Settings.Secure.ACCESSIBILITY_ENABLED, 0);
   1614         Settings.Secure.putString(getActivity().getContentResolver(),
   1615                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
   1616     }
   1617 
   1618     /**
   1619      * Asserts the next <code>expectedSelectionString</code> to be received.
   1620      */
   1621     private void assertSelectionString(String expectedSelectionString) {
   1622         assertTrue("MockAccessibilityService not ready", sIsAccessibilityServiceReady);
   1623 
   1624         long incrementStep = TIMEOUT_WAIT_FOR_SELECTION_STRING / 5;
   1625         long start = SystemClock.uptimeMillis();
   1626         while (SystemClock.uptimeMillis() - start < TIMEOUT_WAIT_FOR_SELECTION_STRING &&
   1627                 sReceivedSelectionString == SELECTION_STRING_UNKNOWN) {
   1628             synchronized (sTestLock) {
   1629                 try {
   1630                     sTestLock.wait(incrementStep);
   1631                 } catch (InterruptedException ie) {
   1632                     /* ignore */
   1633                 }
   1634             }
   1635         }
   1636         try {
   1637             if (sReceivedSelectionString == SELECTION_STRING_UNKNOWN) {
   1638                 fail("No selection string received. Expected: " + expectedSelectionString);
   1639             }
   1640             assertEquals(expectedSelectionString, sReceivedSelectionString);
   1641         } finally {
   1642             sReceivedSelectionString = SELECTION_STRING_UNKNOWN;
   1643         }
   1644     }
   1645 
   1646     /**
   1647      * Sends a {@link KeyEvent} (up and down) to the {@link WebView}.
   1648      *
   1649      * @param keyCode The event key code.
   1650      */
   1651     private void sendKeyEvent(WebView webView, int keyCode, int metaState) {
   1652         webView.onKeyDown(keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 1, metaState));
   1653         webView.onKeyUp(keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 1, metaState));
   1654     }
   1655 
   1656     /**
   1657      * Loads HTML content in a {@link WebView}.
   1658      *
   1659      * @param html The HTML content;
   1660      * @return The {@link WebView} view.
   1661      */
   1662     private WebView loadHTML(final String html) {
   1663         mWorker.getHandler().post(new Runnable() {
   1664             public void run() {
   1665                 if (mWebView == null) {
   1666                     mWebView = getActivity().getWebView();
   1667                     mWebView.setWebViewClient(new WebViewClient() {
   1668                         @Override
   1669                         public void onPageFinished(WebView view, String url) {
   1670                             mWorker.getHandler().post(new Runnable() {
   1671                                 public void run() {
   1672                                     synchronized (sTestLock) {
   1673                                         sTestLock.notifyAll();
   1674                                     }
   1675                                 }
   1676                             });
   1677                         }
   1678                     });
   1679                 }
   1680                 mWebView.loadData(html, "text/html", null);
   1681             }
   1682         });
   1683         synchronized (sTestLock) {
   1684             try {
   1685                 sTestLock.wait();
   1686             } catch (InterruptedException ie) {
   1687                 /* ignore */
   1688             }
   1689         }
   1690         return mWebView;
   1691     }
   1692 
   1693     /**
   1694      * Injects web content key bindings used for testing. This is required
   1695      * to ensure that this test will be agnostic to changes of the bindings.
   1696      */
   1697     private void injectTestWebContentKeyBindings() {
   1698         ContentResolver contentResolver = getActivity().getContentResolver();
   1699         sDefaultKeyBindings = Settings.Secure.getString(contentResolver,
   1700                 Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS);
   1701         Settings.Secure.putString(contentResolver,
   1702                 Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS, TEST_KEY_DINDINGS);
   1703     }
   1704 
   1705     /**
   1706      * Restores the default web content key bindings.
   1707      */
   1708     private void restoreDefaultWebContentKeyBindings() {
   1709         Settings.Secure.putString(getActivity().getContentResolver(),
   1710                 Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
   1711                 sDefaultKeyBindings);
   1712     }
   1713 
   1714     /**
   1715      * This is a worker thread responsible for creating the {@link WebView}.
   1716      */
   1717     private class Worker implements Runnable {
   1718         private final Object mWorkerLock = new Object();
   1719         private Handler mHandler;
   1720 
   1721        public Worker() {
   1722             new Thread(this).start();
   1723             synchronized (mWorkerLock) {
   1724                 while (mHandler == null) {
   1725                     try {
   1726                         mWorkerLock.wait();
   1727                     } catch (InterruptedException ex) {
   1728                         /* ignore */
   1729                     }
   1730                 }
   1731             }
   1732         }
   1733 
   1734         public void run() {
   1735             synchronized (mWorkerLock) {
   1736                 Looper.prepare();
   1737                 mHandler = new Handler();
   1738                 mWorkerLock.notifyAll();
   1739             }
   1740             Looper.loop();
   1741         }
   1742 
   1743         public Handler getHandler() {
   1744             return mHandler;
   1745         }
   1746 
   1747         public void stop() {
   1748             mHandler.getLooper().quit();
   1749         }
   1750     }
   1751 
   1752     /**
   1753      * Mock accessibility service to receive the accessibility events
   1754      * with the current {@link WebView} selection.
   1755      */
   1756     public static class MockAccessibilityService extends AccessibilityService {
   1757         private boolean mIsServiceInfoSet;
   1758 
   1759         @Override
   1760         protected void onServiceConnected() {
   1761             if (mIsServiceInfoSet) {
   1762                 return;
   1763             }
   1764             AccessibilityServiceInfo info = new AccessibilityServiceInfo();
   1765             info.eventTypes = AccessibilityEvent.TYPE_VIEW_SELECTED;
   1766             info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
   1767             setServiceInfo(info);
   1768             mIsServiceInfoSet = true;
   1769 
   1770             sIsAccessibilityServiceReady = true;
   1771 
   1772             if (sInstance == null) {
   1773                 return;
   1774             }
   1775             synchronized (sTestLock) {
   1776                 sTestLock.notifyAll();
   1777             }
   1778         }
   1779 
   1780         @Override
   1781         public void onAccessibilityEvent(AccessibilityEvent event) {
   1782             if (sInstance == null) {
   1783                 return;
   1784             }
   1785             if (!event.getText().isEmpty()) {
   1786                 CharSequence text = event.getText().get(0);
   1787                 if (text != null) {
   1788                     sReceivedSelectionString = stripAppleSpanFromMarkup(text.toString());
   1789                 } else {
   1790                     sReceivedSelectionString = null;
   1791                 }
   1792             }
   1793             synchronized (sTestLock) {
   1794                 sTestLock.notifyAll();
   1795             }
   1796         }
   1797 
   1798         @Override
   1799         public void onInterrupt() {
   1800             /* do nothing */
   1801         }
   1802 
   1803         @Override
   1804         public boolean onUnbind(Intent intent) {
   1805             sIsAccessibilityServiceReady = false;
   1806             return false;
   1807         }
   1808     }
   1809 }
   1810