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 " " + 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(" "); 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(" "); 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> <small>a</small></a>" + 1114 "</span> <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 <small>a</small></a>"); 1127 1128 // go to the third sentence 1129 sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); 1130 assertSelectionString(" "); 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(" "); 1147 1148 // go to the second sentence 1149 sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); 1150 assertSelectionString("<a href=\"#\"><span>Second <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