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

import static org.chromium.components.permissions.PermissionDialogDelegate.getRequestTypeEnumSize;

import android.Manifest;
import android.content.Context;
import android.content.res.Resources;

import androidx.test.filters.MediumTest;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import org.chromium.base.ThreadUtils;
import org.chromium.base.supplier.OneshotSupplierImpl;
import org.chromium.base.test.params.ParameterAnnotations;
import org.chromium.base.test.params.ParameterProvider;
import org.chromium.base.test.params.ParameterSet;
import org.chromium.base.test.params.ParameterizedRunner;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge;
import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
import org.chromium.chrome.browser.browsing_data.TimePeriod;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
import org.chromium.chrome.browser.omnibox.status.PageInfoIPHController;
import org.chromium.chrome.browser.omnibox.status.StatusMediator;
import org.chromium.chrome.browser.omnibox.status.StatusProperties;
import org.chromium.chrome.browser.permissions.PermissionTestRule;
import org.chromium.chrome.browser.permissions.RuntimePermissionTestUtils;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.components.content_settings.ContentSettingValues;
import org.chromium.components.content_settings.ContentSettingsType;
import org.chromium.components.location.LocationUtils;
import org.chromium.components.permissions.PermissionDialogController;
import org.chromium.components.search_engines.TemplateUrlService;
import org.chromium.content_public.browser.ContentFeatureList;
import org.chromium.content_public.browser.ContentFeatureMap;
import org.chromium.device.geolocation.LocationProviderOverrider;
import org.chromium.ui.modelutil.PropertyModel;

import java.util.ArrayList;
import java.util.List;

/** Testing the interactions with permissions on a site and how it affects page info. */
@RunWith(ParameterizedRunner.class)
@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
// TODO(crbug.com/344672094): Failing when batched, batch this again.
public class PageInfoDiscoverabilityTest {
    @ClassRule
    public static final PermissionTestRule sPermissionTestRule = new PermissionTestRule();

    @Rule
    public final BlankCTATabInitialStateRule mInitialStateRule =
            new BlankCTATabInitialStateRule(sPermissionTestRule, false);

    private static final String GEOLOCATION_TEST =
            "/chrome/test/data/geolocation/geolocation_on_load.html";

    /**
     * Parameter provider for testing the different |RequestType|s that affect discoverability. The
     * RequestType enum values are defined in components/permissions/request_type.h.
     */
    public static class RequestTypeTestParams implements ParameterProvider {
        @Override
        public List<ParameterSet> getParameters() {
            List<ParameterSet> list = new ArrayList<>();
            list.addAll(getPermissionRequestParameters());
            list.addAll(getChooserParameters());
            return list;
        }

