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