CallbackProxy.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/*
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
19import android.content.ActivityNotFoundException;
20import android.content.Context;
21import android.content.Intent;
22import android.graphics.Bitmap;
23import android.net.Uri;
24import android.net.http.SslCertificate;
25import android.net.http.SslError;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Message;
29import android.os.SystemClock;
30import android.util.Config;
31import android.util.Log;
32import android.view.KeyEvent;
33
34import java.util.HashMap;
35
36/**
37 * This class is a proxy class for handling WebCore -> UI thread messaging. All
38 * the callback functions are called from the WebCore thread and messages are
39 * posted to the UI thread for the actual client callback.
40 */
41/*
42 * This class is created in the UI thread so its handler and any private classes
43 * that extend Handler will operate in the UI thread.
44 */
45class CallbackProxy extends Handler {
46    // Logging tag
47    private static final String LOGTAG = "CallbackProxy";
48    // Instance of WebViewClient that is the client callback.
49    private volatile WebViewClient mWebViewClient;
50    // Instance of WebChromeClient for handling all chrome functions.
51    private volatile WebChromeClient mWebChromeClient;
52    // Instance of WebView for handling UI requests.
53    private final WebView mWebView;
54    // Client registered callback listener for download events
55    private volatile DownloadListener mDownloadListener;
56    // Keep track of multiple progress updates.
57    private boolean mProgressUpdatePending;
58    // Keep track of the last progress amount.
59    private volatile int mLatestProgress;
60    // Back/Forward list
61    private final WebBackForwardList mBackForwardList;
62    // Used to call startActivity during url override.
63    private final Context mContext;
64
65    // Message Ids
66    private static final int PAGE_STARTED         = 100;
67    private static final int RECEIVED_ICON        = 101;
68    private static final int RECEIVED_TITLE       = 102;
69    private static final int OVERRIDE_URL         = 103;
70    private static final int AUTH_REQUEST         = 104;
71    private static final int SSL_ERROR            = 105;
72    private static final int PROGRESS             = 106;
73    private static final int UPDATE_VISITED       = 107;
74    private static final int LOAD_RESOURCE        = 108;
75    private static final int CREATE_WINDOW        = 109;
76    private static final int CLOSE_WINDOW         = 110;
77    private static final int SAVE_PASSWORD        = 111;
78    private static final int JS_ALERT             = 112;
79    private static final int JS_CONFIRM           = 113;
80    private static final int JS_PROMPT            = 114;
81    private static final int JS_UNLOAD            = 115;
82    private static final int ASYNC_KEYEVENTS      = 116;
83    private static final int TOO_MANY_REDIRECTS   = 117;
84    private static final int DOWNLOAD_FILE        = 118;
85    private static final int REPORT_ERROR         = 119;
86    private static final int RESEND_POST_DATA     = 120;
87    private static final int PAGE_FINISHED        = 121;
88    private static final int REQUEST_FOCUS        = 122;
89    private static final int SCALE_CHANGED        = 123;
90    private static final int RECEIVED_CERTIFICATE = 124;
91    private static final int SWITCH_OUT_HISTORY   = 125;
92
93    // Message triggered by the client to resume execution
94    private static final int NOTIFY               = 200;
95
96    // Result transportation object for returning results across thread
97    // boundaries.
98    private class ResultTransport<E> {
99        // Private result object
100        private E mResult;
101
102        public synchronized void setResult(E result) {
103            mResult = result;
104        }
105
106        public synchronized E getResult() {
107            return mResult;
108        }
109    }
110
111    /**
112     * Construct a new CallbackProxy.
113     */
114    public CallbackProxy(Context context, WebView w) {
115        // Used to start a default activity.
116        mContext = context;
117        mWebView = w;
118        mBackForwardList = new WebBackForwardList();
119    }
120
121    /**
122     * Set the WebViewClient.
123     * @param client An implementation of WebViewClient.
124     */
125    public void setWebViewClient(WebViewClient client) {
126        mWebViewClient = client;
127    }
128
129    /**
130     * Set the WebChromeClient.
131     * @param client An implementation of WebChromeClient.
132     */
133    public void setWebChromeClient(WebChromeClient client) {
134        mWebChromeClient = client;
135    }
136
137    /**
138     * Set the client DownloadListener.
139     * @param client An implementation of DownloadListener.
140     */
141    public void setDownloadListener(DownloadListener client) {
142        mDownloadListener = client;
143    }
144
145    /**
146     * Get the Back/Forward list to return to the user or to update the cached
147     * history list.
148     */
149    public WebBackForwardList getBackForwardList() {
150        return mBackForwardList;
151    }
152
153    /**
154     * Called by the UI side.  Calling overrideUrlLoading from the WebCore
155     * side will post a message to call this method.
156     */
157    public boolean uiOverrideUrlLoading(String overrideUrl) {
158        if (overrideUrl == null || overrideUrl.length() == 0) {
159            return false;
160        }
161        boolean override = false;
162        if (mWebViewClient != null) {
163            override = mWebViewClient.shouldOverrideUrlLoading(mWebView,
164                    overrideUrl);
165        } else {
166            Intent intent = new Intent(Intent.ACTION_VIEW,
167                    Uri.parse(overrideUrl));
168            intent.addCategory(Intent.CATEGORY_BROWSABLE);
169            try {
170                mContext.startActivity(intent);
171                override = true;
172            } catch (ActivityNotFoundException ex) {
173                // If no application can handle the URL, assume that the
174                // browser can handle it.
175            }
176        }
177        return override;
178    }
179
180    /**
181     * Called by UI side.
182     */
183    public boolean uiOverrideKeyEvent(KeyEvent event) {
184        if (mWebViewClient != null) {
185            return mWebViewClient.shouldOverrideKeyEvent(mWebView, event);
186        }
187        return false;
188    }
189
190    @Override
191    public void handleMessage(Message msg) {
192        // We don't have to do synchronization because this function operates
193        // in the UI thread. The WebViewClient and WebChromeClient functions
194        // that check for a non-null callback are ok because java ensures atomic
195        // 32-bit reads and writes.
196        switch (msg.what) {
197            case PAGE_STARTED:
198                if (mWebViewClient != null) {
199                    mWebViewClient.onPageStarted(mWebView,
200                            msg.getData().getString("url"),
201                            (Bitmap) msg.obj);
202                }
203                break;
204
205            case PAGE_FINISHED:
206                if (mWebViewClient != null) {
207                    mWebViewClient.onPageFinished(mWebView, (String) msg.obj);
208                }
209                break;
210
211            case RECEIVED_ICON:
212                if (mWebChromeClient != null) {
213                    mWebChromeClient.onReceivedIcon(mWebView, (Bitmap) msg.obj);
214                }
215                break;
216
217            case RECEIVED_TITLE:
218                if (mWebChromeClient != null) {
219                    mWebChromeClient.onReceivedTitle(mWebView,
220                            (String) msg.obj);
221                }
222                break;
223
224            case TOO_MANY_REDIRECTS:
225                Message cancelMsg =
226                        (Message) msg.getData().getParcelable("cancelMsg");
227                Message continueMsg =
228                        (Message) msg.getData().getParcelable("continueMsg");
229                if (mWebViewClient != null) {
230                    mWebViewClient.onTooManyRedirects(mWebView, cancelMsg,
231                            continueMsg);
232                } else {
233                    cancelMsg.sendToTarget();
234                }
235                break;
236
237            case REPORT_ERROR:
238                if (mWebViewClient != null) {
239                    int reasonCode = msg.arg1;
240                    final String description  = msg.getData().getString("description");
241                    final String failUrl  = msg.getData().getString("failingUrl");
242                    mWebViewClient.onReceivedError(mWebView, reasonCode,
243                            description, failUrl);
244                }
245                break;
246
247            case RESEND_POST_DATA:
248                Message resend =
249                        (Message) msg.getData().getParcelable("resend");
250                Message dontResend =
251                        (Message) msg.getData().getParcelable("dontResend");
252                if (mWebViewClient != null) {
253                    mWebViewClient.onFormResubmission(mWebView, dontResend,
254                            resend);
255                } else {
256                    dontResend.sendToTarget();
257                }
258                break;
259
260            case OVERRIDE_URL:
261                String overrideUrl = msg.getData().getString("url");
262                boolean override = uiOverrideUrlLoading(overrideUrl);
263                ResultTransport<Boolean> result =
264                        (ResultTransport<Boolean>) msg.obj;
265                synchronized (this) {
266                    result.setResult(override);
267                    notify();
268                }
269                break;
270
271            case AUTH_REQUEST:
272                if (mWebViewClient != null) {
273                    HttpAuthHandler handler = (HttpAuthHandler) msg.obj;
274                    String host = msg.getData().getString("host");
275                    String realm = msg.getData().getString("realm");
276                    mWebViewClient.onReceivedHttpAuthRequest(mWebView, handler,
277                            host, realm);
278                }
279                break;
280
281            case SSL_ERROR:
282                if (mWebViewClient != null) {
283                    HashMap<String, Object> map =
284                        (HashMap<String, Object>) msg.obj;
285                    mWebViewClient.onReceivedSslError(mWebView,
286                            (SslErrorHandler) map.get("handler"),
287                            (SslError) map.get("error"));
288                }
289                break;
290
291            case PROGRESS:
292                // Synchronize to ensure mLatestProgress is not modified after
293                // setProgress is called and before mProgressUpdatePending is
294                // changed.
295                synchronized (this) {
296                    if (mWebChromeClient != null) {
297                        mWebChromeClient.onProgressChanged(mWebView,
298                                mLatestProgress);
299                    }
300                    mProgressUpdatePending = false;
301                }
302                break;
303
304            case UPDATE_VISITED:
305                if (mWebViewClient != null) {
306                    mWebViewClient.doUpdateVisitedHistory(mWebView,
307                            (String) msg.obj, msg.arg1 != 0);
308                }
309                break;
310
311            case LOAD_RESOURCE:
312                if (mWebViewClient != null) {
313                    mWebViewClient.onLoadResource(mWebView, (String) msg.obj);
314                }
315                break;
316
317            case DOWNLOAD_FILE:
318                if (mDownloadListener != null) {
319                    String url = msg.getData().getString("url");
320                    String userAgent = msg.getData().getString("userAgent");
321                    String contentDisposition =
322                        msg.getData().getString("contentDisposition");
323                    String mimetype = msg.getData().getString("mimetype");
324                    Long contentLength = msg.getData().getLong("contentLength");
325
326                    mDownloadListener.onDownloadStart(url, userAgent,
327                            contentDisposition, mimetype, contentLength);
328                }
329                break;
330
331            case CREATE_WINDOW:
332                if (mWebChromeClient != null) {
333                    if (!mWebChromeClient.onCreateWindow(mWebView,
334                                msg.arg1 == 1, msg.arg2 == 1,
335                                (Message) msg.obj)) {
336                        synchronized (this) {
337                            notify();
338                        }
339                    }
340                }
341                break;
342
343            case REQUEST_FOCUS:
344                if (mWebChromeClient != null) {
345                    mWebChromeClient.onRequestFocus(mWebView);
346                }
347                break;
348
349            case CLOSE_WINDOW:
350                if (mWebChromeClient != null) {
351                    mWebChromeClient.onCloseWindow((WebView) msg.obj);
352                }
353                break;
354
355            case SAVE_PASSWORD:
356                Bundle bundle = msg.getData();
357                String schemePlusHost = bundle.getString("host");
358                String username = bundle.getString("username");
359                String password = bundle.getString("password");
360                // If the client returned false it means that the notify message
361                // will not be sent and we should notify WebCore ourselves.
362                if (!mWebView.onSavePassword(schemePlusHost, username, password,
363                            (Message) msg.obj)) {
364                    synchronized (this) {
365                        notify();
366                    }
367                }
368                break;
369
370            case ASYNC_KEYEVENTS:
371                if (mWebViewClient != null) {
372                    mWebViewClient.onUnhandledKeyEvent(mWebView,
373                            (KeyEvent) msg.obj);
374                }
375                break;
376
377            case JS_ALERT:
378                if (mWebChromeClient != null) {
379                    JsResult res = (JsResult) msg.obj;
380                    String message = msg.getData().getString("message");
381                    String url = msg.getData().getString("url");
382                    if (!mWebChromeClient.onJsAlert(mWebView, url, message,
383                                res)) {
384                        res.handleDefault();
385                    }
386                    res.setReady();
387                }
388                break;
389
390            case JS_CONFIRM:
391                if (mWebChromeClient != null) {
392                    JsResult res = (JsResult) msg.obj;
393                    String message = msg.getData().getString("message");
394                    String url = msg.getData().getString("url");
395                    if (!mWebChromeClient.onJsConfirm(mWebView, url, message,
396                                res)) {
397                        res.handleDefault();
398                    }
399                    // Tell the JsResult that it is ready for client
400                    // interaction.
401                    res.setReady();
402                }
403                break;
404
405            case JS_PROMPT:
406                if (mWebChromeClient != null) {
407                    JsPromptResult res = (JsPromptResult) msg.obj;
408                    String message = msg.getData().getString("message");
409                    String defaultVal = msg.getData().getString("default");
410                    String url = msg.getData().getString("url");
411                    if (!mWebChromeClient.onJsPrompt(mWebView, url, message,
412                                defaultVal, res)) {
413                        res.handleDefault();
414                    }
415                    // Tell the JsResult that it is ready for client
416                    // interaction.
417                    res.setReady();
418                }
419                break;
420
421            case JS_UNLOAD:
422                if (mWebChromeClient != null) {
423                    JsResult res = (JsResult) msg.obj;
424                    String message = msg.getData().getString("message");
425                    String url = msg.getData().getString("url");
426                    if (!mWebChromeClient.onJsBeforeUnload(mWebView, url,
427                                message, res)) {
428                        res.handleDefault();
429                    }
430                    res.setReady();
431                }
432                break;
433
434            case RECEIVED_CERTIFICATE:
435                mWebView.setCertificate((SslCertificate) msg.obj);
436                break;
437
438            case NOTIFY:
439                synchronized (this) {
440                    notify();
441                }
442                break;
443
444            case SCALE_CHANGED:
445                if (mWebViewClient != null) {
446                    mWebViewClient.onScaleChanged(mWebView, msg.getData()
447                            .getFloat("old"), msg.getData().getFloat("new"));
448                }
449                break;
450
451            case SWITCH_OUT_HISTORY:
452                mWebView.switchOutDrawHistory();
453                break;
454        }
455    }
456
457    /**
458     * Return the latest progress.
459     */
460    public int getProgress() {
461        return mLatestProgress;
462    }
463
464    /**
465     * Called by WebCore side to switch out of history Picture drawing mode
466     */
467    void switchOutDrawHistory() {
468        sendMessage(obtainMessage(SWITCH_OUT_HISTORY));
469    }
470
471    //--------------------------------------------------------------------------
472    // WebViewClient functions.
473    // NOTE: shouldOverrideKeyEvent is never called from the WebCore thread so
474    // it is not necessary to include it here.
475    //--------------------------------------------------------------------------
476
477    // Performance probe
478    private long mWebCoreThreadTime;
479
480    public void onPageStarted(String url, Bitmap favicon) {
481        // Do an unsynchronized quick check to avoid posting if no callback has
482        // been set.
483        if (mWebViewClient == null) {
484            return;
485        }
486        // Performance probe
487        if (false) {
488            mWebCoreThreadTime = SystemClock.currentThreadTimeMillis();
489            Network.getInstance(mContext).startTiming();
490        }
491        Message msg = obtainMessage(PAGE_STARTED);
492        msg.obj = favicon;
493        msg.getData().putString("url", url);
494        sendMessage(msg);
495    }
496
497    public void onPageFinished(String url) {
498        // Do an unsynchronized quick check to avoid posting if no callback has
499        // been set.
500        if (mWebViewClient == null) {
501            return;
502        }
503        // Performance probe
504        if (false) {
505            Log.d("WebCore", "WebCore thread used " +
506                    (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime)
507                    + " ms");
508            Network.getInstance(mContext).stopTiming();
509        }
510        Message msg = obtainMessage(PAGE_FINISHED, url);
511        sendMessage(msg);
512    }
513
514    public void onTooManyRedirects(Message cancelMsg, Message continueMsg) {
515        // Do an unsynchronized quick check to avoid posting if no callback has
516        // been set.
517        if (mWebViewClient == null) {
518            cancelMsg.sendToTarget();
519            return;
520        }
521
522        Message msg = obtainMessage(TOO_MANY_REDIRECTS);
523        Bundle bundle = msg.getData();
524        bundle.putParcelable("cancelMsg", cancelMsg);
525        bundle.putParcelable("continueMsg", continueMsg);
526        sendMessage(msg);
527    }
528
529    public void onReceivedError(int errorCode, String description,
530            String failingUrl) {
531        // Do an unsynchronized quick check to avoid posting if no callback has
532        // been set.
533        if (mWebViewClient == null) {
534            return;
535        }
536
537        Message msg = obtainMessage(REPORT_ERROR);
538        msg.arg1 = errorCode;
539        msg.getData().putString("description", description);
540        msg.getData().putString("failingUrl", failingUrl);
541        sendMessage(msg);
542    }
543
544    public void onFormResubmission(Message dontResend,
545            Message resend) {
546        // Do an unsynchronized quick check to avoid posting if no callback has
547        // been set.
548        if (mWebViewClient == null) {
549            dontResend.sendToTarget();
550            return;
551        }
552
553        Message msg = obtainMessage(RESEND_POST_DATA);
554        Bundle bundle = msg.getData();
555        bundle.putParcelable("resend", resend);
556        bundle.putParcelable("dontResend", dontResend);
557        sendMessage(msg);
558    }
559
560    /**
561     * Called by the WebCore side
562     */
563    public boolean shouldOverrideUrlLoading(String url) {
564        // We have a default behavior if no client exists so always send the
565        // message.
566        ResultTransport<Boolean> res = new ResultTransport<Boolean>();
567        Message msg = obtainMessage(OVERRIDE_URL);
568        msg.getData().putString("url", url);
569        msg.obj = res;
570        synchronized (this) {
571            sendMessage(msg);
572            try {
573                wait();
574            } catch (InterruptedException e) {
575                Log.e(LOGTAG, "Caught exception while waiting for overrideUrl");
576                Log.e(LOGTAG, Log.getStackTraceString(e));
577            }
578        }
579        return res.getResult().booleanValue();
580    }
581
582    public void onReceivedHttpAuthRequest(HttpAuthHandler handler,
583            String hostName, String realmName) {
584        // Do an unsynchronized quick check to avoid posting if no callback has
585        // been set.
586        if (mWebViewClient == null) {
587            handler.cancel();
588            return;
589        }
590        Message msg = obtainMessage(AUTH_REQUEST, handler);
591        msg.getData().putString("host", hostName);
592        msg.getData().putString("realm", realmName);
593        sendMessage(msg);
594    }
595    /**
596     * @hide - hide this because it contains a parameter of type SslError.
597     * SslError is located in a hidden package.
598     */
599    public void onReceivedSslError(SslErrorHandler handler, SslError error) {
600        // Do an unsynchronized quick check to avoid posting if no callback has
601        // been set.
602        if (mWebViewClient == null) {
603            handler.cancel();
604            return;
605        }
606        Message msg = obtainMessage(SSL_ERROR);
607        //, handler);
608        HashMap<String, Object> map = new HashMap();
609        map.put("handler", handler);
610        map.put("error", error);
611        msg.obj = map;
612        sendMessage(msg);
613    }
614    /**
615     * @hide - hide this because it contains a parameter of type SslCertificate,
616     * which is located in a hidden package.
617     */
618
619    public void onReceivedCertificate(SslCertificate certificate) {
620        // Do an unsynchronized quick check to avoid posting if no callback has
621        // been set.
622        if (mWebViewClient == null) {
623            return;
624        }
625        // here, certificate can be null (if the site is not secure)
626        sendMessage(obtainMessage(RECEIVED_CERTIFICATE, certificate));
627    }
628
629    public void doUpdateVisitedHistory(String url, boolean isReload) {
630        // Do an unsynchronized quick check to avoid posting if no callback has
631        // been set.
632        if (mWebViewClient == null) {
633            return;
634        }
635        sendMessage(obtainMessage(UPDATE_VISITED, isReload ? 1 : 0, 0, url));
636    }
637
638    public void onLoadResource(String url) {
639        // Do an unsynchronized quick check to avoid posting if no callback has
640        // been set.
641        if (mWebViewClient == null) {
642            return;
643        }
644        sendMessage(obtainMessage(LOAD_RESOURCE, url));
645    }
646
647    public void onUnhandledKeyEvent(KeyEvent event) {
648        // Do an unsynchronized quick check to avoid posting if no callback has
649        // been set.
650        if (mWebViewClient == null) {
651            return;
652        }
653        sendMessage(obtainMessage(ASYNC_KEYEVENTS, event));
654    }
655
656    public void onScaleChanged(float oldScale, float newScale) {
657        // Do an unsynchronized quick check to avoid posting if no callback has
658        // been set.
659        if (mWebViewClient == null) {
660            return;
661        }
662        Message msg = obtainMessage(SCALE_CHANGED);
663        Bundle bundle = msg.getData();
664        bundle.putFloat("old", oldScale);
665        bundle.putFloat("new", newScale);
666        sendMessage(msg);
667    }
668
669    //--------------------------------------------------------------------------
670    // DownloadListener functions.
671    //--------------------------------------------------------------------------
672
673    /**
674     * Starts a download if a download listener has been registered, otherwise
675     * return false.
676     */
677    public boolean onDownloadStart(String url, String userAgent,
678            String contentDisposition, String mimetype, long contentLength) {
679        // Do an unsynchronized quick check to avoid posting if no callback has
680        // been set.
681        if (mDownloadListener == null) {
682            // Cancel the download if there is no browser client.
683            return false;
684        }
685
686        Message msg = obtainMessage(DOWNLOAD_FILE);
687        Bundle bundle = msg.getData();
688        bundle.putString("url", url);
689        bundle.putString("userAgent", userAgent);
690        bundle.putString("mimetype", mimetype);
691        bundle.putLong("contentLength", contentLength);
692        bundle.putString("contentDisposition", contentDisposition);
693        sendMessage(msg);
694        return true;
695    }
696
697
698    //--------------------------------------------------------------------------
699    // WebView specific functions that do not interact with a client. These
700    // functions just need to operate within the UI thread.
701    //--------------------------------------------------------------------------
702
703    public boolean onSavePassword(String schemePlusHost, String username,
704            String password, Message resumeMsg) {
705        // resumeMsg should be null at this point because we want to create it
706        // within the CallbackProxy.
707        if (Config.DEBUG) {
708            junit.framework.Assert.assertNull(resumeMsg);
709        }
710        resumeMsg = obtainMessage(NOTIFY);
711
712        Message msg = obtainMessage(SAVE_PASSWORD, resumeMsg);
713        Bundle bundle = msg.getData();
714        bundle.putString("host", schemePlusHost);
715        bundle.putString("username", username);
716        bundle.putString("password", password);
717        synchronized (this) {
718            sendMessage(msg);
719            try {
720                wait();
721            } catch (InterruptedException e) {
722                Log.e(LOGTAG,
723                        "Caught exception while waiting for onSavePassword");
724                Log.e(LOGTAG, Log.getStackTraceString(e));
725            }
726        }
727        // Doesn't matter here
728        return false;
729    }
730
731    //--------------------------------------------------------------------------
732    // WebChromeClient methods
733    //--------------------------------------------------------------------------
734
735    public void onProgressChanged(int newProgress) {
736        // Synchronize so that mLatestProgress is up-to-date.
737        synchronized (this) {
738            mLatestProgress = newProgress;
739            if (mWebChromeClient == null) {
740                return;
741            }
742            if (!mProgressUpdatePending) {
743                sendEmptyMessage(PROGRESS);
744                mProgressUpdatePending = true;
745            }
746        }
747    }
748
749    public WebView createWindow(boolean dialog, boolean userGesture) {
750        // Do an unsynchronized quick check to avoid posting if no callback has
751        // been set.
752        if (mWebChromeClient == null) {
753            return null;
754        }
755
756        WebView.WebViewTransport transport = mWebView.new WebViewTransport();
757        final Message msg = obtainMessage(NOTIFY);
758        msg.obj = transport;
759        synchronized (this) {
760            sendMessage(obtainMessage(CREATE_WINDOW, dialog ? 1 : 0,
761                    userGesture ? 1 : 0, msg));
762            try {
763                wait();
764            } catch (InterruptedException e) {
765                Log.e(LOGTAG,
766                        "Caught exception while waiting for createWindow");
767                Log.e(LOGTAG, Log.getStackTraceString(e));
768            }
769        }
770
771        WebView w = transport.getWebView();
772        if (w != null) {
773            w.getWebViewCore().initializeSubwindow();
774        }
775        return w;
776    }
777
778    public void onRequestFocus() {
779        // Do an unsynchronized quick check to avoid posting if no callback has
780        // been set.
781        if (mWebChromeClient == null) {
782            return;
783        }
784
785        sendEmptyMessage(REQUEST_FOCUS);
786    }
787
788    public void onCloseWindow(WebView window) {
789        // Do an unsynchronized quick check to avoid posting if no callback has
790        // been set.
791        if (mWebChromeClient == null) {
792            return;
793        }
794        sendMessage(obtainMessage(CLOSE_WINDOW, window));
795    }
796
797    public void onReceivedIcon(Bitmap icon) {
798        if (Config.DEBUG && mBackForwardList.getCurrentItem() == null) {
799            throw new AssertionError();
800        }
801        mBackForwardList.getCurrentItem().setFavicon(icon);
802        // Do an unsynchronized quick check to avoid posting if no callback has
803        // been set.
804        if (mWebChromeClient == null) {
805            return;
806        }
807        sendMessage(obtainMessage(RECEIVED_ICON, icon));
808    }
809
810    public void onReceivedTitle(String title) {
811        // Do an unsynchronized quick check to avoid posting if no callback has
812        // been set.
813        if (mWebChromeClient == null) {
814            return;
815        }
816        sendMessage(obtainMessage(RECEIVED_TITLE, title));
817    }
818
819    public void onJsAlert(String url, String message) {
820        // Do an unsynchronized quick check to avoid posting if no callback has
821        // been set.
822        if (mWebChromeClient == null) {
823            return;
824        }
825        JsResult result = new JsResult(this, false);
826        Message alert = obtainMessage(JS_ALERT, result);
827        alert.getData().putString("message", message);
828        alert.getData().putString("url", url);
829        synchronized (this) {
830            sendMessage(alert);
831            try {
832                wait();
833            } catch (InterruptedException e) {
834                Log.e(LOGTAG, "Caught exception while waiting for jsAlert");
835                Log.e(LOGTAG, Log.getStackTraceString(e));
836            }
837        }
838    }
839
840    public boolean onJsConfirm(String url, String message) {
841        // Do an unsynchronized quick check to avoid posting if no callback has
842        // been set.
843        if (mWebChromeClient == null) {
844            return false;
845        }
846        JsResult result = new JsResult(this, false);
847        Message confirm = obtainMessage(JS_CONFIRM, result);
848        confirm.getData().putString("message", message);
849        confirm.getData().putString("url", url);
850        synchronized (this) {
851            sendMessage(confirm);
852            try {
853                wait();
854            } catch (InterruptedException e) {
855                Log.e(LOGTAG, "Caught exception while waiting for jsConfirm");
856                Log.e(LOGTAG, Log.getStackTraceString(e));
857            }
858        }
859        return result.getResult();
860    }
861
862    public String onJsPrompt(String url, String message, String defaultValue) {
863        // Do an unsynchronized quick check to avoid posting if no callback has
864        // been set.
865        if (mWebChromeClient == null) {
866            return null;
867        }
868        JsPromptResult result = new JsPromptResult(this);
869        Message prompt = obtainMessage(JS_PROMPT, result);
870        prompt.getData().putString("message", message);
871        prompt.getData().putString("default", defaultValue);
872        prompt.getData().putString("url", url);
873        synchronized (this) {
874            sendMessage(prompt);
875            try {
876                wait();
877            } catch (InterruptedException e) {
878                Log.e(LOGTAG, "Caught exception while waiting for jsPrompt");
879                Log.e(LOGTAG, Log.getStackTraceString(e));
880            }
881        }
882        return result.getStringResult();
883    }
884
885    public boolean onJsBeforeUnload(String url, String message) {
886        // Do an unsynchronized quick check to avoid posting if no callback has
887        // been set.
888        if (mWebChromeClient == null) {
889            return true;
890        }
891        JsResult result = new JsResult(this, true);
892        Message confirm = obtainMessage(JS_UNLOAD, result);
893        confirm.getData().putString("message", message);
894        confirm.getData().putString("url", url);
895        synchronized (this) {
896            sendMessage(confirm);
897            try {
898                wait();
899            } catch (InterruptedException e) {
900                Log.e(LOGTAG, "Caught exception while waiting for jsUnload");
901                Log.e(LOGTAG, Log.getStackTraceString(e));
902            }
903        }
904        return result.getResult();
905    }
906}
907