chromium/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/ThinWebViewImpl.java

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

package org.chromium.components.thinwebview.internal;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.annotation.Nullable;

import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;

import org.chromium.base.ContextUtils;
import org.chromium.build.annotations.DoNotInline;
import org.chromium.components.embedder_support.delegate.WebContentsDelegateAndroid;
import org.chromium.components.thinwebview.CompositorView;
import org.chromium.components.thinwebview.ThinWebView;
import org.chromium.components.thinwebview.ThinWebViewConstraints;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.IntentRequestTracker;
import org.chromium.ui.base.WindowAndroid;

/** An android view backed by a {@link Surface} that is able to display a live {@link WebContents}. */
@JNINamespace("thin_webview::android")
public class ThinWebViewImpl extends FrameLayout implements ThinWebView {
    private final CompositorView mCompositorView;
    private final WindowAndroid mWindowAndroid;
    private long mNativeThinWebViewImpl;
    private WebContents mWebContents;
    private View mContentView;
    // Passed to native and stored as a weak reference, so ensure this strong
    // reference is not optimized away by R8.
    @DoNotInline private WebContentsDelegateAndroid mWebContentsDelegate;

    /**
     * Creates a {@link ThinWebViewImpl} backed by a {@link Surface}.
     * @param context The Context to create this view.
     * @param constraints A set of constraints associated with this view.
     * @param intentRequestTracker The {@link IntentRequestTracker} of the current activity.
     */
    public ThinWebViewImpl(
            Context context,
            ThinWebViewConstraints constraints,
            IntentRequestTracker intentRequestTracker) {
        super(context);
        if (ContextUtils.activityFromContext(context) != null) {
            mWindowAndroid =
                    new ActivityWindowAndroid(
                            context, /* listenToActivityState= */ true, intentRequestTracker);
        } else {
            mWindowAndroid = new WindowAndroid(context);
        }
        mCompositorView = new CompositorViewImpl(context, mWindowAndroid, constraints);

        LayoutParams layoutParams =
                new LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        addView(mCompositorView.getView(), layoutParams);

        mNativeThinWebViewImpl =
                ThinWebViewImplJni.get()
                        .init(ThinWebViewImpl.this, mCompositorView, mWindowAndroid);
    }

    @Override
    public View getView() {
        return this;
    }

    @Override
    public void attachWebContents(
            WebContents webContents,
            @Nullable View contentView,
            @Nullable WebContentsDelegateAndroid delegate) {
        if (mNativeThinWebViewImpl == 0) return;
        mWebContents = webContents;
        // Native code holds only a weak reference to this object.
        mWebContentsDelegate = delegate;
        setContentView(contentView);
        ThinWebViewImplJni.get()
                .setWebContents(
                        mNativeThinWebViewImpl, ThinWebViewImpl.this, webContents, delegate);
        webContents.onShow();
    }

    @Override
    public void destroy() {
        if (mNativeThinWebViewImpl == 0) return;
        if (mContentView != null) {
            removeView(mContentView);
            mContentView = null;
        }
        mCompositorView.destroy();
        ThinWebViewImplJni.get().destroy(mNativeThinWebViewImpl, ThinWebViewImpl.this);
        mNativeThinWebViewImpl = 0;
        mWindowAndroid.destroy();
    }

    @Override
    public void setAlpha(float alpha) {
        mCompositorView.setAlpha(alpha);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (mNativeThinWebViewImpl == 0) return;
        if (w != oldw || h != oldh) {
            ThinWebViewImplJni.get()
                    .sizeChanged(mNativeThinWebViewImpl, ThinWebViewImpl.this, w, h);
        }
    }

    private void setContentView(View contentView) {
        if (mContentView == contentView) return;

        if (mContentView != null) {
            assert getChildCount() > 1;
            removeViewAt(1);
        }

        mContentView = contentView;
        if (mContentView != null) addView(mContentView, 1);
    }

    @NativeMethods
    interface Natives {
        long init(
                ThinWebViewImpl caller, CompositorView compositorView, WindowAndroid windowAndroid);

        void destroy(long nativeThinWebView, ThinWebViewImpl caller);

        void setWebContents(
                long nativeThinWebView,
                ThinWebViewImpl caller,
                WebContents webContents,
                WebContentsDelegateAndroid delegate);

        void sizeChanged(long nativeThinWebView, ThinWebViewImpl caller, int width, int height);
    }
}