package us.costan.chrome; import java.util.Map; import org.chromium.android_webview.AwBrowserContext; import org.chromium.android_webview.AwContents; import org.chromium.content.browser.ContentViewCore; import org.chromium.content.browser.ContentViewStatics; import org.chromium.content.browser.LoadUrlParams; import org.chromium.content.browser.ContentViewCore.JavaScriptCallback; import us.costan.chrome.impl.ChromeAwContentsClientProxy; import us.costan.chrome.impl.ChromeInitializer; import us.costan.chrome.impl.ChromeSettingsProxy; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Picture; import android.graphics.Rect; import android.net.http.SslCertificate; import android.os.Build; import android.os.Bundle; import android.os.Message; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.webkit.DownloadListener; import android.webkit.ValueCallback; import android.webkit.WebSettings; import android.webkit.WebView.FindListener; import android.widget.FrameLayout; /** WebView-like layer. */ public class ChromeView extends FrameLayout { /** The closest thing to a WebView that Chromium has to offer. */ private AwContents awContents_; /** Implements some of AwContents. */ private ContentViewCore contentViewCore_; /** Glue that passes calls from the Chromium view to a WebChromeClient. */ private ChromeAwContentsClientProxy awContentsClient_; /** Everything pertaining to the user's browsing session. */ private AwBrowserContext browserContext_; /** Glue that passes calls from the Chromium view to its parent (us). */ private ChromeInternalAcccessAdapter internalAccessAdapter_; public ChromeView(Context context) { this(context, null); } /** Constructor for inflating via XML. */ public ChromeView(Context context, AttributeSet attrs) { super(context, attrs, android.R.attr.webViewStyle); if (isInEditMode()) { return; // Chromium isn't loaded in edit mode. } try { Activity activity = (Activity)context; activity.getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); } catch(ClassCastException e) { // Hope that hardware acceleration is enabled. } SharedPreferences sharedPreferences = context.getSharedPreferences( "chromeview", Context.MODE_PRIVATE); // TODO(pwnall): is there a better way to get an AwBrowserContext? browserContext_ = new AwBrowserContext(sharedPreferences); internalAccessAdapter_ = new ChromeView.ChromeInternalAcccessAdapter(); awContentsClient_ = new ChromeAwContentsClientProxy(this); awContents_ = new AwContents(browserContext_, this, internalAccessAdapter_, awContentsClient_, true); contentViewCore_ = awContents_.getContentViewCore(); } //// Non-WebView extensions /** * Injects the passed Javascript code in the current page and evaluates it. * If a result is required, pass in a callback. * Used in automation tests. * * @param script The Javascript to execute. * @param message The callback to be fired off when a result is ready. The script's * result will be json encoded and passed as the parameter, and the call * will be made on the main thread. * If no result is required, pass null. * @throws IllegalStateException If the ContentView has been destroyed. */ public void evaluateJavaScript(String script, JavaScriptCallback callback) { if (awContents_ != null) { contentViewCore_.evaluateJavaScript(script, callback); } } //// WebView methods /** * Return the first substring consisting of the address of a physical * location. Currently, only addresses in the United States are detected, * and consist of: * - a house number * - a street name * - a street type (Road, Circle, etc), either spelled out or abbreviated * - a city name * - a state or territory, either spelled out or two-letter abbr. * - an optional 5 digit or 9 digit zip code. * * All names must be correctly capitalized, and the zip code, if present, * must be valid for the state. The street type must be a standard USPS * spelling or abbreviation. The state or territory must also be spelled * or abbreviated using USPS standards. The house number may not exceed * five digits. * @param addr The string to search for addresses. * * @return the address, or if no address is found, return null. */ public static String findAddress(String addr) { return ContentViewStatics.findAddress(addr); } /** * Sets the ChromeViewClient associated with this view. * * @param chromeViewClient the new handler; replaces the old handler * @see android.webkit.WebView#setWebViewClient(android.webkit.WebViewClient) * @see android.webkit.WebViewClient */ public void setChromeViewClient(ChromeViewClient chromeViewClient) { awContentsClient_.setChromeViewClient(chromeViewClient); } /** * Sets the ChromeWebClient associated with this view. * * @param chromeWebClient the new handler; replaces the old handler * @see android.webkit.WebView#setWebChromeClient(android.webkit.WebChromeClient) * @see android.webkit.WebChromeClient */ public void setChromeWebClient(ChromeWebClient chromeWebClient) { awContentsClient_.setChromeWebClient(chromeWebClient); } /** * Sets the DownloadListener associated with this view. * * @param downloadListener the new listener; replaces the old listener * @see android.webkit.WebView#setDownloadListener(DownloadListener) */ public void setDownloadListener(DownloadListener downloadListener) { awContentsClient_.setDownloadListener(downloadListener); } /** * Sets the FindListener associated with this view. * * @param findListener the new listener; replaces the old listener * @see android.webkit.WebView#setFindListener(FindListener) */ public void setFindListener(FindListener findListener) { awContentsClient_.setFindListener(findListener); } /** * Gets the SSL certificate for the main top-level page or null if there is * no certificate (the site is not secure). * * @return the SSL certificate for the main top-level page */ public SslCertificate getCertificate() { return awContents_.getCertificate(); } /** * Stores HTTP authentication credentials for a given host and realm. This * method is intended to be used with * {@link ChromeViewClient#onReceivedHttpAuthRequest(ChromeView, ChromeHttpAuthHandler, String, String)} * * @param host the host to which the credentials apply * @param realm the realm to which the credentials apply * @param username the username * @param password the password * @see getHttpAuthUsernamePassword * @see android.webkit.WebViewDatabase#hasHttpAuthUsernamePassword() * @see android.webkit.WebViewDatabase#clearHttpAuthUsernamePassword() */ public void setHttpAuthUsernamePassword(String host, String realm, String username, String password) { awContents_.setHttpAuthUsernamePassword(host, realm, username, password); } /** * Retrieves HTTP authentication credentials for a given host and realm. * This method is intended to be used with * {@link ChromeViewClient#onReceivedHttpAuthRequest(ChromeView, ChromeHttpAuthHandler, String, String)}. * * @param host the host to which the credentials apply * @param realm the realm to which the credentials apply * @return the credentials as a String array, if found. The first element * is the username and the second element is the password. Null if no * credentials are found. * @see setHttpAuthUsernamePassword * @see android.webkit.WebViewDatabase#hasHttpAuthUsernamePassword() * @see android.webkit.WebViewDatabase#clearHttpAuthUsernamePassword() */ public String[] getHttpAuthUsernamePassword(String host, String realm) { return awContents_.getHttpAuthUsernamePassword(host, realm); } /** * Saves the state of this WebView used in * {@link android.app.Activity#onSaveInstanceState}. Please note that this * method no longer stores the display data for this WebView. The previous * behavior could potentially leak files if {@link #restoreState} was never * called. * * @param outState the Bundle to store this WebView's state * @return false if saveState fails */ public boolean saveState(Bundle outState) { return awContents_.saveState(outState); } /** * Restores the state of this WebView from the given Bundle. This method is * intended for use in {@link android.app.Activity#onRestoreInstanceState} * and should be called to restore the state of this WebView. If * it is called after this WebView has had a chance to build state (load * pages, create a back/forward list, etc.) there may be undesirable * side-effects. Please note that this method no longer restores the * display data for this WebView. * * @param inState the incoming Bundle of state * @return false if restoreState failed */ public boolean restoreState(Bundle inState) { return awContents_.restoreState(inState); } /** * Loads the given URL with the specified additional HTTP headers. * * @param url the URL of the resource to load * @param additionalHttpHeaders the additional headers to be used in the * HTTP request for this URL, specified as a map from name to * value. Note that if this map contains any of the headers * that are set by default by this WebView, such as those * controlling caching, accept types or the User-Agent, their * values may be overriden by this WebView's defaults. */ public void loadUrl(String url, Map<String, String> additionalHttpHeaders) { LoadUrlParams loadUrlParams = new LoadUrlParams(url); loadUrlParams.setExtraHeaders(additionalHttpHeaders); awContents_.loadUrl(loadUrlParams); } /** * Loads the given URL. * * @param url the URL of the resource to load */ public void loadUrl(String url) { awContents_.loadUrl(new LoadUrlParams(url)); } /** * Loads the URL with postData using "POST" method into this WebView. If url * is not a network URL, it will be loaded with {link * {@link #loadUrl(String)} instead. * * @param url the URL of the resource to load * @param postData the data will be passed to "POST" request */ public void postUrl(String url, byte[] postData) { awContents_.loadUrl(LoadUrlParams.createLoadHttpPostParams(url, postData)); } /** * Loads the given data into this WebView using a 'data' scheme URL. * <p> * Note that JavaScript's same origin policy means that script running in a * page loaded using this method will be unable to access content loaded * using any scheme other than 'data', including 'http(s)'. To avoid this * restriction, use {@link * #loadDataWithBaseURL(String,String,String,String,String) * loadDataWithBaseURL()} with an appropriate base URL. * <p> * The encoding parameter specifies whether the data is base64 or URL * encoded. If the data is base64 encoded, the value of the encoding * parameter must be 'base64'. For all other values of the parameter, * including null, it is assumed that the data uses ASCII encoding for * octets inside the range of safe URL characters and use the standard %xx * hex encoding of URLs for octets outside that range. For example, '#', * '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively. * <p> * The 'data' scheme URL formed by this method uses the default US-ASCII * charset. If you need need to set a different charset, you should form a * 'data' scheme URL which explicitly specifies a charset parameter in the * mediatype portion of the URL and call {@link #loadUrl(String)} instead. * Note that the charset obtained from the mediatype portion of a data URL * always overrides that specified in the HTML or XML document itself. * * @param data a String of data in the given encoding * @param mimeType the MIME type of the data, e.g. 'text/html' * @param encoding the encoding of the data */ public void loadData(String data, String mimeType, String encoding) { LoadUrlParams loadUrlParams = LoadUrlParams.createLoadDataParams(data, mimeType, encoding.equals("base64")); awContents_.loadUrl(loadUrlParams); } /** * Loads the given data into this WebView, using baseUrl as the base URL for * the content. The base URL is used both to resolve relative URLs and when * applying JavaScript's same origin policy. The historyUrl is used for the * history entry. * <p> * Note that content specified in this way can access local device files * (via 'file' scheme URLs) only if baseUrl specifies a scheme other than * 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'. * <p> * If the base URL uses the data scheme, this method is equivalent to * calling {@link #loadData(String,String,String) loadData()} and the * historyUrl is ignored. * * @param baseUrl the URL to use as the page's base URL. If null defaults to * 'about:blank'. * @param data a String of data in the given encoding * @param mimeType the MIMEType of the data, e.g. 'text/html'. If null, * defaults to 'text/html'. * @param encoding the encoding of the data * @param historyUrl the URL to use as the history entry. If null defaults * to 'about:blank'. */ public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) { LoadUrlParams loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(data, mimeType, encoding.equals("base64"), baseUrl, historyUrl); awContents_.loadUrl(loadUrlParams); } /** * Saves the current view as a web archive. * * @param filename the filename where the archive should be placed */ public void saveWebArchive(String filename) { ValueCallback<String> callback = new ValueCallback<String>() { @Override public void onReceiveValue(String value) { } }; awContents_.saveWebArchive(filename, false, callback); } /** * Saves the current view as a web archive. * * @param basename the filename where the archive should be placed * @param autoname if false, takes basename to be a file. If true, basename * is assumed to be a directory in which a filename will be * chosen according to the URL of the current page. * @param callback called after the web archive has been saved. The * parameter for onReceiveValue will either be the filename * under which the file was saved, or null if saving the * file failed. */ public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) { awContents_.saveWebArchive(basename, autoname, callback); } /** * Stops the current load. */ public void stopLoading() { awContents_.stopLoading(); } /** * Reloads the current URL. */ public void reload() { awContents_.reload(); } /** * Gets whether this WebView has a back history item. * * @return true iff this WebView has a back history item */ public boolean canGoBack() { return awContents_.canGoBack(); } /** * Goes back in the history of this WebView. */ public void goBack() { awContents_.goBack(); } /** * Gets whether this WebView has a forward history item. * * @return true iff this Webview has a forward history item */ public boolean canGoForward() { return awContents_.canGoForward(); } /** * Goes forward in the history of this WebView. */ public void goForward() { awContents_.goForward(); } /** * Gets whether the page can go back or forward the given * number of steps. * * @param steps the negative or positive number of steps to move the * history */ public boolean canGoBackOrForward(int steps) { return awContents_.canGoBackOrForward(steps); } /** * Goes to the history item that is the number of steps away from * the current item. Steps is negative if backward and positive * if forward. * * @param steps the number of steps to take back or forward in the back * forward list */ public void goBackOrForward(int steps) { awContents_.goBackOrForward(steps); } /** * Gets whether private browsing is enabled in this WebView. */ public boolean isPrivateBrowsingEnabled() { return false; } /** * Scrolls the contents of this WebView up by half the view size. * * @param top true to jump to the top of the page * @return true if the page was scrolled */ public boolean pageUp(boolean top) { return awContents_.pageUp(top); } /** * Scrolls the contents of this WebView down by half the page size. * * @param bottom true to jump to bottom of page * @return true if the page was scrolled */ public boolean pageDown(boolean bottom) { return awContents_.pageDown(bottom); } /** * Gets a new picture that captures the current contents of this WebView. * The picture is of the entire document being displayed, and is not * limited to the area currently displayed by this WebView. Also, the * picture is a static copy and is unaffected by later changes to the * content being displayed. * <p> * Note that due to internal changes, for API levels between * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} inclusive, the * picture does not include fixed position elements or scrollable divs. * * @return a picture that captures the current contents of this WebView */ public Picture capturePicture() { return awContents_.capturePicture(); } /** * Sets the initial scale for this WebView. 0 means default. If * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the * way. Otherwise it starts with 100%. If initial scale is greater than 0, * WebView starts with this value as initial scale. * Please note that unlike the scale properties in the viewport meta tag, * this method doesn't take the screen density into account. * * @param scaleInPercent the initial scale in percent */ public void setInitialScale(int scaleInPercent) { // TODO(pwnall): seems useful } /** * Invokes the graphical zoom picker widget for this WebView. This will * result in the zoom widget appearing on the screen to control the zoom * level of this WebView. */ public void invokeZoomPicker() { awContents_.invokeZoomPicker(); } /** * Requests the anchor or image element URL at the last tapped point. * If hrefMsg is null, this method returns immediately and does not * dispatch hrefMsg to its target. If the tapped point hits an image, * an anchor, or an image in an anchor, the message associates * strings in named keys in its data. The value paired with the key * may be an empty string. * * @param hrefMsg the message to be dispatched with the result of the * request. The message data contains three keys. "url" * returns the anchor's href attribute. "title" returns the * anchor's text. "src" returns the image's src attribute. */ public void requestFocusNodeHref(Message hrefMsg) { awContents_.requestFocusNodeHref(hrefMsg); } /** * Requests the URL of the image last touched by the user. msg will be sent * to its target with a String representing the URL as its object. * * @param msg the message to be dispatched with the result of the request * as the data member with "url" as key. The result can be null. */ public void requestImageRef(Message msg) { awContents_.requestImageRef(msg); } /** * Gets the URL for the current page. This is not always the same as the URL * passed to WebViewClient.onPageStarted because although the load for * that URL has begun, the current page may not have changed. * * @return the URL for the current page */ public String getUrl() { return awContents_.getUrl(); } /** * Gets the original URL for the current page. This is not always the same * as the URL passed to WebViewClient.onPageStarted because although the * load for that URL has begun, the current page may not have changed. * Also, there may have been redirects resulting in a different URL to that * originally requested. * * @return the URL that was originally requested for the current page */ public String getOriginalUrl() { return awContents_.getOriginalUrl(); } /** * Gets the title for the current page. This is the title of the current page * until WebViewClient.onReceivedTitle is called. * * @return the title for the current page */ public String getTitle() { return awContents_.getTitle(); } /** * Gets the favicon for the current page. This is the favicon of the current * page until WebViewClient.onReceivedIcon is called. * * @return the favicon for the current page */ public Bitmap getFavicon() { return awContents_.getFavicon(); } /** * Gets the height of the HTML content. * * @return the height of the HTML content */ public int getContentHeight() { return awContents_.getContentHeightCss(); } /** * Gets the width of the HTML content. * * @return the width of the HTML content * @hide */ public int getContentWidth() { return awContents_.getContentWidthCss(); } /** * Pauses all layout, parsing, and JavaScript timers for all WebViews. This * is a global requests, not restricted to just this WebView. This can be * useful if the application has been paused. */ public void pauseTimers() { awContents_.pauseTimers(); } /** * Resumes all layout, parsing, and JavaScript timers for all WebViews. * This will resume dispatching all timers. */ public void resumeTimers() { awContents_.resumeTimers(); } /** * Pauses any extra processing associated with this WebView and its * associated DOM, plugins, JavaScript etc. For example, if this WebView is * taken offscreen, this could be called to reduce unnecessary CPU or * network traffic. When this WebView is again "active", call onResume(). * Note that this differs from pauseTimers(), which affects all WebViews. */ public void onPause() { awContents_.onPause(); } /** * Resumes a WebView after a previous call to onPause(). */ public void onResume() { awContents_.onResume(); } /** * Gets whether this WebView is paused, meaning onPause() was called. * Calling onResume() sets the paused state back to false. * * @hide */ public boolean isPaused() { return awContents_.isPaused(); } /** * Clears the resource cache. Note that the cache is per-application, so * this will clear the cache for all WebViews used. * * @param includeDiskFiles if false, only the RAM cache is cleared */ public void clearCache(boolean includeDiskFiles) { awContents_.clearCache(includeDiskFiles); } /** * Tells this WebView to clear its internal back/forward list. */ public void clearHistory() { awContents_.clearHistory(); } /** * Clears the SSL preferences table stored in response to proceeding with * SSL certificate errors. */ public void clearSslPreferences() { awContents_.clearSslPreferences(); } /** * Highlights and scrolls to the next match found by * {@link #findAllAsync}, wrapping around page boundaries as necessary. * Notifies any registered {@link FindListener}. If {@link #findAllAsync(String)} * has not been called yet, or if {@link #clearMatches} has been called since the * last find operation, this function does nothing. * * @param forward the direction to search * @see #setFindListener */ public void findNext(boolean forward) { awContents_.findNext(forward); } /** * Finds all instances of find on the page and highlights them, * asynchronously. Notifies any registered {@link FindListener}. * Successive calls to this will cancel any pending searches. * * @param find the string to find. * @see #setFindListener */ public void findAllAsync(String find) { awContents_.findAllAsync(find); } /** * Starts an ActionMode for finding text in this WebView. Only works if this * WebView is attached to the view system. * * @param text if non-null, will be the initial text to search for. * Otherwise, the last String searched for in this WebView will * be used to start. * @param showIme if true, show the IME, assuming the user will begin typing. * If false and text is non-null, perform a find all. * @return true if the find dialog is shown, false otherwise */ public boolean showFindDialog(String text, boolean showIme) { // TODO(pwnall): seems useful return false; } /** * Clears the highlighting surrounding text matches created by * {@link #findAllAsync}. */ public void clearMatches() { awContents_.clearMatches(); } /** * Queries the document to see if it contains any image references. The * message object will be dispatched with arg1 being set to 1 if images * were found and 0 if the document does not reference any images. * * @param response the message that will be dispatched with the result */ public void documentHasImages(Message response) { awContents_.documentHasImages(response); } /** * Injects the supplied Java object into this WebView. The object is * injected into the JavaScript context of the main frame, using the * supplied name. This allows the Java object's methods to be * accessed from JavaScript. Only public methods that are annotated with * {@link us.costan.chrome.ChromeJavascriptInterface} can be accessed from * JavaScript. * <p> Note that injected objects will not * appear in JavaScript until the page is next (re)loaded. For example: * <pre> * class JsObject { * {@literal @}ChromeJavascriptInterface * public String toString() { return "injectedObject"; } * } * webView.addJavascriptInterface(new JsObject(), "injectedObject"); * webView.loadData("<!DOCTYPE html><title></title>", "text/html", null); * webView.loadUrl("javascript:alert(injectedObject.toString())");</pre> * <p> * <strong>IMPORTANT:</strong> * <ul> * <li> This method can be used to allow JavaScript to control the host * application. Use of this method in a WebView * containing untrusted content could allow an attacker to manipulate the * host application in unintended ways, executing Java code with the * permissions of the host application. Use extreme care when using this * method in a WebView which could contain untrusted content.</li> * <li> JavaScript interacts with Java object on a private, background * thread of this WebView. Care is therefore required to maintain thread * safety.</li> * <li> The Java object's fields are not accessible.</li> * </ul> * * @param object the Java object to inject into this WebView's JavaScript * context. Null values are ignored. * @param name the name used to expose the object in JavaScript */ public void addJavascriptInterface(Object object, String name) { awContents_.addPossiblyUnsafeJavascriptInterface(object, name, ChromeJavascriptInterface.class); } /** * Removes a previously injected Java object from this WebView. Note that * the removal will not be reflected in JavaScript until the page is next * (re)loaded. See {@link #addJavascriptInterface}. * * @param name the name used to expose the object in JavaScript */ public void removeJavascriptInterface(String name) { awContents_.removeJavascriptInterface(name); } /** * Gets the ChromeSettings object used to control the settings for this * ChromeView. * * @return a ChromeSettings object that can be used to control this * ChromeView's settings */ public ChromeSettings getSettings() { return new ChromeSettingsProxy(awContents_); } public void flingScroll(int vx, int vy) { awContents_.flingScroll(vx, vy); } /** * Performs zoom in in this WebView. * * @return true if zoom in succeeds, false if no zoom changes */ public boolean zoomIn() { return awContents_.zoomIn(); } /** * Performs zoom out in this WebView. * * @return true if zoom out succeeds, false if no zoom changes */ public boolean zoomOut() { return awContents_.zoomOut(); } //// Methods outside WebView. /** * Sets up the Chromium libraries backing ChromeView. * * This should be called from {@link android.app.Application#onCreate()}. * * @param context Android context for the application using ChromeView */ public static void initialize(Context context) { ChromeInitializer.initialize(context); } /** * The object that implements the WebView API. */ public ContentViewCore getContentViewCore() { return contentViewCore_; } //// Forward a bunch of calls to the Chromium view. //// Lifted from chromium/src/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView public void destroy() { awContents_.destroy(); } @SuppressLint("WrongCall") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (awContents_ != null) { awContents_.onMeasure(widthMeasureSpec, heightMeasureSpec); } else { } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (awContents_ != null) { awContents_.onSizeChanged(w, h, oldw, oldh); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (awContents_ != null) { awContents_.onAttachedToWindow(); } } @Override protected void onConfigurationChanged(Configuration newConfig) { if (awContents_ != null) { awContents_.onConfigurationChanged(newConfig); } else { super.onConfigurationChanged(newConfig); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (awContents_ != null) { awContents_.onDetachedFromWindow(); } } @SuppressLint("WrongCall") @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (awContents_ != null) { awContents_.onDraw(canvas); } } @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); if (awContents_ != null) { awContents_.onFocusChanged(gainFocus, direction, previouslyFocusedRect); } } @Override public boolean onTouchEvent(MotionEvent event) { if (awContents_ != null) { return awContents_.onTouchEvent(event); } else { return super.onTouchEvent(event); } } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (awContents_ != null) { awContents_.onVisibilityChanged(changedView, visibility); } } @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); if (awContents_ != null) { awContents_.onWindowVisibilityChanged(visibility); } } //// Forward a bunch more calls to the Chromium view. //// Lifted from //// platform/frameworks/base ./core/java/android/webkit/WebKit.java @Override protected int computeHorizontalScrollRange() { if (awContents_ != null) { return awContents_.computeHorizontalScrollRange(); } else { return super.computeHorizontalScrollRange(); } } @Override protected int computeHorizontalScrollOffset() { if (awContents_ != null) { return awContents_.computeHorizontalScrollOffset(); } else { return super.computeHorizontalScrollOffset(); } } @Override protected int computeVerticalScrollRange() { if (awContents_ != null) { return awContents_.computeVerticalScrollRange(); } else { return super.computeVerticalScrollRange(); } } @Override protected int computeVerticalScrollOffset() { if (awContents_ != null) { return awContents_.computeVerticalScrollOffset(); } else { return super.computeVerticalScrollOffset(); } } @Override protected int computeVerticalScrollExtent() { if (awContents_ != null) { return awContents_.computeVerticalScrollExtent(); } else { return super.computeVerticalScrollExtent(); } } @Override public boolean onHoverEvent(MotionEvent event) { if (awContents_ != null) { return awContents_.onHoverEvent(event); } else { return super.onHoverEvent(event); } } @Override public boolean onGenericMotionEvent(MotionEvent event) { if (awContents_ != null) { return awContents_.onGenericMotionEvent(event); } else { return super.onGenericMotionEvent(event); } } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (awContents_ != null) { return awContents_.onKeyUp(keyCode, event); } else { return super.onKeyUp(keyCode, event); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); if (awContents_ != null) { info.setClassName(ChromeView.class.getName()); awContents_.onInitializeAccessibilityNodeInfo(info); } } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); if (awContents_ != null) { event.setClassName(ChromeView.class.getName()); awContents_.onInitializeAccessibilityEvent(event); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public boolean performAccessibilityAction(int action, Bundle arguments) { if (awContents_ != null) { // Lifted from content.browser.JellyBeanContentView if (contentViewCore_.supportsAccessibilityAction(action)) { return awContents_.performAccessibilityAction(action, arguments); } } return super.performAccessibilityAction(action, arguments); } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { if (awContents_ != null) { return awContents_.onCreateInputConnection(outAttrs); } else { return super.onCreateInputConnection(outAttrs); } } @Override public void onWindowFocusChanged(boolean hasWindowFocus) { if (awContents_ != null) { awContents_.onWindowFocusChanged(hasWindowFocus); } super.onWindowFocusChanged(hasWindowFocus); } @Override public boolean dispatchKeyEvent(KeyEvent event) { if (awContents_ != null) { return awContents_.dispatchKeyEvent(event); } else { return super.dispatchKeyEvent(event); } } @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { if (awContents_ != null) { return contentViewCore_.dispatchKeyEventPreIme(event); } else { return super.dispatchKeyEventPreIme(event); } } //// Forward calls that ContentViewCore responds to. @Override public boolean onCheckIsTextEditor() { if (awContents_ != null) { return contentViewCore_.onCheckIsTextEditor(); } else { return super.onCheckIsTextEditor(); } } @Override public void scrollTo(int x, int y) { super.scrollTo(x, y); if (awContents_ != null) { contentViewCore_.scrollTo(x, y); } } @Override public void scrollBy(int x, int y) { super.scrollBy(x, y); if (awContents_ != null) { contentViewCore_.scrollBy(x, y); } } @Override protected boolean awakenScrollBars(int startDelay, boolean invalidate) { if (awContents_ != null) { return contentViewCore_.awakenScrollBars(startDelay, invalidate); } else { return super.awakenScrollBars(startDelay, invalidate); } } /** Glue that passes calls from the Chromium view to its container (us). */ private class ChromeInternalAcccessAdapter implements AwContents.InternalAccessDelegate { //// Lifted from chromium/src/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView @Override public boolean drawChild(Canvas canvas, View child, long drawingTime) { return ChromeView.this.drawChild(canvas, child, drawingTime); } @Override public boolean super_onKeyUp(int keyCode, KeyEvent event) { return ChromeView.super.onKeyUp(keyCode, event); } @Override public boolean super_dispatchKeyEventPreIme(KeyEvent event) { return ChromeView.super.dispatchKeyEventPreIme(event); } @Override public boolean super_dispatchKeyEvent(KeyEvent event) { return ChromeView.super.dispatchKeyEvent(event); } @Override public boolean super_onGenericMotionEvent(MotionEvent event) { return ChromeView.super.onGenericMotionEvent(event); } @Override public void super_onConfigurationChanged(Configuration newConfig) { ChromeView.super.onConfigurationChanged(newConfig); } @Override public void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix) { ChromeView.this.onScrollChanged(lPix, tPix, oldlPix, oldtPix); } @Override public boolean awakenScrollBars() { return ChromeView.this.awakenScrollBars(); } @Override public boolean super_awakenScrollBars(int startDelay, boolean invalidate) { return ChromeView.super.awakenScrollBars(startDelay, invalidate); } @Override public void setMeasuredDimension(int measuredWidth, int measuredHeight) { ChromeView.this.setMeasuredDimension(measuredWidth, measuredHeight); } @Override public boolean requestDrawGL(Canvas canvas) { if (canvas != null) { if (canvas.isHardwareAccelerated()) { // TODO(pwnall): figure out what AwContents wants from us, and do it; // most likely something to do with // AwContents.getAwDrawGLFunction() return false; } else { return false; } } else { if (ChromeView.this.isHardwareAccelerated()) { // TODO(pwnall): figure out what AwContents wants from us, and do it; // most likely something to do with // AwContents.getAwDrawGLFunction() return false; } else { return false; } } } } }