| // Copyright 2021 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.site_settings; |
| |
| import static androidx.test.espresso.Espresso.onView; |
| import static androidx.test.espresso.action.ViewActions.click; |
| import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; |
| import static androidx.test.espresso.assertion.ViewAssertions.matches; |
| import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; |
| import static androidx.test.espresso.matcher.ViewMatchers.withText; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.hamcrest.Matchers.containsString; |
| import static org.junit.Assert.assertEquals; |
| |
| import android.os.Build; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import org.junit.Assert; |
| import org.junit.Assume; |
| import org.junit.ClassRule; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter; |
| import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate; |
| 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.Batch; |
| import org.chromium.base.test.util.CommandLineFlags; |
| import org.chromium.base.test.util.DisableIf; |
| import org.chromium.chrome.browser.flags.ChromeSwitches; |
| import org.chromium.chrome.browser.profiles.ProfileManager; |
| import org.chromium.chrome.browser.settings.SettingsActivity; |
| import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; |
| import org.chromium.chrome.test.ChromeTabbedActivityTestRule; |
| import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule; |
| import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; |
| import org.chromium.components.browser_ui.site_settings.ChosenObjectInfo; |
| import org.chromium.components.browser_ui.site_settings.ContentSettingException; |
| import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings; |
| import org.chromium.components.browser_ui.site_settings.SiteSettingsUtil; |
| import org.chromium.components.browser_ui.site_settings.Website; |
| import org.chromium.components.browser_ui.site_settings.WebsiteAddress; |
| import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge; |
| import org.chromium.components.content_settings.ContentSettingValues; |
| import org.chromium.components.content_settings.ContentSettingsType; |
| import org.chromium.content_public.browser.test.util.TestThreadUtils; |
| import org.chromium.url.GURL; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** Tests that exercise functionality when showing details for a single site. */ |
| @RunWith(ParameterizedRunner.class) |
| @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class) |
| @CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE) |
| @Batch(SingleWebsiteSettingsTest.TEST_BATCH_NAME) |
| public class SingleWebsiteSettingsTest { |
| private static final String EXAMPLE_ADDRESS = "https://example.com"; |
| |
| static final String TEST_BATCH_NAME = "SingleWebsiteSettingsTest"; |
| |
| @ClassRule |
| public static ChromeTabbedActivityTestRule sCTATestRule = new ChromeTabbedActivityTestRule(); |
| |
| @Rule |
| public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule = |
| new BlankCTATabInitialStateRule(sCTATestRule, false); |
| |
| /** |
| * A provider supplying params for {@link #testExceptionToggleShowing}. |
| * |
| * <p>Entries in SingleWebsiteSettings should only have Allow/Block values (independent of what |
| * the global toggle specifies), because if there's a ContentSettingValue entry for the site, |
| * then the user has already made a decision. That decision can only be Allow/Block (a decision |
| * of ASK doesn't make sense because we don't support 'Ask me every time' for a given site). |
| */ |
| public static class SingleWebsiteSettingsParams implements ParameterProvider { |
| @Override |
| public Iterable<ParameterSet> getParameters() { |
| ArrayList<ParameterSet> testCases = new ArrayList<>(); |
| for (@ContentSettingsType.EnumType |
| int contentSettings : SiteSettingsUtil.SETTINGS_ORDER) { |
| testCases.add( |
| createParameterSet("Allow_", contentSettings, ContentSettingValues.ALLOW)); |
| testCases.add( |
| createParameterSet("Block_", contentSettings, ContentSettingValues.BLOCK)); |
| } |
| return testCases; |
| } |
| } |
| |
| @Test |
| @SmallTest |
| @UseMethodParameter(SingleWebsiteSettingsParams.class) |
| public void testExceptionToggleShowing( |
| @ContentSettingsType.EnumType int contentSettingsType, |
| @ContentSettingValues int contentSettingValue) { |
| // Preference for Notification on O+ is added as a ChromeImageViewPreference. See |
| // SingleWebsiteSettings#setUpNotificationsPreference |
| Assume.assumeFalse( |
| "Preference for Notification is not a toggle on Android N-.", |
| contentSettingsType == ContentSettingsType.NOTIFICATIONS |
| && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O); |
| |
| new SingleExceptionTestCase(contentSettingsType, contentSettingValue).run(); |
| } |
| |
| @Test |
| @SmallTest |
| @DisableIf.Build( |
| sdk_is_less_than = Build.VERSION_CODES.O, |
| message = "Notification does not have a toggle when disabled.") |
| public void testNotificationException() { |
| SettingsActivity settingsActivity = |
| SiteSettingsTestUtils.startSingleWebsitePreferences( |
| createWebsiteWithContentSettingException( |
| ContentSettingsType.NOTIFICATIONS, ContentSettingValues.BLOCK)); |
| |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> { |
| SingleWebsiteSettings websitePreferences = |
| (SingleWebsiteSettings) settingsActivity.getMainFragment(); |
| Assert.assertNotNull( |
| "Notification Preference not found.", |
| websitePreferences.findPreference( |
| SingleWebsiteSettings.getPreferenceKey( |
| ContentSettingsType.NOTIFICATIONS))); |
| }); |
| |
| settingsActivity.finish(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testDesktopSiteException() { |
| SettingsActivity settingsActivity = |
| SiteSettingsTestUtils.startSingleWebsitePreferences( |
| createWebsiteWithContentSettingException( |
| ContentSettingsType.REQUEST_DESKTOP_SITE, |
| ContentSettingValues.ALLOW)); |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> { |
| var websitePreferences = |
| (SingleWebsiteSettings) settingsActivity.getMainFragment(); |
| Assert.assertNotNull( |
| "Desktop site preference should be present.", |
| websitePreferences.findPreference( |
| SingleWebsiteSettings.getPreferenceKey( |
| ContentSettingsType.REQUEST_DESKTOP_SITE))); |
| }); |
| settingsActivity.finish(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testChoosenObjectPermission() { |
| String origin = "https://example.com"; |
| Website website = new Website(WebsiteAddress.create(origin), WebsiteAddress.create(origin)); |
| String object = |
| """ |
| {"name": "Some device", |
| "ephemeral-guid": "1", |
| "product-id": "2", |
| "serial-number": "3"}"""; |
| website.addChosenObjectInfo( |
| new ChosenObjectInfo( |
| ContentSettingsType.USB_CHOOSER_DATA, |
| origin, |
| "Some device", |
| object, |
| /* isManaged= */ false)); |
| website.addChosenObjectInfo( |
| new ChosenObjectInfo( |
| ContentSettingsType.USB_CHOOSER_DATA, |
| origin, |
| "A managed device", |
| "not needed", |
| /* isManaged= */ true)); |
| |
| // Open site settings and check that permissions are displayed. |
| SettingsActivity activity = SiteSettingsTestUtils.startSingleWebsitePreferences(website); |
| onView(withText("Some device")).check(matches(isDisplayed())); |
| onView(withText("A managed device")).check(matches(isDisplayed())); |
| |
| // Reset permission and check that only the non-managed permission is removed. |
| onView(withText(containsString("reset"))).perform(click()); |
| onView(withText("Delete & reset")).perform(click()); |
| onView(withText("Some device")).check(doesNotExist()); |
| onView(withText("A managed device")).check(matches(isDisplayed())); |
| activity.finish(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testStorageAccessPermission() { |
| int type = ContentSettingsType.STORAGE_ACCESS; |
| GURL example = new GURL("https://example.com"); |
| GURL embedded2 = new GURL("https://embedded2.com"); |
| |
| Website website = |
| createWebsiteWithStorageAccessPermission( |
| "https://[*.]embedded.com", |
| "https://[*.]example.com", |
| ContentSettingValues.ALLOW); |
| Website website2 = |
| createWebsiteWithStorageAccessPermission( |
| "https://[*.]embedded2.com", |
| "https://[*.]example.com", |
| ContentSettingValues.BLOCK); |
| Website other = |
| createWebsiteWithStorageAccessPermission( |
| "https://[*.]embedded.com", |
| "https://[*.]foo.com", |
| ContentSettingValues.BLOCK); |
| Website merged = |
| SingleWebsiteSettings.mergePermissionAndStorageInfoForTopLevelOrigin( |
| WebsiteAddress.create(EXAMPLE_ADDRESS), List.of(website, website2, other)); |
| |
| var exceptions = merged.getEmbeddedPermissions().get(type); |
| assertThat(exceptions.size()).isEqualTo(2); |
| assertThat(exceptions.get(0).getContentSetting()).isEqualTo(ContentSettingValues.ALLOW); |
| assertThat(exceptions.get(1).getContentSetting()).isEqualTo(ContentSettingValues.BLOCK); |
| assertEquals(ContentSettingValues.BLOCK, getStorageAccessSetting(type, embedded2, example)); |
| |
| // Open site settings. |
| SettingsActivity activity = SiteSettingsTestUtils.startSingleWebsitePreferences(merged); |
| onView(withText("embedded.com allowed")).check(matches(isDisplayed())); |
| |
| // Toggle the second permission. |
| onView(withText("embedded2.com blocked")).check(matches(isDisplayed())).perform(click()); |
| assertEquals(ContentSettingValues.ALLOW, getStorageAccessSetting(type, embedded2, example)); |
| |
| // Reset permission. |
| onView(withText(containsString("reset"))).perform(click()); |
| onView(withText("Delete & reset")).perform(click()); |
| onView(withText("Embedded content")).check(doesNotExist()); |
| assertEquals(ContentSettingValues.ASK, getStorageAccessSetting(type, embedded2, example)); |
| activity.finish(); |
| } |
| |
| private static int getStorageAccessSetting( |
| @ContentSettingsType.EnumType int contentSettingType, |
| GURL primaryUrl, |
| GURL secondaryUrl) { |
| int[] result = {0}; |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> { |
| result[0] = |
| WebsitePreferenceBridge.getContentSetting( |
| ProfileManager.getLastUsedRegularProfile(), |
| contentSettingType, |
| primaryUrl, |
| secondaryUrl); |
| }); |
| return result[0]; |
| } |
| |
| /** |
| * Helper function for creating a {@link ParameterSet} for {@link SingleWebsiteSettingsParams}. |
| */ |
| private static ParameterSet createParameterSet( |
| String namePrefix, |
| @ContentSettingsType.EnumType int contentSettingsType, |
| @ContentSettingValues int contentSettingValue) { |
| String prefKey = SingleWebsiteSettings.getPreferenceKey(contentSettingsType); |
| Assert.assertNotNull( |
| "Preference key is missing for ContentSettingsType <" + contentSettingsType + ">.", |
| prefKey); |
| |
| return new ParameterSet() |
| .name(namePrefix + prefKey) |
| .value(contentSettingsType, contentSettingValue); |
| } |
| |
| /** Test case class that check whether a toggle exists for a given content setting. */ |
| private static class SingleExceptionTestCase { |
| @ContentSettingValues int mContentSettingValue; |
| @ContentSettingsType.EnumType int mContentSettingsType; |
| |
| private SettingsActivity mSettingsActivity; |
| |
| SingleExceptionTestCase( |
| @ContentSettingsType.EnumType int contentSettingsType, |
| @ContentSettingValues int contentSettingValue) { |
| mContentSettingsType = contentSettingsType; |
| mContentSettingValue = contentSettingValue; |
| } |
| |
| public void run() { |
| Website website = |
| createWebsiteWithContentSettingException( |
| mContentSettingsType, mContentSettingValue); |
| mSettingsActivity = SiteSettingsTestUtils.startSingleWebsitePreferences(website); |
| |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> { |
| SingleWebsiteSettings websitePreferences = |
| (SingleWebsiteSettings) mSettingsActivity.getMainFragment(); |
| doTest(websitePreferences); |
| }); |
| |
| mSettingsActivity.finish(); |
| } |
| |
| protected void doTest(SingleWebsiteSettings websitePreferences) { |
| String prefKey = SingleWebsiteSettings.getPreferenceKey(mContentSettingsType); |
| ChromeSwitchPreference switchPref = websitePreferences.findPreference(prefKey); |
| Assert.assertNotNull("Preference cannot be found on screen.", switchPref); |
| assertEquals( |
| "Switch check state is different than test setting.", |
| mContentSettingValue == ContentSettingValues.ALLOW, |
| switchPref.isChecked()); |
| } |
| } |
| |
| private static Website createWebsiteWithContentSettingException( |
| @ContentSettingsType.EnumType int type, @ContentSettingValues int value) { |
| WebsiteAddress address = WebsiteAddress.create(EXAMPLE_ADDRESS); |
| Website website = new Website(address, address); |
| website.setContentSettingException( |
| type, |
| new ContentSettingException( |
| type, |
| website.getAddress().getOrigin(), |
| value, |
| "preference", |
| /* isEmbargoed= */ false)); |
| |
| return website; |
| } |
| |
| private static Website createWebsiteWithStorageAccessPermission( |
| String origin, String embedder, @ContentSettingValues int setting) { |
| Website website = |
| new Website(WebsiteAddress.create(origin), WebsiteAddress.create(embedder)); |
| ContentSettingException info = |
| new ContentSettingException( |
| ContentSettingsType.STORAGE_ACCESS, |
| origin, |
| embedder, |
| ContentSettingValues.ASK, |
| /* source= */ "", |
| /* expiration= */ 0, |
| /* isEmbargoed= */ false); |
| // Set setting explicitly to write it to prefs. |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> { |
| info.setContentSetting(ProfileManager.getLastUsedRegularProfile(), setting); |
| }); |
| website.addEmbeddedPermission(info); |
| return website; |
| } |
| } |