chromium/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityShareTargetTest.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.chrome.browser.browserservices;

import static org.chromium.chrome.browser.browserservices.TrustedWebActivityTestUtil.createSession;
import static org.chromium.chrome.browser.browserservices.TrustedWebActivityTestUtil.createTrustedWebActivityIntent;
import static org.chromium.chrome.browser.browserservices.TrustedWebActivityTestUtil.spoofVerification;

import android.content.Intent;

import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
import androidx.browser.trusted.sharing.ShareData;
import androidx.browser.trusted.sharing.ShareTarget;
import androidx.test.filters.MediumTest;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.webapps.WebApkPostShareTargetNavigator;
import org.chromium.chrome.browser.webapps.WebApkPostShareTargetNavigatorJni;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.ChromeTabUtils;
import org.chromium.content_public.browser.WebContents;
import org.chromium.net.test.EmbeddedTestServerRule;

import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@Batch(Batch.PER_CLASS)
public class TrustedWebActivityShareTargetTest {
    // We are not actually navigating to POST target, so ok not to use test pages here.
    private static final ShareTarget POST_SHARE_TARGET =
            new ShareTarget(
                    "https://pwa.rocks/share.html",
                    "POST",
                    null,
                    new ShareTarget.Params("received_title", "received_text", null));

    private static final ShareTarget UNVERIFIED_ORIGIN_POST_SHARE_TARGET =
            new ShareTarget(
                    "https://random.website/share.html",
                    "POST",
                    null,
                    new ShareTarget.Params("received_title", "received_text", null));

    @Rule
    public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();

    @Rule public EmbeddedTestServerRule mEmbeddedTestServerRule = new EmbeddedTestServerRule();

    @Rule public JniMocker mJniMocker = new JniMocker();

    private static final String TEST_PAGE = "/chrome/test/data/android/google.html";
    private static final String SHARE_TEST_PAGE = "/chrome/test/data/android/about.html";
    private static final String PACKAGE_NAME =
            ContextUtils.getApplicationContext().getPackageName();

    private final MockPostNavigatorNatives mPostNavigatorNatives = new MockPostNavigatorNatives();

    private final CallbackHelper mPostNavigatorCallback = new CallbackHelper();

    private Intent mIntent;
    private ShareTarget mGetShareTarget;

    // Expected URL when using mGetShareTarget as input.
    private String mExpectedGetRequestUrl;

    @Before
    public void setUp() throws Exception {
        mJniMocker.mock(WebApkPostShareTargetNavigatorJni.TEST_HOOKS, mPostNavigatorNatives);
        mCustomTabActivityTestRule.setFinishActivity(true);

        LibraryLoader.getInstance().ensureInitialized();
        mEmbeddedTestServerRule.setServerUsesHttps(true);
        String testPage = mEmbeddedTestServerRule.getServer().getURL(TEST_PAGE);
        String shareTestPage = mEmbeddedTestServerRule.getServer().getURL(SHARE_TEST_PAGE);
        mGetShareTarget =
                new ShareTarget(
                        shareTestPage,
                        "GET",
                        null,
                        new ShareTarget.Params("received_title", "received_text", null));
        mExpectedGetRequestUrl =
                shareTestPage + "?received_title=test_title&received_text=test_text";
        spoofVerification(PACKAGE_NAME, testPage);
        spoofVerification(PACKAGE_NAME, "https://pwa.rocks");
        mIntent = createTrustedWebActivityIntent(testPage);
        createSession(mIntent, PACKAGE_NAME);
    }

    @Test
    @MediumTest
    public void sharesDataWithGet_FromInitialIntent() {
        putShareData(mIntent, mGetShareTarget);
        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(mIntent);
        assertGetRequestUrl(mExpectedGetRequestUrl);
    }

    @Test
    @MediumTest
    public void sharesDataWithPost_FromInitialIntent() throws Exception {
        putShareData(mIntent, POST_SHARE_TARGET);
        mCustomTabActivityTestRule.startCustomTabActivityWithIntentNotWaitingForFirstFrame(mIntent);
        assertPostNavigatorCalled();
    }

    @Test
    @MediumTest
    public void sharesDataWithPost_FromNewIntent() throws Exception {
        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(mIntent);
        putShareData(mIntent, POST_SHARE_TARGET);
        deliverNewIntent(mIntent);

        assertPostNavigatorCalled();
    }

    @Test
    @MediumTest
    public void sharesDataWithGet_FromNewIntent() {
        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(mIntent);
        putShareData(mIntent, mGetShareTarget);

        deliverNewIntent(mIntent);

        assertGetRequestUrl(mExpectedGetRequestUrl);
    }

    @Test(expected = TimeoutException.class)
    @MediumTest
    public void doesntShareWithUnverifiedOrigin() throws Exception {
        putShareData(mIntent, UNVERIFIED_ORIGIN_POST_SHARE_TARGET);
        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(mIntent);
        mPostNavigatorCallback.waitForCallback(0, 1, 1000, TimeUnit.MILLISECONDS);
    }

    private void putShareData(Intent intent, ShareTarget shareTarget) {
        ShareData shareData = new ShareData("test_title", "test_text", Collections.emptyList());
        intent.putExtra(TrustedWebActivityIntentBuilder.EXTRA_SHARE_DATA, shareData.toBundle());
        intent.putExtra(TrustedWebActivityIntentBuilder.EXTRA_SHARE_TARGET, shareTarget.toBundle());
    }

    private void assertGetRequestUrl(final String expectedGetRequestUrl) {
        // startCustomTabActivityWithIntent waits for native, so the tab must be present already.
        Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
        ChromeTabUtils.waitForTabPageLoaded(tab, expectedGetRequestUrl);
    }

    private void assertPostNavigatorCalled() throws TimeoutException {
        // Constructing POST requests is unit-tested elsewhere.
        // Here we only care that the request reaches the native code.
        mPostNavigatorCallback.waitForCallback(0);
    }

    private void deliverNewIntent(Intent intent) {
        // Delivering intents to existing CustomTabActivity in tests is error-prone and out of scope
        // of these tests. Thus calling onNewIntent directly.
        ThreadUtils.runOnUiThreadBlocking(
                () -> mCustomTabActivityTestRule.getActivity().onNewIntent(intent));
    }

    private class MockPostNavigatorNatives implements WebApkPostShareTargetNavigator.Natives {
        @Override
        public void nativeLoadViewForShareTargetPost(
                boolean isMultipartEncoding,
                String[] names,
                String[] values,
                boolean[] isValueFileUris,
                String[] filenames,
                String[] types,
                String startUrl,
                WebContents webContents) {
            mPostNavigatorCallback.notifyCalled();
        }
    }
}