| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "build/branding_buildflags.h" |
| #include "chrome/browser/chrome_content_browser_client.h" |
| #include "chrome/browser/content_settings/generated_javascript_optimizer_pref.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/history/history_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
| #include "chrome/browser/safe_browsing/test_safe_browsing_service.h" |
| #include "chrome/browser/site_protection/site_familiarity_fetcher.h" |
| #include "chrome/browser/site_protection/site_familiarity_utils.h" |
| #include "chrome/browser/ui/views/infobars/confirm_infobar.h" |
| #include "chrome/browser/ui/views/js_optimization/js_optimizations_infobar_delegate.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/policy/core/browser/browser_policy_connector.h" |
| #include "components/policy/core/common/mock_configuration_policy_provider.h" |
| #include "components/policy/policy_constants.h" |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| #include "base/test/bind.h" |
| #include "chrome/browser/ui/browser_actions.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/browser/ui/views/frame/toolbar_button_provider.h" |
| #include "chrome/browser/ui/views/js_optimization/js_optimizations_page_action_controller.h" |
| #include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h" |
| #include "chrome/browser/ui/views/page_action/action_ids.h" |
| #include "chrome/browser/ui/views/page_action/test_support/page_action_interactive_test_mixin.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "chrome/test/interaction/interactive_browser_test.h" |
| #include "ui/actions/actions.h" |
| #include "ui/base/interaction/element_tracker.h" |
| #include "ui/views/bubble/bubble_dialog_model_host.h" |
| #endif // !BUILDFLAG(IS_ANDROID) |
| |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/test/base/chrome_test_utils.h" |
| #include "chrome/test/base/platform_browser_test.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/common/features.h" |
| #include "components/history/core/browser/history_service.h" |
| #include "components/keyed_service/core/service_access_type.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/safe_browsing/core/browser/db/fake_database_manager.h" |
| #include "components/safe_browsing/core/common/safe_browsing_prefs.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/process_selection_deferring_condition.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/site_isolation_policy.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/test_host_resolver.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/interaction/element_tracker.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "url/origin.h" |
| |
| typedef site_protection::SiteFamiliarityFetcher::Verdict FamiliarityVerdict; |
| |
| template <typename T> |
| class JavascriptOptimizerBrowserTestMixin : public T { |
| public: |
| void SetUpOnMainThread() override { |
| T::SetUpOnMainThread(); |
| this->host_resolver()->AddRule("*", "127.0.0.1"); |
| content::SetupCrossSiteRedirector(&this->embedded_https_test_server()); |
| |
| this->embedded_https_test_server().SetCertHostnames( |
| {"a.com", "*.a.com", "b.com", "*.b.com", "unrelated.com"}); |
| ASSERT_TRUE(this->embedded_https_test_server().Start()); |
| } |
| |
| content::WebContents* web_contents() { |
| return chrome_test_utils::GetActiveWebContents(this); |
| } |
| |
| Profile* profile() { return chrome_test_utils::GetProfile(this); } |
| |
| bool AreV8OptimizationsDisabledForRenderFrame(content::RenderFrameHost* rfh) { |
| return rfh->GetProcess()->AreV8OptimizationsDisabled(); |
| } |
| |
| bool AreV8OptimizationsDisabled(content::WebContents* web_contents) { |
| return AreV8OptimizationsDisabledForRenderFrame( |
| web_contents->GetPrimaryMainFrame()); |
| } |
| |
| bool AreV8OptimizationsDisabledOnActiveWebContents() { |
| return AreV8OptimizationsDisabled(web_contents()); |
| } |
| }; |
| |
| class JavascriptOptimizerBrowserTest |
| : public JavascriptOptimizerBrowserTestMixin<PlatformBrowserTest> {}; |
| |
| class JavascriptOptimizerBrowserTest_NoOriginKeyedProcessesByDefault |
| : public JavascriptOptimizerBrowserTest { |
| public: |
| JavascriptOptimizerBrowserTest_NoOriginKeyedProcessesByDefault() { |
| feature_list_.InitAndDisableFeature( |
| features::kOriginKeyedProcessesByDefault); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| class JavascriptOptimizerBrowserTest_OriginKeyedProcessesByDefault |
| : public JavascriptOptimizerBrowserTest { |
| public: |
| JavascriptOptimizerBrowserTest_OriginKeyedProcessesByDefault() { |
| feature_list_.InitWithFeatures( |
| {features::kOriginKeyedProcessesByDefault, features::kSitePerProcess}, |
| {}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| // Test that V8 optimization is disabled when the user disables v8 optimization |
| // by default via chrome://settings. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest, |
| V8SiteSettingDefaultOff) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // Test that V8 optimization is disabled when the user disables v8 optimization |
| // via chrome://settings for a specific site. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest, |
| DisabledViaSiteSpecificSetting) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // Test when an origin that matches an exception is loaded in a subframe, the |
| // origin is not isolated and the exception is not applied. This test does not |
| // apply if OriginKeyedProcessesByDefault is enabled because in that mode all |
| // origins would already be isolated on first navigation. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_NoOriginKeyedProcessesByDefault, |
| ExceptionOriginLoadedInSubframeIsNotIsolatedOnFirstNavigation) { |
| auto* policy = content::ChildProcessSecurityPolicy::GetInstance(); |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://b.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| // Request a.com, which loads b.com in an iframe. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/iframe_cross_site.html"))); |
| content::RenderFrameHost* child_frame = |
| content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| // b.com will be marked for isolation when loaded in a subframe. But the |
| // exception won't be followed until it is loaded in a future browsing |
| // context. |
| EXPECT_TRUE(child_frame->GetLastCommittedOrigin().DomainIs("b.com")); |
| EXPECT_TRUE(policy->IsIsolatedSiteFromSource( |
| url::Origin::Create(GURL("https://b.com/")), |
| content::ChildProcessSecurityPolicy::IsolatedOriginSource:: |
| USER_TRIGGERED)); |
| |
| if (content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) { |
| // If dedicated processes are used for all sites, then b.com's rules will be |
| // followed so this case doesn't apply. So in these cases, we are verifying |
| // that new exceptions are respected in sub-frames. |
| EXPECT_TRUE(child_frame->GetProcess()->AreV8OptimizationsDisabled()); |
| EXPECT_TRUE(child_frame->GetSiteInstance()->RequiresDedicatedProcess()); |
| } else { |
| EXPECT_FALSE(child_frame->GetProcess()->AreV8OptimizationsDisabled()); |
| EXPECT_FALSE(child_frame->GetSiteInstance()->RequiresDedicatedProcess()); |
| } |
| |
| // Confirm that the exception applies when b.com is loaded in a new |
| // BrowsingInstance. (This is because NavigateToURL performs a |
| // browser-initiated navigation which will swap BrowsingInstances when |
| // navigating cross-site.) |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("b.com", "/title1.html"))); |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->GetSiteInstance() |
| ->RequiresDedicatedProcess()); |
| |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->GetProcess() |
| ->IsProcessLockedToSiteForTesting()); |
| } |
| |
| // Test that when an origin that matches an exception is loaded in a main frame |
| // first, then if the origin is loaded in a subframe later, the origin will be |
| // isolated and the exception will be applied in both cases. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_NoOriginKeyedProcessesByDefault, |
| ExceptionOriginLoadedFirstWillBeIsolatedInSubframe) { |
| auto* policy = content::ChildProcessSecurityPolicy::GetInstance(); |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://b.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| // Navigate to b.com directly. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("b.com", "/title1.html"))); |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->GetSiteInstance() |
| ->RequiresDedicatedProcess()); |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->GetProcess() |
| ->IsProcessLockedToSiteForTesting()); |
| |
| // Then navigate to a.com that embeds b.com in an iframe. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/iframe_cross_site.html"))); |
| content::RenderFrameHost* child_frame = |
| content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| // Since b.com is already marked for isolation, when loaded as a subframe, the |
| // subframe will still have the isolation (and the js-opt setting) applied. |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| EXPECT_TRUE(child_frame->GetLastCommittedOrigin().DomainIs("b.com")); |
| EXPECT_TRUE(policy->IsIsolatedSiteFromSource( |
| url::Origin::Create(GURL("https://b.com/")), |
| content::ChildProcessSecurityPolicy::IsolatedOriginSource:: |
| USER_TRIGGERED)); |
| EXPECT_TRUE(child_frame->GetProcess()->AreV8OptimizationsDisabled()); |
| EXPECT_TRUE(child_frame->GetSiteInstance()->RequiresDedicatedProcess()); |
| EXPECT_TRUE(child_frame->GetProcess()->IsProcessLockedToSiteForTesting()); |
| } |
| |
| // Test that when a rule is removed during a session, then the origin will still |
| // be isolated but the updated js-opt setting will be applied. This test does |
| // not apply under OriginKeyedProcessesByDefault because all origins would be |
| // isolated. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_NoOriginKeyedProcessesByDefault, |
| RemoveRuleOriginIsStillIsolatedButIsAllowed) { |
| auto* policy = content::ChildProcessSecurityPolicy::GetInstance(); |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| // Before any navigation, a site with an exception is not isolated. |
| EXPECT_FALSE(policy->IsIsolatedSiteFromSource( |
| url::Origin::Create(GURL("https://a.com/")), |
| content::ChildProcessSecurityPolicy::IsolatedOriginSource:: |
| USER_TRIGGERED)); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| |
| // After navigation, the site will be isolated. |
| EXPECT_TRUE(policy->IsIsolatedSiteFromSource( |
| url::Origin::Create(GURL("https://a.com/")), |
| content::ChildProcessSecurityPolicy::IsolatedOriginSource:: |
| USER_TRIGGERED)); |
| |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| EXPECT_TRUE(web_contents()->GetSiteInstance()->RequiresDedicatedProcess()); |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->GetProcess() |
| ->IsProcessLockedToSiteForTesting()); |
| |
| // Delete the custom setting |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_DEFAULT); |
| |
| // Navigate to different origin so that the subsequent navigation to a.com |
| // occurs in a different BrowsingInstanceId. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("unrelated.com", "/simple.html"))); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| |
| // Despite the settings change, the new a.com document should still be |
| // isolated because policy changes that result in no longer isolating an |
| // origin only take effect after restart. |
| EXPECT_TRUE(policy->IsIsolatedSiteFromSource( |
| url::Origin::Create(GURL("https://a.com/")), |
| content::ChildProcessSecurityPolicy::IsolatedOriginSource:: |
| USER_TRIGGERED)); |
| EXPECT_TRUE(web_contents()->GetSiteInstance()->RequiresDedicatedProcess()); |
| EXPECT_TRUE(web_contents() |
| ->GetPrimaryMainFrame() |
| ->GetProcess() |
| ->IsProcessLockedToSiteForTesting()); |
| // Additionally, since a.com no longer has a specific policy, the loaded |
| // document should follow the default setting (allow optimizations). |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // Test that when an exception exists for a.com, navigation to sub.a.com will |
| // also have the setting applied. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_NoOriginKeyedProcessesByDefault, |
| ExceptionForSiteAppliesToSubSite) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("sub.a.com", "/simple.html"))); |
| |
| // True under site isolation. |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // Under origin isolation, test that when an exception exists for a.com, |
| // navigation to sub.a.com will not have the setting applied. This is because |
| // the origin is passed in to AreV8OptimizationsDisabledForSite() when |
| // evaluating the rule. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_OriginKeyedProcessesByDefault, |
| ExceptionForSiteDoesNotApplyToSubSite) { |
| if (!content::SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault( |
| profile())) { |
| GTEST_SKIP() |
| << "skipping: OriginKeyedProcessesEnabledByDefault needs to be true"; |
| } |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("sub.a.com", "/simple.html"))); |
| |
| // False under origin isolation because the origin won't match the content |
| // setting for a.com. |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // Test that if there's a rule for a.com that differs from the default, then the |
| // user can't specify a rule for sub.a.com that contradicts that rule. |
| // TODO(crbug.com/413695645): Make it possible for users to specify overrides so |
| // that sub.a.com's behavior can differ from a.com's behavior. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest, |
| ExceptionForSiteAppliesToSubSiteButCannotBeOverridden) { |
| auto* policy = content::ChildProcessSecurityPolicy::GetInstance(); |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://sub.a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| // Since this exception matches the default, it will not be isolated. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("sub.a.com", "/simple.html"))); |
| |
| EXPECT_FALSE(policy->IsIsolatedSiteFromSource( |
| url::Origin::Create(GURL("https://a.com/")), |
| content::ChildProcessSecurityPolicy::IsolatedOriginSource:: |
| USER_TRIGGERED)); |
| |
| if (content::AreStrictSiteInstancesEnabled() && |
| !content::SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault( |
| profile())) { |
| // if a.com is isolated already (as is the case with full site isolation) |
| // or if DefaultSiteInstanceGroups are enabled, and origin isolation is not |
| // used, the navigation to sub.a.com will be made in a SiteInstance with a |
| // "a.com" site URL, which will match a.com BLOCK rule. |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } else { |
| // If nothing is isolated by default (like on Android), we'll navigate in a |
| // default SiteInstance which won't match that rule and will instead |
| // retrieve the default rule. TODO(crbug.com/413695645): make it possible |
| // for users to specify overrides so that sub.a.com's behavior can differ |
| // from a.com's behavior. |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| |
| // Navigate back to sub.a.com, and we would like for js-opt to be enabled, but |
| // they are disabled instead, because url provided to |
| // AreV8OptimizationsDisabledForSite is a.com, which matches the block rule. |
| // Ideally we'd be able to specify rules here, but to do that we need to pass |
| // in the origin instead of the site. Currently, the site is passed because |
| // sub.a.com is not origin isolated. TODO(crbug.com/413695645): Make it |
| // possible for users to specify overrides so that sub.a.com's behavior can |
| // differ from a.com's behavior. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("sub.a.com", "/simple.html"))); |
| if (content::SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault( |
| profile())) { |
| // Under origin isolation, the rule won't match sub.a.com, so optimizers |
| // remain enabled. |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } else { |
| // Under site isolation, sub.a.com will be evaluated as a.com so the rule |
| // will match. |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| } |
| |
| // Test that a rule can be specified for sub.a.com. and a.com can have different |
| // behavior. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest, |
| RuleForSubSiteCanBeSpecifiedAndSiteCanStillFollowDefaultRule) { |
| #if BUILDFLAG(IS_LINUX) |
| // TODO(421325694): This test fails on linux when bfcache is disabled. |
| if (!base::FeatureList::IsEnabled(features::kBackForwardCache)) { |
| GTEST_SKIP(); |
| } |
| #endif |
| |
| auto* policy = content::ChildProcessSecurityPolicy::GetInstance(); |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://sub.a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| |
| EXPECT_FALSE(policy->IsIsolatedSiteFromSource( |
| url::Origin::Create(GURL("https://a.com/")), |
| content::ChildProcessSecurityPolicy::IsolatedOriginSource:: |
| USER_TRIGGERED)); |
| |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| |
| // In this case, since a.com's policy matched the default, a.com is not |
| // isolated, but sub.a.com will be isolated so sub.a.com follows its |
| // exception. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("sub.a.com", "/simple.html"))); |
| |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| |
| // If we now navigate back to a.com, just like before, the a.com will still |
| // not be isolated, and optimizers will be allowed. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| EXPECT_FALSE(policy->IsIsolatedSiteFromSource( |
| url::Origin::Create(GURL("https://a.com/")), |
| content::ChildProcessSecurityPolicy::IsolatedOriginSource:: |
| USER_TRIGGERED)); |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // Test that exceptions which match the main frame are not propagated down to |
| // subframes from different sites. Additionally, if the subframe is later |
| // navigated to a site that matches the main frame, the main frame's exception |
| // will apply. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_NoOriginKeyedProcessesByDefault, |
| ExceptionForTopFrameDoesNotApplyToSubFrame) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| // Navigate to a.com which embeds b.com. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/iframe_cross_site.html"))); |
| |
| // The top-level frame should have optimizations disabled. |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| |
| // But the frame for b.com follows the default policy. |
| content::RenderFrameHost* child_frame = |
| content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| EXPECT_FALSE(child_frame->GetProcess()->AreV8OptimizationsDisabled()); |
| EXPECT_TRUE(child_frame->GetLastCommittedOrigin().DomainIs("b.com")); |
| |
| // Now, navigate the child_frame to sub.a.com and confirm that a.com's disable |
| // setting applies to sub.a.com. |
| ASSERT_TRUE(content::NavigateIframeToURL( |
| web_contents(), "frame1", |
| embedded_https_test_server().GetURL("sub.a.com", "/simple.html"))); |
| |
| child_frame = content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| EXPECT_EQ(child_frame->GetLastCommittedURL().GetHost(), "sub.a.com"); |
| // True under site isolation but not origin isolation. |
| EXPECT_TRUE(child_frame->GetProcess()->AreV8OptimizationsDisabled()); |
| } |
| |
| // Test that sites isolated due to JavaScript optimization setting will not be |
| // put into processes for other sites when over the limit. This should already |
| // be covered by other IsolatedOriginTests, but this case ensures that |
| // JavaScript optimization is handled correctly. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest, ProcessLimitWorks) { |
| content::RenderProcessHost::SetMaxRendererProcessCount(1); |
| |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://b.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| // Navigate to b.com first to ensure it is on the isolated origins list. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("b.com", "/simple.html"))); |
| |
| // Visit a.com in a new BrowsingInstance, which iframes b.com and c.com. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/iframe_cross_site.html"))); |
| |
| content::RenderFrameHost* a_com_frame = web_contents()->GetPrimaryMainFrame(); |
| content::RenderFrameHost* b_com_frame = content::ChildFrameAt(a_com_frame, 0); |
| content::RenderFrameHost* c_com_frame = content::ChildFrameAt(a_com_frame, 1); |
| |
| if (content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) { |
| // When all sites are isolated, each frame should be in its own process. |
| EXPECT_NE(a_com_frame->GetProcess(), b_com_frame->GetProcess()); |
| EXPECT_NE(c_com_frame->GetProcess(), b_com_frame->GetProcess()); |
| EXPECT_NE(a_com_frame->GetProcess(), c_com_frame->GetProcess()); |
| |
| } else { |
| // When partial site isolation is enabled, the result is that b.com should |
| // be put into its own process despite Chrome being at the soft process |
| // limit. a.com and c.com will end up together. |
| EXPECT_NE(a_com_frame->GetProcess(), b_com_frame->GetProcess()); |
| EXPECT_NE(c_com_frame->GetProcess(), b_com_frame->GetProcess()); |
| EXPECT_EQ(a_com_frame->GetProcess(), c_com_frame->GetProcess()); |
| } |
| } |
| |
| namespace { |
| |
| void NavigateChangeV8OptPriorToWindowOpen(content::WebContents* web_contents, |
| const GURL& navigate_url, |
| const GURL& window_open_url) { |
| Profile* profile = |
| Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| ASSERT_TRUE(content::NavigateToURL(web_contents, navigate_url)); |
| |
| // Simulate changing the default v8-optimization preference via |
| // chrome://settings in a different tab. |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| content::TestNavigationObserver popup_observer(nullptr); |
| popup_observer.StartWatchingNewWebContents(); |
| content::EvalJsResult result = |
| content::EvalJs(web_contents->GetPrimaryMainFrame(), |
| "window.open(\"" + window_open_url.spec() + "\");"); |
| popup_observer.Wait(); |
| } |
| |
| } // anonymous namespace |
| |
| // Test that a same-origin window.open() call uses the same process regardless |
| // of whether the user changed the v8-optimization state. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest, |
| ChangeJavascriptOptimizerStatePriorToSameOriginWindowOpen) { |
| GURL url = embedded_https_test_server().GetURL("a.com", "/simple.html"); |
| NavigateChangeV8OptPriorToWindowOpen(web_contents(), url, url); |
| |
| std::vector<content::WebContents*> matched_tabs; |
| for (content::WebContents* wc : content::GetAllWebContents()) { |
| if (wc->GetLastCommittedURL() == url) { |
| matched_tabs.push_back(wc); |
| } |
| } |
| ASSERT_EQ(2u, matched_tabs.size()); |
| content::RenderFrameHost* frame0 = matched_tabs[0]->GetPrimaryMainFrame(); |
| content::RenderFrameHost* frame1 = matched_tabs[1]->GetPrimaryMainFrame(); |
| EXPECT_EQ(frame0->GetProcess(), frame1->GetProcess()); |
| EXPECT_EQ(frame0->GetSiteInstance(), frame1->GetSiteInstance()); |
| } |
| |
| // Test that when the features::kOriginKeyedProcessesByDefault feature is |
| // disabled that a same-site window.open() call uses the same process regardless |
| // of whether the user changed the v8-optimization state. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_NoOriginKeyedProcessesByDefault, |
| ChangeJavascriptOptimizerStatePriorToSameSiteWindowOpen) { |
| GURL url = embedded_https_test_server().GetURL("a.com", "/simple.html"); |
| GURL same_site_url = |
| embedded_https_test_server().GetURL("foo.a.com", "/simple.html"); |
| NavigateChangeV8OptPriorToWindowOpen(web_contents(), url, same_site_url); |
| |
| std::vector<content::WebContents*> matched_tabs; |
| for (content::WebContents* wc : content::GetAllWebContents()) { |
| if (wc->GetLastCommittedURL() == url || |
| wc->GetLastCommittedURL() == same_site_url) { |
| matched_tabs.push_back(wc); |
| } |
| } |
| ASSERT_EQ(2u, matched_tabs.size()); |
| content::RenderFrameHost* frame0 = matched_tabs[0]->GetPrimaryMainFrame(); |
| content::RenderFrameHost* frame1 = matched_tabs[1]->GetPrimaryMainFrame(); |
| EXPECT_EQ(frame0->GetProcess(), frame1->GetProcess()); |
| EXPECT_EQ(frame0->GetSiteInstance(), frame1->GetSiteInstance()); |
| } |
| |
| namespace { |
| |
| // content::ProcessSelectionDeferringCondition subclass which sets |
| // `did_select_final_process` bool passed to constructor when |
| // ProcessSelectionDeferringCondition::OnWillSelectFinalProcess() is called. |
| class ProcessSelectionObserverCondition |
| : public content::ProcessSelectionDeferringCondition { |
| public: |
| ProcessSelectionObserverCondition( |
| content::NavigationHandle& navigation_handle, |
| bool* did_select_final_process) |
| : content::ProcessSelectionDeferringCondition(navigation_handle), |
| did_select_final_process_(did_select_final_process) {} |
| ~ProcessSelectionObserverCondition() override = default; |
| |
| content::ProcessSelectionDeferringCondition::Result OnWillSelectFinalProcess( |
| base::OnceClosure resume) override { |
| *did_select_final_process_ = true; |
| return ProcessSelectionDeferringCondition::Result::kProceed; |
| } |
| |
| private: |
| raw_ptr<bool> did_select_final_process_; |
| }; |
| |
| // ChromeContentBrowserClient subclass which uses DeferringCondition. |
| class DeferProcessSelectionBrowserClient : public ChromeContentBrowserClient { |
| public: |
| DeferProcessSelectionBrowserClient() = default; |
| ~DeferProcessSelectionBrowserClient() override = default; |
| |
| std::vector<std::unique_ptr<content::ProcessSelectionDeferringCondition>> |
| CreateProcessSelectionDeferringConditionsForNavigation( |
| content::NavigationHandle& navigation_handle) override { |
| auto condition = std::make_unique<ProcessSelectionObserverCondition>( |
| navigation_handle, &did_select_final_process_); |
| std::vector<std::unique_ptr<content::ProcessSelectionDeferringCondition>> |
| conditions; |
| conditions.push_back(std::move(condition)); |
| return conditions; |
| } |
| |
| bool AreV8OptimizationsEnabledForSite( |
| content::BrowserContext* browser_context, |
| const std::optional<base::SafeRef<content::ProcessSelectionUserData>>& |
| process_selection_user_data, |
| const GURL& site_url) override { |
| return !should_disable_v8_optimizations_; |
| } |
| |
| bool DidSelectFinalProcess() { return did_select_final_process_; } |
| |
| bool should_disable_v8_optimizations_ = false; |
| |
| private: |
| bool did_select_final_process_ = false; |
| }; |
| |
| } // anonymous namespace |
| |
| class JavascriptOptimizerBrowserTest_CustomDeferralCondition |
| : public JavascriptOptimizerBrowserTest { |
| public: |
| JavascriptOptimizerBrowserTest_CustomDeferralCondition() { |
| feature_list_ |
| .InitWithFeatures(/*enabled_features=*/ |
| {features::kProcessSelectionDeferringConditions, |
| content_settings::features:: |
| kBlockV8OptimizerOnUnfamiliarSitesSetting}, |
| /*disabled_features=*/{}); |
| } |
| |
| void SetUpOnMainThread() override { |
| browser_client_ = std::make_unique<DeferProcessSelectionBrowserClient>(); |
| old_browser_client_ = |
| content::SetBrowserClientForTesting(browser_client_.get()); |
| JavascriptOptimizerBrowserTest::SetUpOnMainThread(); |
| } |
| |
| void TearDownOnMainThread() override { |
| JavascriptOptimizerBrowserTest::TearDownOnMainThread(); |
| content::SetBrowserClientForTesting(old_browser_client_.get()); |
| browser_client_.reset(); |
| } |
| |
| protected: |
| base::test::ScopedFeatureList feature_list_; |
| std::unique_ptr<DeferProcessSelectionBrowserClient> browser_client_; |
| raw_ptr<content::ContentBrowserClient> old_browser_client_; |
| }; |
| |
| // Test that crbug.com/441727826 is fixed. Test that navigations use the |
| // v8-optimizer state at final process selection time and not prior. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_CustomDeferralCondition, |
| ChangeJavascriptOptimizerStatePriorToProcessSelection) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("a.com", "/simple.html"); |
| |
| browser_client_->should_disable_v8_optimizations_ = false; |
| |
| content::TestNavigationObserver navigation_observer(web_contents(), 1); |
| |
| web_contents()->GetController().LoadURLWithParams( |
| content::NavigationController::LoadURLParams(kTestUrl)); |
| EXPECT_FALSE(browser_client_->DidSelectFinalProcess()); |
| |
| browser_client_->should_disable_v8_optimizations_ = true; |
| |
| navigation_observer.Wait(); |
| EXPECT_TRUE(browser_client_->DidSelectFinalProcess()); |
| content::RenderFrameHost* frame = web_contents()->GetPrimaryMainFrame(); |
| EXPECT_TRUE(frame->GetProcess()->AreV8OptimizationsDisabled()); |
| } |
| |
| // Base class for integration tests which enable/disable the "disable JavaScript |
| // optimization for unfamiliar sites" feature. |
| class JavascriptOptimizerBrowserTest_UseSiteFamiliarityBase |
| : public JavascriptOptimizerBrowserTest { |
| public: |
| JavascriptOptimizerBrowserTest_UseSiteFamiliarityBase() = default; |
| ~JavascriptOptimizerBrowserTest_UseSiteFamiliarityBase() override = default; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| JavascriptOptimizerBrowserTest::SetUpCommandLine(command_line); |
| |
| if (ShouldForceSitePerProcess()) { |
| command_line->AppendSwitch(::switches::kSitePerProcess); |
| } |
| |
| if (ShouldEnableSiteFamiliarityFeature()) { |
| feature_list_ |
| .InitWithFeatures(/*enabled_features=*/ |
| {features::kProcessSelectionDeferringConditions, |
| content_settings::features:: |
| kBlockV8OptimizerOnUnfamiliarSitesSetting}, |
| /*disabled_features=*/{}); |
| } else { |
| feature_list_.InitWithFeatures( |
| /*enabled_features=*/{}, |
| /*disabled_features=*/ |
| {features::kProcessSelectionDeferringConditions, |
| content_settings::features:: |
| kBlockV8OptimizerOnUnfamiliarSitesSetting}); |
| } |
| } |
| |
| void CreatedBrowserMainParts( |
| content::BrowserMainParts* browser_main_parts) override { |
| JavascriptOptimizerBrowserTest::CreatedBrowserMainParts(browser_main_parts); |
| // Test UI manager and test database manager should be set before |
| // the browser is started but after threads are created. |
| factory_.SetTestDatabaseManager( |
| new safe_browsing::FakeSafeBrowsingDatabaseManager( |
| content::GetUIThreadTaskRunner({}))); |
| safe_browsing::SafeBrowsingService::RegisterFactory(&factory_); |
| } |
| |
| void SetUpOnMainThread() override { |
| JavascriptOptimizerBrowserTest::SetUpOnMainThread(); |
| |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| profile()->GetPrefs()->SetBoolean( |
| prefs::kJavascriptOptimizerBlockedForUnfamiliarSites, true); |
| EXPECT_EQ(ShouldEnableSiteFamiliarityFeature(), |
| site_protection::AreV8OptimizationsDisabledOnUnfamiliarSites( |
| profile())); |
| } |
| |
| void RunCallbackAndStoreVerdict(const base::RepeatingClosure& callback, |
| FamiliarityVerdict* verdict_out, |
| FamiliarityVerdict verdict) { |
| *verdict_out = verdict; |
| callback.Run(); |
| } |
| |
| void CheckSiteFamiliarity(const GURL& url, |
| FamiliarityVerdict expected_verdict) { |
| site_protection::SiteFamiliarityFetcher fetcher(profile()); |
| FamiliarityVerdict verdict = FamiliarityVerdict::kFamiliar; |
| FamiliarityVerdict* verdict_ptr = &verdict; |
| base::RunLoop run_loop; |
| fetcher.Start( |
| url, base::BindOnce( |
| &JavascriptOptimizerBrowserTest_UseSiteFamiliarityBase:: |
| RunCallbackAndStoreVerdict, |
| base::Unretained(this), run_loop.QuitClosure(), verdict_ptr)); |
| run_loop.Run(); |
| EXPECT_EQ(expected_verdict, verdict); |
| } |
| |
| void NavigateToUnfamiliarSite(bool expect_v8_optimizations_enabled) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("a.com", "/simple.html"); |
| NavigateToUnfamiliarUrl(web_contents(), kTestUrl, |
| expect_v8_optimizations_enabled); |
| } |
| |
| void NavigateToUnfamiliarUrl(content::WebContents* web_contents, |
| const GURL& url, |
| bool expect_v8_optimizations_enabled) { |
| ASSERT_TRUE(content::NavigateToURL(web_contents, url)); |
| EXPECT_EQ(!expect_v8_optimizations_enabled, |
| AreV8OptimizationsDisabled(web_contents)); |
| CheckSiteFamiliarity(url, FamiliarityVerdict::kUnfamiliar); |
| } |
| |
| void MarkAsFamiliar(const GURL& url) { |
| // Mark site as familiar by adding a chrome://history entry older than 24 |
| // hours. |
| HistoryServiceFactory::GetInstance() |
| ->GetForProfile(profile(), ServiceAccessType::IMPLICIT_ACCESS) |
| ->AddPage(url, base::Time::Now() - base::Hours(25), |
| history::SOURCE_BROWSED); |
| CheckSiteFamiliarity(url, FamiliarityVerdict::kFamiliar); |
| } |
| |
| protected: |
| virtual bool ShouldEnableSiteFamiliarityFeature() = 0; |
| |
| virtual bool ShouldForceSitePerProcess() { return true; } |
| |
| private: |
| safe_browsing::TestSafeBrowsingServiceFactory factory_; |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| // Test harness for integration tests which enable the "disable JavaScript |
| // optimization for unfamiliar sites" feature. |
| class JavascriptOptimizerBrowserTest_UseSiteFamiliarity |
| : public JavascriptOptimizerBrowserTest_UseSiteFamiliarityBase { |
| public: |
| JavascriptOptimizerBrowserTest_UseSiteFamiliarity() = default; |
| ~JavascriptOptimizerBrowserTest_UseSiteFamiliarity() override = default; |
| |
| protected: |
| bool ShouldEnableSiteFamiliarityFeature() override { return true; } |
| }; |
| |
| class JavascriptOptimizerParamBrowserTest |
| : public JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| public testing::WithParamInterface<bool> { |
| public: |
| JavascriptOptimizerParamBrowserTest() = default; |
| ~JavascriptOptimizerParamBrowserTest() override = default; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| JavascriptOptimizerBrowserTest_UseSiteFamiliarity::SetUpCommandLine( |
| command_line); |
| if (GetParam()) { |
| feature_list_.InitAndEnableFeature(features::kStrictOriginIsolation); |
| } else { |
| feature_list_.InitAndDisableFeature(features::kStrictOriginIsolation); |
| } |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| ExpectOptimizationEnabledFamiliarSite) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("a.com", "/simple.html"); |
| MarkAsFamiliar(kTestUrl); |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kTestUrl)); |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| ExpectOptimizationDisabledForUnfamiliarSite) { |
| NavigateToUnfamiliarSite(/*expect_v8_optimizations_enabled=*/false); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(JavascriptOptimizerParamBrowserTest, |
| ExpectOptimizationCanBeEnabledForUnfamiliarOrigin) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("www.a.com", "/simple.html"); |
| |
| NavigateToUnfamiliarUrl(web_contents(), kTestUrl, |
| /*expect_v8_optimizations_enabled=*/false); |
| site_protection::EnableV8Optimizations(web_contents()); |
| |
| // The content-setting-exception takes effect in a new browsing instance. |
| std::unique_ptr<content::WebContents> new_web_contents = |
| content::WebContents::Create( |
| content::WebContents::CreateParams(profile())); |
| NavigateToUnfamiliarUrl(new_web_contents.get(), kTestUrl, |
| /*expect_v8_optimizations_enabled=*/true); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| JavascriptOptimizerParamBrowserTest, |
| ::testing::Bool(), |
| [](const testing::TestParamInfo<bool>& info) { |
| return info.param ? "WithStrictOriginIsolation" |
| : "WithoutStrictOriginIsolation"; |
| }); |
| |
| // Test that if there is a content-setting-exception to enable v8-optimizers |
| // for a specific site but the site is unfamiliar due to the heuristic that |
| // the content-setting-exception takes precedence. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| ExpectContentSettingExceptionTakesPrecedence) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("a.com", "/simple.html"); |
| const ContentSettingsPattern kTestOriginPattern = |
| ContentSettingsPattern::FromString("https://a.com/"); |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetContentSettingCustomScope(kTestOriginPattern, |
| ContentSettingsPattern::Wildcard(), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kTestUrl)); |
| CheckSiteFamiliarity(kTestUrl, FamiliarityVerdict::kUnfamiliar); |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // Check that v8-optimization is enabled for chrome:// URLs. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| ExpectOptimizationEnabledForChromeScheme) { |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), GURL("chrome://version"))); |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // TODO(crbug.com/460621062): Re-enable the test |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| DISABLED_ExpectOptimizationsEnabledInSpeculativeRenderFrameHost) { |
| // TODO(crbug.com/452130797): Determine desired behavior for this test case. |
| |
| const GURL kTestUrl = embedded_https_test_server().GetURL( |
| "a.com", "/infinitely_loading_image.html"); |
| content::TestNavigationManager navigation_manager(web_contents(), kTestUrl); |
| web_contents()->GetController().LoadURLWithParams( |
| content::NavigationController::LoadURLParams(kTestUrl)); |
| navigation_manager.WaitForSpeculativeRenderFrameHostCreation(); |
| content::RenderFrameHost* speculative_render_frame_host = |
| navigation_manager.GetCreatedSpeculativeRFH(); |
| EXPECT_FALSE(speculative_render_frame_host->GetProcess() |
| ->AreV8OptimizationsDisabled()); |
| } |
| |
| // TODO(crbug.com/461777786): Flaky on Linux and CrOS. |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_ExpectOptimizationsEnabledInSpareRenderer \ |
| DISABLED_ExpectOptimizationsEnabledInSpareRenderer |
| #else |
| #define MAYBE_ExpectOptimizationsEnabledInSpareRenderer \ |
| ExpectOptimizationsEnabledInSpareRenderer |
| #endif |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| MAYBE_ExpectOptimizationsEnabledInSpareRenderer) { |
| // TODO(crbug.com/452689705): Consider creating both: (1) spare renderer |
| // processes with v8-optimizer enabled and (2) spare renderer processes with |
| // v8-optimizer disabled. |
| |
| auto& spare_manager = content::SpareRenderProcessHostManager::Get(); |
| spare_manager.WarmupSpare(profile()); |
| ASSERT_EQ(spare_manager.GetSpares().size(), 1u); |
| EXPECT_FALSE(spare_manager.GetSpares()[0]->AreV8OptimizationsDisabled()); |
| } |
| |
| // Check that v8-optimizer is enabled if <iframe> URL is familiar. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| ExpectOptimizationEnabledForFamiliarIframe) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("a.com", "/iframe_cross_site.html"); |
| const url::Origin kChildOrigin = |
| url::Origin::Create(embedded_https_test_server().GetURL("b.com", "/")); |
| |
| MarkAsFamiliar(kChildOrigin.GetURL()); |
| CheckSiteFamiliarity(kTestUrl, FamiliarityVerdict::kUnfamiliar); |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kTestUrl)); |
| |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| content::RenderFrameHost* child_frame = |
| content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| EXPECT_EQ(kChildOrigin, child_frame->GetLastCommittedOrigin()); |
| EXPECT_FALSE(AreV8OptimizationsDisabledForRenderFrame(child_frame)); |
| } |
| |
| // Check that v8-optimizer is disabled if <iframe> URL is unfamiliar. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| ExpectOptimizationDisabledForUnfamiliarIframe) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("a.com", "/iframe_cross_site.html"); |
| const url::Origin kChildOrigin = |
| url::Origin::Create(embedded_https_test_server().GetURL("b.com", "/")); |
| |
| MarkAsFamiliar(kTestUrl); |
| CheckSiteFamiliarity(kChildOrigin.GetURL(), FamiliarityVerdict::kUnfamiliar); |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kTestUrl)); |
| |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| content::RenderFrameHost* child_frame = |
| content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| EXPECT_EQ(kChildOrigin, child_frame->GetLastCommittedOrigin()); |
| EXPECT_TRUE(AreV8OptimizationsDisabledForRenderFrame(child_frame)); |
| } |
| |
| // Check that v8-optimization is disabled for data: <iframe> when |
| // the data:// URL is navigated to from a cross-origin <iframe> that also has v8 |
| // optimizations disabled. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| ExpectOptimizationDisabledForDataUrlIframe) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("a.com", "/iframe_cross_site.html"); |
| const GURL kDataUrl = GURL("data:,hello%20world"); |
| |
| MarkAsFamiliar(kTestUrl); |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kTestUrl)); |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| |
| content::RenderFrameHost* child_frame = |
| content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| ASSERT_TRUE(content::NavigateToURLFromRenderer(child_frame, kDataUrl)); |
| |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| child_frame = content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| EXPECT_TRUE(child_frame->GetProcess()->AreV8OptimizationsDisabled()); |
| } |
| |
| // Check that site-familiarity does not impede a data:// URL iframe from sharing |
| // a render process with the iframe parent. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| ExpectDataUrlIframeSameProcessAsParent) { |
| const GURL kTestUrl = |
| embedded_https_test_server().GetURL("a.com", "/iframe_data_url.html"); |
| MarkAsFamiliar(kTestUrl); |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), kTestUrl)); |
| |
| // Parent frame is familiar. V8-optimization should be enabled. |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| |
| // Child frame should use same render process as main frame. |
| content::RenderFrameHost* child_frame = |
| content::ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0); |
| ASSERT_EQ(child_frame->GetLastCommittedURL().scheme(), "data"); |
| EXPECT_EQ(child_frame->GetProcess(), |
| web_contents()->GetPrimaryMainFrame()->GetProcess()); |
| } |
| |
| // Test that a renderer-initiated navigation to about:blank from a data: URL |
| // with V8 optimizations disabled does not swap BrowsingInstances. |
| // |
| // `about:blank` URLs default to having JS optimizations enabled while `data:` |
| // URLs default to having JS optimizations disabled. If the mismatched V8 |
| // optimization flags triggered a BrowsingInstance swap (via the |
| // BI-swap-on-flags functionality), it would break the assumption that |
| // renderer-initiated about:blank navigations stay in their initiator's |
| // SiteInstance and process. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| RendererInitiatedAboutBlankNavigationDoesNotSwap) { |
| ASSERT_TRUE( |
| content::NavigateToURL(web_contents(), GURL("data:text/html,foo"))); |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| scoped_refptr<content::SiteInstance> initial_instance = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| |
| // Perform a renderer-initiated navigation to about:blank. |
| const GURL about_blank_url(url::kAboutBlankURL); |
| EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), about_blank_url)); |
| ASSERT_TRUE(content::WaitForLoadStop(web_contents())); |
| |
| EXPECT_EQ(GURL("about:blank"), web_contents()->GetLastCommittedURL()); |
| // V8 optimizations should match the initiator and be disabled. |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| // No BrowsingInstance swap occurs, so the site instances should be the same. |
| EXPECT_EQ(initial_instance, |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); |
| } |
| |
| // Test that a browser-initiated navigation to about:blank from a site with |
| // V8 optimizations disabled does not swap BrowsingInstances, because it |
| // navigates to about:blank. |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_UseSiteFamiliarity, |
| BrowserInitiatedAboutBlankNavigationSwaps) { |
| ASSERT_TRUE( |
| content::NavigateToURL(web_contents(), GURL("data:text/html,foo"))); |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| scoped_refptr<content::SiteInstance> initial_instance = |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); |
| |
| // Perform a browser-initiated navigation to about:blank. |
| ASSERT_TRUE(content::NavigateToURL(web_contents(), GURL("about:blank"))); |
| |
| EXPECT_EQ(GURL("about:blank"), web_contents()->GetLastCommittedURL()); |
| // V8 optimizations should be disabled. |
| EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| // No BrowsingInstance swap occurs, so the site instances should be the same. |
| EXPECT_EQ(initial_instance, |
| web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); |
| } |
| |
| class JavascriptOptimizerBrowserTest_UseSiteFamiliarity_DisableSiteIsolation |
| : public JavascriptOptimizerBrowserTest_UseSiteFamiliarity { |
| public: |
| JavascriptOptimizerBrowserTest_UseSiteFamiliarity_DisableSiteIsolation() = |
| default; |
| ~JavascriptOptimizerBrowserTest_UseSiteFamiliarity_DisableSiteIsolation() |
| override = default; |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| JavascriptOptimizerBrowserTest_UseSiteFamiliarity::SetUpCommandLine( |
| command_line); |
| command_line->AppendSwitch(switches::kDisableSiteIsolation); |
| } |
| |
| bool ShouldForceSitePerProcess() override { return false; } |
| }; |
| |
| // Check that site-familiarity is ignored when site-isolation is disabled. |
| // TODO(crbug.com/454006392): Determine desired site-familiarity behavior for |
| // unlocked processes if the site-familiarity feature is launched on Android. |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerBrowserTest_UseSiteFamiliarity_DisableSiteIsolation, |
| ExpectIgnoreFamiliarityWhenSiteIsolationDisabled) { |
| NavigateToUnfamiliarSite(/*expect_v8_optimizations_enabled=*/true); |
| EXPECT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| } |
| |
| // Test harness for integration tests which disable the "disable JavaScript |
| // optimization for unfamiliar sites" feature. |
| class JavascriptOptimizerBrowserTest_DoNotUseSiteFamiliarity |
| : public JavascriptOptimizerBrowserTest_UseSiteFamiliarityBase { |
| public: |
| JavascriptOptimizerBrowserTest_DoNotUseSiteFamiliarity() = default; |
| ~JavascriptOptimizerBrowserTest_DoNotUseSiteFamiliarity() override = default; |
| |
| protected: |
| bool ShouldEnableSiteFamiliarityFeature() override { return false; } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_DoNotUseSiteFamiliarity, |
| ExpectIgnoreFamiliarity_OptimizerAllowedByDefault) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| NavigateToUnfamiliarSite(/*expect_v8_optimizations_enabled=*/true); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBrowserTest_DoNotUseSiteFamiliarity, |
| ExpectIgnoreFamiliarity_OptimizerBlockedByDefault) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| NavigateToUnfamiliarSite(/*expect_v8_optimizations_enabled=*/false); |
| } |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| |
| constexpr char kSkipPixelTestsReason[] = "Should only run in pixel_tests."; |
| // Baseline Gerrit CL number of the most recent CL that modified the UI. |
| constexpr char kScreenshotBaselineCL[] = "7516391"; |
| |
| class JavascriptOptimizerUiBaseBrowserTest |
| : public JavascriptOptimizerBrowserTestMixin<InteractiveBrowserTest> { |
| public: |
| void SetUpInProcessBrowserTestFixture() override { |
| policy_provider_.SetDefaultReturns( |
| /*is_initialization_complete_return=*/true, |
| /*is_first_policy_load_complete_return=*/true); |
| policy::BrowserPolicyConnector::SetPolicyProviderForTesting( |
| &policy_provider_); |
| } |
| |
| void SetUpOnMainThread() override { |
| JavascriptOptimizerBrowserTestMixin< |
| InteractiveBrowserTest>::SetUpOnMainThread(); |
| |
| JsOptimizationsPageActionController::SetBubbleCreatedCallbackForTesting( |
| base::BindRepeating([](views::BubbleDialogModelHost* bubble) { |
| bubble->set_close_on_deactivate(false); |
| })); |
| } |
| |
| void TearDownOnMainThread() override { |
| JsOptimizationsPageActionController::SetBubbleCreatedCallbackForTesting( |
| base::NullCallback()); |
| JavascriptOptimizerBrowserTestMixin< |
| InteractiveBrowserTest>::TearDownOnMainThread(); |
| } |
| |
| protected: |
| void EnableEnterprisePolicy() { |
| policy::PolicyMap policies; |
| policies.Set(policy::key::kDefaultJavaScriptOptimizerSetting, |
| policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, |
| policy::POLICY_SOURCE_ENTERPRISE_DEFAULT, |
| base::Value(CONTENT_SETTING_BLOCK), |
| /*external_data_fetcher=*/nullptr); |
| policy_provider_.UpdateChromePolicy(policies); |
| } |
| |
| private: |
| testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_; |
| }; |
| |
| class JavascriptOptimizerOmnibarIconBrowserTest |
| : public PageActionInteractiveTestMixin< |
| JavascriptOptimizerUiBaseBrowserTest> { |
| public: |
| // Returns true iff the JS Optimizations omnibar icon is visible. |
| bool IsOmnibarIconVisible() { |
| const auto* view = BrowserView::GetBrowserViewForBrowser(browser()) |
| ->toolbar_button_provider() |
| ->GetPageActionView(kActionShowJsOptimizationsIcon); |
| return view && view->GetVisible(); |
| } |
| |
| // Returns true iff the JS Optimizations bubble is visible. |
| bool IsBubbleVisible() { |
| if (ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| JsOptimizationsPageActionController::kBubbleBodyElementId, |
| browser()->GetBrowserView().GetElementContext()) == nullptr) { |
| return false; |
| } |
| |
| actions::ActionItem* action_item = actions::ActionManager::Get().FindAction( |
| kActionShowJsOptimizationsIcon, |
| browser()->browser_actions()->root_action_item()); |
| return action_item && action_item->GetIsShowingBubble(); |
| } |
| |
| // Returns true iff the JS Optimizations bubble button is visible. |
| bool IsBubbleButtonVisible() { |
| if (!IsBubbleVisible()) { |
| return false; |
| } |
| return ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| JsOptimizationsPageActionController::kBubbleButtonElementId, |
| browser()->GetBrowserView().GetElementContext()) != nullptr; |
| } |
| |
| using PageActionInteractiveTestMixin::WaitForPageActionButtonVisible; |
| }; |
| |
| class JavascriptOptimizerOmnibarIconBrowserTest_WithFlag |
| : public JavascriptOptimizerOmnibarIconBrowserTest { |
| public: |
| JavascriptOptimizerOmnibarIconBrowserTest_WithFlag() { |
| feature_list_.InitWithFeatures( |
| {content_settings::features::kBlockV8OptimizerOnUnfamiliarSitesSetting}, |
| {}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerOmnibarIconBrowserTest_WithFlag, |
| IconShowsWhenOptimizationsDisabled) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| ASSERT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| RunTestSequence( |
| WaitForPageActionButtonVisible(kActionShowJsOptimizationsIcon)); |
| EXPECT_TRUE(IsOmnibarIconVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerOmnibarIconBrowserTest_WithFlag, |
| IconDoesNotShowWhenOptimizationsNotDisabled) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| ASSERT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| RunTestSequence( |
| WaitForPageActionChipNotVisible(kActionShowJsOptimizationsIcon)); |
| EXPECT_FALSE(IsOmnibarIconVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerOmnibarIconBrowserTest_WithFlag, |
| IconShowsWhenNavigatingToPageWhereOptimizationsDisabled) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| // Optimizations enabled for all except a.com |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| // At first, optimizations not disabled, so icon is not visible. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("b.com", "/simple.html"))); |
| ASSERT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| RunTestSequence( |
| WaitForPageActionChipNotVisible(kActionShowJsOptimizationsIcon)); |
| EXPECT_FALSE(IsOmnibarIconVisible()); |
| // After navigating to a.com, icon is visible. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| ASSERT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| RunTestSequence( |
| WaitForPageActionButtonVisible(kActionShowJsOptimizationsIcon)); |
| EXPECT_TRUE(IsOmnibarIconVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| JavascriptOptimizerOmnibarIconBrowserTest_WithFlag, |
| IconDisappearsWhenNavigatingToPageWhereOptimizationsNotDisabled) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| // Optimizations enabled for all except a.com |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_ALLOW); |
| map->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString("https://a.com:*"), |
| ContentSettingsPattern::FromString("*"), |
| ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| // At first, optimizations disabled, so icon is visible. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("a.com", "/simple.html"))); |
| ASSERT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| RunTestSequence( |
| WaitForPageActionButtonVisible(kActionShowJsOptimizationsIcon)); |
| EXPECT_TRUE(IsOmnibarIconVisible()); |
| // After navigating to b.com, icon is not visible. |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), |
| embedded_https_test_server().GetURL("b.com", "/simple.html"))); |
| ASSERT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| RunTestSequence( |
| WaitForPageActionChipNotVisible(kActionShowJsOptimizationsIcon)); |
| EXPECT_FALSE(IsOmnibarIconVisible()); |
| } |
| |
| class JavascriptOptimizerOmnibarIconBrowserTest_WithoutFlag |
| : public JavascriptOptimizerOmnibarIconBrowserTest { |
| public: |
| JavascriptOptimizerOmnibarIconBrowserTest_WithoutFlag() { |
| feature_list_.InitAndDisableFeature( |
| content_settings::features::kBlockV8OptimizerOnUnfamiliarSitesSetting); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerOmnibarIconBrowserTest_WithoutFlag, |
| IconDoesNotShowWhenFlagNotEnabled) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| // V8 optimizations are disabled, but omnibar icon is not visible. |
| ASSERT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| const auto* icon_view = |
| BrowserView::GetBrowserViewForBrowser(browser()) |
| ->toolbar_button_provider() |
| ->GetPageActionView(kActionShowJsOptimizationsIcon); |
| // There is no view initialized because the flag is disabled. |
| ASSERT_EQ(icon_view, nullptr); |
| } |
| class JavascriptOptimizerBubbleBrowserTest |
| : public JavascriptOptimizerOmnibarIconBrowserTest { |
| public: |
| JavascriptOptimizerBubbleBrowserTest() { |
| feature_list_.InitWithFeatures( |
| {content_settings::features::kBlockV8OptimizerOnUnfamiliarSitesSetting}, |
| {}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBubbleBrowserTest, |
| BubbleShowsOnClick) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| ASSERT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| ASSERT_TRUE(IsOmnibarIconVisible()); |
| |
| // Click on icon. |
| RunTestSequence(PressButton(kJsOptimizationsIconElementId)); |
| // Assert that bubble is visible. |
| RunTestSequence( |
| WaitForShow(JsOptimizationsPageActionController::kBubbleBodyElementId)); |
| EXPECT_TRUE(IsBubbleVisible()); |
| // Assert that button is visible. |
| RunTestSequence( |
| WaitForShow(JsOptimizationsPageActionController::kBubbleButtonElementId)); |
| EXPECT_TRUE(IsBubbleButtonVisible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBubbleBrowserTest, |
| AfterEnablingOptimizationsIconIsHidden) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| ASSERT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| ASSERT_TRUE(IsOmnibarIconVisible()); |
| |
| // Click on icon. |
| RunTestSequence(PressButton(kJsOptimizationsIconElementId)); |
| // Assert that bubble is visible. |
| RunTestSequence( |
| WaitForShow(JsOptimizationsPageActionController::kBubbleBodyElementId)); |
| EXPECT_TRUE(IsBubbleVisible()); |
| // Assert that button is visible. |
| RunTestSequence( |
| WaitForShow(JsOptimizationsPageActionController::kBubbleButtonElementId)); |
| EXPECT_TRUE(IsBubbleButtonVisible()); |
| // Click on button. |
| RunTestSequence( |
| PressButton(JsOptimizationsPageActionController::kBubbleButtonElementId)); |
| // Open new tab and go to the same site. |
| ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition( |
| browser(), embedded_https_test_server().GetURL("/simple.html"), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP)); |
| // TODO: crbug.com/457420369 - Use reload functionality to check settings |
| // application instead of opening a new tab |
| // RunTestSequence(WaitForShow(ConfirmInfoBar::kInfoBarElementId), |
| // PressButton(ConfirmInfoBar::kOkButtonElementId), |
| // WaitForHide(ConfirmInfoBar::kInfoBarElementId)); |
| // Assert that the icon is not visible. |
| ASSERT_FALSE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| ASSERT_FALSE(IsOmnibarIconVisible()); |
| } |
| |
| // JS optimizations disabled by enterprise policy. |
| class JavascriptOptimizerBubbleBrowserTest_WithPolicy |
| : public JavascriptOptimizerBubbleBrowserTest {}; |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerBubbleBrowserTest_WithPolicy, |
| BubbleShowsOnClick) { |
| EnableEnterprisePolicy(); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| ASSERT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents()); |
| ASSERT_TRUE(IsOmnibarIconVisible()); |
| |
| // Click on icon. |
| RunTestSequence(PressButton(kJsOptimizationsIconElementId)); |
| // Assert that bubble is visible. |
| RunTestSequence( |
| WaitForShow(JsOptimizationsPageActionController::kBubbleBodyElementId)); |
| EXPECT_TRUE(IsBubbleVisible()); |
| // Assert that button is not visible. |
| RunTestSequence( |
| WaitForHide(JsOptimizationsPageActionController::kBubbleButtonElementId)); |
| EXPECT_FALSE(IsBubbleButtonVisible()); |
| } |
| |
| class JavascriptOptimizerUiTest : public JavascriptOptimizerUiBaseBrowserTest { |
| public: |
| JavascriptOptimizerUiTest() { |
| feature_list_.InitWithFeatures( |
| {content_settings::features::kBlockV8OptimizerOnUnfamiliarSitesSetting}, |
| {}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerUiTest, OmniboxIconPixelTest) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| auto* screenshot_name = "js_no_opt_omnibox_icon_chrome_branding"; |
| #else |
| auto* screenshot_name = "js_no_opt_omnibox_icon_non_chrome_branding"; |
| #endif |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| RunTestSequence(SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest, |
| kSkipPixelTestsReason), |
| // Get screenshot of the icon |
| Screenshot(kJsOptimizationsIconElementId, |
| /*screenshot_name=*/screenshot_name, |
| /*baseline_cl=*/kScreenshotBaselineCL)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerUiTest, BubblePixelTest) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| RunTestSequence( |
| SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest, |
| kSkipPixelTestsReason), |
| // Click on icon |
| PressButton(kJsOptimizationsIconElementId), |
| // Wait for bubble to show |
| WaitForShow(JsOptimizationsPageActionController::kBubbleBodyElementId), |
| // Grab screenshot of bubble with OK button |
| ScreenshotSurface( |
| JsOptimizationsPageActionController::kBubbleBodyElementId, |
| /*screenshot_name=*/"js_no_opt_bubble_dialog", |
| /*baseline_cl=*/kScreenshotBaselineCL)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerUiTest, BubbleWithPolicyPixelTest) { |
| EnableEnterprisePolicy(); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| RunTestSequence( |
| SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest, |
| kSkipPixelTestsReason), |
| // Click on icon |
| PressButton(kJsOptimizationsIconElementId), |
| // Wait for bubble to show |
| WaitForShow(JsOptimizationsPageActionController::kBubbleBodyElementId), |
| // Grab screenshot of bubble without button |
| ScreenshotSurface( |
| JsOptimizationsPageActionController::kBubbleBodyElementId, |
| /*screenshot_name=*/"js_no_opt_bubble_dialog_with_policy", |
| /*baseline_cl=*/kScreenshotBaselineCL)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavascriptOptimizerUiTest, ReloadInfoBarPixelTest) { |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_OPTIMIZER, |
| ContentSetting::CONTENT_SETTING_BLOCK); |
| |
| ASSERT_TRUE(content::NavigateToURL( |
| web_contents(), embedded_https_test_server().GetURL("/simple.html"))); |
| RunTestSequence( |
| SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest, |
| kSkipPixelTestsReason), |
| // Click on icon |
| PressButton(kJsOptimizationsIconElementId), |
| // Click on button |
| PressButton(JsOptimizationsPageActionController::kBubbleButtonElementId), |
| // Wait for reload infobar to show |
| WaitForShow(ConfirmInfoBar::kInfoBarElementId), |
| // Grab screenshot of reload infobar |
| Screenshot(ConfirmInfoBar::kInfoBarElementId, |
| /*screenshot_name=*/"js_no_opt_reload_infobar", |
| /*baseline_cl=*/kScreenshotBaselineCL)); |
| } |
| |
| #endif // !BUILDFLAG(IS_ANDROID) |