        public List<ParameterSet> getPermissionRequestParameters() {
            List<ParameterSet> parameters = new ArrayList<>();
            // ParameterSet.value = {ContentSettingsType, isInSiteSettings}
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kAccessibilityEvents")
                            .value(ContentSettingsType.ACCESSIBILITY_EVENTS, false));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kArSession")
                            .value(ContentSettingsType.AR, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kCameraStream")
                            .value(ContentSettingsType.MEDIASTREAM_CAMERA, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kClipboard")
                            .value(ContentSettingsType.CLIPBOARD_READ_WRITE, true));
            // No associated ContentSettingsType
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kDiskQuota")
                            .value(ContentSettingsType.DEFAULT, false));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kGeolocation")
                            .value(ContentSettingsType.GEOLOCATION, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kHandTracking")
                            .value(ContentSettingsType.HAND_TRACKING, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kIdleDetection")
                            .value(ContentSettingsType.IDLE_DETECTION, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kIdentityProvider")
                            .value(
                                    ContentSettingsType
                                            .FEDERATED_IDENTITY_IDENTITY_PROVIDER_REGISTRATION,
                                    false));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kMicStream")
                            .value(ContentSettingsType.MEDIASTREAM_MIC, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kMidiSysex")
                            .value(ContentSettingsType.MIDI_SYSEX, true));
            // No associated ContentSettingsType
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kMultipleDownloads")
                            .value(ContentSettingsType.DEFAULT, false));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kNfcDevice")
                            .value(ContentSettingsType.NFC, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kNotifications")
                            .value(ContentSettingsType.NOTIFICATIONS, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kProtectedMediaIdentifier")
                            .value(ContentSettingsType.PROTECTED_MEDIA_IDENTIFIER, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kStorageAccess")
                            .value(ContentSettingsType.STORAGE_ACCESS, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kVrSession")
                            .value(ContentSettingsType.VR, true));
            parameters.add(
                    new ParameterSet()
                            .name("RequestType.kTopLevelStorageAccess")
                            .value(ContentSettingsType.TOP_LEVEL_STORAGE_ACCESS, false));

            return parameters;
        }

        public List<ParameterSet> getChooserParameters() {
            List<ParameterSet> parameters = new ArrayList<>();
            // ParameterSet.value = {ContentSettingsType, isInSiteSettings}
            parameters.add(
                    new ParameterSet()
                            .name("Chooser.USB")
                            .value(ContentSettingsType.USB_CHOOSER_DATA, true));
            parameters.add(
                    new ParameterSet()
                            .name("Chooser.Bluetooth")
                            .value(ContentSettingsType.BLUETOOTH_CHOOSER_DATA, true));
            parameters.add(
                    new ParameterSet()
                            .name("Chooser.HID")
                            .value(ContentSettingsType.HID_CHOOSER_DATA, false));
            parameters.add(
                    new ParameterSet()
                            .name("Chooser.Serial")
                            .value(ContentSettingsType.SERIAL_CHOOSER_DATA, false));

            return parameters;
        }
    }

    @Mock LocationBarDataProvider mLocationBarDataProvider;
    @Mock UrlBarEditingTextStateProvider mUrlBarEditingTextStateProvider;
    @Mock Profile mProfile;
    @Mock TemplateUrlService mTemplateUrlService;
    @Mock PageInfoIPHController mPageInfoIPHController;

    Context mContext;
    Resources mResources;
    PropertyModel mModel;
    PermissionDialogController mPermissionDialogController;
    StatusMediator mMediator;
    OneshotSupplierImpl<TemplateUrlService> mTemplateUrlServiceSupplier;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mContext = sPermissionTestRule.getActivity();
        mResources = mContext.getResources();
        mPermissionDialogController = PermissionDialogController.getInstance();

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mModel = new PropertyModel(StatusProperties.ALL_KEYS);
                    mTemplateUrlServiceSupplier = new OneshotSupplierImpl<>();
                    mMediator =
                            new StatusMediator(
                                    mModel,
                                    mResources,
                                    mContext,
                                    mUrlBarEditingTextStateProvider,
                                    /* isTablet= */ false,
                                    mLocationBarDataProvider,
                                    mPermissionDialogController,
                                    mTemplateUrlServiceSupplier,
                                    () -> mProfile,
                                    mPageInfoIPHController,
                                    sPermissionTestRule.getActivity().getWindowAndroid(),
                                    null);
                    mTemplateUrlServiceSupplier.set(mTemplateUrlService);
                });
    }

