| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.webkit; |
| |
| import android.accessibilityservice.AccessibilityService; |
| import android.accessibilityservice.AccessibilityServiceInfo; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Intent; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.SystemClock; |
| import android.provider.Settings; |
| import android.test.ActivityInstrumentationTestCase2; |
| import android.test.suitebuilder.annotation.LargeTest; |
| import android.view.KeyEvent; |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.AccessibilityManager; |
| |
| /** |
| * This is a test for the behavior of the {@link AccessibilityInjector} |
| * which is used by {@link WebView} to provide basic accessibility support |
| * in case JavaScript is disabled. |
| * </p> |
| * Note: This test works against the generated {@link AccessibilityEvent}s |
| * to so it also checks if the test for announcing navigation axis and |
| * status messages as appropriate. |
| */ |
| public class AccessibilityInjectorTest |
| extends ActivityInstrumentationTestCase2<AccessibilityInjectorTestActivity> { |
| |
| /** The timeout to wait for the expected selection. */ |
| private static final long TIMEOUT_WAIT_FOR_SELECTION_STRING = 1000; |
| |
| /** The timeout to wait for accessibility and the mock service to be enabled. */ |
| private static final long TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE = 1000; |
| |
| /** The count of tests to detect when to shut down the service. */ |
| private static final int TEST_CASE_COUNT = 19; |
| |
| /** The meta state for pressed left ALT. */ |
| private static final int META_STATE_ALT_LEFT_ON = KeyEvent.META_ALT_ON |
| | KeyEvent.META_ALT_LEFT_ON; |
| |
| /** Prefix for the CSS style span appended by WebKit. */ |
| private static final String APPLE_SPAN_PREFIX = "<span class=\"Apple-style-span\""; |
| |
| /** Suffix for the CSS style span appended by WebKit. */ |
| private static final String APPLE_SPAN_SUFFIX = "</span>"; |
| |
| /** The value for not specified selection string since null is a valid value. */ |
| private static final String SELECTION_STRING_UNKNOWN = "Unknown"; |
| |
| /** Lock for locking the test. */ |
| private static final Object sTestLock = new Object(); |
| |
| /** Key bindings used for testing. */ |
| private static final String TEST_KEY_DINDINGS = |
| "0x13=0x01000100;" + |
| "0x14=0x01010100;" + |
| "0x15=0x04000000;" + |
| "0x16=0x04000000;" + |
| "0x200000013=0x03020701:0x03010201:0x03000101:0x03030001:0x03040001:0x03050001:0x03060001;" + |
| "0x200000014=0x03010001:0x03020101:0x03070201:0x03030701:0x03040701:0x03050701:0x03060701;" + |
| "0x200000015=0x03040301:0x03050401:0x03060501:0x03000601:0x03010601:0x03020601:0x03070601;" + |
| "0x200000016=0x03050601:0x03040501:0x03030401:0x03020301:0x03070301:0x03010301:0x03000301;"; |
| |
| /** Handle to the test for use by the mock service. */ |
| private static AccessibilityInjectorTest sInstance; |
| |
| /** Flag indicating if the accessibility service is ready to receive events. */ |
| private static boolean sIsAccessibilityServiceReady; |
| |
| /** The count of executed tests to detect when to toggle accessibility and the service. */ |
| private static int sExecutedTestCount; |
| |
| /** Worker thread with a handler to perform non test thread processing. */ |
| private Worker mWorker; |
| |
| /** Handle to the {@link WebView} to load data in. */ |
| private WebView mWebView; |
| |
| /** Used for caching the default bindings so they can be restored. */ |
| private String mDefaultKeyBindings; |
| |
| /** The received selection string for assertion checking. */ |
| private static String sReceivedSelectionString = SELECTION_STRING_UNKNOWN; |
| |
| public AccessibilityInjectorTest() { |
| super(AccessibilityInjectorTestActivity.class); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| mWorker = new Worker(); |
| sInstance = this; |
| if (sExecutedTestCount == 0) { |
| // until JUnit4 comes to play with @BeforeTest |
| disableAccessibilityAndMockAccessibilityService(); |
| enableAccessibilityAndMockAccessibilityService(); |
| injectTestWebContentKeyBindings(); |
| } |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| if (mWorker != null) { |
| mWorker.stop(); |
| } |
| if (sExecutedTestCount == TEST_CASE_COUNT) { |
| // until JUnit4 comes to play with @AfterTest |
| disableAccessibilityAndMockAccessibilityService(); |
| restoreDefaultWebContentKeyBindings(); |
| } |
| super.tearDown(); |
| } |
| |
| /** |
| * Tests navigation by character. |
| */ |
| @LargeTest |
| public void testNavigationByCharacter() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<p>" + |
| "a<b>b</b>c" + |
| "</p>" + |
| "<p>" + |
| "d" + |
| "<p/>" + |
| "e" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("1"); // expect the word navigation axis |
| |
| // change navigation axis to character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("0"); // expect the character navigation axis |
| |
| // go to the first character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("a"); |
| |
| // go to the second character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<b>b</b>"); |
| |
| // go to the third character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("c"); |
| |
| // go to the fourth character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("d"); |
| |
| // go to the fifth character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("e"); |
| |
| // try to go past the last character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the fifth character (reverse) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("e"); |
| |
| // go to the fourth character (reverse) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("d"); |
| |
| // go to the third character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("c"); |
| |
| // go to the second character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<b>b</b>"); |
| |
| // go to the first character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("a"); |
| |
| // try to go before the first character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("a"); |
| |
| // go to the second character (reverse again) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<b>b</b>"); |
| } |
| |
| /** |
| * Tests navigation by word. |
| */ |
| @LargeTest |
| public void testNavigationByWord() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<p>" + |
| "This is <b>a</b> sentence" + |
| "</p>" + |
| "<p>" + |
| " scattered " + |
| "<p/>" + |
| " all over " + |
| "</p>" + |
| "<div>" + |
| "<p>the place.</p>" + |
| "</div>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("1"); // expect the word navigation axis |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This"); |
| |
| // go to the second word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("is"); |
| |
| // go to the third word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<b>a</b>"); |
| |
| // go to the fourth word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("sentence"); |
| |
| // go to the fifth word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("scattered"); |
| |
| // go to the sixth word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("all"); |
| |
| // go to the seventh word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("over"); |
| |
| // go to the eight word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("the"); |
| |
| // go to the ninth word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("place"); |
| |
| // NOTE: WebKit selection returns the dot as a word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("."); |
| |
| // try to go past the last word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the last word (reverse) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("place."); |
| |
| // go to the eight word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("the"); |
| |
| // go to the seventh word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("over"); |
| |
| // go to the sixth word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("all"); |
| |
| // go to the fifth word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("scattered"); |
| |
| // go to the fourth word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("sentence"); |
| |
| // go to the third word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<b>a</b>"); |
| |
| // go to the second word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("is"); |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("This"); |
| |
| // try to go before the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This"); |
| |
| // go to the second word (reverse again) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("is"); |
| } |
| |
| /** |
| * Tests navigation by sentence. |
| */ |
| @LargeTest |
| public void testNavigationBySentence() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>" + |
| "<p>" + |
| "This is the first sentence of the first paragraph and has an <b>inline bold tag</b>." + |
| "This is the second sentence of the first paragraph." + |
| "</p>" + |
| "<h1>This is a heading</h1>" + |
| "<p>" + |
| "This is the first sentence of the second paragraph." + |
| "This is the second sentence of the second paragraph." + |
| "</p>" + |
| "</div>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // Sentence axis is the default |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This is the first sentence of the first paragraph and has an " |
| + "<b>inline bold tag</b>."); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This is the second sentence of the first paragraph."); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This is a heading"); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This is the first sentence of the second paragraph."); |
| |
| // go to the fifth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This is the second sentence of the second paragraph."); |
| |
| // try to go past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the fifth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("This is the second sentence of the second paragraph."); |
| |
| // go to the fourth sentence (reverse) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("This is the first sentence of the second paragraph."); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("This is a heading"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("This is the second sentence of the first paragraph."); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("This is the first sentence of the first paragraph and has an " |
| + "<b>inline bold tag</b>."); |
| |
| // try to go before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This is the first sentence of the first paragraph and has an " |
| + "<b>inline bold tag</b>."); |
| |
| // go to the second sentence (reverse again) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("This is the second sentence of the first paragraph."); |
| } |
| |
| /** |
| * Tests navigation by heading. |
| */ |
| @LargeTest |
| public void testNavigationByHeading() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<h1>Heading one</h1>" + |
| "<p>" + |
| "This is some text" + |
| "</p>" + |
| "<h2>Heading two</h2>" + |
| "<p>" + |
| "This is some text" + |
| "</p>" + |
| "<h3>Heading three</h3>" + |
| "<p>" + |
| "This is some text" + |
| "</p>" + |
| "<h4>Heading four</h4>" + |
| "<p>" + |
| "This is some text" + |
| "</p>" + |
| "<h5>Heading five</h5>" + |
| "<p>" + |
| "This is some text" + |
| "</p>" + |
| "<h6>Heading six</h6>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("3"); // expect the heading navigation axis |
| |
| // go to the first heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<h1>Heading one</h1>"); |
| |
| // go to the second heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<h2>Heading two</h2>"); |
| |
| // go to the third heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<h3>Heading three</h3>"); |
| |
| // go to the fourth heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<h4>Heading four</h4>"); |
| |
| // go to the fifth heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<h5>Heading five</h5>"); |
| |
| // go to the sixth heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<h6>Heading six</h6>"); |
| |
| // try to go past the last heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the fifth heading (reverse) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<h5>Heading five</h5>"); |
| |
| // go to the fourth heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<h4>Heading four</h4>"); |
| |
| // go to the third heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<h3>Heading three</h3>"); |
| |
| // go to the second heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<h2>Heading two</h2>"); |
| |
| // go to the first heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<h1>Heading one</h1>"); |
| |
| // try to go before the first heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the second heading (reverse again) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<h2>Heading two</h2>"); |
| } |
| |
| /** |
| * Tests navigation by sibling. |
| */ |
| @LargeTest |
| public void testNavigationBySibing() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<h1>Heading one</h1>" + |
| "<p>" + |
| "This is some text" + |
| "</p>" + |
| "<div>" + |
| "<button>Input</button>" + |
| "</div>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("3"); // expect the heading navigation axis |
| |
| // change navigation axis to sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("4"); // expect the sibling navigation axis |
| |
| // change navigation axis to parent/first child |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("5"); // expect the parent/first child navigation axis |
| |
| // go to the first child of the body |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<h1>Heading one</h1>"); |
| |
| // change navigation axis to sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("4"); // expect the sibling navigation axis |
| |
| // go to the next sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<p>This is some text</p>"); |
| |
| // go to the next sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<div><button>Input</button></div>"); |
| |
| // try to go past the last sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the previous sibling (reverse) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<p>This is some text</p>"); |
| |
| // go to the previous sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<h1>Heading one</h1>"); |
| |
| // try to go before the previous sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the next sibling (reverse again) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<p>This is some text</p>"); |
| } |
| |
| /** |
| * Tests navigation by parent/first child. |
| */ |
| @LargeTest |
| public void testNavigationByParentFirstChild() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>" + |
| "<button>Input</button>" + |
| "</div>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to document |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("6"); // expect the document navigation axis |
| |
| // change navigation axis to parent/first child |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("5"); // expect the parent/first child navigation axis |
| |
| // go to the first child |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<div><button>Input</button></div>"); |
| |
| // go to the first child |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<button>Input</button>"); |
| |
| // try to go to the first child of a leaf element |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the parent (reverse) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<div><button>Input</button></div>"); |
| |
| // go to the parent |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<body><div><button>Input</button></div></body>"); |
| |
| // try to go to the body parent |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first child (reverse again) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<div><button>Input</button></div>"); |
| } |
| |
| /** |
| * Tests navigation by document. |
| */ |
| @LargeTest |
| public void testNavigationByDocument() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<button>Click</button>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to document |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("6"); // expect the document navigation axis |
| |
| // go to the bottom of the document |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Click"); |
| |
| // go to the top of the document (reverse) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<body><button>Click</button></body>"); |
| |
| // go to the bottom of the document (reverse again) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Click"); |
| } |
| |
| /** |
| * Tests the sync between the text navigation and navigation by DOM elements. |
| */ |
| @LargeTest |
| public void testSyncBetweenTextAndDomNodeNavigation() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<p>" + |
| "First" + |
| "</p>" + |
| "<button>Second</button>" + |
| "<p>" + |
| "Third" + |
| "</p>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("1"); // expect the word navigation axis |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| |
| // change navigation axis to heading |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("3"); // expect the heading navigation axis |
| |
| // change navigation axis to sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("4"); // expect the sibling navigation axis |
| |
| // go to the next sibling |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<button>Second</button>"); |
| |
| // change navigation axis to character |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("0"); // expect the character navigation axis |
| |
| // change navigation axis to word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("1"); // expect the word navigation axis |
| |
| // go to the next word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Third"); |
| } |
| |
| /** |
| * Tests that the selection does not cross anchor boundaries. This is a |
| * workaround for the asymmetric and inconsistent handling of text with |
| * links by WebKit while traversing by sentence. |
| */ |
| @LargeTest |
| public void testEnforceSelectionDoesNotCrossAnchorBoundary1() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>First</div>" + |
| "<p>" + |
| "<a href=\"\">Second</a> Third" + |
| "</p>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<div>First</div>"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<a href=\"\">Second</a>"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Third"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("Third"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<a href=\"\">Second</a>"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("First"); |
| |
| // go to before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<div>First</div>"); |
| } |
| |
| /** |
| * Tests that the selection does not cross anchor boundaries. This is a |
| * workaround for the asymmetric and inconsistent handling of text with |
| * links by WebKit while traversing by sentence. |
| */ |
| @LargeTest |
| public void testEnforceSelectionDoesNotCrossAnchorBoundary2() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>First</div>" + |
| "<a href=\"#\">Second</a>" + |
| " " + |
| "<a href=\"#\">Third</a>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<a href=\"#\">Second</a>"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(" "); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<a href=\"#\">Third</a>"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<a href=\"#\">Third</a>"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(" "); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<a href=\"#\">Second</a>"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("First"); |
| |
| // go to before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| } |
| |
| /** |
| * Tests that the selection does not cross anchor boundaries. This is a |
| * workaround for the asymmetric and inconsistent handling of text with |
| * links by WebKit while traversing by sentence. |
| */ |
| @LargeTest |
| public void testEnforceSelectionDoesNotCrossAnchorBoundary3() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>" + |
| "First" + |
| "<div>" + |
| "<div>" + |
| "<a href=\"#\">Second</a>" + |
| "</div>" + |
| "<div>" + |
| "Third" + |
| "</div>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<a href=\"#\">Second</a>"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Third"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("Third"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<a href=\"#\">Second</a>"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("First"); |
| |
| // go to before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| } |
| |
| /** |
| * Tests skipping of content with hidden visibility. |
| */ |
| @LargeTest |
| public void testSkipVisibilityHidden() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>First </div>" + |
| "<div style=\"visibility:hidden;\">Second</div>" + |
| "<div> Third</div>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("1"); // expect the word navigation axis |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| |
| // go to the third word (the second is invisible) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Third"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the third word (the second is invisible) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("Third"); |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("First"); |
| |
| // go to before the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| } |
| |
| /** |
| * Tests skipping of content with display none. |
| */ |
| @LargeTest |
| public void testSkipDisplayNone() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>First</div>" + |
| "<div style=\"display: none;\">Second</div>" + |
| "<div>Third</div>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // change navigation axis to word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); |
| assertSelectionString("1"); // expect the word navigation axis |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| |
| // go to the third word (the second is invisible) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Third"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the third word (the second is invisible) |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("Third"); |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("First"); |
| |
| // go to before the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first word |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| } |
| |
| /** |
| * Tests for the selection not getting stuck. |
| * |
| * Note: The selection always proceeds but if it can |
| * be selecting the same content i.e. between the start |
| * and end are contained the same text nodes. |
| */ |
| @LargeTest |
| public void testSelectionTextProceed() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<a href=\"#\">First</a>" + |
| "<span><a href=\"#\"><span>Second</span> <small>a</small></a>" + |
| "</span> <a href=\"#\">Third</a>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<a href=\"#\">First</a>"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<a href=\"#\"><span>Second <small>a</small></a>"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(" "); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<a href=\"#\">Third</a>"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<a href=\"#\">Third</a>"); |
| |
| // NOTE: Here we are a bit asymmetric around whitespace but we can live with it |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(" "); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<a href=\"#\"><span>Second <small>a</small></a>"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<a href=\"#\">First</a>"); |
| |
| // go to before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<a href=\"#\">First</a>"); |
| } |
| |
| /** |
| * Tests if input elements are selected rather skipped. |
| */ |
| @LargeTest |
| public void testSelectionOfInputElements() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<p>" + |
| "First" + |
| "</p>" + |
| "<input type=\"text\"/>" + |
| "<p>" + |
| "Second" + |
| "</p>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Second"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("Second"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("First"); |
| |
| // go to before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| } |
| |
| /** |
| * Tests traversing of input controls. |
| */ |
| @LargeTest |
| public void testSelectionOfInputElements2() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>" + |
| "First" + |
| "<input type=\"text\"/>" + |
| "<span>" + |
| "<input type=\"text\"/>" + |
| "</span>" + |
| "<button type=\"button\">Click Me!</button>" + |
| "<div>" + |
| "<input type=\"submit\"/>" + |
| "</div>" + |
| "<p>" + |
| "Second" + |
| "</p>" + |
| "</div>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<button type=\"button\">Click Me!</button>"); |
| |
| // go to the fifth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"submit\">"); |
| |
| // go to the sixth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Second"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the sixth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("Second"); |
| |
| // go to the fifth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"submit\">"); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<button type=\"button\">Click Me!</button>"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("First"); |
| } |
| |
| /** |
| * Tests traversing of input controls. |
| */ |
| @LargeTest |
| public void testSelectionOfInputElements3() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<input type=\"text\"/>" + |
| "<button type=\"button\">Click Me!</button>" + |
| "<select>" + |
| "<option value=\"volvo\">Volvo</option>" + |
| "<option value=\"saab\">Saab</option>" + |
| "</select>" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<button type=\"button\">Click Me!</button>"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<select><option value=\"volvo\">Volvo</option>" + |
| "<option value=\"saab\">Saab</option></select>"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<select><option value=\"volvo\">Volvo</option>" + |
| "<option value=\"saab\">Saab</option></select>"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<button type=\"button\">Click Me!</button>"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"text\">"); |
| } |
| |
| /** |
| * Tests traversing of input controls. |
| */ |
| @LargeTest |
| public void testSelectionOfInputElements4() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "Start" + |
| "<span>" + |
| "<span>" + |
| "<input type=\"submit\">" + |
| "</span>" + |
| "</span>" + |
| "<input type=\"text\" size=\"30\">" + |
| "<span>" + |
| "<span>" + |
| "<input type=\"submit\" size=\"30\">" + |
| "</span>" + |
| "</span>" + |
| "End" + |
| "</body>" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Start"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"submit\">"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"text\" size=\"30\">"); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"submit\" size=\"30\">"); |
| |
| // go to the fifth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("End"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the fifth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("End"); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"submit\" size=\"30\">"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"text\" size=\"30\">"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"submit\">"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("Start"); |
| |
| // go to before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Start"); |
| } |
| |
| /** |
| * Tests traversing of input controls. |
| */ |
| @LargeTest |
| public void testSelectionOfInputElements5() throws Exception { |
| // a bit ugly but helps detect beginning and end of all tests so accessibility |
| // and the mock service are not toggled on every test (expensive) |
| sExecutedTestCount++; |
| |
| String html = |
| "<!DOCTYPE html>" + |
| "<html>" + |
| "<head>" + |
| "</head>" + |
| "<body>" + |
| "<div>" + |
| "First" + |
| "<input type=\"hidden\">" + |
| "<input type=\"hidden\">" + |
| "<input type=\"hidden\">" + |
| "<input type=\"hidden\">" + |
| "<input type=\"text\">" + |
| "<span>" + |
| "<span>" + |
| "<input type=\"submit\">" + |
| "</span>" + |
| "</span>" + |
| "</div>" + |
| "</body>" + |
| "Second" + |
| "</html>"; |
| |
| WebView webView = loadHTML(html); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("<input type=\"submit\">"); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("Second"); |
| |
| // go to past the last sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString(null); |
| |
| // go to the fourth sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("Second"); |
| |
| // go to the third sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"submit\">"); |
| |
| // go to the second sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("<input type=\"text\">"); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString("First"); |
| |
| // go to before the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); |
| assertSelectionString(null); |
| |
| // go to the first sentence |
| sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); |
| assertSelectionString("First"); |
| } |
| |
| /** |
| * Enable accessibility and the mock accessibility service. |
| */ |
| private void enableAccessibilityAndMockAccessibilityService() { |
| // make sure the manager is instantiated so the system initializes it |
| AccessibilityManager.getInstance(getActivity()); |
| |
| // enable accessibility and the mock accessibility service |
| Settings.Secure.putInt(getActivity().getContentResolver(), |
| Settings.Secure.ACCESSIBILITY_ENABLED, 1); |
| String enabledServices = new ComponentName(getActivity().getPackageName(), |
| MockAccessibilityService.class.getName()).flattenToShortString(); |
| Settings.Secure.putString(getActivity().getContentResolver(), |
| Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices); |
| |
| // poll within a timeout and let be interrupted in case of success |
| long incrementStep = TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE / 5; |
| long start = SystemClock.uptimeMillis(); |
| while (SystemClock.uptimeMillis() - start < TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE && |
| !sIsAccessibilityServiceReady) { |
| synchronized (sTestLock) { |
| try { |
| sTestLock.wait(incrementStep); |
| } catch (InterruptedException ie) { |
| /* ignore */ |
| } |
| } |
| } |
| |
| if (!sIsAccessibilityServiceReady) { |
| throw new IllegalStateException("MockAccessibilityService not ready. Did you add " + |
| "tests and forgot to update AccessibilityInjectorTest#TEST_CASE_COUNT?"); |
| } |
| } |
| |
| @Override |
| protected void scrubClass(Class<?> testCaseClass) { |
| /* do nothing - avoid superclass behavior */ |
| } |
| |
| /** |
| * Strips the apple span appended by WebKit while generating |
| * the selection markup. |
| * |
| * @param markup The markup. |
| * @return Stripped from apple spans markup. |
| */ |
| private static String stripAppleSpanFromMarkup(String markup) { |
| StringBuilder stripped = new StringBuilder(markup); |
| int prefixBegIdx = stripped.indexOf(APPLE_SPAN_PREFIX); |
| while (prefixBegIdx >= 0) { |
| int prefixEndIdx = stripped.indexOf(">", prefixBegIdx) + 1; |
| stripped.replace(prefixBegIdx, prefixEndIdx, ""); |
| int suffixBegIdx = stripped.lastIndexOf(APPLE_SPAN_SUFFIX); |
| int suffixEndIdx = suffixBegIdx + APPLE_SPAN_SUFFIX.length(); |
| stripped.replace(suffixBegIdx, suffixEndIdx, ""); |
| prefixBegIdx = stripped.indexOf(APPLE_SPAN_PREFIX); |
| } |
| return stripped.toString(); |
| } |
| |
| /** |
| * Disables accessibility and the mock accessibility service. |
| */ |
| private void disableAccessibilityAndMockAccessibilityService() { |
| // disable accessibility and the mock accessibility service |
| Settings.Secure.putInt(getActivity().getContentResolver(), |
| Settings.Secure.ACCESSIBILITY_ENABLED, 0); |
| Settings.Secure.putString(getActivity().getContentResolver(), |
| Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, ""); |
| } |
| |
| /** |
| * Asserts the next <code>expectedSelectionString</code> to be received. |
| */ |
| private void assertSelectionString(String expectedSelectionString) { |
| assertTrue("MockAccessibilityService not ready", sIsAccessibilityServiceReady); |
| |
| long incrementStep = TIMEOUT_WAIT_FOR_SELECTION_STRING / 5; |
| long start = SystemClock.uptimeMillis(); |
| while (SystemClock.uptimeMillis() - start < TIMEOUT_WAIT_FOR_SELECTION_STRING && |
| sReceivedSelectionString == SELECTION_STRING_UNKNOWN) { |
| synchronized (sTestLock) { |
| try { |
| sTestLock.wait(incrementStep); |
| } catch (InterruptedException ie) { |
| /* ignore */ |
| } |
| } |
| } |
| try { |
| if (sReceivedSelectionString == SELECTION_STRING_UNKNOWN) { |
| fail("No selection string received. Expected: " + expectedSelectionString); |
| } |
| assertEquals(expectedSelectionString, sReceivedSelectionString); |
| } finally { |
| sReceivedSelectionString = SELECTION_STRING_UNKNOWN; |
| } |
| } |
| |
| /** |
| * Sends a {@link KeyEvent} (up and down) to the {@link WebView}. |
| * |
| * @param keyCode The event key code. |
| */ |
| private void sendKeyEvent(WebView webView, int keyCode, int metaState) { |
| webView.onKeyDown(keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 1, metaState)); |
| webView.onKeyUp(keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 1, metaState)); |
| } |
| |
| /** |
| * Loads HTML content in a {@link WebView}. |
| * |
| * @param html The HTML content; |
| * @return The {@link WebView} view. |
| */ |
| private WebView loadHTML(final String html) { |
| mWorker.getHandler().post(new Runnable() { |
| public void run() { |
| if (mWebView == null) { |
| mWebView = getActivity().getWebView(); |
| mWebView.setWebViewClient(new WebViewClient() { |
| @Override |
| public void onPageFinished(WebView view, String url) { |
| mWorker.getHandler().post(new Runnable() { |
| public void run() { |
| synchronized (sTestLock) { |
| sTestLock.notifyAll(); |
| } |
| } |
| }); |
| } |
| }); |
| } |
| mWebView.loadData(html, "text/html", "utf-8"); |
| } |
| }); |
| synchronized (sTestLock) { |
| try { |
| sTestLock.wait(); |
| } catch (InterruptedException ie) { |
| /* ignore */ |
| } |
| } |
| return mWebView; |
| } |
| |
| /** |
| * Injects web content key bindings used for testing. This is required |
| * to ensure that this test will be agnostic to changes of the bindings. |
| */ |
| private void injectTestWebContentKeyBindings() { |
| ContentResolver contentResolver = getActivity().getContentResolver(); |
| mDefaultKeyBindings = Settings.Secure.getString(contentResolver, |
| Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS); |
| Settings.Secure.putString(contentResolver, |
| Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS, TEST_KEY_DINDINGS); |
| } |
| |
| /** |
| * Restores the default web content key bindings. |
| */ |
| private void restoreDefaultWebContentKeyBindings() { |
| Settings.Secure.putString(getActivity().getContentResolver(), |
| Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS, |
| mDefaultKeyBindings); |
| } |
| |
| /** |
| * This is a worker thread responsible for creating the {@link WebView}. |
| */ |
| private class Worker implements Runnable { |
| private final Object mWorkerLock = new Object(); |
| private Handler mHandler; |
| |
| public Worker() { |
| new Thread(this).start(); |
| synchronized (mWorkerLock) { |
| while (mHandler == null) { |
| try { |
| mWorkerLock.wait(); |
| } catch (InterruptedException ex) { |
| /* ignore */ |
| } |
| } |
| } |
| } |
| |
| public void run() { |
| synchronized (mWorkerLock) { |
| Looper.prepare(); |
| mHandler = new Handler(); |
| mWorkerLock.notifyAll(); |
| } |
| Looper.loop(); |
| } |
| |
| public Handler getHandler() { |
| return mHandler; |
| } |
| |
| public void stop() { |
| mHandler.getLooper().quit(); |
| } |
| } |
| |
| /** |
| * Mock accessibility service to receive the accessibility events |
| * with the current {@link WebView} selection. |
| */ |
| public static class MockAccessibilityService extends AccessibilityService { |
| private boolean mIsServiceInfoSet; |
| |
| @Override |
| protected void onServiceConnected() { |
| if (mIsServiceInfoSet) { |
| return; |
| } |
| AccessibilityServiceInfo info = new AccessibilityServiceInfo(); |
| info.eventTypes = AccessibilityEvent.TYPE_VIEW_SELECTED; |
| info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; |
| setServiceInfo(info); |
| mIsServiceInfoSet = true; |
| |
| sIsAccessibilityServiceReady = true; |
| |
| if (sInstance == null) { |
| return; |
| } |
| synchronized (sTestLock) { |
| sTestLock.notifyAll(); |
| } |
| } |
| |
| @Override |
| public void onAccessibilityEvent(AccessibilityEvent event) { |
| if (sInstance == null) { |
| return; |
| } |
| if (!event.getText().isEmpty()) { |
| CharSequence text = event.getText().get(0); |
| if (text != null) { |
| sReceivedSelectionString = stripAppleSpanFromMarkup(text.toString()); |
| } else { |
| sReceivedSelectionString = null; |
| } |
| } |
| synchronized (sTestLock) { |
| sTestLock.notifyAll(); |
| } |
| } |
| |
| @Override |
| public void onInterrupt() { |
| /* do nothing */ |
| } |
| |
| @Override |
| public boolean onUnbind(Intent intent) { |
| sIsAccessibilityServiceReady = false; |
| return false; |
| } |
| } |
| } |