chromium/chrome/browser/contextmenu/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuNativeDelegateImpl.java

// Copyright 2020 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.chrome.browser.contextmenu;

import android.graphics.Bitmap;
import android.net.Uri;

import androidx.annotation.VisibleForTesting;

import org.jni_zero.CalledByNative;
import org.jni_zero.NativeMethods;

import org.chromium.base.Callback;
import org.chromium.components.browser_ui.share.ShareImageFileUtils;
import org.chromium.components.embedder_support.contextmenu.ContextMenuImageFormat;
import org.chromium.components.embedder_support.contextmenu.ContextMenuNativeDelegate;
import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.WebContents;

class ContextMenuNativeDelegateImpl implements ContextMenuNativeDelegate {
    private static final int MAX_SHARE_DIMEN_PX = 2048;

    private final RenderFrameHost mRenderFrameHost;

    private long mNativePtr;

    /** See function for details. */
    private static byte[] sHardcodedImageBytesForTesting;

    private static String sHardcodedImageExtensionForTesting;

    /**
     * The tests trigger the context menu via JS rather than via a true native call which means
     * the native code does not have a reference to the image's RenderFrameHost. Instead allow
     * test cases to hardcode the test image bytes that will be shared.
     * @param hardcodedImageBytes The hard coded image bytes to fake or null if image should not be
     *         faked.
     * @param hardcodedImageExtension The hard coded image extension.
     */
    public static void setHardcodedImageBytesForTesting(
            byte[] hardcodedImageBytes, String hardcodedImageExtension) {
        sHardcodedImageBytesForTesting = hardcodedImageBytes;
        sHardcodedImageExtensionForTesting = hardcodedImageExtension;
    }

    public ContextMenuNativeDelegateImpl(
            WebContents webContents,
            RenderFrameHost renderFrameHost,
            ContextMenuParams contextMenuParams) {
        mRenderFrameHost = renderFrameHost;
        mNativePtr = ContextMenuNativeDelegateImplJni.get().init(webContents, contextMenuParams);
    }

    @Override
    public void destroy() {
        mNativePtr = 0;
    }

    @Override
    public void retrieveImageForShare(int imageFormat, Callback<Uri> callback) {
        if (mNativePtr == 0) return;

        Callback<ImageCallbackResult> imageRetrieveCallback =
                (result) -> {
                    ShareImageFileUtils.generateTemporaryUriFromData(
                            result.imageData, result.extension, callback);
                };

        if (sHardcodedImageBytesForTesting != null) {
            imageRetrieveCallback.onResult(createImageCallbackResultForTesting());
        } else {
            ContextMenuNativeDelegateImplJni.get()
                    .retrieveImageForShare(
                            mNativePtr,
                            ContextMenuNativeDelegateImpl.this,
                            mRenderFrameHost,
                            imageRetrieveCallback,
                            MAX_SHARE_DIMEN_PX,
                            MAX_SHARE_DIMEN_PX,
                            imageFormat);
        }
    }

    @Override
    public void retrieveImageForContextMenu(
            int maxWidthPx, int maxHeightPx, Callback<Bitmap> callback) {
        if (mNativePtr == 0) return;

        ContextMenuNativeDelegateImplJni.get()
                .retrieveImageForContextMenu(
                        mNativePtr,
                        ContextMenuNativeDelegateImpl.this,
                        mRenderFrameHost,
                        callback,
                        maxWidthPx,
                        maxHeightPx);
    }

    @Override
    public void startDownload(boolean isLink) {
        if (mNativePtr == 0) return;

        ContextMenuNativeDelegateImplJni.get()
                .startDownload(mNativePtr, ContextMenuNativeDelegateImpl.this, isLink);
    }

    @Override
    public void searchForImage() {
        if (mNativePtr == 0) return;

        ContextMenuNativeDelegateImplJni.get()
                .searchForImage(mNativePtr, ContextMenuNativeDelegateImpl.this, mRenderFrameHost);
    }

    @Override
    public RenderFrameHost getRenderFrameHost() {
        return mRenderFrameHost;
    }

    /** The class hold the |retrieveImageForShare| callback result. */
    @VisibleForTesting
    static class ImageCallbackResult {
        public byte[] imageData;
        public String extension;

        public ImageCallbackResult(byte[] imageData, String extension) {
            this.imageData = imageData;
            this.extension = extension;
        }
    }

    private static ImageCallbackResult createImageCallbackResultForTesting() {
        return new ImageCallbackResult(
                sHardcodedImageBytesForTesting, sHardcodedImageExtensionForTesting);
    }

    @CalledByNative
    private static ImageCallbackResult createImageCallbackResult(
            byte[] imageData, String extension) {
        return new ImageCallbackResult(imageData, extension);
    }

    @NativeMethods
    interface Natives {
        long init(WebContents webContents, ContextMenuParams contextMenuParams);

        void retrieveImageForShare(
                long nativeContextMenuNativeDelegateImpl,
                ContextMenuNativeDelegateImpl caller,
                RenderFrameHost renderFrameHost,
                Callback<ImageCallbackResult> callback,
                int maxWidthPx,
                int maxHeightPx,
                @ContextMenuImageFormat int imageFormat);

        void retrieveImageForContextMenu(
                long nativeContextMenuNativeDelegateImpl,
                ContextMenuNativeDelegateImpl caller,
                RenderFrameHost renderFrameHost,
                Callback<Bitmap> callback,
                int maxWidthPx,
                int maxHeightPx);

        void startDownload(
                long nativeContextMenuNativeDelegateImpl,
                ContextMenuNativeDelegateImpl caller,
                boolean isLink);

        void searchForImage(
                long nativeContextMenuNativeDelegateImpl,
                ContextMenuNativeDelegateImpl caller,
                RenderFrameHost renderFrameHost);
    }
}