blob: 23fd12d99f0e70bea2786c1b85eaafa78a524d1f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
Grace Klobafba31972010-06-04 14:34:56 -070019import android.app.Activity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.app.AlertDialog;
21import android.content.ActivityNotFoundException;
22import android.content.Context;
23import android.content.DialogInterface;
24import android.content.Intent;
25import android.graphics.Bitmap;
26import android.net.Uri;
27import android.net.http.SslCertificate;
28import android.net.http.SslError;
29import android.os.Bundle;
30import android.os.Handler;
31import android.os.Message;
32import android.os.SystemClock;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -070033import android.provider.Browser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.util.Log;
35import android.view.KeyEvent;
36import android.view.LayoutInflater;
37import android.view.View;
38import android.widget.EditText;
39import android.widget.TextView;
40import com.android.internal.R;
41
42import java.net.MalformedURLException;
43import java.net.URL;
44import java.util.HashMap;
Narayan Kamath9497c5f2011-02-22 12:05:34 +000045import java.util.List;
Ben Murdoch4ae32f52010-05-18 14:30:39 +010046import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48/**
49 * This class is a proxy class for handling WebCore -> UI thread messaging. All
50 * the callback functions are called from the WebCore thread and messages are
51 * posted to the UI thread for the actual client callback.
52 */
53/*
54 * This class is created in the UI thread so its handler and any private classes
55 * that extend Handler will operate in the UI thread.
56 */
57class CallbackProxy extends Handler {
58 // Logging tag
59 private static final String LOGTAG = "CallbackProxy";
60 // Instance of WebViewClient that is the client callback.
61 private volatile WebViewClient mWebViewClient;
62 // Instance of WebChromeClient for handling all chrome functions.
63 private volatile WebChromeClient mWebChromeClient;
64 // Instance of WebView for handling UI requests.
65 private final WebView mWebView;
66 // Client registered callback listener for download events
67 private volatile DownloadListener mDownloadListener;
68 // Keep track of multiple progress updates.
69 private boolean mProgressUpdatePending;
70 // Keep track of the last progress amount.
Grace Kloba5a4fbd62009-09-17 14:20:28 -070071 // Start with 100 to indicate it is not in load for the empty page.
72 private volatile int mLatestProgress = 100;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 // Back/Forward list
74 private final WebBackForwardList mBackForwardList;
Patrick Scott0b2e84b2010-03-02 08:58:44 -050075 // Back/Forward list client
76 private volatile WebBackForwardListClient mWebBackForwardListClient;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 // Used to call startActivity during url override.
78 private final Context mContext;
79
80 // Message Ids
Steve Block4faee092009-07-28 18:20:50 +010081 private static final int PAGE_STARTED = 100;
82 private static final int RECEIVED_ICON = 101;
83 private static final int RECEIVED_TITLE = 102;
84 private static final int OVERRIDE_URL = 103;
85 private static final int AUTH_REQUEST = 104;
86 private static final int SSL_ERROR = 105;
87 private static final int PROGRESS = 106;
88 private static final int UPDATE_VISITED = 107;
89 private static final int LOAD_RESOURCE = 108;
90 private static final int CREATE_WINDOW = 109;
91 private static final int CLOSE_WINDOW = 110;
92 private static final int SAVE_PASSWORD = 111;
93 private static final int JS_ALERT = 112;
94 private static final int JS_CONFIRM = 113;
95 private static final int JS_PROMPT = 114;
96 private static final int JS_UNLOAD = 115;
97 private static final int ASYNC_KEYEVENTS = 116;
Steve Block4faee092009-07-28 18:20:50 +010098 private static final int DOWNLOAD_FILE = 118;
99 private static final int REPORT_ERROR = 119;
100 private static final int RESEND_POST_DATA = 120;
101 private static final int PAGE_FINISHED = 121;
102 private static final int REQUEST_FOCUS = 122;
103 private static final int SCALE_CHANGED = 123;
104 private static final int RECEIVED_CERTIFICATE = 124;
105 private static final int SWITCH_OUT_HISTORY = 125;
106 private static final int EXCEEDED_DATABASE_QUOTA = 126;
107 private static final int REACHED_APPCACHE_MAXSIZE = 127;
108 private static final int JS_TIMEOUT = 128;
109 private static final int ADD_MESSAGE_TO_CONSOLE = 129;
110 private static final int GEOLOCATION_PERMISSIONS_SHOW_PROMPT = 130;
111 private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT = 131;
Patrick Scott2ba12622009-08-04 13:20:05 -0400112 private static final int RECEIVED_TOUCH_ICON_URL = 132;
Leon Clarke194e3452009-09-28 11:42:12 +0100113 private static final int GET_VISITED_HISTORY = 133;
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400114 private static final int OPEN_FILE_CHOOSER = 134;
Patrick Scott0b2e84b2010-03-02 08:58:44 -0500115 private static final int ADD_HISTORY_ITEM = 135;
116 private static final int HISTORY_INDEX_CHANGED = 136;
Steve Blockc6a90a52010-03-25 15:38:04 +0000117 private static final int AUTH_CREDENTIALS = 137;
Ben Murdoch6312de22010-06-29 19:20:11 +0100118 private static final int SET_INSTALLABLE_WEBAPP = 138;
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000119 private static final int NOTIFY_SEARCHBOX_LISTENERS = 139;
Patrick Scott85a50ff2011-01-25 14:42:12 -0500120 private static final int AUTO_LOGIN = 140;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
122 // Message triggered by the client to resume execution
Steve Block4faee092009-07-28 18:20:50 +0100123 private static final int NOTIFY = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
125 // Result transportation object for returning results across thread
126 // boundaries.
Cary Clark7ec19872009-08-11 16:57:43 -0400127 private static class ResultTransport<E> {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 // Private result object
129 private E mResult;
130
Grace Klobadbad3202009-09-24 12:27:14 -0700131 public ResultTransport(E defaultResult) {
132 mResult = defaultResult;
133 }
134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 public synchronized void setResult(E result) {
136 mResult = result;
137 }
138
139 public synchronized E getResult() {
140 return mResult;
141 }
142 }
143
144 /**
145 * Construct a new CallbackProxy.
146 */
147 public CallbackProxy(Context context, WebView w) {
148 // Used to start a default activity.
149 mContext = context;
150 mWebView = w;
Patrick Scott0b2e84b2010-03-02 08:58:44 -0500151 mBackForwardList = new WebBackForwardList(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 }
153
154 /**
155 * Set the WebViewClient.
156 * @param client An implementation of WebViewClient.
157 */
158 public void setWebViewClient(WebViewClient client) {
159 mWebViewClient = client;
160 }
161
162 /**
Grace Kloba94ab3b62009-10-07 18:00:19 -0700163 * Get the WebViewClient.
164 * @return the current WebViewClient instance.
165 *
166 *@hide pending API council approval.
167 */
168 public WebViewClient getWebViewClient() {
169 return mWebViewClient;
170 }
171
172 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 * Set the WebChromeClient.
174 * @param client An implementation of WebChromeClient.
175 */
176 public void setWebChromeClient(WebChromeClient client) {
177 mWebChromeClient = client;
178 }
179
180 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +0100181 * Get the WebChromeClient.
182 * @return the current WebChromeClient instance.
Andrei Popescu6fa29582009-06-19 14:54:09 +0100183 */
184 public WebChromeClient getWebChromeClient() {
185 return mWebChromeClient;
186 }
187
188 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 * Set the client DownloadListener.
190 * @param client An implementation of DownloadListener.
191 */
192 public void setDownloadListener(DownloadListener client) {
193 mDownloadListener = client;
194 }
195
196 /**
197 * Get the Back/Forward list to return to the user or to update the cached
198 * history list.
199 */
200 public WebBackForwardList getBackForwardList() {
201 return mBackForwardList;
202 }
203
Patrick Scott0b2e84b2010-03-02 08:58:44 -0500204 void setWebBackForwardListClient(WebBackForwardListClient client) {
205 mWebBackForwardListClient = client;
206 }
207
208 WebBackForwardListClient getWebBackForwardListClient() {
209 return mWebBackForwardListClient;
210 }
211
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 /**
213 * Called by the UI side. Calling overrideUrlLoading from the WebCore
214 * side will post a message to call this method.
215 */
216 public boolean uiOverrideUrlLoading(String overrideUrl) {
217 if (overrideUrl == null || overrideUrl.length() == 0) {
218 return false;
219 }
220 boolean override = false;
221 if (mWebViewClient != null) {
222 override = mWebViewClient.shouldOverrideUrlLoading(mWebView,
223 overrideUrl);
224 } else {
225 Intent intent = new Intent(Intent.ACTION_VIEW,
226 Uri.parse(overrideUrl));
227 intent.addCategory(Intent.CATEGORY_BROWSABLE);
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700228 // If another application is running a WebView and launches the
229 // Browser through this Intent, we want to reuse the same window if
230 // possible.
231 intent.putExtra(Browser.EXTRA_APPLICATION_ID,
232 mContext.getPackageName());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 try {
234 mContext.startActivity(intent);
235 override = true;
236 } catch (ActivityNotFoundException ex) {
237 // If no application can handle the URL, assume that the
238 // browser can handle it.
239 }
240 }
241 return override;
242 }
243
244 /**
245 * Called by UI side.
246 */
247 public boolean uiOverrideKeyEvent(KeyEvent event) {
248 if (mWebViewClient != null) {
249 return mWebViewClient.shouldOverrideKeyEvent(mWebView, event);
250 }
251 return false;
252 }
253
254 @Override
255 public void handleMessage(Message msg) {
256 // We don't have to do synchronization because this function operates
257 // in the UI thread. The WebViewClient and WebChromeClient functions
258 // that check for a non-null callback are ok because java ensures atomic
259 // 32-bit reads and writes.
260 switch (msg.what) {
261 case PAGE_STARTED:
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -0700262 String startedUrl = msg.getData().getString("url");
263 mWebView.onPageStarted(startedUrl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 if (mWebViewClient != null) {
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -0700265 mWebViewClient.onPageStarted(mWebView, startedUrl, (Bitmap) msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 }
267 break;
268
269 case PAGE_FINISHED:
Leon Scroggins405d7852009-11-02 14:50:54 -0800270 String finishedUrl = (String) msg.obj;
271 mWebView.onPageFinished(finishedUrl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 if (mWebViewClient != null) {
Leon Scroggins405d7852009-11-02 14:50:54 -0800273 mWebViewClient.onPageFinished(mWebView, finishedUrl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
275 break;
276
277 case RECEIVED_ICON:
278 if (mWebChromeClient != null) {
279 mWebChromeClient.onReceivedIcon(mWebView, (Bitmap) msg.obj);
280 }
281 break;
282
Patrick Scott2ba12622009-08-04 13:20:05 -0400283 case RECEIVED_TOUCH_ICON_URL:
284 if (mWebChromeClient != null) {
285 mWebChromeClient.onReceivedTouchIconUrl(mWebView,
Patrick Scottd58ccff2009-09-18 16:29:00 -0400286 (String) msg.obj, msg.arg1 == 1);
Patrick Scott2ba12622009-08-04 13:20:05 -0400287 }
288 break;
289
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 case RECEIVED_TITLE:
291 if (mWebChromeClient != null) {
292 mWebChromeClient.onReceivedTitle(mWebView,
293 (String) msg.obj);
294 }
295 break;
296
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 case REPORT_ERROR:
298 if (mWebViewClient != null) {
299 int reasonCode = msg.arg1;
300 final String description = msg.getData().getString("description");
301 final String failUrl = msg.getData().getString("failingUrl");
302 mWebViewClient.onReceivedError(mWebView, reasonCode,
303 description, failUrl);
304 }
305 break;
306
307 case RESEND_POST_DATA:
308 Message resend =
309 (Message) msg.getData().getParcelable("resend");
310 Message dontResend =
311 (Message) msg.getData().getParcelable("dontResend");
312 if (mWebViewClient != null) {
313 mWebViewClient.onFormResubmission(mWebView, dontResend,
314 resend);
315 } else {
316 dontResend.sendToTarget();
317 }
318 break;
319
320 case OVERRIDE_URL:
321 String overrideUrl = msg.getData().getString("url");
322 boolean override = uiOverrideUrlLoading(overrideUrl);
323 ResultTransport<Boolean> result =
324 (ResultTransport<Boolean>) msg.obj;
325 synchronized (this) {
326 result.setResult(override);
327 notify();
328 }
329 break;
330
331 case AUTH_REQUEST:
332 if (mWebViewClient != null) {
333 HttpAuthHandler handler = (HttpAuthHandler) msg.obj;
334 String host = msg.getData().getString("host");
335 String realm = msg.getData().getString("realm");
336 mWebViewClient.onReceivedHttpAuthRequest(mWebView, handler,
337 host, realm);
338 }
339 break;
340
341 case SSL_ERROR:
342 if (mWebViewClient != null) {
343 HashMap<String, Object> map =
344 (HashMap<String, Object>) msg.obj;
345 mWebViewClient.onReceivedSslError(mWebView,
346 (SslErrorHandler) map.get("handler"),
347 (SslError) map.get("error"));
348 }
349 break;
350
351 case PROGRESS:
352 // Synchronize to ensure mLatestProgress is not modified after
353 // setProgress is called and before mProgressUpdatePending is
354 // changed.
355 synchronized (this) {
356 if (mWebChromeClient != null) {
357 mWebChromeClient.onProgressChanged(mWebView,
358 mLatestProgress);
359 }
360 mProgressUpdatePending = false;
361 }
362 break;
363
364 case UPDATE_VISITED:
365 if (mWebViewClient != null) {
366 mWebViewClient.doUpdateVisitedHistory(mWebView,
367 (String) msg.obj, msg.arg1 != 0);
368 }
369 break;
370
371 case LOAD_RESOURCE:
372 if (mWebViewClient != null) {
373 mWebViewClient.onLoadResource(mWebView, (String) msg.obj);
374 }
375 break;
376
377 case DOWNLOAD_FILE:
378 if (mDownloadListener != null) {
379 String url = msg.getData().getString("url");
380 String userAgent = msg.getData().getString("userAgent");
381 String contentDisposition =
382 msg.getData().getString("contentDisposition");
383 String mimetype = msg.getData().getString("mimetype");
384 Long contentLength = msg.getData().getLong("contentLength");
385
386 mDownloadListener.onDownloadStart(url, userAgent,
387 contentDisposition, mimetype, contentLength);
388 }
389 break;
390
391 case CREATE_WINDOW:
392 if (mWebChromeClient != null) {
393 if (!mWebChromeClient.onCreateWindow(mWebView,
394 msg.arg1 == 1, msg.arg2 == 1,
395 (Message) msg.obj)) {
396 synchronized (this) {
397 notify();
398 }
399 }
Grace Kloba21817f62010-03-19 18:28:33 -0700400 mWebView.dismissZoomControl();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 }
402 break;
403
404 case REQUEST_FOCUS:
405 if (mWebChromeClient != null) {
406 mWebChromeClient.onRequestFocus(mWebView);
407 }
408 break;
409
410 case CLOSE_WINDOW:
411 if (mWebChromeClient != null) {
412 mWebChromeClient.onCloseWindow((WebView) msg.obj);
413 }
414 break;
415
416 case SAVE_PASSWORD:
417 Bundle bundle = msg.getData();
418 String schemePlusHost = bundle.getString("host");
419 String username = bundle.getString("username");
420 String password = bundle.getString("password");
421 // If the client returned false it means that the notify message
422 // will not be sent and we should notify WebCore ourselves.
423 if (!mWebView.onSavePassword(schemePlusHost, username, password,
424 (Message) msg.obj)) {
425 synchronized (this) {
426 notify();
427 }
428 }
429 break;
430
431 case ASYNC_KEYEVENTS:
432 if (mWebViewClient != null) {
433 mWebViewClient.onUnhandledKeyEvent(mWebView,
434 (KeyEvent) msg.obj);
435 }
436 break;
437
Ben Murdoch7df19852009-04-22 13:07:58 +0100438 case EXCEEDED_DATABASE_QUOTA:
439 if (mWebChromeClient != null) {
440 HashMap<String, Object> map =
441 (HashMap<String, Object>) msg.obj;
442 String databaseIdentifier =
443 (String) map.get("databaseIdentifier");
444 String url = (String) map.get("url");
445 long currentQuota =
446 ((Long) map.get("currentQuota")).longValue();
Andrei Popescu59e2ad92009-07-28 13:38:06 +0100447 long totalUsedQuota =
448 ((Long) map.get("totalUsedQuota")).longValue();
Ben Murdochd497d872009-08-25 19:32:54 +0100449 long estimatedSize =
450 ((Long) map.get("estimatedSize")).longValue();
Ben Murdoch7df19852009-04-22 13:07:58 +0100451 WebStorage.QuotaUpdater quotaUpdater =
452 (WebStorage.QuotaUpdater) map.get("quotaUpdater");
453
454 mWebChromeClient.onExceededDatabaseQuota(url,
Ben Murdochd497d872009-08-25 19:32:54 +0100455 databaseIdentifier, currentQuota, estimatedSize,
456 totalUsedQuota, quotaUpdater);
Andrei Popescu59e2ad92009-07-28 13:38:06 +0100457 }
458 break;
459
460 case REACHED_APPCACHE_MAXSIZE:
461 if (mWebChromeClient != null) {
462 HashMap<String, Object> map =
463 (HashMap<String, Object>) msg.obj;
464 long spaceNeeded =
465 ((Long) map.get("spaceNeeded")).longValue();
466 long totalUsedQuota =
467 ((Long) map.get("totalUsedQuota")).longValue();
468 WebStorage.QuotaUpdater quotaUpdater =
469 (WebStorage.QuotaUpdater) map.get("quotaUpdater");
470
471 mWebChromeClient.onReachedMaxAppCacheSize(spaceNeeded,
472 totalUsedQuota, quotaUpdater);
Ben Murdoch7df19852009-04-22 13:07:58 +0100473 }
474 break;
475
Steve Block4faee092009-07-28 18:20:50 +0100476 case GEOLOCATION_PERMISSIONS_SHOW_PROMPT:
477 if (mWebChromeClient != null) {
478 HashMap<String, Object> map =
479 (HashMap<String, Object>) msg.obj;
480 String origin = (String) map.get("origin");
481 GeolocationPermissions.Callback callback =
482 (GeolocationPermissions.Callback)
483 map.get("callback");
484 mWebChromeClient.onGeolocationPermissionsShowPrompt(origin,
485 callback);
486 }
487 break;
488
489 case GEOLOCATION_PERMISSIONS_HIDE_PROMPT:
490 if (mWebChromeClient != null) {
491 mWebChromeClient.onGeolocationPermissionsHidePrompt();
492 }
493 break;
494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 case JS_ALERT:
496 if (mWebChromeClient != null) {
497 final JsResult res = (JsResult) msg.obj;
498 String message = msg.getData().getString("message");
499 String url = msg.getData().getString("url");
500 if (!mWebChromeClient.onJsAlert(mWebView, url, message,
501 res)) {
Ben Murdoch0406e482010-11-03 19:35:52 +0000502 if (!canShowAlertDialog()) {
Grace Klobafba31972010-06-04 14:34:56 -0700503 res.cancel();
504 res.setReady();
505 break;
506 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 new AlertDialog.Builder(mContext)
508 .setTitle(getJsDialogTitle(url))
509 .setMessage(message)
510 .setPositiveButton(R.string.ok,
Grace Klobafba31972010-06-04 14:34:56 -0700511 new DialogInterface.OnClickListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 public void onClick(
513 DialogInterface dialog,
514 int which) {
515 res.confirm();
516 }
517 })
Grace Klobafba31972010-06-04 14:34:56 -0700518 .setOnCancelListener(
519 new DialogInterface.OnCancelListener() {
520 public void onCancel(
521 DialogInterface dialog) {
522 res.cancel();
523 }
524 })
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 .show();
526 }
527 res.setReady();
528 }
529 break;
530
531 case JS_CONFIRM:
532 if (mWebChromeClient != null) {
533 final JsResult res = (JsResult) msg.obj;
534 String message = msg.getData().getString("message");
535 String url = msg.getData().getString("url");
536 if (!mWebChromeClient.onJsConfirm(mWebView, url, message,
537 res)) {
Ben Murdoch0406e482010-11-03 19:35:52 +0000538 if (!canShowAlertDialog()) {
Grace Klobafba31972010-06-04 14:34:56 -0700539 res.cancel();
540 res.setReady();
541 break;
542 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 new AlertDialog.Builder(mContext)
544 .setTitle(getJsDialogTitle(url))
545 .setMessage(message)
546 .setPositiveButton(R.string.ok,
547 new DialogInterface.OnClickListener() {
548 public void onClick(
549 DialogInterface dialog,
550 int which) {
551 res.confirm();
552 }})
553 .setNegativeButton(R.string.cancel,
554 new DialogInterface.OnClickListener() {
555 public void onClick(
556 DialogInterface dialog,
557 int which) {
558 res.cancel();
559 }})
Grace Kloba336d7dc2010-05-12 14:28:07 -0700560 .setOnCancelListener(
561 new DialogInterface.OnCancelListener() {
562 public void onCancel(
563 DialogInterface dialog) {
564 res.cancel();
565 }
566 })
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 .show();
568 }
569 // Tell the JsResult that it is ready for client
570 // interaction.
571 res.setReady();
572 }
573 break;
574
575 case JS_PROMPT:
576 if (mWebChromeClient != null) {
577 final JsPromptResult res = (JsPromptResult) msg.obj;
578 String message = msg.getData().getString("message");
579 String defaultVal = msg.getData().getString("default");
580 String url = msg.getData().getString("url");
581 if (!mWebChromeClient.onJsPrompt(mWebView, url, message,
582 defaultVal, res)) {
Ben Murdoch0406e482010-11-03 19:35:52 +0000583 if (!canShowAlertDialog()) {
Grace Klobafba31972010-06-04 14:34:56 -0700584 res.cancel();
585 res.setReady();
586 break;
587 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 final LayoutInflater factory = LayoutInflater
589 .from(mContext);
590 final View view = factory.inflate(R.layout.js_prompt,
591 null);
592 final EditText v = (EditText) view
593 .findViewById(R.id.value);
594 v.setText(defaultVal);
595 ((TextView) view.findViewById(R.id.message))
596 .setText(message);
597 new AlertDialog.Builder(mContext)
598 .setTitle(getJsDialogTitle(url))
599 .setView(view)
600 .setPositiveButton(R.string.ok,
601 new DialogInterface.OnClickListener() {
602 public void onClick(
603 DialogInterface dialog,
604 int whichButton) {
605 res.confirm(v.getText()
606 .toString());
607 }
608 })
609 .setNegativeButton(R.string.cancel,
610 new DialogInterface.OnClickListener() {
611 public void onClick(
612 DialogInterface dialog,
613 int whichButton) {
614 res.cancel();
615 }
616 })
617 .setOnCancelListener(
618 new DialogInterface.OnCancelListener() {
619 public void onCancel(
620 DialogInterface dialog) {
621 res.cancel();
622 }
623 })
624 .show();
625 }
626 // Tell the JsResult that it is ready for client
627 // interaction.
628 res.setReady();
629 }
630 break;
631
632 case JS_UNLOAD:
633 if (mWebChromeClient != null) {
634 final JsResult res = (JsResult) msg.obj;
635 String message = msg.getData().getString("message");
636 String url = msg.getData().getString("url");
637 if (!mWebChromeClient.onJsBeforeUnload(mWebView, url,
638 message, res)) {
Ben Murdoch0406e482010-11-03 19:35:52 +0000639 if (!canShowAlertDialog()) {
Grace Klobafba31972010-06-04 14:34:56 -0700640 res.cancel();
641 res.setReady();
642 break;
643 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 final String m = mContext.getString(
645 R.string.js_dialog_before_unload, message);
646 new AlertDialog.Builder(mContext)
647 .setMessage(m)
648 .setPositiveButton(R.string.ok,
649 new DialogInterface.OnClickListener() {
650 public void onClick(
651 DialogInterface dialog,
652 int which) {
653 res.confirm();
654 }
655 })
656 .setNegativeButton(R.string.cancel,
657 new DialogInterface.OnClickListener() {
658 public void onClick(
659 DialogInterface dialog,
660 int which) {
661 res.cancel();
662 }
663 })
664 .show();
665 }
666 res.setReady();
667 }
668 break;
669
Guang Zhu81e41432009-05-08 16:09:55 -0700670 case JS_TIMEOUT:
671 if(mWebChromeClient != null) {
672 final JsResult res = (JsResult) msg.obj;
673 if(mWebChromeClient.onJsTimeout()) {
674 res.confirm();
675 } else {
676 res.cancel();
677 }
678 res.setReady();
679 }
680 break;
681
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 case RECEIVED_CERTIFICATE:
683 mWebView.setCertificate((SslCertificate) msg.obj);
684 break;
685
686 case NOTIFY:
687 synchronized (this) {
688 notify();
689 }
690 break;
691
692 case SCALE_CHANGED:
693 if (mWebViewClient != null) {
694 mWebViewClient.onScaleChanged(mWebView, msg.getData()
695 .getFloat("old"), msg.getData().getFloat("new"));
696 }
697 break;
698
699 case SWITCH_OUT_HISTORY:
700 mWebView.switchOutDrawHistory();
701 break;
Ben Murdoch6262ae52009-04-17 13:21:53 +0100702
703 case ADD_MESSAGE_TO_CONSOLE:
Patrick Scott5fe721f2010-10-08 09:50:57 -0400704 if (mWebChromeClient == null) {
705 break;
706 }
Ben Murdoch6262ae52009-04-17 13:21:53 +0100707 String message = msg.getData().getString("message");
708 String sourceID = msg.getData().getString("sourceID");
709 int lineNumber = msg.getData().getInt("lineNumber");
Ben Murdoch3141e0a2010-01-28 15:06:32 +0000710 int msgLevel = msg.getData().getInt("msgLevel");
711 int numberOfMessageLevels = ConsoleMessage.MessageLevel.values().length;
712 // Sanity bounds check as we'll index an array with msgLevel
713 if (msgLevel < 0 || msgLevel >= numberOfMessageLevels) {
714 msgLevel = 0;
715 }
716
717 ConsoleMessage.MessageLevel messageLevel =
718 ConsoleMessage.MessageLevel.values()[msgLevel];
719
720 if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID,
721 lineNumber, messageLevel))) {
722 // If false was returned the user did not provide their own console function so
723 // we should output some default messages to the system log.
724 String logTag = "Web Console";
725 String logMessage = message + " at " + sourceID + ":" + lineNumber;
726
727 switch (messageLevel) {
728 case TIP:
729 Log.v(logTag, logMessage);
730 break;
731 case LOG:
732 Log.i(logTag, logMessage);
733 break;
734 case WARNING:
735 Log.w(logTag, logMessage);
736 break;
737 case ERROR:
738 Log.e(logTag, logMessage);
739 break;
740 case DEBUG:
741 Log.d(logTag, logMessage);
742 break;
743 }
744 }
745
Ben Murdoch6262ae52009-04-17 13:21:53 +0100746 break;
Leon Clarke194e3452009-09-28 11:42:12 +0100747
748 case GET_VISITED_HISTORY:
749 if (mWebChromeClient != null) {
750 mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj);
751 }
752 break;
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400753
754 case OPEN_FILE_CHOOSER:
755 if (mWebChromeClient != null) {
Ben Murdoch4ae32f52010-05-18 14:30:39 +0100756 UploadFileMessageData data = (UploadFileMessageData)msg.obj;
757 mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType());
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400758 }
759 break;
Patrick Scott0b2e84b2010-03-02 08:58:44 -0500760
761 case ADD_HISTORY_ITEM:
762 if (mWebBackForwardListClient != null) {
763 mWebBackForwardListClient.onNewHistoryItem(
764 (WebHistoryItem) msg.obj);
765 }
766 break;
767
768 case HISTORY_INDEX_CHANGED:
769 if (mWebBackForwardListClient != null) {
770 mWebBackForwardListClient.onIndexChanged(
771 (WebHistoryItem) msg.obj, msg.arg1);
772 }
773 break;
Patrick Scott85a50ff2011-01-25 14:42:12 -0500774 case AUTH_CREDENTIALS: {
Steve Block03484402010-03-26 09:10:58 +0000775 String host = msg.getData().getString("host");
776 String realm = msg.getData().getString("realm");
777 username = msg.getData().getString("username");
778 password = msg.getData().getString("password");
779 mWebView.setHttpAuthUsernamePassword(
780 host, realm, username, password);
Steve Blockc6a90a52010-03-25 15:38:04 +0000781 break;
Patrick Scott85a50ff2011-01-25 14:42:12 -0500782 }
Ben Murdoch6312de22010-06-29 19:20:11 +0100783 case SET_INSTALLABLE_WEBAPP:
Patrick Scott5fe721f2010-10-08 09:50:57 -0400784 if (mWebChromeClient != null) {
785 mWebChromeClient.setInstallableWebApp();
786 }
Ben Murdoch6312de22010-06-29 19:20:11 +0100787 break;
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000788 case NOTIFY_SEARCHBOX_LISTENERS:
789 SearchBoxImpl searchBox = (SearchBoxImpl) mWebView.getSearchBox();
790
791 @SuppressWarnings("unchecked")
792 List<String> suggestions = (List<String>) msg.obj;
793 searchBox.handleSuggestions(msg.getData().getString("query"), suggestions);
Patrick Scott85a50ff2011-01-25 14:42:12 -0500794 break;
795 case AUTO_LOGIN: {
796 if (mWebViewClient != null) {
797 String realm = msg.getData().getString("realm");
798 String account = msg.getData().getString("account");
799 String args = msg.getData().getString("args");
800 mWebViewClient.onReceivedLoginRequest(mWebView, realm,
801 account, args);
802 }
803 break;
804 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 }
806 }
807
808 /**
809 * Return the latest progress.
810 */
811 public int getProgress() {
812 return mLatestProgress;
813 }
814
815 /**
816 * Called by WebCore side to switch out of history Picture drawing mode
817 */
818 void switchOutDrawHistory() {
819 sendMessage(obtainMessage(SWITCH_OUT_HISTORY));
820 }
821
822 private String getJsDialogTitle(String url) {
823 String title = url;
824 if (URLUtil.isDataUrl(url)) {
825 // For data: urls, we just display 'JavaScript' similar to Safari.
826 title = mContext.getString(R.string.js_dialog_title_default);
827 } else {
828 try {
829 URL aUrl = new URL(url);
830 // For example: "The page at 'http://www.mit.edu' says:"
831 title = mContext.getString(R.string.js_dialog_title,
832 aUrl.getProtocol() + "://" + aUrl.getHost());
833 } catch (MalformedURLException ex) {
834 // do nothing. just use the url as the title
835 }
836 }
837 return title;
838 }
839
840 //--------------------------------------------------------------------------
841 // WebViewClient functions.
842 // NOTE: shouldOverrideKeyEvent is never called from the WebCore thread so
843 // it is not necessary to include it here.
844 //--------------------------------------------------------------------------
845
846 // Performance probe
Grace Klobaf0994812009-07-02 17:22:01 -0700847 private static final boolean PERF_PROBE = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 private long mWebCoreThreadTime;
Grace Klobaf0994812009-07-02 17:22:01 -0700849 private long mWebCoreIdleTime;
850
851 /*
852 * If PERF_PROBE is true, this block needs to be added to MessageQueue.java.
853 * startWait() and finishWait() should be called before and after wait().
854
855 private WaitCallback mWaitCallback = null;
856 public static interface WaitCallback {
857 void startWait();
858 void finishWait();
859 }
860 public final void setWaitCallback(WaitCallback callback) {
861 mWaitCallback = callback;
862 }
863 */
864
865 // un-comment this block if PERF_PROBE is true
866 /*
867 private IdleCallback mIdleCallback = new IdleCallback();
868
869 private final class IdleCallback implements MessageQueue.WaitCallback {
870 private long mStartTime = 0;
871
872 public void finishWait() {
873 mWebCoreIdleTime += SystemClock.uptimeMillis() - mStartTime;
874 }
875
876 public void startWait() {
877 mStartTime = SystemClock.uptimeMillis();
878 }
879 }
880 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881
882 public void onPageStarted(String url, Bitmap favicon) {
883 // Do an unsynchronized quick check to avoid posting if no callback has
884 // been set.
885 if (mWebViewClient == null) {
886 return;
887 }
888 // Performance probe
Grace Klobaf0994812009-07-02 17:22:01 -0700889 if (PERF_PROBE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 mWebCoreThreadTime = SystemClock.currentThreadTimeMillis();
Grace Klobaf0994812009-07-02 17:22:01 -0700891 mWebCoreIdleTime = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 Network.getInstance(mContext).startTiming();
Grace Klobaf0994812009-07-02 17:22:01 -0700893 // un-comment this if PERF_PROBE is true
894// Looper.myQueue().setWaitCallback(mIdleCallback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 }
896 Message msg = obtainMessage(PAGE_STARTED);
897 msg.obj = favicon;
898 msg.getData().putString("url", url);
899 sendMessage(msg);
900 }
901
902 public void onPageFinished(String url) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 // Performance probe
Grace Klobaf0994812009-07-02 17:22:01 -0700904 if (PERF_PROBE) {
905 // un-comment this if PERF_PROBE is true
906// Looper.myQueue().setWaitCallback(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 Log.d("WebCore", "WebCore thread used " +
908 (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime)
Grace Klobaf0994812009-07-02 17:22:01 -0700909 + " ms and idled " + mWebCoreIdleTime + " ms");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 Network.getInstance(mContext).stopTiming();
911 }
912 Message msg = obtainMessage(PAGE_FINISHED, url);
913 sendMessage(msg);
914 }
915
Patrick Scott6a5b0ec2010-01-08 09:55:33 -0500916 // Because this method is public and because CallbackProxy is mistakenly
917 // party of the public classes, we cannot remove this method.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 public void onTooManyRedirects(Message cancelMsg, Message continueMsg) {
Patrick Scott6a5b0ec2010-01-08 09:55:33 -0500919 // deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 }
921
922 public void onReceivedError(int errorCode, String description,
923 String failingUrl) {
924 // Do an unsynchronized quick check to avoid posting if no callback has
925 // been set.
926 if (mWebViewClient == null) {
927 return;
928 }
929
930 Message msg = obtainMessage(REPORT_ERROR);
931 msg.arg1 = errorCode;
932 msg.getData().putString("description", description);
933 msg.getData().putString("failingUrl", failingUrl);
934 sendMessage(msg);
935 }
936
937 public void onFormResubmission(Message dontResend,
938 Message resend) {
939 // Do an unsynchronized quick check to avoid posting if no callback has
940 // been set.
941 if (mWebViewClient == null) {
942 dontResend.sendToTarget();
943 return;
944 }
945
946 Message msg = obtainMessage(RESEND_POST_DATA);
947 Bundle bundle = msg.getData();
948 bundle.putParcelable("resend", resend);
949 bundle.putParcelable("dontResend", dontResend);
950 sendMessage(msg);
951 }
952
953 /**
954 * Called by the WebCore side
955 */
956 public boolean shouldOverrideUrlLoading(String url) {
957 // We have a default behavior if no client exists so always send the
958 // message.
Grace Klobadbad3202009-09-24 12:27:14 -0700959 ResultTransport<Boolean> res = new ResultTransport<Boolean>(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 Message msg = obtainMessage(OVERRIDE_URL);
961 msg.getData().putString("url", url);
962 msg.obj = res;
963 synchronized (this) {
964 sendMessage(msg);
965 try {
966 wait();
967 } catch (InterruptedException e) {
968 Log.e(LOGTAG, "Caught exception while waiting for overrideUrl");
969 Log.e(LOGTAG, Log.getStackTraceString(e));
970 }
971 }
972 return res.getResult().booleanValue();
973 }
974
975 public void onReceivedHttpAuthRequest(HttpAuthHandler handler,
976 String hostName, String realmName) {
977 // Do an unsynchronized quick check to avoid posting if no callback has
978 // been set.
979 if (mWebViewClient == null) {
980 handler.cancel();
981 return;
982 }
983 Message msg = obtainMessage(AUTH_REQUEST, handler);
984 msg.getData().putString("host", hostName);
985 msg.getData().putString("realm", realmName);
986 sendMessage(msg);
987 }
Steve Blockc6a90a52010-03-25 15:38:04 +0000988
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 /**
990 * @hide - hide this because it contains a parameter of type SslError.
991 * SslError is located in a hidden package.
992 */
993 public void onReceivedSslError(SslErrorHandler handler, SslError error) {
994 // Do an unsynchronized quick check to avoid posting if no callback has
995 // been set.
996 if (mWebViewClient == null) {
997 handler.cancel();
998 return;
999 }
1000 Message msg = obtainMessage(SSL_ERROR);
1001 //, handler);
1002 HashMap<String, Object> map = new HashMap();
1003 map.put("handler", handler);
1004 map.put("error", error);
1005 msg.obj = map;
1006 sendMessage(msg);
1007 }
1008 /**
1009 * @hide - hide this because it contains a parameter of type SslCertificate,
1010 * which is located in a hidden package.
1011 */
1012
1013 public void onReceivedCertificate(SslCertificate certificate) {
1014 // Do an unsynchronized quick check to avoid posting if no callback has
1015 // been set.
1016 if (mWebViewClient == null) {
1017 return;
1018 }
1019 // here, certificate can be null (if the site is not secure)
1020 sendMessage(obtainMessage(RECEIVED_CERTIFICATE, certificate));
1021 }
1022
1023 public void doUpdateVisitedHistory(String url, boolean isReload) {
1024 // Do an unsynchronized quick check to avoid posting if no callback has
1025 // been set.
1026 if (mWebViewClient == null) {
1027 return;
1028 }
1029 sendMessage(obtainMessage(UPDATE_VISITED, isReload ? 1 : 0, 0, url));
1030 }
1031
Patrick Scottc12544a2010-11-11 13:16:44 -05001032 WebResourceResponse shouldInterceptRequest(String url) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 if (mWebViewClient == null) {
Patrick Scottc12544a2010-11-11 13:16:44 -05001034 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 }
Patrick Scottc12544a2010-11-11 13:16:44 -05001036 // Note: This method does _not_ send a message.
1037 WebResourceResponse r =
1038 mWebViewClient.shouldInterceptRequest(mWebView, url);
1039 if (r == null) {
1040 sendMessage(obtainMessage(LOAD_RESOURCE, url));
1041 }
1042 return r;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 }
1044
1045 public void onUnhandledKeyEvent(KeyEvent event) {
1046 // Do an unsynchronized quick check to avoid posting if no callback has
1047 // been set.
1048 if (mWebViewClient == null) {
1049 return;
1050 }
1051 sendMessage(obtainMessage(ASYNC_KEYEVENTS, event));
1052 }
1053
1054 public void onScaleChanged(float oldScale, float newScale) {
1055 // Do an unsynchronized quick check to avoid posting if no callback has
1056 // been set.
1057 if (mWebViewClient == null) {
1058 return;
1059 }
1060 Message msg = obtainMessage(SCALE_CHANGED);
1061 Bundle bundle = msg.getData();
1062 bundle.putFloat("old", oldScale);
1063 bundle.putFloat("new", newScale);
1064 sendMessage(msg);
1065 }
1066
Patrick Scott85a50ff2011-01-25 14:42:12 -05001067 void onReceivedLoginRequest(String realm, String account, String args) {
1068 // Do an unsynchronized quick check to avoid posting if no callback has
1069 // been set.
1070 if (mWebViewClient == null) {
1071 return;
1072 }
1073 Message msg = obtainMessage(AUTO_LOGIN);
1074 Bundle bundle = msg.getData();
1075 bundle.putString("realm", realm);
1076 bundle.putString("account", account);
1077 bundle.putString("args", args);
1078 sendMessage(msg);
1079 }
1080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 //--------------------------------------------------------------------------
1082 // DownloadListener functions.
1083 //--------------------------------------------------------------------------
1084
1085 /**
1086 * Starts a download if a download listener has been registered, otherwise
1087 * return false.
1088 */
1089 public boolean onDownloadStart(String url, String userAgent,
1090 String contentDisposition, String mimetype, long contentLength) {
1091 // Do an unsynchronized quick check to avoid posting if no callback has
1092 // been set.
1093 if (mDownloadListener == null) {
1094 // Cancel the download if there is no browser client.
1095 return false;
1096 }
1097
1098 Message msg = obtainMessage(DOWNLOAD_FILE);
1099 Bundle bundle = msg.getData();
1100 bundle.putString("url", url);
1101 bundle.putString("userAgent", userAgent);
1102 bundle.putString("mimetype", mimetype);
1103 bundle.putLong("contentLength", contentLength);
1104 bundle.putString("contentDisposition", contentDisposition);
1105 sendMessage(msg);
1106 return true;
1107 }
1108
1109
1110 //--------------------------------------------------------------------------
1111 // WebView specific functions that do not interact with a client. These
1112 // functions just need to operate within the UI thread.
1113 //--------------------------------------------------------------------------
1114
1115 public boolean onSavePassword(String schemePlusHost, String username,
1116 String password, Message resumeMsg) {
1117 // resumeMsg should be null at this point because we want to create it
1118 // within the CallbackProxy.
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04001119 if (DebugFlags.CALLBACK_PROXY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 junit.framework.Assert.assertNull(resumeMsg);
1121 }
1122 resumeMsg = obtainMessage(NOTIFY);
1123
1124 Message msg = obtainMessage(SAVE_PASSWORD, resumeMsg);
1125 Bundle bundle = msg.getData();
1126 bundle.putString("host", schemePlusHost);
1127 bundle.putString("username", username);
1128 bundle.putString("password", password);
1129 synchronized (this) {
1130 sendMessage(msg);
1131 try {
1132 wait();
1133 } catch (InterruptedException e) {
1134 Log.e(LOGTAG,
1135 "Caught exception while waiting for onSavePassword");
1136 Log.e(LOGTAG, Log.getStackTraceString(e));
1137 }
1138 }
1139 // Doesn't matter here
1140 return false;
1141 }
1142
Steve Block03484402010-03-26 09:10:58 +00001143 public void onReceivedHttpAuthCredentials(String host, String realm,
1144 String username, String password) {
1145 Message msg = obtainMessage(AUTH_CREDENTIALS);
1146 msg.getData().putString("host", host);
1147 msg.getData().putString("realm", realm);
1148 msg.getData().putString("username", username);
1149 msg.getData().putString("password", password);
1150 sendMessage(msg);
1151 }
1152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 //--------------------------------------------------------------------------
1154 // WebChromeClient methods
1155 //--------------------------------------------------------------------------
1156
1157 public void onProgressChanged(int newProgress) {
1158 // Synchronize so that mLatestProgress is up-to-date.
1159 synchronized (this) {
Grace Kloba20ca1172010-05-17 13:08:05 -07001160 // update mLatestProgress even mWebChromeClient is null as
1161 // WebView.getProgress() needs it
1162 if (mLatestProgress == newProgress) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 return;
1164 }
Leon Scroggins36133852009-11-04 14:02:03 -08001165 mLatestProgress = newProgress;
Grace Kloba20ca1172010-05-17 13:08:05 -07001166 if (mWebChromeClient == null) {
1167 return;
1168 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 if (!mProgressUpdatePending) {
1170 sendEmptyMessage(PROGRESS);
1171 mProgressUpdatePending = true;
1172 }
1173 }
1174 }
1175
Patrick Scott97147282010-06-16 12:27:06 -04001176 public BrowserFrame createWindow(boolean dialog, boolean userGesture) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 // Do an unsynchronized quick check to avoid posting if no callback has
1178 // been set.
1179 if (mWebChromeClient == null) {
1180 return null;
1181 }
1182
1183 WebView.WebViewTransport transport = mWebView.new WebViewTransport();
1184 final Message msg = obtainMessage(NOTIFY);
1185 msg.obj = transport;
1186 synchronized (this) {
1187 sendMessage(obtainMessage(CREATE_WINDOW, dialog ? 1 : 0,
1188 userGesture ? 1 : 0, msg));
1189 try {
1190 wait();
1191 } catch (InterruptedException e) {
1192 Log.e(LOGTAG,
1193 "Caught exception while waiting for createWindow");
1194 Log.e(LOGTAG, Log.getStackTraceString(e));
1195 }
1196 }
1197
1198 WebView w = transport.getWebView();
1199 if (w != null) {
Patrick Scott97147282010-06-16 12:27:06 -04001200 WebViewCore core = w.getWebViewCore();
1201 // If WebView.destroy() has been called, core may be null. Skip
1202 // initialization in that case and return null.
1203 if (core != null) {
1204 core.initializeSubwindow();
1205 return core.getBrowserFrame();
1206 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
Patrick Scott97147282010-06-16 12:27:06 -04001208 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 }
1210
1211 public void onRequestFocus() {
1212 // Do an unsynchronized quick check to avoid posting if no callback has
1213 // been set.
1214 if (mWebChromeClient == null) {
1215 return;
1216 }
1217
1218 sendEmptyMessage(REQUEST_FOCUS);
1219 }
1220
1221 public void onCloseWindow(WebView window) {
1222 // Do an unsynchronized quick check to avoid posting if no callback has
1223 // been set.
1224 if (mWebChromeClient == null) {
1225 return;
1226 }
1227 sendMessage(obtainMessage(CLOSE_WINDOW, window));
1228 }
1229
1230 public void onReceivedIcon(Bitmap icon) {
1231 // The current item might be null if the icon was already stored in the
1232 // database and this is a new WebView.
1233 WebHistoryItem i = mBackForwardList.getCurrentItem();
1234 if (i != null) {
1235 i.setFavicon(icon);
1236 }
1237 // Do an unsynchronized quick check to avoid posting if no callback has
1238 // been set.
1239 if (mWebChromeClient == null) {
1240 return;
1241 }
1242 sendMessage(obtainMessage(RECEIVED_ICON, icon));
1243 }
1244
Patrick Scottd58ccff2009-09-18 16:29:00 -04001245 /* package */ void onReceivedTouchIconUrl(String url, boolean precomposed) {
Patrick Scott2ba12622009-08-04 13:20:05 -04001246 // We should have a current item but we do not want to crash so check
1247 // for null.
1248 WebHistoryItem i = mBackForwardList.getCurrentItem();
1249 if (i != null) {
Ben Murdoch372dfc82010-07-01 15:56:01 +01001250 i.setTouchIconUrl(url, precomposed);
Patrick Scott2ba12622009-08-04 13:20:05 -04001251 }
1252 // Do an unsynchronized quick check to avoid posting if no callback has
1253 // been set.
1254 if (mWebChromeClient == null) {
1255 return;
1256 }
Patrick Scottd58ccff2009-09-18 16:29:00 -04001257 sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL,
1258 precomposed ? 1 : 0, 0, url));
Patrick Scott2ba12622009-08-04 13:20:05 -04001259 }
1260
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 public void onReceivedTitle(String title) {
1262 // Do an unsynchronized quick check to avoid posting if no callback has
1263 // been set.
1264 if (mWebChromeClient == null) {
1265 return;
1266 }
1267 sendMessage(obtainMessage(RECEIVED_TITLE, title));
1268 }
1269
1270 public void onJsAlert(String url, String message) {
1271 // Do an unsynchronized quick check to avoid posting if no callback has
1272 // been set.
1273 if (mWebChromeClient == null) {
1274 return;
1275 }
1276 JsResult result = new JsResult(this, false);
1277 Message alert = obtainMessage(JS_ALERT, result);
1278 alert.getData().putString("message", message);
1279 alert.getData().putString("url", url);
1280 synchronized (this) {
1281 sendMessage(alert);
1282 try {
1283 wait();
1284 } catch (InterruptedException e) {
1285 Log.e(LOGTAG, "Caught exception while waiting for jsAlert");
1286 Log.e(LOGTAG, Log.getStackTraceString(e));
1287 }
1288 }
1289 }
1290
1291 public boolean onJsConfirm(String url, String message) {
1292 // Do an unsynchronized quick check to avoid posting if no callback has
1293 // been set.
1294 if (mWebChromeClient == null) {
1295 return false;
1296 }
1297 JsResult result = new JsResult(this, false);
1298 Message confirm = obtainMessage(JS_CONFIRM, result);
1299 confirm.getData().putString("message", message);
1300 confirm.getData().putString("url", url);
1301 synchronized (this) {
1302 sendMessage(confirm);
1303 try {
1304 wait();
1305 } catch (InterruptedException e) {
1306 Log.e(LOGTAG, "Caught exception while waiting for jsConfirm");
1307 Log.e(LOGTAG, Log.getStackTraceString(e));
1308 }
1309 }
1310 return result.getResult();
1311 }
1312
1313 public String onJsPrompt(String url, String message, String defaultValue) {
1314 // Do an unsynchronized quick check to avoid posting if no callback has
1315 // been set.
1316 if (mWebChromeClient == null) {
1317 return null;
1318 }
1319 JsPromptResult result = new JsPromptResult(this);
1320 Message prompt = obtainMessage(JS_PROMPT, result);
1321 prompt.getData().putString("message", message);
1322 prompt.getData().putString("default", defaultValue);
1323 prompt.getData().putString("url", url);
1324 synchronized (this) {
1325 sendMessage(prompt);
1326 try {
1327 wait();
1328 } catch (InterruptedException e) {
1329 Log.e(LOGTAG, "Caught exception while waiting for jsPrompt");
1330 Log.e(LOGTAG, Log.getStackTraceString(e));
1331 }
1332 }
1333 return result.getStringResult();
1334 }
1335
1336 public boolean onJsBeforeUnload(String url, String message) {
1337 // Do an unsynchronized quick check to avoid posting if no callback has
1338 // been set.
1339 if (mWebChromeClient == null) {
1340 return true;
1341 }
1342 JsResult result = new JsResult(this, true);
1343 Message confirm = obtainMessage(JS_UNLOAD, result);
1344 confirm.getData().putString("message", message);
1345 confirm.getData().putString("url", url);
1346 synchronized (this) {
1347 sendMessage(confirm);
1348 try {
1349 wait();
1350 } catch (InterruptedException e) {
1351 Log.e(LOGTAG, "Caught exception while waiting for jsUnload");
1352 Log.e(LOGTAG, Log.getStackTraceString(e));
1353 }
1354 }
1355 return result.getResult();
1356 }
Ben Murdoch7df19852009-04-22 13:07:58 +01001357
1358 /**
1359 * Called by WebViewCore to inform the Java side that the current origin
1360 * has overflowed it's database quota. Called in the WebCore thread so
1361 * posts a message to the UI thread that will prompt the WebChromeClient
1362 * for what to do. On return back to C++ side, the WebCore thread will
1363 * sleep pending a new quota value.
1364 * @param url The URL that caused the quota overflow.
1365 * @param databaseIdentifier The identifier of the database that the
1366 * transaction that caused the overflow was running on.
1367 * @param currentQuota The current quota the origin is allowed.
Ben Murdochd497d872009-08-25 19:32:54 +01001368 * @param estimatedSize The estimated size of the database.
Andrei Popescu59e2ad92009-07-28 13:38:06 +01001369 * @param totalUsedQuota is the sum of all origins' quota.
Ben Murdoch7df19852009-04-22 13:07:58 +01001370 * @param quotaUpdater An instance of a class encapsulating a callback
1371 * to WebViewCore to run when the decision to allow or deny more
1372 * quota has been made.
1373 */
1374 public void onExceededDatabaseQuota(
1375 String url, String databaseIdentifier, long currentQuota,
Ben Murdochd497d872009-08-25 19:32:54 +01001376 long estimatedSize, long totalUsedQuota,
1377 WebStorage.QuotaUpdater quotaUpdater) {
Ben Murdoch7df19852009-04-22 13:07:58 +01001378 if (mWebChromeClient == null) {
1379 quotaUpdater.updateQuota(currentQuota);
1380 return;
1381 }
1382
1383 Message exceededQuota = obtainMessage(EXCEEDED_DATABASE_QUOTA);
1384 HashMap<String, Object> map = new HashMap();
1385 map.put("databaseIdentifier", databaseIdentifier);
1386 map.put("url", url);
1387 map.put("currentQuota", currentQuota);
Ben Murdochd497d872009-08-25 19:32:54 +01001388 map.put("estimatedSize", estimatedSize);
Andrei Popescu59e2ad92009-07-28 13:38:06 +01001389 map.put("totalUsedQuota", totalUsedQuota);
Ben Murdoch7df19852009-04-22 13:07:58 +01001390 map.put("quotaUpdater", quotaUpdater);
1391 exceededQuota.obj = map;
1392 sendMessage(exceededQuota);
1393 }
1394
Guang Zhu10e4d202009-05-11 18:09:51 -07001395 /**
Andrei Popescu59e2ad92009-07-28 13:38:06 +01001396 * Called by WebViewCore to inform the Java side that the appcache has
1397 * exceeded its max size.
1398 * @param spaceNeeded is the amount of disk space that would be needed
1399 * in order for the last appcache operation to succeed.
1400 * @param totalUsedQuota is the sum of all origins' quota.
1401 * @param quotaUpdater An instance of a class encapsulating a callback
1402 * to WebViewCore to run when the decision to allow or deny a bigger
1403 * app cache size has been made.
Andrei Popescu59e2ad92009-07-28 13:38:06 +01001404 */
1405 public void onReachedMaxAppCacheSize(long spaceNeeded,
1406 long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
1407 if (mWebChromeClient == null) {
1408 quotaUpdater.updateQuota(0);
1409 return;
1410 }
1411
1412 Message msg = obtainMessage(REACHED_APPCACHE_MAXSIZE);
1413 HashMap<String, Object> map = new HashMap();
1414 map.put("spaceNeeded", spaceNeeded);
1415 map.put("totalUsedQuota", totalUsedQuota);
1416 map.put("quotaUpdater", quotaUpdater);
1417 msg.obj = map;
1418 sendMessage(msg);
1419 }
1420
1421 /**
Steve Block4faee092009-07-28 18:20:50 +01001422 * Called by WebViewCore to instruct the browser to display a prompt to ask
1423 * the user to set the Geolocation permission state for the given origin.
1424 * @param origin The origin requesting Geolocation permsissions.
1425 * @param callback The callback to call once a permission state has been
1426 * obtained.
Steve Block4faee092009-07-28 18:20:50 +01001427 */
1428 public void onGeolocationPermissionsShowPrompt(String origin,
1429 GeolocationPermissions.Callback callback) {
1430 if (mWebChromeClient == null) {
1431 return;
1432 }
1433
1434 Message showMessage =
1435 obtainMessage(GEOLOCATION_PERMISSIONS_SHOW_PROMPT);
1436 HashMap<String, Object> map = new HashMap();
1437 map.put("origin", origin);
1438 map.put("callback", callback);
1439 showMessage.obj = map;
1440 sendMessage(showMessage);
1441 }
1442
1443 /**
1444 * Called by WebViewCore to instruct the browser to hide the Geolocation
1445 * permissions prompt.
Steve Block4faee092009-07-28 18:20:50 +01001446 */
1447 public void onGeolocationPermissionsHidePrompt() {
1448 if (mWebChromeClient == null) {
1449 return;
1450 }
1451
1452 Message hideMessage = obtainMessage(GEOLOCATION_PERMISSIONS_HIDE_PROMPT);
1453 sendMessage(hideMessage);
1454 }
1455
1456 /**
Ben Murdoch6262ae52009-04-17 13:21:53 +01001457 * Called by WebViewCore when we have a message to be added to the JavaScript
1458 * error console. Sends a message to the Java side with the details.
1459 * @param message The message to add to the console.
1460 * @param lineNumber The lineNumber of the source file on which the error
1461 * occurred.
1462 * @param sourceID The filename of the source file in which the error
1463 * occurred.
Ben Murdoch3141e0a2010-01-28 15:06:32 +00001464 * @param msgLevel The message level, corresponding to the MessageLevel enum in
1465 * WebCore/page/Console.h
Ben Murdoch6262ae52009-04-17 13:21:53 +01001466 */
Ben Murdoch3141e0a2010-01-28 15:06:32 +00001467 public void addMessageToConsole(String message, int lineNumber, String sourceID, int msgLevel) {
Ben Murdoch6262ae52009-04-17 13:21:53 +01001468 if (mWebChromeClient == null) {
1469 return;
1470 }
1471
1472 Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE);
1473 msg.getData().putString("message", message);
1474 msg.getData().putString("sourceID", sourceID);
1475 msg.getData().putInt("lineNumber", lineNumber);
Ben Murdoch3141e0a2010-01-28 15:06:32 +00001476 msg.getData().putInt("msgLevel", msgLevel);
Ben Murdoch6262ae52009-04-17 13:21:53 +01001477 sendMessage(msg);
1478 }
1479
Guang Zhu81e41432009-05-08 16:09:55 -07001480 public boolean onJsTimeout() {
1481 //always interrupt timedout JS by default
1482 if (mWebChromeClient == null) {
1483 return true;
1484 }
1485 JsResult result = new JsResult(this, true);
1486 Message timeout = obtainMessage(JS_TIMEOUT, result);
1487 synchronized (this) {
1488 sendMessage(timeout);
1489 try {
1490 wait();
1491 } catch (InterruptedException e) {
1492 Log.e(LOGTAG, "Caught exception while waiting for jsUnload");
1493 Log.e(LOGTAG, Log.getStackTraceString(e));
1494 }
1495 }
1496 return result.getResult();
1497 }
Leon Clarke194e3452009-09-28 11:42:12 +01001498
Leon Clarke194e3452009-09-28 11:42:12 +01001499 public void getVisitedHistory(ValueCallback<String[]> callback) {
1500 if (mWebChromeClient == null) {
1501 return;
1502 }
1503 Message msg = obtainMessage(GET_VISITED_HISTORY);
1504 msg.obj = callback;
1505 sendMessage(msg);
1506 }
Leon Scroggins70ca3c22009-10-02 15:58:55 -04001507
Ben Murdoch4ae32f52010-05-18 14:30:39 +01001508 private static class UploadFileMessageData {
1509 private UploadFile mCallback;
1510 private String mAcceptType;
1511
1512 public UploadFileMessageData(UploadFile uploadFile, String acceptType) {
1513 mCallback = uploadFile;
1514 mAcceptType = acceptType;
1515 }
1516
1517 public UploadFile getUploadFile() {
1518 return mCallback;
1519 }
1520
1521 public String getAcceptType() {
1522 return mAcceptType;
1523 }
1524 }
1525
Leon Scroggins70ca3c22009-10-02 15:58:55 -04001526 private class UploadFile implements ValueCallback<Uri> {
1527 private Uri mValue;
1528 public void onReceiveValue(Uri value) {
1529 mValue = value;
1530 synchronized (CallbackProxy.this) {
1531 CallbackProxy.this.notify();
1532 }
1533 }
1534 public Uri getResult() {
1535 return mValue;
1536 }
1537 }
1538
1539 /**
1540 * Called by WebViewCore to open a file chooser.
1541 */
Ben Murdoch4ae32f52010-05-18 14:30:39 +01001542 /* package */ Uri openFileChooser(String acceptType) {
Leon Scroggins70ca3c22009-10-02 15:58:55 -04001543 if (mWebChromeClient == null) {
1544 return null;
1545 }
1546 Message myMessage = obtainMessage(OPEN_FILE_CHOOSER);
1547 UploadFile uploadFile = new UploadFile();
Ben Murdoch4ae32f52010-05-18 14:30:39 +01001548 UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType);
1549 myMessage.obj = data;
Leon Scroggins70ca3c22009-10-02 15:58:55 -04001550 synchronized (this) {
1551 sendMessage(myMessage);
1552 try {
1553 wait();
1554 } catch (InterruptedException e) {
1555 Log.e(LOGTAG,
1556 "Caught exception while waiting for openFileChooser");
1557 Log.e(LOGTAG, Log.getStackTraceString(e));
1558 }
1559 }
1560 return uploadFile.getResult();
1561 }
Patrick Scott0b2e84b2010-03-02 08:58:44 -05001562
1563 void onNewHistoryItem(WebHistoryItem item) {
1564 if (mWebBackForwardListClient == null) {
1565 return;
1566 }
1567 Message msg = obtainMessage(ADD_HISTORY_ITEM, item);
1568 sendMessage(msg);
1569 }
1570
1571 void onIndexChanged(WebHistoryItem item, int index) {
1572 if (mWebBackForwardListClient == null) {
1573 return;
1574 }
1575 Message msg = obtainMessage(HISTORY_INDEX_CHANGED, index, 0, item);
1576 sendMessage(msg);
1577 }
Ben Murdoch6312de22010-06-29 19:20:11 +01001578
1579 void setInstallableWebApp() {
1580 if (mWebChromeClient == null) {
1581 return;
1582 }
1583 sendMessage(obtainMessage(SET_INSTALLABLE_WEBAPP));
1584 }
Ben Murdoch0406e482010-11-03 19:35:52 +00001585
1586 boolean canShowAlertDialog() {
1587 // We can only display the alert dialog if mContext is
1588 // an Activity context.
1589 // FIXME: Should we display dialogs if mContext does
1590 // not have the window focus (e.g. if the user is viewing
1591 // another Activity when the alert should be displayed?
1592 // See bug 3166409
1593 return mContext instanceof Activity;
1594 }
Narayan Kamath9497c5f2011-02-22 12:05:34 +00001595
1596 void onSearchboxSuggestionsReceived(String query, List<String> suggestions) {
1597 Message msg = obtainMessage(NOTIFY_SEARCHBOX_LISTENERS);
1598 msg.obj = suggestions;
1599 msg.getData().putString("query", query);
1600
1601 sendMessage(msg);
1602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603}