// Copyright (c) 2014 mogoweb. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mogoweb.chrome.impl;

import java.lang.ref.SoftReference;
import java.net.URISyntaxException;

import org.chromium.android_webview.AwContentsClient;
import org.chromium.android_webview.AwHttpAuthHandler;
import org.chromium.android_webview.InterceptedRequestData;
import org.chromium.android_webview.JsPromptResultReceiver;
import org.chromium.android_webview.JsResultReceiver;
import org.chromium.base.TraceEvent;
import org.chromium.content.browser.ContentView;
import org.chromium.content.browser.ContentViewClient;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Picture;
import android.net.http.SslError;
import android.os.Handler;
import android.os.Message;
import android.provider.Browser;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient.CustomViewCallback;
import android.webkit.WebResourceResponse;

import com.mogoweb.chrome.DownloadListener;
import com.mogoweb.chrome.JsPromptResult;
import com.mogoweb.chrome.JsResult;
import com.mogoweb.chrome.SslErrorHandler;
import com.mogoweb.chrome.WebChromeClient;
import com.mogoweb.chrome.WebView;
import com.mogoweb.chrome.WebViewClient;

/**
 * An adapter class that forwards the callbacks from {@link ContentViewClient}
 * to the appropriate {@link WebViewClient} or {@link WebChromeClient}.
 *
 * An instance of this class is associated with one {@link WebViewChromium}
 * instance. A WebViewChromium is a WebView implementation provider (that is
 * android.webkit.WebView delegates all functionality to it) and has exactly
 * one corresponding {@link ContentView} instance.
 *
 * A {@link ContentViewClient} may be shared between multiple {@link ContentView}s,
 * and hence multiple WebViews. Many WebViewClient methods pass the source
 * WebView as an argument. This means that we either need to pass the
 * corresponding ContentView to the corresponding ContentViewClient methods,
 * or use an instance of ContentViewClientAdapter per WebViewChromium, to
 * allow the source WebView to be injected by ContentViewClientAdapter. We
 * choose the latter, because it makes for a cleaner design.
 */
public class WebViewContentsClientAdapter extends AwContentsClient {
    // TAG is chosen for consistency with classic webview tracing.
    private static final String TAG = "WebViewCallback";
    // Enables API callback tracing
    private static final boolean TRACE = DebugFlags.TRACE_CALLBACK;
    // The WebView instance that this adapter is serving.
    private final WebView mWebView;
    // The WebViewClient instance that was passed to WebView.setWebViewClient().
    private WebViewClient mWebViewClient;
    // The WebChromeClient instance that was passed to WebView.setContentViewClient().
    private WebChromeClient mWebChromeClient;
    // The listener receiving find-in-page API results.
    private WebView.FindListener mFindListener;

    private DownloadListener mDownloadListener;

    private Handler mUiThreadHandler;

    private SoftReference<Bitmap> mCachedDefaultVideoPoster;

    private static final int NEW_WEBVIEW_CREATED = 100;

