Loading core/java/android/webkit/webdriver/WebDriver.java 0 → 100644 +216 −0 Original line number Diff line number Diff line /* * Copyright (C) 2011 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.webdriver; import android.graphics.Bitmap; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.view.View; import android.webkit.ConsoleMessage; import android.webkit.GeolocationPermissions; import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebStorage; import android.webkit.WebView; /** * Drives a web application by controlling the WebView. This class * provides a DOM-like API allowing to get information about the page, * navigate, and interact with the web application. This is particularly useful * for testing a web application. * * <p/>{@link android.webkit.webdriver.WebDriver} should be created in the main * thread, and invoked from another thread. Here is a sample usage: * * public class WebDriverStubActivity extends Activity { * private WebDriver mDriver; * * public void onCreate(Bundle savedInstanceState) { * super.onCreate(savedInstanceState); * WebView view = new WebView(this); * mDriver = new WebDriver(view); * setContentView(view); * } * * * public WebDriver getDriver() { * return mDriver; * } *} * * public class WebDriverTest extends * ActivityInstrumentationTestCase2<WebDriverStubActivity>{ * private WebDriver mDriver; * * public WebDriverTest() { * super(WebDriverStubActivity.class); * } * * protected void setUp() throws Exception { * super.setUp(); * mDriver = getActivity().getDriver(); * } * * public void testGetIsBlocking() { * mDriver.get("http://google.com"); * assertTrue(mDriver.getPageSource().startsWith("<html")); * } *} * * @hide */ public class WebDriver { // Timeout for page load in milliseconds private static final int LOADING_TIMEOUT = 30000; // Timeout for executing JavaScript in the WebView in milliseconds private static final int JS_EXECUTION_TIMEOUT = 10000; // Commands posted to the handler private static final int GET_URL = 1; private static final int EXECUTE_SCRIPT = 2; private static final long MAIN_THREAD = Thread.currentThread().getId(); // This is updated by a callabck from JavaScript when the result is ready private String mJsResult; // Used for synchronization private final Object mSyncObject; // Updated when the command is done executing in the main thread. private volatile boolean mCommandDone; private WebView mWebView; // This Handler runs in the main UI thread private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == GET_URL) { final String url = (String) msg.obj; mWebView.loadUrl(url); } else if (msg.what == EXECUTE_SCRIPT) { executeScript((String) msg.obj); } } }; public WebDriver(WebView webview) { if (!mWebView.getSettings().getJavaScriptEnabled()) { throw new RuntimeException("Javascript is disabled in the WebView. " + "Enable it to use WebDriver"); } shouldRunInMainThread(true); mSyncObject = new Object(); this.mWebView = webview; WebchromeClientWrapper chromeWrapper = new WebchromeClientWrapper( webview.getWebChromeClient(), this); mWebView.setWebChromeClient(chromeWrapper); mWebView.addJavascriptInterface(new JavascriptResultReady(), "webdriver"); } /** * Loads a URL in the WebView. This function is blocking and will return * when the page has finished loading. * * @param url The URL to load. */ public void get(String url) { executeCommand(GET_URL, url, LOADING_TIMEOUT); } /** * @return The source page of the currently loaded page in WebView. */ public String getPageSource() { executeCommand(EXECUTE_SCRIPT, "return (new XMLSerializer())" + ".serializeToString(document.documentElement);", JS_EXECUTION_TIMEOUT); return mJsResult; } private void executeScript(String script) { mWebView.loadUrl("javascript:" + script); } private void shouldRunInMainThread(boolean value) { assert (value == (MAIN_THREAD == Thread.currentThread().getId())); } /** * Interface called from JavaScript when the result is ready. */ private class JavascriptResultReady { /** * A callback from JavaScript to Java that passes the result as a * parameter. This method is available from the WebView's * JavaScript DOM as window.webdriver.resultReady(). * * @param result The result that should be sent to Java from Javascript. */ public void resultReady(String result) { synchronized (mSyncObject) { mJsResult = result; mCommandDone = true; mSyncObject.notify(); } } } /* package */ void notifyCommandDone() { synchronized (mSyncObject) { mCommandDone = true; mSyncObject.notify(); } } /** * Executes the given command by posting a message to mHandler. This thread * will block until the command which runs in the main thread is done. * * @param command The command to run. * @param arg The argument for that command. * @param timeout A timeout in milliseconds. */ private void executeCommand(int command, String arg, long timeout) { shouldRunInMainThread(false); synchronized (mSyncObject) { mCommandDone = false; Message msg = mHandler.obtainMessage(command); msg.obj = arg; mHandler.sendMessage(msg); long end = System.currentTimeMillis() + timeout; while (!mCommandDone) { if (System.currentTimeMillis() >= end) { throw new RuntimeException("Timeout executing command."); } try { mSyncObject.wait(timeout); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } } core/java/android/webkit/webdriver/WebchromeClientWrapper.java 0 → 100644 +193 −0 Original line number Diff line number Diff line /* * Copyright (C) 2011 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.webdriver; import android.graphics.Bitmap; import android.net.Uri; import android.os.Message; import android.view.View; import android.webkit.ConsoleMessage; import android.webkit.GeolocationPermissions; import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebStorage; import android.webkit.WebView; /* package */ class WebchromeClientWrapper extends WebChromeClient { private final WebChromeClient mDelegate; private final WebDriver mDriver; public WebchromeClientWrapper(WebChromeClient delegate, WebDriver driver) { if (delegate == null) { this.mDelegate = new WebChromeClient(); } else { this.mDelegate = delegate; } this.mDriver = driver; } @Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress == 100) { mDriver.notifyCommandDone(); } mDelegate.onProgressChanged(view, newProgress); } @Override public void onReceivedTitle(WebView view, String title) { mDelegate.onReceivedTitle(view, title); } @Override public void onReceivedIcon(WebView view, Bitmap icon) { mDelegate.onReceivedIcon(view, icon); } @Override public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) { mDelegate.onReceivedTouchIconUrl(view, url, precomposed); } @Override public void onShowCustomView(View view, CustomViewCallback callback) { mDelegate.onShowCustomView(view, callback); } @Override public void onHideCustomView() { mDelegate.onHideCustomView(); } @Override public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture, Message resultMsg) { return mDelegate.onCreateWindow(view, dialog, userGesture, resultMsg); } @Override public void onRequestFocus(WebView view) { mDelegate.onRequestFocus(view); } @Override public void onCloseWindow(WebView window) { mDelegate.onCloseWindow(window); } @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return mDelegate.onJsAlert(view, url, message, result); } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return mDelegate.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { return mDelegate.onJsPrompt(view, url, message, defaultValue, result); } @Override public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) { return mDelegate.onJsBeforeUnload(view, url, message, result); } @Override public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { mDelegate.onExceededDatabaseQuota(url, databaseIdentifier, currentQuota, estimatedSize, totalUsedQuota, quotaUpdater); } @Override public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { mDelegate.onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota, quotaUpdater); } @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { mDelegate.onGeolocationPermissionsShowPrompt(origin, callback); } @Override public void onGeolocationPermissionsHidePrompt() { mDelegate.onGeolocationPermissionsHidePrompt(); } @Override public boolean onJsTimeout() { return mDelegate.onJsTimeout(); } @Override public void onConsoleMessage(String message, int lineNumber, String sourceID) { mDelegate.onConsoleMessage(message, lineNumber, sourceID); } @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { return mDelegate.onConsoleMessage(consoleMessage); } @Override public Bitmap getDefaultVideoPoster() { return mDelegate.getDefaultVideoPoster(); } @Override public View getVideoLoadingProgressView() { return mDelegate.getVideoLoadingProgressView(); } @Override public void getVisitedHistory(ValueCallback<String[]> callback) { mDelegate.getVisitedHistory(callback); } @Override public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) { mDelegate.openFileChooser(uploadFile, acceptType); } @Override public void setInstallableWebApp() { mDelegate.setInstallableWebApp(); } @Override public void setupAutoFill(Message msg) { mDelegate.setupAutoFill(msg); } } Loading
core/java/android/webkit/webdriver/WebDriver.java 0 → 100644 +216 −0 Original line number Diff line number Diff line /* * Copyright (C) 2011 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.webdriver; import android.graphics.Bitmap; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.view.View; import android.webkit.ConsoleMessage; import android.webkit.GeolocationPermissions; import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebStorage; import android.webkit.WebView; /** * Drives a web application by controlling the WebView. This class * provides a DOM-like API allowing to get information about the page, * navigate, and interact with the web application. This is particularly useful * for testing a web application. * * <p/>{@link android.webkit.webdriver.WebDriver} should be created in the main * thread, and invoked from another thread. Here is a sample usage: * * public class WebDriverStubActivity extends Activity { * private WebDriver mDriver; * * public void onCreate(Bundle savedInstanceState) { * super.onCreate(savedInstanceState); * WebView view = new WebView(this); * mDriver = new WebDriver(view); * setContentView(view); * } * * * public WebDriver getDriver() { * return mDriver; * } *} * * public class WebDriverTest extends * ActivityInstrumentationTestCase2<WebDriverStubActivity>{ * private WebDriver mDriver; * * public WebDriverTest() { * super(WebDriverStubActivity.class); * } * * protected void setUp() throws Exception { * super.setUp(); * mDriver = getActivity().getDriver(); * } * * public void testGetIsBlocking() { * mDriver.get("http://google.com"); * assertTrue(mDriver.getPageSource().startsWith("<html")); * } *} * * @hide */ public class WebDriver { // Timeout for page load in milliseconds private static final int LOADING_TIMEOUT = 30000; // Timeout for executing JavaScript in the WebView in milliseconds private static final int JS_EXECUTION_TIMEOUT = 10000; // Commands posted to the handler private static final int GET_URL = 1; private static final int EXECUTE_SCRIPT = 2; private static final long MAIN_THREAD = Thread.currentThread().getId(); // This is updated by a callabck from JavaScript when the result is ready private String mJsResult; // Used for synchronization private final Object mSyncObject; // Updated when the command is done executing in the main thread. private volatile boolean mCommandDone; private WebView mWebView; // This Handler runs in the main UI thread private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == GET_URL) { final String url = (String) msg.obj; mWebView.loadUrl(url); } else if (msg.what == EXECUTE_SCRIPT) { executeScript((String) msg.obj); } } }; public WebDriver(WebView webview) { if (!mWebView.getSettings().getJavaScriptEnabled()) { throw new RuntimeException("Javascript is disabled in the WebView. " + "Enable it to use WebDriver"); } shouldRunInMainThread(true); mSyncObject = new Object(); this.mWebView = webview; WebchromeClientWrapper chromeWrapper = new WebchromeClientWrapper( webview.getWebChromeClient(), this); mWebView.setWebChromeClient(chromeWrapper); mWebView.addJavascriptInterface(new JavascriptResultReady(), "webdriver"); } /** * Loads a URL in the WebView. This function is blocking and will return * when the page has finished loading. * * @param url The URL to load. */ public void get(String url) { executeCommand(GET_URL, url, LOADING_TIMEOUT); } /** * @return The source page of the currently loaded page in WebView. */ public String getPageSource() { executeCommand(EXECUTE_SCRIPT, "return (new XMLSerializer())" + ".serializeToString(document.documentElement);", JS_EXECUTION_TIMEOUT); return mJsResult; } private void executeScript(String script) { mWebView.loadUrl("javascript:" + script); } private void shouldRunInMainThread(boolean value) { assert (value == (MAIN_THREAD == Thread.currentThread().getId())); } /** * Interface called from JavaScript when the result is ready. */ private class JavascriptResultReady { /** * A callback from JavaScript to Java that passes the result as a * parameter. This method is available from the WebView's * JavaScript DOM as window.webdriver.resultReady(). * * @param result The result that should be sent to Java from Javascript. */ public void resultReady(String result) { synchronized (mSyncObject) { mJsResult = result; mCommandDone = true; mSyncObject.notify(); } } } /* package */ void notifyCommandDone() { synchronized (mSyncObject) { mCommandDone = true; mSyncObject.notify(); } } /** * Executes the given command by posting a message to mHandler. This thread * will block until the command which runs in the main thread is done. * * @param command The command to run. * @param arg The argument for that command. * @param timeout A timeout in milliseconds. */ private void executeCommand(int command, String arg, long timeout) { shouldRunInMainThread(false); synchronized (mSyncObject) { mCommandDone = false; Message msg = mHandler.obtainMessage(command); msg.obj = arg; mHandler.sendMessage(msg); long end = System.currentTimeMillis() + timeout; while (!mCommandDone) { if (System.currentTimeMillis() >= end) { throw new RuntimeException("Timeout executing command."); } try { mSyncObject.wait(timeout); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } }
core/java/android/webkit/webdriver/WebchromeClientWrapper.java 0 → 100644 +193 −0 Original line number Diff line number Diff line /* * Copyright (C) 2011 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.webdriver; import android.graphics.Bitmap; import android.net.Uri; import android.os.Message; import android.view.View; import android.webkit.ConsoleMessage; import android.webkit.GeolocationPermissions; import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebStorage; import android.webkit.WebView; /* package */ class WebchromeClientWrapper extends WebChromeClient { private final WebChromeClient mDelegate; private final WebDriver mDriver; public WebchromeClientWrapper(WebChromeClient delegate, WebDriver driver) { if (delegate == null) { this.mDelegate = new WebChromeClient(); } else { this.mDelegate = delegate; } this.mDriver = driver; } @Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress == 100) { mDriver.notifyCommandDone(); } mDelegate.onProgressChanged(view, newProgress); } @Override public void onReceivedTitle(WebView view, String title) { mDelegate.onReceivedTitle(view, title); } @Override public void onReceivedIcon(WebView view, Bitmap icon) { mDelegate.onReceivedIcon(view, icon); } @Override public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) { mDelegate.onReceivedTouchIconUrl(view, url, precomposed); } @Override public void onShowCustomView(View view, CustomViewCallback callback) { mDelegate.onShowCustomView(view, callback); } @Override public void onHideCustomView() { mDelegate.onHideCustomView(); } @Override public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture, Message resultMsg) { return mDelegate.onCreateWindow(view, dialog, userGesture, resultMsg); } @Override public void onRequestFocus(WebView view) { mDelegate.onRequestFocus(view); } @Override public void onCloseWindow(WebView window) { mDelegate.onCloseWindow(window); } @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return mDelegate.onJsAlert(view, url, message, result); } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return mDelegate.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { return mDelegate.onJsPrompt(view, url, message, defaultValue, result); } @Override public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) { return mDelegate.onJsBeforeUnload(view, url, message, result); } @Override public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { mDelegate.onExceededDatabaseQuota(url, databaseIdentifier, currentQuota, estimatedSize, totalUsedQuota, quotaUpdater); } @Override public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { mDelegate.onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota, quotaUpdater); } @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { mDelegate.onGeolocationPermissionsShowPrompt(origin, callback); } @Override public void onGeolocationPermissionsHidePrompt() { mDelegate.onGeolocationPermissionsHidePrompt(); } @Override public boolean onJsTimeout() { return mDelegate.onJsTimeout(); } @Override public void onConsoleMessage(String message, int lineNumber, String sourceID) { mDelegate.onConsoleMessage(message, lineNumber, sourceID); } @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { return mDelegate.onConsoleMessage(consoleMessage); } @Override public Bitmap getDefaultVideoPoster() { return mDelegate.getDefaultVideoPoster(); } @Override public View getVideoLoadingProgressView() { return mDelegate.getVideoLoadingProgressView(); } @Override public void getVisitedHistory(ValueCallback<String[]> callback) { mDelegate.getVisitedHistory(callback); } @Override public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) { mDelegate.openFileChooser(uploadFile, acceptType); } @Override public void setInstallableWebApp() { mDelegate.setInstallableWebApp(); } @Override public void setupAutoFill(Message msg) { mDelegate.setupAutoFill(msg); } }