chromium/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package com.android.webview.chromium;

import android.webkit.WebChromeClient;
import android.webkit.WebViewClient;

import org.chromium.android_webview.AwBrowserContextStore;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwRenderProcess;
import org.chromium.android_webview.ScriptHandler;
import org.chromium.android_webview.WebMessageListener;
import org.chromium.android_webview.WebViewChromiumRunQueue;
import org.chromium.base.ThreadUtils;
import org.chromium.content_public.browser.MessagePayload;
import org.chromium.content_public.browser.MessagePort;

import java.util.concurrent.Callable;

/**
 * This class contains the parts of WebViewChromium that should be shared between the webkit-glue
 * layer and the support library glue layer.
 */
public class SharedWebViewChromium {
    private final WebViewChromiumRunQueue mRunQueue;
    private final WebViewChromiumAwInit mAwInit;
    // The WebView wrapper for WebContents and required browser components.
    private AwContents mAwContents;

    private SharedWebViewContentsClientAdapter mContentsClientAdapter;

    // Default WebViewClient used to avoid null checks.
    static final WebViewClient sNullWebViewClient = new WebViewClient();
    // The WebViewClient instance that was passed to WebView.setWebViewClient().
    private WebViewClient mWebViewClient = sNullWebViewClient;
    private WebChromeClient mWebChromeClient;

    public SharedWebViewChromium(WebViewChromiumRunQueue runQueue, WebViewChromiumAwInit awInit) {
        mRunQueue = runQueue;
        mAwInit = awInit;
    }

    void setWebViewClient(WebViewClient client) {
        mWebViewClient = client != null ? client : sNullWebViewClient;
    }

    public WebViewClient getWebViewClient() {
        return mWebViewClient;
    }

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

    public WebChromeClient getWebChromeClient() {
        return mWebChromeClient;
    }

    public AwRenderProcess getRenderProcess() {
        mAwInit.startYourEngines(true);
        if (checkNeedsPost()) {
            return mRunQueue.runOnUiThreadBlocking(() -> getRenderProcess());
        }
        return mAwContents.getRenderProcess();
    }

    public void init(SharedWebViewContentsClientAdapter contentsClientAdapter) {
        mContentsClientAdapter = contentsClientAdapter;
    }

    public void initForReal(AwContents awContents) {
        assert ThreadUtils.runningOnUiThread();

        if (mAwContents != null) {
            throw new RuntimeException(
                    "Cannot create multiple AwContents for the same SharedWebViewChromium");
        }
        mAwContents = awContents;
    }

    public void insertVisualStateCallback(long requestId, AwContents.VisualStateCallback callback) {
        if (checkNeedsPost()) {
            mRunQueue.addTask(
                    new Runnable() {
                        @Override
                        public void run() {
                            insertVisualStateCallback(requestId, callback);
                        }
                    });
            return;
        }
        mAwContents.insertVisualStateCallback(requestId, callback);
    }

    public MessagePort[] createWebMessageChannel() {
        mAwInit.startYourEngines(true);
        if (checkNeedsPost()) {
            MessagePort[] ret =
                    mRunQueue.runOnUiThreadBlocking(
                            new Callable<MessagePort[]>() {
                                @Override
                                public MessagePort[] call() {
                                    return createWebMessageChannel();
                                }
                            });
            return ret;
        }
        return mAwContents.createMessageChannel();
    }

    public void postMessageToMainFrame(
            final MessagePayload messagePayload,
            final String targetOrigin,
            final MessagePort[] sentPorts) {
        if (checkNeedsPost()) {
            mRunQueue.addTask(
                    new Runnable() {
                        @Override
                        public void run() {
                            postMessageToMainFrame(messagePayload, targetOrigin, sentPorts);
                        }
                    });
            return;
        }
        mAwContents.postMessageToMainFrame(messagePayload, targetOrigin, sentPorts);
    }

    public void addWebMessageListener(
            final String jsObjectName,
            final String[] allowedOriginRules,
            final WebMessageListener listener) {
        if (checkNeedsPost()) {
            mRunQueue.addTask(
                    () -> addWebMessageListener(jsObjectName, allowedOriginRules, listener));
            return;
        }
        mAwContents.addWebMessageListener(jsObjectName, allowedOriginRules, listener);
    }

    public void removeWebMessageListener(final String jsObjectName) {
        if (checkNeedsPost()) {
            mRunQueue.addTask(() -> removeWebMessageListener(jsObjectName));
            return;
        }
        mAwContents.removeWebMessageListener(jsObjectName);
    }

    public ScriptHandler addDocumentStartJavaScript(
            final String script, final String[] allowedOriginRules) {
        if (checkNeedsPost()) {
            return mRunQueue.runOnUiThreadBlocking(
                    () -> addDocumentStartJavaScript(script, allowedOriginRules));
        }
        return mAwContents.addDocumentStartJavaScript(script, allowedOriginRules);
    }

    public void setWebViewRendererClientAdapter(
            SharedWebViewRendererClientAdapter webViewRendererClientAdapter) {
        if (checkNeedsPost()) {
            mRunQueue.addTask(
                    new Runnable() {
                        @Override
                        public void run() {
                            setWebViewRendererClientAdapter(webViewRendererClientAdapter);
                        }
                    });
            return;
        }
        mContentsClientAdapter.setWebViewRendererClientAdapter(webViewRendererClientAdapter);
    }

    public SharedWebViewRendererClientAdapter getWebViewRendererClientAdapter() {
        mAwInit.startYourEngines(true);
        if (checkNeedsPost()) {
            return mRunQueue.runOnUiThreadBlocking(
                    new Callable<SharedWebViewRendererClientAdapter>() {
                        @Override
                        public SharedWebViewRendererClientAdapter call() {
                            return getWebViewRendererClientAdapter();
                        }
                    });
        }
        return mContentsClientAdapter.getWebViewRendererClientAdapter();
    }

    public void setProfile(String profileName) {
        if (checkNeedsPost()) {
            mRunQueue.addTask(() -> setProfile(profileName));
            return;
        }
        mAwContents.setBrowserContext(AwBrowserContextStore.getNamedContext(profileName, true));
    }

    public Profile getProfile() {
        if (checkNeedsPost()) {
            return mRunQueue.runOnUiThreadBlocking(this::getProfile);
        }
        String profileName = mAwContents.getBrowserContext().getName();
        return ProfileStore.getInstance().getProfile(profileName);
    }

    protected boolean checkNeedsPost() {
        boolean needsPost = !mRunQueue.chromiumHasStarted() || !ThreadUtils.runningOnUiThread();
        if (!needsPost && mAwContents == null) {
            throw new IllegalStateException("AwContents must be created if we are not posting!");
        }
        return needsPost;
    }

    public AwContents getAwContents() {
        return mAwContents;
    }
}