    /**
     * Adapter constructor.
     *
     * @param webView the {@link WebView} instance that this adapter is serving.
     */
    WebViewContentsClientAdapter(WebView webView) {
        if (webView == null) {
            throw new IllegalArgumentException("webView can't be null");
        }

        mWebView = webView;
        setWebViewClient(null);

        mUiThreadHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                switch(msg.what) {
                    case NEW_WEBVIEW_CREATED:
                        WebView.WebViewTransport t = (WebView.WebViewTransport) msg.obj;
                        WebView newWebView = t.getWebView();
                        if (newWebView == mWebView) {
                            throw new IllegalArgumentException(
                                    "Parent WebView cannot host it's own popup window. Please " +
                                    "use WebSettings.setSupportMultipleWindows(false)");
                        }

                        if (newWebView != null && newWebView.copyBackForwardList().getSize() != 0) {
                            throw new IllegalArgumentException(
                                    "New WebView for popup window must not have been previously " +
                                    "navigated.");
                        }

                        WebViewChromium.completeWindowCreation(mWebView, newWebView);
                        break;
                    default:
                        throw new IllegalStateException();
                }
            }
        };

    }

    // WebViewClassic is coded in such a way that even if a null WebViewClient is set,
    // certain actions take place.
    // We choose to replicate this behavior by using a NullWebViewClient implementation (also known
    // as the Null Object pattern) rather than duplicating the WebViewClassic approach in
    // ContentView.
    static class NullWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
            // TODO: Investigate more and add a test case.
            // This is a copy of what Clank does. The WebViewCore key handling code and Clank key
            // handling code differ enough that it's not trivial to figure out how keycodes are
            // being filtered.
            int keyCode = event.getKeyCode();
            if (keyCode == KeyEvent.KEYCODE_MENU ||
                keyCode == KeyEvent.KEYCODE_HOME ||
                keyCode == KeyEvent.KEYCODE_BACK ||
                keyCode == KeyEvent.KEYCODE_CALL ||
                keyCode == KeyEvent.KEYCODE_ENDCALL ||
                keyCode == KeyEvent.KEYCODE_POWER ||
                keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
                keyCode == KeyEvent.KEYCODE_CAMERA ||
                keyCode == KeyEvent.KEYCODE_FOCUS ||
                keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
                keyCode == KeyEvent.KEYCODE_VOLUME_MUTE ||
                keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
                return true;
            }
            return false;
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Intent intent;
            // Perform generic parsing of the URI to turn it into an Intent.
            try {
                intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
            } catch (URISyntaxException ex) {
                Log.w(TAG, "Bad URI " + url + ": " + ex.getMessage());
                return false;
            }
            // Sanitize the Intent, ensuring web pages can not bypass browser
            // security (only access to BROWSABLE activities).
            intent.addCategory(Intent.CATEGORY_BROWSABLE);
            intent.setComponent(null);
            // Pass the package name as application ID so that the intent from the
            // same application can be opened in the same tab.
            intent.putExtra(Browser.EXTRA_APPLICATION_ID,
                    view.getContext().getPackageName());
            try {
                view.getContext().startActivity(intent);
            } catch (ActivityNotFoundException ex) {
                Log.w(TAG, "No application can handle " + url);
                return false;
            }
            return true;
        }
    }

    void setWebViewClient(WebViewClient client) {
        if (client != null) {
            mWebViewClient = client;
        } else {
            mWebViewClient = new NullWebViewClient();
        }
    }

    void setWebChromeClient(WebChromeClient client) {
        mWebChromeClient = client;
    }

    void setDownloadListener(DownloadListener listener) {
        mDownloadListener = listener;
    }

    void setFindListener(WebView.FindListener listener) {
        mFindListener = listener;
    }

    //--------------------------------------------------------------------------------------------
    //                        Adapter for all the methods.
    //--------------------------------------------------------------------------------------------

    /**
     * @see AwContentsClient#getVisitedHistory
     */
    @Override
    public void getVisitedHistory(ValueCallback<String[]> callback) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "getVisitedHistory");
            mWebChromeClient.getVisitedHistory(callback);
        }
        TraceEvent.end();
    }

    /**
     * @see AwContentsClient#doUpdateVisiteHistory(String, boolean)
     */
    @Override
    public void doUpdateVisitedHistory(String url, boolean isReload) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "doUpdateVisitedHistory=" + url + " reload=" + isReload);
        mWebViewClient.doUpdateVisitedHistory(mWebView, url, isReload);
        TraceEvent.end();
    }

    /**
     * @see AwContentsClient#onProgressChanged(int)
     */
    @Override
    public void onProgressChanged(int progress) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onProgressChanged=" + progress);
            mWebChromeClient.onProgressChanged(mWebView, progress);
        }
        TraceEvent.end();
    }

    /**
     * @see AwContentsClient#shouldInterceptRequest(java.lang.String)
     */
    @Override
    public InterceptedRequestData shouldInterceptRequest(String url) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "shouldInterceptRequest=" + url);
        WebResourceResponse response = mWebViewClient.shouldInterceptRequest(mWebView, url);
        TraceEvent.end();
        if (response == null) return null;
        return new InterceptedRequestData(
                response.getMimeType(),
                response.getEncoding(),
                response.getData());
    }

    /**
     * @see AwContentsClient#shouldOverrideUrlLoading(java.lang.String)
     */
    @Override
    public boolean shouldOverrideUrlLoading(String url) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + url);
        boolean result = mWebViewClient.shouldOverrideUrlLoading(mWebView, url);
        TraceEvent.end();
        return result;
    }

    /**
     * @see AwContentsClient#onUnhandledKeyEvent(android.view.KeyEvent)
     */
    @Override
    public void onUnhandledKeyEvent(KeyEvent event) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onUnhandledKeyEvent");
        mWebViewClient.onUnhandledKeyEvent(mWebView, event);
        TraceEvent.end();
    }

    /**
     * @see AwContentsClient#onConsoleMessage(android.webkit.ConsoleMessage)
     */
    @Override
    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
        TraceEvent.begin();
        boolean result;
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onConsoleMessage");
            result = mWebChromeClient.onConsoleMessage(consoleMessage);
            String message = consoleMessage.message();
            if (result && message != null && message.startsWith("[blocked]")) {
                Log.e(TAG, "Blocked URL: " + message);
            }
        } else {
            result = false;
        }
        TraceEvent.end();
        return result;
    }

    /**
     * @see AwContentsClient#onFindResultReceived(int,int,boolean)
     */
    @Override
    public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
            boolean isDoneCounting) {
        if (mFindListener == null) return;
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onFindResultReceived");
        mFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
        TraceEvent.end();
    }

    /**
     * @See AwContentsClient#onNewPicture(Picture)
     */
    @Override
    public void onNewPicture(Picture picture) {
    }

    @Override
    public void onLoadResource(String url) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onLoadResource=" + url);
        mWebViewClient.onLoadResource(mWebView, url);
        TraceEvent.end();
    }

    @Override
    public boolean onCreateWindow(boolean isDialog, boolean isUserGesture) {
        Message m = mUiThreadHandler.obtainMessage(
                NEW_WEBVIEW_CREATED, mWebView.new WebViewTransport());
        TraceEvent.begin();
        boolean result;
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onCreateWindow");
            result = mWebChromeClient.onCreateWindow(mWebView, isDialog, isUserGesture, m);
        } else {
            result = false;
        }
        TraceEvent.end();
        return result;
    }

    /**
     * @see AwContentsClient#onCloseWindow()
     */
    @Override
    public void onCloseWindow() {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onCloseWindow");
            mWebChromeClient.onCloseWindow(mWebView);
        }
        TraceEvent.end();
    }

    /**
     * @see AwContentsClient#onRequestFocus()
     */
    @Override
    public void onRequestFocus() {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onRequestFocus");
            mWebChromeClient.onRequestFocus(mWebView);
        }
        TraceEvent.end();
    }

    /**
     * @see AwContentsClient#onReceivedTouchIconUrl(String url, boolean precomposed)
     */
    @Override
    public void onReceivedTouchIconUrl(String url, boolean precomposed) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onReceivedTouchIconUrl=" + url);
            mWebChromeClient.onReceivedTouchIconUrl(mWebView, url, precomposed);
        }
        TraceEvent.end();
    }

    /**
     * @see AwContentsClient#onReceivedIcon(Bitmap bitmap)
     */
    @Override
    public void onReceivedIcon(Bitmap bitmap) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onReceivedIcon");
            mWebChromeClient.onReceivedIcon(mWebView, bitmap);
        }
        TraceEvent.end();
    }

    /**
     * @see ContentViewClient#onPageStarted(String)
     */
    @Override
    public void onPageStarted(String url) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onPageStarted=" + url);
        mWebViewClient.onPageStarted(mWebView, url, mWebView.getFavicon());
        TraceEvent.end();
    }

    /**
     * @see ContentViewClient#onPageFinished(String)
     */
    @Override
    public void onPageFinished(String url) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onPageFinished=" + url);
        mWebViewClient.onPageFinished(mWebView, url);
        TraceEvent.end();
    }

    /**
     * @see ContentViewClient#onReceivedError(int,String,String)
     */
    @Override
    public void onReceivedError(int errorCode, String description, String failingUrl) {
        if (description == null || description.isEmpty()) {
            // ErrorStrings is @hidden, so we can't do this in AwContents.
            // Normally the net/ layer will set a valid description, but for synthesized callbacks
            // (like in the case for intercepted requests) AwContents will pass in null.
//            description = ErrorStrings.getString(errorCode, mWebView.getContext());
        }
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onReceivedError=" + failingUrl);
        mWebViewClient.onReceivedError(mWebView, errorCode, description, failingUrl);
        TraceEvent.end();
    }

    /**
     * @see ContentViewClient#onReceivedTitle(String)
     */
    @Override
    public void onReceivedTitle(String title) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onReceivedTitle");
            mWebChromeClient.onReceivedTitle(mWebView, title);
        }
        TraceEvent.end();
    }


    /**
     * @see ContentViewClient#shouldOverrideKeyEvent(KeyEvent)
     */
    @Override
    public boolean shouldOverrideKeyEvent(KeyEvent event) {
        // TODO(joth): The expression here is a workaround for http://b/7697782 :-
        // 1. The check for system key should be made in AwContents or ContentViewCore,
        //    before shouldOverrideKeyEvent() is called at all.
        // 2. shouldOverrideKeyEvent() should be called in onKeyDown/onKeyUp, not from
        //    dispatchKeyEvent().
        if (event.isSystem()) return true;
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "shouldOverrideKeyEvent");
        boolean result = mWebViewClient.shouldOverrideKeyEvent(mWebView, event);
        TraceEvent.end();
        return result;
    }


    /**
     * @see ContentViewClient#onStartContentIntent(Context, String)
     * Callback when detecting a click on a content link.
     */
    // TODO: Delete this method when removed from base class.
    public void onStartContentIntent(Context context, String contentUrl) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + contentUrl);
        mWebViewClient.shouldOverrideUrlLoading(mWebView, contentUrl);
        TraceEvent.end();
    }

    @Override
    public void onGeolocationPermissionsShowPrompt(String origin,
            GeolocationPermissions.Callback callback) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onGeolocationPermissionsShowPrompt");
            mWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
        }
        TraceEvent.end();
    }

    @Override
    public void onGeolocationPermissionsHidePrompt() {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onGeolocationPermissionsHidePrompt");
            mWebChromeClient.onGeolocationPermissionsHidePrompt();
        }
        TraceEvent.end();
    }

    private static class JsPromptResultReceiverAdapter implements JsResult.ResultReceiver {
        private JsPromptResultReceiver mChromePromptResultReceiver;
        private JsResultReceiver mChromeResultReceiver;
        // We hold onto the JsPromptResult here, just to avoid the need to downcast
        // in onJsResultComplete.
        private final JsPromptResult mPromptResult = new JsPromptResult(this);

        public JsPromptResultReceiverAdapter(JsPromptResultReceiver receiver) {
            mChromePromptResultReceiver = receiver;
        }

        public JsPromptResultReceiverAdapter(JsResultReceiver receiver) {
            mChromeResultReceiver = receiver;
        }

        public JsPromptResult getPromptResult() {
            return mPromptResult;
        }

        @Override
        public void onJsResultComplete(JsResult result) {
            if (mChromePromptResultReceiver != null) {
                if (mPromptResult.getResult()) {
                    mChromePromptResultReceiver.confirm(mPromptResult.getStringResult());
                } else {
                    mChromePromptResultReceiver.cancel();
                }
            } else {
                if (mPromptResult.getResult()) {
                    mChromeResultReceiver.confirm();
                } else {
                    mChromeResultReceiver.cancel();
                }
            }
        }
    }

    @Override
    public void handleJsAlert(String url, String message, JsResultReceiver receiver) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            final JsPromptResult res =
                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
            if (TRACE) Log.d(TAG, "onJsAlert");
            if (!mWebChromeClient.onJsAlert(mWebView, url, message, res)) {
                new JsDialogHelper(res, JsDialogHelper.ALERT, null, message, url)
                        .showDialog(mWebView.getContext());
            }
        } else {
            receiver.cancel();
        }
        TraceEvent.end();
    }

    @Override
    public void handleJsBeforeUnload(String url, String message, JsResultReceiver receiver) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            final JsPromptResult res =
                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
            if (TRACE) Log.d(TAG, "onJsBeforeUnload");
            if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, message, res)) {
                new JsDialogHelper(res, JsDialogHelper.UNLOAD, null, message, url)
                        .showDialog(mWebView.getContext());
            }
        } else {
            receiver.cancel();
        }
        TraceEvent.end();
    }

    @Override
    public void handleJsConfirm(String url, String message, JsResultReceiver receiver) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            final JsPromptResult res =
                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
            if (TRACE) Log.d(TAG, "onJsConfirm");
            if (!mWebChromeClient.onJsConfirm(mWebView, url, message, res)) {
                new JsDialogHelper(res, JsDialogHelper.CONFIRM, null, message, url)
                        .showDialog(mWebView.getContext());
            }
        } else {
            receiver.cancel();
        }
        TraceEvent.end();
    }

    @Override
    public void handleJsPrompt(String url, String message, String defaultValue,
            JsPromptResultReceiver receiver) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            final JsPromptResult res =
                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
            if (TRACE) Log.d(TAG, "onJsPrompt");
            if (!mWebChromeClient.onJsPrompt(mWebView, url, message, defaultValue, res)) {
                new JsDialogHelper(res, JsDialogHelper.PROMPT, defaultValue, message, url)
                        .showDialog(mWebView.getContext());
            }
        } else {
            receiver.cancel();
        }
        TraceEvent.end();
    }

    @Override
    public void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onReceivedHttpAuthRequest=" + host);
        mWebViewClient.onReceivedHttpAuthRequest(mWebView,
                new AwHttpAuthHandlerAdapter(handler), host, realm);
        TraceEvent.end();
    }

    @Override
    public void onReceivedSslError(final ValueCallback<Boolean> callback, SslError error) {
        SslErrorHandler handler = new SslErrorHandler() {
            @Override
            public void proceed() {
                postProceed(true);
            }
            @Override
            public void cancel() {
                postProceed(false);
            }
            private void postProceed(final boolean proceed) {
                post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onReceiveValue(proceed);
                        }
                    });
            }
        };
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onReceivedSslError");
        mWebViewClient.onReceivedSslError(mWebView, handler, error);
        TraceEvent.end();
    }

    @Override
    public void onReceivedLoginRequest(String realm, String account, String args) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onReceivedLoginRequest=" + realm);
        mWebViewClient.onReceivedLoginRequest(mWebView, realm, account, args);
        TraceEvent.end();
    }

    @Override
    public void onFormResubmission(Message dontResend, Message resend) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, "onFormResubmission");
        mWebViewClient.onFormResubmission(mWebView, dontResend, resend);
        TraceEvent.end();
    }

    @Override
    public void onDownloadStart(String url,
                                String userAgent,
                                String contentDisposition,
                                String mimeType,
                                long contentLength) {
        if (mDownloadListener != null) {
            TraceEvent.begin();
            if (TRACE) Log.d(TAG, "onDownloadStart");
            mDownloadListener.onDownloadStart(url,
                                              userAgent,
                                              contentDisposition,
                                              mimeType,
                                              contentLength);
            TraceEvent.end();
        }
    }

    @Override
    public void onScaleChangedScaled(float oldScale, float newScale) {
        TraceEvent.begin();
        if (TRACE) Log.d(TAG, " onScaleChangedScaled");
        mWebViewClient.onScaleChanged(mWebView, oldScale, newScale);
        TraceEvent.end();
    }

    @Override
    public void onShowCustomView(View view, CustomViewCallback cb) {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onShowCustomView");
            mWebChromeClient.onShowCustomView(view, cb);
        }
        TraceEvent.end();
    }

    @Override
    public void onHideCustomView() {
        TraceEvent.begin();
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "onHideCustomView");
            mWebChromeClient.onHideCustomView();
        }
        TraceEvent.end();
    }

    @Override
    protected View getVideoLoadingProgressView() {
        TraceEvent.begin();
        View result;
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "getVideoLoadingProgressView");
            result = mWebChromeClient.getVideoLoadingProgressView();
        } else {
            result = null;
        }
        TraceEvent.end();
        return result;
    }

    @Override
    public Bitmap getDefaultVideoPoster() {
        TraceEvent.begin();
        Bitmap result = null;
        if (mWebChromeClient != null) {
            if (TRACE) Log.d(TAG, "getDefaultVideoPoster");
            result = mWebChromeClient.getDefaultVideoPoster();
        }
        if (result == null) {
            if (mCachedDefaultVideoPoster != null) {
                result = mCachedDefaultVideoPoster.get();
            }
            if (result == null) {
                Bitmap poster = BitmapFactory.decodeResource(
                        mWebView.getContext().getResources(),
                        com.mogoweb.chrome.R.drawable.ic_media_video_poster);
                result = Bitmap.createBitmap(poster.getWidth(),
                                             poster.getHeight(),
                                             poster.getConfig());
                result.eraseColor(Color.GRAY);
                Canvas canvas = new Canvas(result);
                canvas.drawBitmap(poster, 0f, 0f, null);
                mCachedDefaultVideoPoster = new SoftReference<Bitmap>(result);
            }
        }
        TraceEvent.end();
        return result;
    }

    private static class AwHttpAuthHandlerAdapter extends com.mogoweb.chrome.HttpAuthHandler {
        private AwHttpAuthHandler mAwHandler;

        public AwHttpAuthHandlerAdapter(AwHttpAuthHandler awHandler) {
            mAwHandler = awHandler;
        }

        @Override
        public void proceed(String username, String password) {
            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }
            mAwHandler.proceed(username, password);
        }

        @Override
        public void cancel() {
            mAwHandler.cancel();
        }

        @Override
        public boolean useHttpAuthUsernamePassword() {
            return mAwHandler.isFirstAttempt();
        }
    }
}
