Loading core/java/android/webkit/WebView.java +4 −1 Original line number Diff line number Diff line Loading @@ -7324,7 +7324,10 @@ public class WebView extends AbsoluteLayout cursorData(), 1000); } /* package */ synchronized WebViewCore getWebViewCore() { /** * @hide */ public synchronized WebViewCore getWebViewCore() { return mWebViewCore; } Loading core/java/android/webkit/WebViewCore.java +13 −4 Original line number Diff line number Diff line Loading @@ -48,7 +48,10 @@ import java.util.Set; import junit.framework.Assert; final class WebViewCore { /** * @hide */ public final class WebViewCore { private static final String LOGTAG = "webcore"; Loading Loading @@ -905,7 +908,10 @@ final class WebViewCore { "REMOVE_JS_INTERFACE", // = 149; }; class EventHub { /** * @hide */ public class EventHub { // Message Ids static final int REVEAL_SELECTION = 96; static final int REQUEST_LABEL = 97; Loading Loading @@ -936,7 +942,7 @@ final class WebViewCore { static final int DELETE_SELECTION = 122; static final int LISTBOX_CHOICES = 123; static final int SINGLE_LISTBOX_CHOICE = 124; static final int MESSAGE_RELAY = 125; public static final int MESSAGE_RELAY = 125; static final int SET_BACKGROUND_COLOR = 126; static final int SET_MOVE_FOCUS = 127; static final int SAVE_DOCUMENT_STATE = 128; Loading Loading @@ -1702,7 +1708,10 @@ final class WebViewCore { // If it needs WebCore, it has to send message. //------------------------------------------------------------------------- void sendMessage(Message msg) { /** * @hide */ public void sendMessage(Message msg) { mEventHub.sendMessage(msg); } Loading core/java/android/webkit/webdriver/WebDriver.java +311 −19 Original line number Diff line number Diff line Loading @@ -16,15 +16,22 @@ package android.webkit.webdriver; import android.graphics.Point; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.webkit.WebView; import com.android.internal.R; import android.webkit.WebViewCore; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import com.android.internal.R; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; Loading Loading @@ -91,10 +98,24 @@ public class WebDriver { private static final int LOADING_TIMEOUT = 30000; // Timeout for executing JavaScript in the WebView in milliseconds. private static final int JS_EXECUTION_TIMEOUT = 10000; // Timeout for the MotionEvent to be completely handled private static final int MOTION_EVENT_TIMEOUT = 1000; // Timeout for detecting a new page load private static final int PAGE_STARTED_LOADING = 500; // Timeout for handling KeyEvents private static final int KEY_EVENT_TIMEOUT = 2000; // Commands posted to the handler private static final int CMD_GET_URL = 1; private static final int CMD_EXECUTE_SCRIPT = 2; private static final int CMD_SEND_TOUCH = 3; private static final int CMD_SEND_KEYS = 4; private static final int CMD_NAV_REFRESH = 5; private static final int CMD_NAV_BACK = 6; private static final int CMD_NAV_FORWARD = 7; private static final int CMD_SEND_KEYCODE = 8; private static final int CMD_MOVE_CURSOR_RIGHTMOST_POS = 9; private static final int CMD_MESSAGE_RELAY_ECHO = 10; private static final String ELEMENT_KEY = "ELEMENT"; private static final String STATUS = "status"; Loading @@ -107,24 +128,62 @@ public class WebDriver { // Used for synchronization private final Object mSyncObject; private final Object mSyncPageLoad; // Updated when the command is done executing in the main thread. private volatile boolean mCommandDone; // Used by WebViewClientWrapper.onPageStarted() to notify that // a page started loading. private volatile boolean mPageStartedLoading; // Used by WebChromeClientWrapper.onProgressChanged to notify when // a page finished loading. private volatile boolean mPageFinishedLoading; private WebView mWebView; private Navigation mNavigation; // This WebElement represents the object document.documentElement private WebElement mDocumentElement; // This Handler runs in the main UI thread. private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == CMD_GET_URL) { switch (msg.what) { case CMD_GET_URL: final String url = (String) msg.obj; mWebView.loadUrl(url); } else if (msg.what == CMD_EXECUTE_SCRIPT) { break; case CMD_EXECUTE_SCRIPT: mWebView.loadUrl("javascript:" + (String) msg.obj); break; case CMD_MESSAGE_RELAY_ECHO: notifyCommandDone(); break; case CMD_SEND_TOUCH: touchScreen((Point) msg.obj); notifyCommandDone(); break; case CMD_SEND_KEYS: dispatchKeys((CharSequence[]) msg.obj); notifyCommandDone(); break; case CMD_NAV_REFRESH: mWebView.reload(); break; case CMD_NAV_BACK: mWebView.goBack(); break; case CMD_NAV_FORWARD: mWebView.goForward(); break; case CMD_SEND_KEYCODE: dispatchKeyCodes((int[]) msg.obj); notifyCommandDone(); break; case CMD_MOVE_CURSOR_RIGHTMOST_POS: moveCursorToLeftMostPos((String) msg.obj); notifyCommandDone(); break; } } }; Loading Loading @@ -169,13 +228,13 @@ public class WebDriver { return values[i]; } } throw new IllegalArgumentException(intValue + " does not map to any ErrorCode."); return UNKNOWN_ERROR; } } public WebDriver(WebView webview) { this.mWebView = webview; mWebView.requestFocus(); if (mWebView == null) { throw new IllegalArgumentException("WebView cannot be null"); } Loading @@ -186,12 +245,25 @@ public class WebDriver { shouldRunInMainThread(true); mSyncObject = new Object(); mSyncPageLoad = new Object(); this.mWebView = webview; WebchromeClientWrapper chromeWrapper = new WebchromeClientWrapper( WebChromeClientWrapper chromeWrapper = new WebChromeClientWrapper( webview.getWebChromeClient(), this); mWebView.setWebChromeClient(chromeWrapper); WebViewClientWrapper viewWrapper = new WebViewClientWrapper( webview.getWebViewClient(), this); mWebView.setWebViewClient(viewWrapper); mWebView.addJavascriptInterface(new JavascriptResultReady(), "webdriver"); mDocumentElement = new WebElement(this, ""); mNavigation = new Navigation(); } /** * @return The title of the current page, null if not set. */ public String getTitle() { return mWebView.getTitle(); } /** Loading @@ -201,8 +273,7 @@ public class WebDriver { * @param url The URL to load. */ public void get(String url) { executeCommand(CMD_GET_URL, url, LOADING_TIMEOUT); mDocumentElement = (WebElement) executeScript("return document.body;"); mNavigation.to(url); } /** Loading Loading @@ -243,7 +314,7 @@ public class WebDriver { } /** * Clears the WebView. * Clears the WebView's state and closes associated views. */ public void quit() { mWebView.clearCache(true); Loading @@ -251,6 +322,7 @@ public class WebDriver { mWebView.clearHistory(); mWebView.clearSslPreferences(); mWebView.clearView(); mWebView.removeAllViewsInLayout(); } /** Loading Loading @@ -287,6 +359,44 @@ public class WebDriver { ")(" + escapeAndQuote(script) + ", " + scriptArgs + ", true)"); } public Navigation navigate() { return mNavigation; } /** * @hide */ public class Navigation { /* package */ Navigation () {} public void back() { navigate(CMD_NAV_BACK, null); } public void forward() { navigate(CMD_NAV_FORWARD, null); } public void to(String url) { navigate(CMD_GET_URL, url); } public void refresh() { navigate(CMD_NAV_REFRESH, null); } private void navigate(int command, String url) { synchronized (mSyncPageLoad) { mPageFinishedLoading = false; Message msg = mHandler.obtainMessage(command); msg.obj = url; mHandler.sendMessage(msg); waitForPageLoad(); } } } /** * Converts the arguments passed to a JavaScript friendly format. * Loading Loading @@ -338,9 +448,19 @@ public class WebDriver { } /* package */ Object executeRawJavascript(final String script) { if (mWebView.getUrl() == null) { throw new WebDriverException("Cannot operate on a blank page. " + "Load a page using WebDriver.get()."); } String result = executeCommand(CMD_EXECUTE_SCRIPT, "if (!window.webdriver || !window.webdriver.resultReady) {" + " return;" + "}" + "window.webdriver.resultReady(" + script + ")", JS_EXECUTION_TIMEOUT); if (result == null || "undefined".equals(result)) { return null; } try { JSONObject json = new JSONObject(result); throwIfError(json); Loading Loading @@ -369,6 +489,177 @@ public class WebDriver { return sb.toString(); } /* package */ void sendTouchScreen(Point coords) { // Reset state resetPageLoadState(); executeCommand(CMD_SEND_TOUCH, coords,LOADING_TIMEOUT); // Wait for the events to be fully handled waitForMessageRelay(MOTION_EVENT_TIMEOUT); // If a page started loading, block until page finishes loading waitForPageLoadIfNeeded(); } /* package */ void resetPageLoadState() { synchronized (mSyncPageLoad) { mPageStartedLoading = false; mPageFinishedLoading = false; } } /* package */ void waitForPageLoadIfNeeded() { synchronized (mSyncPageLoad) { Long end = System.currentTimeMillis() + PAGE_STARTED_LOADING; // Wait PAGE_STARTED_LOADING milliseconds to see if we detect a // page load. while (!mPageStartedLoading && (System.currentTimeMillis() <= end)) { try { // This is notified by WebChromeClientWrapper#onProgressChanged // when the page finished loading. mSyncPageLoad.wait(PAGE_STARTED_LOADING); } catch (InterruptedException e) { new RuntimeException(e); } } if (mPageStartedLoading) { waitForPageLoad(); } } } private void touchScreen(Point coords) { // Convert to screen coords // screen = JS x zoom - offset float zoom = mWebView.getScale(); float xOffset = mWebView.getX(); float yOffset = mWebView.getY(); Point screenCoords = new Point( (int)(coords.x*zoom - xOffset), (int)(coords.y*zoom - yOffset)); long downTime = SystemClock.uptimeMillis(); MotionEvent down = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, screenCoords.x, screenCoords.y, 0); down.setSource(InputDevice.SOURCE_TOUCHSCREEN); MotionEvent up = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, screenCoords.x, screenCoords.y, 0); up.setSource(InputDevice.SOURCE_TOUCHSCREEN); // Dispatch the events to WebView mWebView.dispatchTouchEvent(down); mWebView.dispatchTouchEvent(up); } /* package */ void notifyPageStartedLoading() { synchronized (mSyncPageLoad) { mPageStartedLoading = true; mSyncPageLoad.notify(); } } /* package */ void notifyPageFinishedLoading() { synchronized (mSyncPageLoad) { mPageFinishedLoading = true; mSyncPageLoad.notify(); } } /** * * @param keys The first element of the CharSequence should be the * existing value in the text input, or the empty string if none. */ /* package */ void sendKeys(CharSequence[] keys) { executeCommand(CMD_SEND_KEYS, keys, KEY_EVENT_TIMEOUT); // Wait for all KeyEvents to be handled waitForMessageRelay(KEY_EVENT_TIMEOUT); } /* package */ void sendKeyCodes(int[] keycodes) { executeCommand(CMD_SEND_KEYCODE, keycodes, KEY_EVENT_TIMEOUT); // Wait for all KeyEvents to be handled waitForMessageRelay(KEY_EVENT_TIMEOUT); } /* package */ void moveCursorToRightMostPosition(String value) { executeCommand(CMD_MOVE_CURSOR_RIGHTMOST_POS, value, KEY_EVENT_TIMEOUT); waitForMessageRelay(KEY_EVENT_TIMEOUT); } private void moveCursorToLeftMostPos(String value) { // If there is text, move the cursor to the rightmost position if (value != null && !value.equals("")) { long downTime = SystemClock.uptimeMillis(); KeyEvent down = new KeyEvent(downTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT, 0); KeyEvent up = new KeyEvent(downTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_RIGHT, value.length()); mWebView.dispatchKeyEvent(down); mWebView.dispatchKeyEvent(up); } } private void dispatchKeyCodes(int[] keycodes) { for (int i = 0; i < keycodes.length; i++) { KeyEvent down = new KeyEvent(KeyEvent.ACTION_DOWN, keycodes[i]); KeyEvent up = new KeyEvent(KeyEvent.ACTION_UP, keycodes[i]); mWebView.dispatchKeyEvent(down); mWebView.dispatchKeyEvent(up); } } private void dispatchKeys(CharSequence[] keys) { KeyCharacterMap chararcterMap = KeyCharacterMap.load( KeyCharacterMap.VIRTUAL_KEYBOARD); for (int i = 0; i < keys.length; i++) { CharSequence s = keys[i]; for (int j = 0; j < s.length(); j++) { KeyEvent[] events = chararcterMap.getEvents(new char[]{s.charAt(j)}); for (KeyEvent e : events) { mWebView.dispatchKeyEvent(e); } } } } private void waitForMessageRelay(long timeout) { synchronized (mSyncObject) { mCommandDone = false; } Message msg = Message.obtain(); msg.what = WebViewCore.EventHub.MESSAGE_RELAY; Message echo = mHandler.obtainMessage(CMD_MESSAGE_RELAY_ECHO); msg.obj = echo; mWebView.getWebViewCore().sendMessage(msg); synchronized (mSyncObject) { long end = System.currentTimeMillis() + timeout; while (!mCommandDone && (System.currentTimeMillis() <= end)) { try { // This is notifed by the mHandler when it receives the // MESSAGE_RELAY back mSyncObject.wait(timeout); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } private void waitForPageLoad() { long endLoad = System.currentTimeMillis() + LOADING_TIMEOUT; while (!mPageFinishedLoading && (System.currentTimeMillis() <= endLoad)) { try { mSyncPageLoad.wait(LOADING_TIMEOUT); } catch (InterruptedException e) { throw new RuntimeException(); } } } /** * Wraps the given string into quotes and escape existing quotes * and backslashes. Loading Loading @@ -531,7 +822,8 @@ public class WebDriver { long end = System.currentTimeMillis() + timeout; while (!mCommandDone) { if (System.currentTimeMillis() >= end) { throw new RuntimeException("Timeout executing command."); throw new RuntimeException("Timeout executing command: " + command); } try { mSyncObject.wait(timeout); Loading core/java/android/webkit/webdriver/WebElement.java +137 −4 Original line number Diff line number Diff line Loading @@ -16,9 +16,13 @@ package android.webkit.webdriver; import android.graphics.Point; import android.view.KeyEvent; import com.android.internal.R; import java.util.List; import java.util.Map; /** * Represents an HTML element. Typically most interactions with a web page Loading Loading @@ -158,6 +162,124 @@ public class WebElement { return (Boolean) executeAtom(toggle, this); } /** * Sends the KeyEvents for the given sequence of characters to the * WebElement to simulate typing. The KeyEvents are generated using the * device's {@link android.view.KeyCharacterMap.VIRTUAL_KEYBOARD}. * * @param keys The keys to send to this WebElement */ public void sendKeys(CharSequence... keys) { if (keys == null || keys.length == 0) { return; } click(); mDriver.moveCursorToRightMostPosition(getAttribute("value")); mDriver.sendKeys(keys); } /** * Use this to send one of the key code constants defined in * {@link android.view.KeyEvent} * * @param keys */ public void sendKeyCodes(int... keys) { if (keys == null || keys.length == 0) { return; } click(); mDriver.moveCursorToRightMostPosition(getAttribute("value")); mDriver.sendKeyCodes(keys); } /** * Sends a touch event to the center coordinates of this WebElement. */ public void click() { Point topLeft = getLocation(); Point size = getSize(); int jsX = topLeft.x + size.x/2; int jsY = topLeft.y + size.y/2; Point center = new Point(jsX, jsY); mDriver.sendTouchScreen(center); } /** * Submits the form containing this WebElement. */ public void submit() { mDriver.resetPageLoadState(); String submit = mDriver.getResourceAsString(R.raw.submit_android); executeAtom(submit, this); mDriver.waitForPageLoadIfNeeded(); } /** * Clears the text value if this is a text entry element. Does nothing * otherwise. */ public void clear() { String value = getAttribute("value"); if (value == null || value.equals("")) { return; } int length = value.length(); int[] keys = new int[length]; for (int i = 0; i < length; i++) { keys[i] = KeyEvent.KEYCODE_DEL; } sendKeyCodes(keys); } /** * @return the value of the given CSS property if found, null otherwise. */ public String getCssValue(String cssProperty) { String getCssProp = mDriver.getResourceAsString( R.raw.get_value_of_css_property_android); return (String) executeAtom(getCssProp, this, cssProperty); } /** * Gets the width and height of the rendered element. * * @return a {@link android.graphics.Point}, where Point.x represents the * width, and Point.y represents the height of the element. */ public Point getSize() { String getSize = mDriver.getResourceAsString(R.raw.get_size_android); Map<String, Long> map = (Map<String, Long>) executeAtom(getSize, this); return new Point(map.get("width").intValue(), map.get("height").intValue()); } /** * Gets the location of the top left corner of this element on the screen. * If the element is not visisble, this will scroll to get the element into * the visisble screen. * * @return a {@link android.graphics.Point} containing the x and y * coordinates of the top left corner of this element. */ public Point getLocation() { String getLocation = mDriver.getResourceAsString( R.raw.get_top_left_coordinates_android); Map<String,Long> map = (Map<String, Long>) executeAtom(getLocation, this); return new Point(map.get("x").intValue(), map.get("y").intValue()); } /** * @return True if the WebElement is displayed on the screen, * false otherwise. */ public boolean isDisplayed() { String isDisplayed = mDriver.getResourceAsString( R.raw.is_displayed_android); return (Boolean) executeAtom(isDisplayed, this); } /*package*/ String getId() { return mId; } Loading Loading @@ -237,15 +359,26 @@ public class WebElement { private List<WebElement> findElements(String strategy, String locator) { String findElements = mDriver.getResourceAsString( R.raw.find_elements_android); if (mId.equals("")) { return (List<WebElement>) executeAtom(findElements, strategy, locator); } else { return (List<WebElement>) executeAtom(findElements, strategy, locator, this); } } private WebElement findElement(String strategy, String locator) { String findElement = mDriver.getResourceAsString( R.raw.find_element_android); WebElement el = (WebElement) executeAtom(findElement, WebElement el; if (mId.equals("")) { el = (WebElement) executeAtom(findElement, strategy, locator); } else { el = (WebElement) executeAtom(findElement, strategy, locator, this); } if (el == null) { throw new WebElementNotFoundException("Could not find element " + "with " + strategy + ": " + locator); Loading core/java/android/webkit/webdriver/WebViewClient.java 0 → 100644 +125 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/webkit/WebView.java +4 −1 Original line number Diff line number Diff line Loading @@ -7324,7 +7324,10 @@ public class WebView extends AbsoluteLayout cursorData(), 1000); } /* package */ synchronized WebViewCore getWebViewCore() { /** * @hide */ public synchronized WebViewCore getWebViewCore() { return mWebViewCore; } Loading
core/java/android/webkit/WebViewCore.java +13 −4 Original line number Diff line number Diff line Loading @@ -48,7 +48,10 @@ import java.util.Set; import junit.framework.Assert; final class WebViewCore { /** * @hide */ public final class WebViewCore { private static final String LOGTAG = "webcore"; Loading Loading @@ -905,7 +908,10 @@ final class WebViewCore { "REMOVE_JS_INTERFACE", // = 149; }; class EventHub { /** * @hide */ public class EventHub { // Message Ids static final int REVEAL_SELECTION = 96; static final int REQUEST_LABEL = 97; Loading Loading @@ -936,7 +942,7 @@ final class WebViewCore { static final int DELETE_SELECTION = 122; static final int LISTBOX_CHOICES = 123; static final int SINGLE_LISTBOX_CHOICE = 124; static final int MESSAGE_RELAY = 125; public static final int MESSAGE_RELAY = 125; static final int SET_BACKGROUND_COLOR = 126; static final int SET_MOVE_FOCUS = 127; static final int SAVE_DOCUMENT_STATE = 128; Loading Loading @@ -1702,7 +1708,10 @@ final class WebViewCore { // If it needs WebCore, it has to send message. //------------------------------------------------------------------------- void sendMessage(Message msg) { /** * @hide */ public void sendMessage(Message msg) { mEventHub.sendMessage(msg); } Loading
core/java/android/webkit/webdriver/WebDriver.java +311 −19 Original line number Diff line number Diff line Loading @@ -16,15 +16,22 @@ package android.webkit.webdriver; import android.graphics.Point; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.webkit.WebView; import com.android.internal.R; import android.webkit.WebViewCore; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import com.android.internal.R; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; Loading Loading @@ -91,10 +98,24 @@ public class WebDriver { private static final int LOADING_TIMEOUT = 30000; // Timeout for executing JavaScript in the WebView in milliseconds. private static final int JS_EXECUTION_TIMEOUT = 10000; // Timeout for the MotionEvent to be completely handled private static final int MOTION_EVENT_TIMEOUT = 1000; // Timeout for detecting a new page load private static final int PAGE_STARTED_LOADING = 500; // Timeout for handling KeyEvents private static final int KEY_EVENT_TIMEOUT = 2000; // Commands posted to the handler private static final int CMD_GET_URL = 1; private static final int CMD_EXECUTE_SCRIPT = 2; private static final int CMD_SEND_TOUCH = 3; private static final int CMD_SEND_KEYS = 4; private static final int CMD_NAV_REFRESH = 5; private static final int CMD_NAV_BACK = 6; private static final int CMD_NAV_FORWARD = 7; private static final int CMD_SEND_KEYCODE = 8; private static final int CMD_MOVE_CURSOR_RIGHTMOST_POS = 9; private static final int CMD_MESSAGE_RELAY_ECHO = 10; private static final String ELEMENT_KEY = "ELEMENT"; private static final String STATUS = "status"; Loading @@ -107,24 +128,62 @@ public class WebDriver { // Used for synchronization private final Object mSyncObject; private final Object mSyncPageLoad; // Updated when the command is done executing in the main thread. private volatile boolean mCommandDone; // Used by WebViewClientWrapper.onPageStarted() to notify that // a page started loading. private volatile boolean mPageStartedLoading; // Used by WebChromeClientWrapper.onProgressChanged to notify when // a page finished loading. private volatile boolean mPageFinishedLoading; private WebView mWebView; private Navigation mNavigation; // This WebElement represents the object document.documentElement private WebElement mDocumentElement; // This Handler runs in the main UI thread. private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == CMD_GET_URL) { switch (msg.what) { case CMD_GET_URL: final String url = (String) msg.obj; mWebView.loadUrl(url); } else if (msg.what == CMD_EXECUTE_SCRIPT) { break; case CMD_EXECUTE_SCRIPT: mWebView.loadUrl("javascript:" + (String) msg.obj); break; case CMD_MESSAGE_RELAY_ECHO: notifyCommandDone(); break; case CMD_SEND_TOUCH: touchScreen((Point) msg.obj); notifyCommandDone(); break; case CMD_SEND_KEYS: dispatchKeys((CharSequence[]) msg.obj); notifyCommandDone(); break; case CMD_NAV_REFRESH: mWebView.reload(); break; case CMD_NAV_BACK: mWebView.goBack(); break; case CMD_NAV_FORWARD: mWebView.goForward(); break; case CMD_SEND_KEYCODE: dispatchKeyCodes((int[]) msg.obj); notifyCommandDone(); break; case CMD_MOVE_CURSOR_RIGHTMOST_POS: moveCursorToLeftMostPos((String) msg.obj); notifyCommandDone(); break; } } }; Loading Loading @@ -169,13 +228,13 @@ public class WebDriver { return values[i]; } } throw new IllegalArgumentException(intValue + " does not map to any ErrorCode."); return UNKNOWN_ERROR; } } public WebDriver(WebView webview) { this.mWebView = webview; mWebView.requestFocus(); if (mWebView == null) { throw new IllegalArgumentException("WebView cannot be null"); } Loading @@ -186,12 +245,25 @@ public class WebDriver { shouldRunInMainThread(true); mSyncObject = new Object(); mSyncPageLoad = new Object(); this.mWebView = webview; WebchromeClientWrapper chromeWrapper = new WebchromeClientWrapper( WebChromeClientWrapper chromeWrapper = new WebChromeClientWrapper( webview.getWebChromeClient(), this); mWebView.setWebChromeClient(chromeWrapper); WebViewClientWrapper viewWrapper = new WebViewClientWrapper( webview.getWebViewClient(), this); mWebView.setWebViewClient(viewWrapper); mWebView.addJavascriptInterface(new JavascriptResultReady(), "webdriver"); mDocumentElement = new WebElement(this, ""); mNavigation = new Navigation(); } /** * @return The title of the current page, null if not set. */ public String getTitle() { return mWebView.getTitle(); } /** Loading @@ -201,8 +273,7 @@ public class WebDriver { * @param url The URL to load. */ public void get(String url) { executeCommand(CMD_GET_URL, url, LOADING_TIMEOUT); mDocumentElement = (WebElement) executeScript("return document.body;"); mNavigation.to(url); } /** Loading Loading @@ -243,7 +314,7 @@ public class WebDriver { } /** * Clears the WebView. * Clears the WebView's state and closes associated views. */ public void quit() { mWebView.clearCache(true); Loading @@ -251,6 +322,7 @@ public class WebDriver { mWebView.clearHistory(); mWebView.clearSslPreferences(); mWebView.clearView(); mWebView.removeAllViewsInLayout(); } /** Loading Loading @@ -287,6 +359,44 @@ public class WebDriver { ")(" + escapeAndQuote(script) + ", " + scriptArgs + ", true)"); } public Navigation navigate() { return mNavigation; } /** * @hide */ public class Navigation { /* package */ Navigation () {} public void back() { navigate(CMD_NAV_BACK, null); } public void forward() { navigate(CMD_NAV_FORWARD, null); } public void to(String url) { navigate(CMD_GET_URL, url); } public void refresh() { navigate(CMD_NAV_REFRESH, null); } private void navigate(int command, String url) { synchronized (mSyncPageLoad) { mPageFinishedLoading = false; Message msg = mHandler.obtainMessage(command); msg.obj = url; mHandler.sendMessage(msg); waitForPageLoad(); } } } /** * Converts the arguments passed to a JavaScript friendly format. * Loading Loading @@ -338,9 +448,19 @@ public class WebDriver { } /* package */ Object executeRawJavascript(final String script) { if (mWebView.getUrl() == null) { throw new WebDriverException("Cannot operate on a blank page. " + "Load a page using WebDriver.get()."); } String result = executeCommand(CMD_EXECUTE_SCRIPT, "if (!window.webdriver || !window.webdriver.resultReady) {" + " return;" + "}" + "window.webdriver.resultReady(" + script + ")", JS_EXECUTION_TIMEOUT); if (result == null || "undefined".equals(result)) { return null; } try { JSONObject json = new JSONObject(result); throwIfError(json); Loading Loading @@ -369,6 +489,177 @@ public class WebDriver { return sb.toString(); } /* package */ void sendTouchScreen(Point coords) { // Reset state resetPageLoadState(); executeCommand(CMD_SEND_TOUCH, coords,LOADING_TIMEOUT); // Wait for the events to be fully handled waitForMessageRelay(MOTION_EVENT_TIMEOUT); // If a page started loading, block until page finishes loading waitForPageLoadIfNeeded(); } /* package */ void resetPageLoadState() { synchronized (mSyncPageLoad) { mPageStartedLoading = false; mPageFinishedLoading = false; } } /* package */ void waitForPageLoadIfNeeded() { synchronized (mSyncPageLoad) { Long end = System.currentTimeMillis() + PAGE_STARTED_LOADING; // Wait PAGE_STARTED_LOADING milliseconds to see if we detect a // page load. while (!mPageStartedLoading && (System.currentTimeMillis() <= end)) { try { // This is notified by WebChromeClientWrapper#onProgressChanged // when the page finished loading. mSyncPageLoad.wait(PAGE_STARTED_LOADING); } catch (InterruptedException e) { new RuntimeException(e); } } if (mPageStartedLoading) { waitForPageLoad(); } } } private void touchScreen(Point coords) { // Convert to screen coords // screen = JS x zoom - offset float zoom = mWebView.getScale(); float xOffset = mWebView.getX(); float yOffset = mWebView.getY(); Point screenCoords = new Point( (int)(coords.x*zoom - xOffset), (int)(coords.y*zoom - yOffset)); long downTime = SystemClock.uptimeMillis(); MotionEvent down = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, screenCoords.x, screenCoords.y, 0); down.setSource(InputDevice.SOURCE_TOUCHSCREEN); MotionEvent up = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, screenCoords.x, screenCoords.y, 0); up.setSource(InputDevice.SOURCE_TOUCHSCREEN); // Dispatch the events to WebView mWebView.dispatchTouchEvent(down); mWebView.dispatchTouchEvent(up); } /* package */ void notifyPageStartedLoading() { synchronized (mSyncPageLoad) { mPageStartedLoading = true; mSyncPageLoad.notify(); } } /* package */ void notifyPageFinishedLoading() { synchronized (mSyncPageLoad) { mPageFinishedLoading = true; mSyncPageLoad.notify(); } } /** * * @param keys The first element of the CharSequence should be the * existing value in the text input, or the empty string if none. */ /* package */ void sendKeys(CharSequence[] keys) { executeCommand(CMD_SEND_KEYS, keys, KEY_EVENT_TIMEOUT); // Wait for all KeyEvents to be handled waitForMessageRelay(KEY_EVENT_TIMEOUT); } /* package */ void sendKeyCodes(int[] keycodes) { executeCommand(CMD_SEND_KEYCODE, keycodes, KEY_EVENT_TIMEOUT); // Wait for all KeyEvents to be handled waitForMessageRelay(KEY_EVENT_TIMEOUT); } /* package */ void moveCursorToRightMostPosition(String value) { executeCommand(CMD_MOVE_CURSOR_RIGHTMOST_POS, value, KEY_EVENT_TIMEOUT); waitForMessageRelay(KEY_EVENT_TIMEOUT); } private void moveCursorToLeftMostPos(String value) { // If there is text, move the cursor to the rightmost position if (value != null && !value.equals("")) { long downTime = SystemClock.uptimeMillis(); KeyEvent down = new KeyEvent(downTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT, 0); KeyEvent up = new KeyEvent(downTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_RIGHT, value.length()); mWebView.dispatchKeyEvent(down); mWebView.dispatchKeyEvent(up); } } private void dispatchKeyCodes(int[] keycodes) { for (int i = 0; i < keycodes.length; i++) { KeyEvent down = new KeyEvent(KeyEvent.ACTION_DOWN, keycodes[i]); KeyEvent up = new KeyEvent(KeyEvent.ACTION_UP, keycodes[i]); mWebView.dispatchKeyEvent(down); mWebView.dispatchKeyEvent(up); } } private void dispatchKeys(CharSequence[] keys) { KeyCharacterMap chararcterMap = KeyCharacterMap.load( KeyCharacterMap.VIRTUAL_KEYBOARD); for (int i = 0; i < keys.length; i++) { CharSequence s = keys[i]; for (int j = 0; j < s.length(); j++) { KeyEvent[] events = chararcterMap.getEvents(new char[]{s.charAt(j)}); for (KeyEvent e : events) { mWebView.dispatchKeyEvent(e); } } } } private void waitForMessageRelay(long timeout) { synchronized (mSyncObject) { mCommandDone = false; } Message msg = Message.obtain(); msg.what = WebViewCore.EventHub.MESSAGE_RELAY; Message echo = mHandler.obtainMessage(CMD_MESSAGE_RELAY_ECHO); msg.obj = echo; mWebView.getWebViewCore().sendMessage(msg); synchronized (mSyncObject) { long end = System.currentTimeMillis() + timeout; while (!mCommandDone && (System.currentTimeMillis() <= end)) { try { // This is notifed by the mHandler when it receives the // MESSAGE_RELAY back mSyncObject.wait(timeout); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } private void waitForPageLoad() { long endLoad = System.currentTimeMillis() + LOADING_TIMEOUT; while (!mPageFinishedLoading && (System.currentTimeMillis() <= endLoad)) { try { mSyncPageLoad.wait(LOADING_TIMEOUT); } catch (InterruptedException e) { throw new RuntimeException(); } } } /** * Wraps the given string into quotes and escape existing quotes * and backslashes. Loading Loading @@ -531,7 +822,8 @@ public class WebDriver { long end = System.currentTimeMillis() + timeout; while (!mCommandDone) { if (System.currentTimeMillis() >= end) { throw new RuntimeException("Timeout executing command."); throw new RuntimeException("Timeout executing command: " + command); } try { mSyncObject.wait(timeout); Loading
core/java/android/webkit/webdriver/WebElement.java +137 −4 Original line number Diff line number Diff line Loading @@ -16,9 +16,13 @@ package android.webkit.webdriver; import android.graphics.Point; import android.view.KeyEvent; import com.android.internal.R; import java.util.List; import java.util.Map; /** * Represents an HTML element. Typically most interactions with a web page Loading Loading @@ -158,6 +162,124 @@ public class WebElement { return (Boolean) executeAtom(toggle, this); } /** * Sends the KeyEvents for the given sequence of characters to the * WebElement to simulate typing. The KeyEvents are generated using the * device's {@link android.view.KeyCharacterMap.VIRTUAL_KEYBOARD}. * * @param keys The keys to send to this WebElement */ public void sendKeys(CharSequence... keys) { if (keys == null || keys.length == 0) { return; } click(); mDriver.moveCursorToRightMostPosition(getAttribute("value")); mDriver.sendKeys(keys); } /** * Use this to send one of the key code constants defined in * {@link android.view.KeyEvent} * * @param keys */ public void sendKeyCodes(int... keys) { if (keys == null || keys.length == 0) { return; } click(); mDriver.moveCursorToRightMostPosition(getAttribute("value")); mDriver.sendKeyCodes(keys); } /** * Sends a touch event to the center coordinates of this WebElement. */ public void click() { Point topLeft = getLocation(); Point size = getSize(); int jsX = topLeft.x + size.x/2; int jsY = topLeft.y + size.y/2; Point center = new Point(jsX, jsY); mDriver.sendTouchScreen(center); } /** * Submits the form containing this WebElement. */ public void submit() { mDriver.resetPageLoadState(); String submit = mDriver.getResourceAsString(R.raw.submit_android); executeAtom(submit, this); mDriver.waitForPageLoadIfNeeded(); } /** * Clears the text value if this is a text entry element. Does nothing * otherwise. */ public void clear() { String value = getAttribute("value"); if (value == null || value.equals("")) { return; } int length = value.length(); int[] keys = new int[length]; for (int i = 0; i < length; i++) { keys[i] = KeyEvent.KEYCODE_DEL; } sendKeyCodes(keys); } /** * @return the value of the given CSS property if found, null otherwise. */ public String getCssValue(String cssProperty) { String getCssProp = mDriver.getResourceAsString( R.raw.get_value_of_css_property_android); return (String) executeAtom(getCssProp, this, cssProperty); } /** * Gets the width and height of the rendered element. * * @return a {@link android.graphics.Point}, where Point.x represents the * width, and Point.y represents the height of the element. */ public Point getSize() { String getSize = mDriver.getResourceAsString(R.raw.get_size_android); Map<String, Long> map = (Map<String, Long>) executeAtom(getSize, this); return new Point(map.get("width").intValue(), map.get("height").intValue()); } /** * Gets the location of the top left corner of this element on the screen. * If the element is not visisble, this will scroll to get the element into * the visisble screen. * * @return a {@link android.graphics.Point} containing the x and y * coordinates of the top left corner of this element. */ public Point getLocation() { String getLocation = mDriver.getResourceAsString( R.raw.get_top_left_coordinates_android); Map<String,Long> map = (Map<String, Long>) executeAtom(getLocation, this); return new Point(map.get("x").intValue(), map.get("y").intValue()); } /** * @return True if the WebElement is displayed on the screen, * false otherwise. */ public boolean isDisplayed() { String isDisplayed = mDriver.getResourceAsString( R.raw.is_displayed_android); return (Boolean) executeAtom(isDisplayed, this); } /*package*/ String getId() { return mId; } Loading Loading @@ -237,15 +359,26 @@ public class WebElement { private List<WebElement> findElements(String strategy, String locator) { String findElements = mDriver.getResourceAsString( R.raw.find_elements_android); if (mId.equals("")) { return (List<WebElement>) executeAtom(findElements, strategy, locator); } else { return (List<WebElement>) executeAtom(findElements, strategy, locator, this); } } private WebElement findElement(String strategy, String locator) { String findElement = mDriver.getResourceAsString( R.raw.find_element_android); WebElement el = (WebElement) executeAtom(findElement, WebElement el; if (mId.equals("")) { el = (WebElement) executeAtom(findElement, strategy, locator); } else { el = (WebElement) executeAtom(findElement, strategy, locator, this); } if (el == null) { throw new WebElementNotFoundException("Could not find element " + "with " + strategy + ": " + locator); Loading
core/java/android/webkit/webdriver/WebViewClient.java 0 → 100644 +125 −0 File added.Preview size limit exceeded, changes collapsed. Show changes