    @After
    public void tearDown() throws Exception {
        LocationUtils.setFactory(null);
        LocationProviderOverrider.setLocationProviderImpl(null);

        // Reset content settings.
        CallbackHelper helper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    BrowsingDataBridge.getForProfile(ProfileManager.getLastUsedRegularProfile())
                            .clearBrowsingData(
                                    helper::notifyCalled,
                                    new int[] {BrowsingDataType.SITE_SETTINGS},
                                    TimePeriod.ALL_TIME);
                });
        helper.waitForCallback(0);
    }

    /** Tests omnibox permission when permission is allowed by the user. */
    @Test
    @MediumTest
    @Feature({"PageInfoDiscoverability"})
    public void testPageInfoDiscoverabilityAllowPrompt() throws Exception {
        Assert.assertEquals(ContentSettingsType.DEFAULT, mMediator.getLastPermission());
        // Prompt for location and accept it.
        RuntimePermissionTestUtils.setupGeolocationSystemMock();
        String[] requestablePermission =
                new String[] {
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.ACCESS_FINE_LOCATION
                };
        RuntimePermissionTestUtils.TestAndroidPermissionDelegate testAndroidPermissionDelegate =
                new RuntimePermissionTestUtils.TestAndroidPermissionDelegate(
                        requestablePermission,
                        RuntimePermissionTestUtils.RuntimePromptResponse.GRANT);
        RuntimePermissionTestUtils.runTest(
                sPermissionTestRule,
                testAndroidPermissionDelegate,
                GEOLOCATION_TEST,
                /* expectPermissionAllowed= */ true,
                /* promptDecision= */ PermissionTestRule.PromptDecision.ALLOW,
                /* waitForMissingPermissionPrompt= */ false,
                /* waitForUpdater= */ true,
                /* javascriptToExecute= */ null,
                /* missingPermissionPromptTextId= */ 0);

        Assert.assertEquals(ContentSettingsType.GEOLOCATION, mMediator.getLastPermission());
    }

    /** Tests omnibox permission when permission is blocked by the user. */
    @Test
    @MediumTest
    @Feature({"PageInfoDiscoverability"})
    public void testPageInfoDiscoverabilityBlockPrompt() throws Exception {
        Assert.assertEquals(ContentSettingsType.DEFAULT, mMediator.getLastPermission());

        // Prompt for location and deny it.
        RuntimePermissionTestUtils.setupGeolocationSystemMock();
        String[] requestablePermission =
                new String[] {
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.ACCESS_FINE_LOCATION
                };
        RuntimePermissionTestUtils.TestAndroidPermissionDelegate testAndroidPermissionDelegate =
                new RuntimePermissionTestUtils.TestAndroidPermissionDelegate(
                        requestablePermission,
                        RuntimePermissionTestUtils.RuntimePromptResponse.DENY);
        RuntimePermissionTestUtils.runTest(
                sPermissionTestRule,
                testAndroidPermissionDelegate,
                GEOLOCATION_TEST,
                /* expectPermissionAllowed= */ false,
                /* promptDecision= */ PermissionTestRule.PromptDecision.DENY,
                /* waitForMissingPermissionPrompt= */ false,
                /* waitForUpdater= */ true,
                /* javascriptToExecute= */ null,
                /* missingPermissionPromptTextId= */ 0);

        Assert.assertEquals(ContentSettingsType.GEOLOCATION, mMediator.getLastPermission());
    }

    @Test
    @MediumTest
    @Feature({"PageInfoDiscoverability"})
    public void testPermissionRequestTypeEnumSize() {
        Assert.assertEquals(
                new RequestTypeTestParams().getPermissionRequestParameters().size(),
                getRequestTypeEnumSize());
    }

    @Test
    @MediumTest
    @Feature({"PageInfoDiscoverability"})
    @ParameterAnnotations.UseMethodParameter(RequestTypeTestParams.class)
    public void testPermissionRequestTypes(
            @ContentSettingsType.EnumType int contentSettingsType, boolean isInSiteSettings) {
        if (contentSettingsType == ContentSettingsType.BLUETOOTH_CHOOSER_DATA) {
            isInSiteSettings =
                    ContentFeatureMap.isEnabled(
                            ContentFeatureList.WEB_BLUETOOTH_NEW_PERMISSIONS_BACKEND);
        }
        Assert.assertEquals(ContentSettingsType.DEFAULT, mMediator.getLastPermission());
        @ContentSettingsType.EnumType int[] permissions = {contentSettingsType};
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mMediator.onDialogResult(
                            sPermissionTestRule.getActivity().getWindowAndroid(),
                            permissions,
                            ContentSettingValues.ALLOW);
                });
        Assert.assertEquals(
                isInSiteSettings ? contentSettingsType : ContentSettingsType.DEFAULT,
                mMediator.getLastPermission());
    }
}