1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.android_webview; 6 7import android.content.Context; 8import android.content.pm.ActivityInfo; 9import android.graphics.Bitmap; 10import android.graphics.Picture; 11import android.graphics.Rect; 12import android.graphics.RectF; 13import android.net.http.SslError; 14import android.os.Handler; 15import android.os.Looper; 16import android.os.Message; 17import android.util.Log; 18import android.view.KeyEvent; 19import android.view.View; 20import android.webkit.ConsoleMessage; 21import android.webkit.GeolocationPermissions; 22import android.webkit.SslErrorHandler; 23import android.webkit.ValueCallback; 24import android.webkit.WebChromeClient; 25 26import org.chromium.base.ThreadUtils; 27import org.chromium.content.browser.ContentVideoView; 28import org.chromium.content.browser.ContentVideoViewClient; 29import org.chromium.content.browser.ContentVideoViewControls; 30import org.chromium.content.browser.ContentViewClient; 31import org.chromium.content.browser.ContentViewCore; 32import org.chromium.content.browser.WebContentsObserverAndroid; 33import org.chromium.net.NetError; 34 35/** 36 * Base-class that an AwContents embedder derives from to receive callbacks. 37 * This extends ContentViewClient, as in many cases we want to pass-thru ContentViewCore 38 * callbacks right to our embedder, and this setup facilities that. 39 * For any other callbacks we need to make transformations of (e.g. adapt parameters 40 * or perform filtering) we can provide final overrides for methods here, and then introduce 41 * new abstract methods that the our own client must implement. 42 * i.e.: all methods in this class should either be final, or abstract. 43 */ 44public abstract class AwContentsClient { 45 46 private static final String TAG = "AwContentsClient"; 47 private final AwContentsClientCallbackHelper mCallbackHelper = 48 new AwContentsClientCallbackHelper(this); 49 50 private AwWebContentsObserver mWebContentsObserver; 51 52 private AwContentViewClient mContentViewClient = new AwContentViewClient(); 53 54 // Last background color reported from the renderer. Holds the sentinal value INVALID_COLOR 55 // if not valid. 56 private int mCachedRendererBackgroundColor = INVALID_COLOR; 57 58 private static final int INVALID_COLOR = 0; 59 60 class AwWebContentsObserver extends WebContentsObserverAndroid { 61 public AwWebContentsObserver(ContentViewCore contentViewCore) { 62 super(contentViewCore); 63 } 64 65 @Override 66 public void didStopLoading(final String url) { 67 ThreadUtils.postOnUiThread(new Runnable() { 68 @Override 69 public void run() { 70 AwContentsClient.this.onPageFinished(url); 71 } 72 }); 73 } 74 75 @Override 76 public void didFailLoad(boolean isProvisionalLoad, 77 boolean isMainFrame, int errorCode, String description, String failingUrl) { 78 if (errorCode == NetError.ERR_ABORTED) { 79 // This error code is generated for the following reasons: 80 // - WebView.stopLoading is called, 81 // - the navigation is intercepted by the embedder via shouldOverrideNavigation. 82 // 83 // The Android WebView does not notify the embedder of these situations using this 84 // error code with the WebViewClient.onReceivedError callback. 85 return; 86 } 87 if (!isMainFrame) { 88 // The Android WebView does not notify the embedder of sub-frame failures. 89 return; 90 } 91 AwContentsClient.this.onReceivedError( 92 ErrorCodeConversionHelper.convertErrorCode(errorCode), description, failingUrl); 93 } 94 95 @Override 96 public void didNavigateAnyFrame(String url, String baseUrl, boolean isReload) { 97 AwContentsClient.this.doUpdateVisitedHistory(url, isReload); 98 } 99 100 } 101 102 private class AwContentViewClient extends ContentViewClient { 103 @Override 104 public void onBackgroundColorChanged(int color) { 105 // Avoid storing the sentinal INVALID_COLOR (note that both 0 and 1 are both 106 // fully transparent so this transpose makes no visible difference). 107 mCachedRendererBackgroundColor = color == INVALID_COLOR ? 1 : color; 108 } 109 110 @Override 111 public void onStartContentIntent(Context context, String contentUrl) { 112 // Callback when detecting a click on a content link. 113 AwContentsClient.this.shouldOverrideUrlLoading(contentUrl); 114 } 115 116 @Override 117 public void onRendererCrash(boolean crashedWhileOomProtected) { 118 // This is not possible so long as the webview is run single process! 119 throw new RuntimeException("Renderer crash reported."); 120 } 121 122 @Override 123 public void onUpdateTitle(String title) { 124 AwContentsClient.this.onReceivedTitle(title); 125 } 126 127 @Override 128 public boolean shouldOverrideKeyEvent(KeyEvent event) { 129 return AwContentsClient.this.shouldOverrideKeyEvent(event); 130 } 131 132 @Override 133 final public ContentVideoViewClient getContentVideoViewClient() { 134 return new AwContentVideoViewClient(); 135 } 136 } 137 138 final void installWebContentsObserver(ContentViewCore contentViewCore) { 139 if (mWebContentsObserver != null) { 140 mWebContentsObserver.detachFromWebContents(); 141 } 142 mWebContentsObserver = new AwWebContentsObserver(contentViewCore); 143 } 144 145 private class AwContentVideoViewClient implements ContentVideoViewClient { 146 @Override 147 public void onShowCustomView(View view) { 148 WebChromeClient.CustomViewCallback cb = new WebChromeClient.CustomViewCallback() { 149 @Override 150 public void onCustomViewHidden() { 151 ContentVideoView contentVideoView = ContentVideoView.getContentVideoView(); 152 if (contentVideoView != null) 153 contentVideoView.exitFullscreen(false); 154 } 155 }; 156 AwContentsClient.this.onShowCustomView(view, cb); 157 } 158 159 @Override 160 public void onDestroyContentVideoView() { 161 AwContentsClient.this.onHideCustomView(); 162 } 163 164 @Override 165 public View getVideoLoadingProgressView() { 166 return AwContentsClient.this.getVideoLoadingProgressView(); 167 } 168 169 @Override 170 public ContentVideoViewControls createControls() { 171 return null; 172 } 173 } 174 175 final AwContentsClientCallbackHelper getCallbackHelper() { 176 return mCallbackHelper; 177 } 178 179 final ContentViewClient getContentViewClient() { 180 return mContentViewClient; 181 } 182 183 final int getCachedRendererBackgroundColor() { 184 assert isCachedRendererBackgroundColorValid(); 185 return mCachedRendererBackgroundColor; 186 } 187 188 final boolean isCachedRendererBackgroundColorValid() { 189 return mCachedRendererBackgroundColor != INVALID_COLOR; 190 } 191 192 //-------------------------------------------------------------------------------------------- 193 // WebView specific methods that map directly to WebViewClient / WebChromeClient 194 //-------------------------------------------------------------------------------------------- 195 196 public static class FileChooserParams { 197 public int mode; 198 public String acceptTypes; 199 public String title; 200 public String defaultFilename; 201 public boolean capture; 202 } 203 204 public abstract void getVisitedHistory(ValueCallback<String[]> callback); 205 206 public abstract void doUpdateVisitedHistory(String url, boolean isReload); 207 208 public abstract void onProgressChanged(int progress); 209 210 public abstract InterceptedRequestData shouldInterceptRequest(String url); 211 212 public abstract boolean shouldOverrideKeyEvent(KeyEvent event); 213 214 public abstract boolean shouldOverrideUrlLoading(String url); 215 216 public abstract void onLoadResource(String url); 217 218 public abstract void onUnhandledKeyEvent(KeyEvent event); 219 220 public abstract boolean onConsoleMessage(ConsoleMessage consoleMessage); 221 222 public abstract void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, 223 String host, String realm); 224 225 public abstract void onReceivedSslError(ValueCallback<Boolean> callback, SslError error); 226 227 public abstract void onReceivedLoginRequest(String realm, String account, String args); 228 229 public abstract void onFormResubmission(Message dontResend, Message resend); 230 231 public abstract void onDownloadStart(String url, String userAgent, String contentDisposition, 232 String mimeType, long contentLength); 233 234 // TODO(joth): Make abstract once this has rolled in downstream. 235 public /*abstract*/ void showFileChooser(ValueCallback<String[]> uploadFilePathsCallback, 236 FileChooserParams fileChooserParams) { } 237 238 public abstract void onGeolocationPermissionsShowPrompt(String origin, 239 GeolocationPermissions.Callback callback); 240 241 public abstract void onGeolocationPermissionsHidePrompt(); 242 243 public abstract void onScaleChangedScaled(float oldScale, float newScale); 244 245 protected abstract void handleJsAlert(String url, String message, JsResultReceiver receiver); 246 247 protected abstract void handleJsBeforeUnload(String url, String message, 248 JsResultReceiver receiver); 249 250 protected abstract void handleJsConfirm(String url, String message, JsResultReceiver receiver); 251 252 protected abstract void handleJsPrompt(String url, String message, String defaultValue, 253 JsPromptResultReceiver receiver); 254 255 protected abstract boolean onCreateWindow(boolean isDialog, boolean isUserGesture); 256 257 protected abstract void onCloseWindow(); 258 259 public abstract void onReceivedTouchIconUrl(String url, boolean precomposed); 260 261 public abstract void onReceivedIcon(Bitmap bitmap); 262 263 public abstract void onReceivedTitle(String title); 264 265 protected abstract void onRequestFocus(); 266 267 protected abstract View getVideoLoadingProgressView(); 268 269 public abstract void onPageStarted(String url); 270 271 public abstract void onPageFinished(String url); 272 273 public abstract void onReceivedError(int errorCode, String description, String failingUrl); 274 275 // TODO (michaelbai): Remove this method once the same method remove from 276 // WebViewContentsClientAdapter. 277 public void onShowCustomView(View view, 278 int requestedOrientation, WebChromeClient.CustomViewCallback callback) { 279 } 280 281 // TODO (michaelbai): This method should be abstract, having empty body here 282 // makes the merge to the Android easy. 283 public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) { 284 onShowCustomView(view, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, callback); 285 } 286 287 public abstract void onHideCustomView(); 288 289 public abstract Bitmap getDefaultVideoPoster(); 290 291 //-------------------------------------------------------------------------------------------- 292 // Other WebView-specific methods 293 //-------------------------------------------------------------------------------------------- 294 // 295 public abstract void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, 296 boolean isDoneCounting); 297 298 /** 299 * Called whenever there is a new content picture available. 300 * @param picture New picture. 301 */ 302 public abstract void onNewPicture(Picture picture); 303 304} 305