| // 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. | 
 |  | 
 | #include "base/containers/adapters.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/functional/callback_helpers.h" | 
 | #include "base/path_service.h" | 
 | #include "base/strings/escape.h" | 
 | #include "base/strings/strcat.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/test/metrics/histogram_tester.h" | 
 | #include "base/test/scoped_feature_list.h" | 
 | #include "chrome/browser/content_settings/cookie_settings_factory.h" | 
 | #include "chrome/browser/content_settings/host_content_settings_map_factory.h" | 
 | #include "chrome/browser/net/storage_test_utils.h" | 
 | #include "chrome/browser/policy/policy_test_utils.h" | 
 | #include "chrome/browser/profiles/profile.h" | 
 | #include "chrome/browser/storage_access_api/storage_access_grant_permission_context.h" | 
 | #include "chrome/browser/ui/browser.h" | 
 | #include "chrome/browser/ui/tabs/tab_strip_model.h" | 
 | #include "chrome/common/pref_names.h" | 
 | #include "chrome/test/base/in_process_browser_test.h" | 
 | #include "chrome/test/base/ui_test_utils.h" | 
 | #include "components/content_settings/core/browser/cookie_settings.h" | 
 | #include "components/content_settings/core/browser/host_content_settings_map.h" | 
 | #include "components/content_settings/core/common/content_settings.h" | 
 | #include "components/content_settings/core/common/features.h" | 
 | #include "components/content_settings/core/common/pref_names.h" | 
 | #include "components/metrics/content/subprocess_metrics_provider.h" | 
 | #include "components/permissions/features.h" | 
 | #include "components/permissions/permission_request_manager.h" | 
 | #include "components/permissions/request_type.h" | 
 | #include "components/permissions/test/mock_permission_prompt_factory.h" | 
 | #include "components/permissions/test/permission_request_observer.h" | 
 | #include "components/policy/policy_constants.h" | 
 | #include "components/prefs/pref_service.h" | 
 | #include "content/public/browser/browser_context.h" | 
 | #include "content/public/browser/storage_partition.h" | 
 | #include "content/public/common/content_paths.h" | 
 | #include "content/public/test/browser_task_environment.h" | 
 | #include "content/public/test/browser_test.h" | 
 | #include "content/public/test/browser_test_utils.h" | 
 | #include "content/public/test/test_navigation_observer.h" | 
 | #include "net/base/features.h" | 
 | #include "net/cookies/cookie_constants.h" | 
 | #include "net/cookies/cookie_partition_key_collection.h" | 
 | #include "net/dns/mock_host_resolver.h" | 
 | #include "net/test/embedded_test_server/embedded_test_server.h" | 
 | #include "services/network/public/cpp/network_switches.h" | 
 | #include "services/network/public/mojom/cookie_manager.mojom.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "third_party/blink/public/common/features.h" | 
 | #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-forward.h" | 
 | #include "ui/base/window_open_disposition.h" | 
 |  | 
 | using content::BrowserThread; | 
 | using testing::Gt; | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr char kHostA[] = "a.test"; | 
 | constexpr char kOriginA[] = "https://a.test"; | 
 | constexpr char kUrlA[] = "https://a.test/random.path"; | 
 | constexpr char kHostASubdomain[] = "subdomain.a.test"; | 
 | constexpr char kHostB[] = "b.test"; | 
 | constexpr char kHostBSubdomain[] = "subdomain.b.test"; | 
 | constexpr char kHostC[] = "c.test"; | 
 | constexpr char kHostD[] = "d.test"; | 
 |  | 
 | constexpr char kUseCounterHistogram[] = "Blink.UseCounter.Features"; | 
 | constexpr char kRequestOutcomeHistogram[] = "API.StorageAccess.RequestOutcome"; | 
 | constexpr char kGrantIsImplicitHistogram[] = | 
 |     "API.StorageAccess.GrantIsImplicit"; | 
 |  | 
 | // Path for URL of custom response | 
 | const char* kEchoCookiesWithCorsPath = "/echocookieswithcors"; | 
 |  | 
 | constexpr char kQueryStorageAccessPermission[] = | 
 |     "navigator.permissions.query({name: 'storage-access'}).then(" | 
 |     "  (permission) => permission.state);"; | 
 |  | 
 | enum class TestType { kFrame, kWorker }; | 
 |  | 
 | // Helpers to express expected | 
 | std::pair<std::string, std::string> CookieBundle(const std::string& cookies) { | 
 |   DCHECK_NE(cookies, "None"); | 
 |   DCHECK_NE(cookies, ""); | 
 |   return {cookies, cookies}; | 
 | } | 
 |  | 
 | std::tuple<std::string, std::string, std::string> CookieBundleWithContent( | 
 |     const std::string& cookies) { | 
 |   DCHECK_NE(cookies, "None"); | 
 |   DCHECK_NE(cookies, ""); | 
 |   return {cookies, cookies, cookies}; | 
 | } | 
 |  | 
 | std::pair<std::string, std::string> NoCookies() { | 
 |   return { | 
 |       "",      // cookie string via `document.cookie` | 
 |       "None",  // cookie string via `echoheader?cookie` | 
 |   }; | 
 | } | 
 |  | 
 | std::tuple<std::string, std::string, std::string> NoCookiesWithContent() { | 
 |   return { | 
 |       "",      // cookie string via `document.cookie` | 
 |       "None",  // cookie string via `echoheader?cookie` | 
 |       "None",  // cookie string via frame content (also via `echoheader?cookie`) | 
 |   }; | 
 | } | 
 |  | 
 | // Responds to a request to /echocookieswithcors with the cookies that were sent | 
 | // with the request. We can't use the default handler /echoheader?Cookie here, | 
 | // because it doesn't send the appropriate Access-Control-Allow-Origin and | 
 | // Access-Control-Allow-Credentials headers (which are required for this to | 
 | // work for cross-origin requests in the tests). | 
 | std::unique_ptr<net::test_server::HttpResponse> | 
 | HandleEchoCookiesWithCorsRequest(const net::test_server::HttpRequest& request) { | 
 |   if (request.relative_url != kEchoCookiesWithCorsPath) { | 
 |     return nullptr; | 
 |   } | 
 |   auto http_response = std::make_unique<net::test_server::BasicHttpResponse>(); | 
 |   std::string content = "None"; | 
 |   // Get the 'Cookie' header that was sent in the request. | 
 |   if (auto it = request.headers.find(net::HttpRequestHeaders::kCookie); | 
 |       it != request.headers.end()) { | 
 |     content = it->second; | 
 |   } | 
 |  | 
 |   http_response->set_code(net::HTTP_OK); | 
 |   http_response->set_content_type("text/plain"); | 
 |   // Set the cors enabled headers. | 
 |   if (auto it = request.headers.find(net::HttpRequestHeaders::kOrigin); | 
 |       it != request.headers.end()) { | 
 |     http_response->AddCustomHeader("Access-Control-Allow-Origin", it->second); | 
 |     http_response->AddCustomHeader("Vary", "origin"); | 
 |     http_response->AddCustomHeader("Access-Control-Allow-Credentials", "true"); | 
 |   } | 
 |   http_response->set_content(content); | 
 |  | 
 |   return http_response; | 
 | } | 
 |  | 
 | std::string QueryPermission(content::RenderFrameHost* render_frame_host) { | 
 |   return content::EvalJs(render_frame_host, kQueryStorageAccessPermission) | 
 |       .ExtractString(); | 
 | } | 
 |  | 
 | class StorageAccessAPIBaseBrowserTest : public policy::PolicyTest { | 
 |  protected: | 
 |   explicit StorageAccessAPIBaseBrowserTest(bool is_storage_partitioned) | 
 |       : https_server_(net::EmbeddedTestServer::TYPE_HTTPS), | 
 |         is_storage_partitioned_(is_storage_partitioned) {} | 
 |  | 
 |   void SetUp() override { | 
 |     features_.InitWithFeaturesAndParameters(GetEnabledFeatures(), | 
 |                                             GetDisabledFeatures()); | 
 |     InProcessBrowserTest::SetUp(); | 
 |   } | 
 |  | 
 |   virtual std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() { | 
 |     std::vector<base::test::FeatureRefAndParams> enabled({ | 
 |         {blink::features::kStorageAccessAPI, | 
 |          { | 
 |              { | 
 |                  blink::features::kStorageAccessAPIAutoGrantInFPS.name, | 
 |                  "false", | 
 |              }, | 
 |              { | 
 |                  blink::features::kStorageAccessAPIAutoDenyOutsideFPS.name, | 
 |                  "false", | 
 |              }, | 
 |              { | 
 |                  blink::features::kStorageAccessAPIImplicitGrantLimit.name, | 
 |                  "0", | 
 |              }, | 
 |          }}, | 
 |     }); | 
 |     if (is_storage_partitioned_) { | 
 |       enabled.push_back({net::features::kThirdPartyStoragePartitioning, {}}); | 
 |     } | 
 |     return enabled; | 
 |   } | 
 |  | 
 |   virtual std::vector<base::test::FeatureRef> GetDisabledFeatures() { | 
 |     std::vector<base::test::FeatureRef> disabled; | 
 |     if (!is_storage_partitioned_) { | 
 |       disabled.push_back(net::features::kThirdPartyStoragePartitioning); | 
 |     } | 
 |     return disabled; | 
 |   } | 
 |  | 
 |   void SetUpOnMainThread() override { | 
 |     host_resolver()->AddRule("*", "127.0.0.1"); | 
 |     base::FilePath path; | 
 |     base::PathService::Get(content::DIR_TEST_DATA, &path); | 
 |     https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); | 
 |     https_server_.ServeFilesFromDirectory(path); | 
 |     https_server_.AddDefaultHandlers(GetChromeTestDataDir()); | 
 |     https_server_.RegisterRequestHandler( | 
 |         base::BindRepeating(&HandleEchoCookiesWithCorsRequest)); | 
 |     ASSERT_TRUE(https_server_.Start()); | 
 |  | 
 |     // All the sites used during these tests should have a cookie. | 
 |     SetCrossSiteCookieOnDomain(kHostA); | 
 |     SetCrossSiteCookieOnDomain(kHostB); | 
 |     SetCrossSiteCookieOnDomain(kHostC); | 
 |     SetCrossSiteCookieOnDomain(kHostD); | 
 |  | 
 |     permissions::PermissionRequestManager* manager = | 
 |         permissions::PermissionRequestManager::FromWebContents( | 
 |             browser()->tab_strip_model()->GetActiveWebContents()); | 
 |     prompt_factory_ = | 
 |         std::make_unique<permissions::MockPermissionPromptFactory>(manager); | 
 |  | 
 |     // Don't respond to the prompt at all, by default. This forces any test that | 
 |     // assumes a particular response to the prompt to explicitly declare that | 
 |     // assumption by setting up the auto-response themselves. | 
 |     prompt_factory_->set_response_type( | 
 |         permissions::PermissionRequestManager::NONE); | 
 |   } | 
 |  | 
 |   void TearDownOnMainThread() override { prompt_factory_.reset(); } | 
 |  | 
 |   void SetCrossSiteCookieOnDomain(const std::string& domain) { | 
 |     GURL domain_url = GetURL(domain); | 
 |     std::string cookie = base::StrCat({"cross-site=", domain}); | 
 |     content::SetCookie( | 
 |         browser()->profile(), domain_url, | 
 |         base::StrCat({cookie, ";SameSite=None;Secure;Domain=", domain})); | 
 |     ASSERT_THAT(content::GetCookies(browser()->profile(), domain_url), | 
 |                 testing::HasSubstr(cookie)); | 
 |   } | 
 |  | 
 |   void SetPartitionedCookieInContext(const std::string& top_level_host, | 
 |                                      const std::string& embedded_host) { | 
 |     GURL host_url = GetURL(embedded_host); | 
 |     std::string cookie = | 
 |         base::StrCat({"cross-site=", embedded_host, "(partitioned)"}); | 
 |     net::CookiePartitionKey partition_key = | 
 |         net::CookiePartitionKey::FromURLForTesting(GetURL(top_level_host)); | 
 |     content::SetPartitionedCookie( | 
 |         browser()->profile(), host_url, | 
 |         base::StrCat({cookie, ";SameSite=None;Secure;Partitioned"}), | 
 |         partition_key); | 
 |     ASSERT_THAT(content::GetCookies( | 
 |                     browser()->profile(), host_url, | 
 |                     net::CookieOptions::SameSiteCookieContext::MakeInclusive(), | 
 |                     net::CookiePartitionKeyCollection(partition_key)), | 
 |                 testing::HasSubstr(cookie)); | 
 |   } | 
 |  | 
 |   void BlockAllCookiesOnHost(const std::string& host) { | 
 |     CookieSettingsFactory::GetForProfile(browser()->profile()) | 
 |         ->SetCookieSetting(GetURL(host), ContentSetting::CONTENT_SETTING_BLOCK); | 
 |   } | 
 |  | 
 |   GURL GetURL(const std::string& host) { | 
 |     return https_server_.GetURL(host, "/"); | 
 |   } | 
 |  | 
 |   void SetBlockThirdPartyCookies(bool value) { | 
 |     browser()->profile()->GetPrefs()->SetInteger( | 
 |         prefs::kCookieControlsMode, | 
 |         static_cast<int>( | 
 |             value ? content_settings::CookieControlsMode::kBlockThirdParty | 
 |                   : content_settings::CookieControlsMode::kOff)); | 
 |   } | 
 |  | 
 |   void NavigateToPage(const std::string& host, const std::string& path) { | 
 |     GURL main_url(https_server_.GetURL(host, path)); | 
 |     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); | 
 |   } | 
 |  | 
 |   void NavigateToPageWithFrame(const std::string& host) { | 
 |     GURL main_url(https_server_.GetURL(host, "/iframe.html")); | 
 |     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); | 
 |   } | 
 |  | 
 |   void NavigateToNewTabWithFrame(const std::string& host) { | 
 |     GURL main_url(https_server_.GetURL(host, "/iframe.html")); | 
 |     ui_test_utils::NavigateToURLWithDisposition( | 
 |         browser(), main_url, WindowOpenDisposition::NEW_FOREGROUND_TAB, | 
 |         ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); | 
 |   } | 
 |  | 
 |   void NavigateFrameTo(const std::string& host, const std::string& path) { | 
 |     NavigateFrameTo(https_server_.GetURL(host, path)); | 
 |   } | 
 |  | 
 |   void NavigateFrameTo(const GURL& url) { | 
 |     content::WebContents* web_contents = | 
 |         browser()->tab_strip_model()->GetActiveWebContents(); | 
 |     EXPECT_TRUE(NavigateIframeToURL(web_contents, "test", url)); | 
 |   } | 
 |  | 
 |   void NavigateNestedFrameTo(const std::string& host, const std::string& path) { | 
 |     NavigateNestedFrameTo(https_server_.GetURL(host, path)); | 
 |   } | 
 |  | 
 |   // Navigates the innermost frame to the given URL. (The web_contents is | 
 |   // assumed to be showing a page containing an iframe that contains another | 
 |   // iframe.) The navigation's initiator is the middle iframe (not the leaf). | 
 |   void NavigateNestedFrameTo(const GURL& url) { | 
 |     content::WebContents* web_contents = | 
 |         browser()->tab_strip_model()->GetActiveWebContents(); | 
 |     content::TestNavigationObserver load_observer(web_contents); | 
 |     ASSERT_TRUE(ExecJs( | 
 |         GetFrame(), | 
 |         base::StringPrintf("document.body.querySelector('iframe').src = '%s';", | 
 |                            url.spec().c_str()))); | 
 |     load_observer.Wait(); | 
 |   } | 
 |  | 
 |   void NavigateToPageWithTwoFrames(const std::string& host) { | 
 |     GURL main_url(https_server_.GetURL(host, "/two_iframes_blank.html")); | 
 |     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); | 
 |   } | 
 |  | 
 |   void NavigateFirstFrameTo(const GURL& url) { | 
 |     content::WebContents* web_contents = | 
 |         browser()->tab_strip_model()->GetActiveWebContents(); | 
 |     EXPECT_TRUE(NavigateIframeToURL(web_contents, "iframe1", url)); | 
 |   } | 
 |  | 
 |   void NavigateSecondFrameTo(const GURL& url) { | 
 |     content::WebContents* web_contents = | 
 |         browser()->tab_strip_model()->GetActiveWebContents(); | 
 |     EXPECT_TRUE(NavigateIframeToURL(web_contents, "iframe2", url)); | 
 |   } | 
 |  | 
 |   GURL EchoCookiesURL(const std::string& host) { | 
 |     return https_server().GetURL(host, "/echoheader?cookie"); | 
 |   } | 
 |  | 
 |   GURL RedirectViaHosts(const std::vector<std::string>& hosts, | 
 |                         const GURL& destination) { | 
 |     GURL url = destination; | 
 |  | 
 |     for (const auto& host : base::Reversed(hosts)) { | 
 |       url = https_server().GetURL( | 
 |           host, base::StrCat( | 
 |                     {"/server-redirect?", base::EscapeQueryParamValue( | 
 |                                               url.spec(), /*use_plus=*/true)})); | 
 |     } | 
 |     return url; | 
 |   } | 
 |  | 
 |   std::string CookiesFromFetch(content::RenderFrameHost* render_frame_host, | 
 |                                const std::string& subresource_host) { | 
 |     return storage::test::FetchWithCredentials( | 
 |         render_frame_host, | 
 |         https_server_.GetURL(subresource_host, kEchoCookiesWithCorsPath), | 
 |         /*cors_enabled=*/true); | 
 |   } | 
 |  | 
 |   // Reads cookies via `document.cookie` in the provided RFH, and via a | 
 |   // subresource request from the provided RFH to the given host. These are | 
 |   // bundled together to ensure that tests always check both, and that they're | 
 |   // consistent. | 
 |   std::pair<std::string, std::string> ReadCookies( | 
 |       content::RenderFrameHost* render_frame_host, | 
 |       const std::string& subresource_host) { | 
 |     return { | 
 |         content::EvalJs(render_frame_host, "document.cookie").ExtractString(), | 
 |         CookiesFromFetch(render_frame_host, subresource_host), | 
 |     }; | 
 |   } | 
 |  | 
 |   // Reads cookies via `document.cookie` in the provided RFH, and via a | 
 |   // subresource request from the provided RFH to the given host, and also | 
 |   // includes the content of the provided RFH. This is most useful to check that | 
 |   // the cookies accessible during navigation (via `echoheader?cookie`), during | 
 |   // load (via `echoheader?cookie` for subresources), and via script execution | 
 |   // (via `document.cookie`) are consistent with each other. | 
 |   std::tuple<std::string, std::string, std::string> ReadCookiesAndContent( | 
 |       content::RenderFrameHost* render_frame_host, | 
 |       const std::string& subresource_host) { | 
 |     auto [js_cookies, subresource_cookies] = | 
 |         ReadCookies(render_frame_host, subresource_host); | 
 |     return { | 
 |         js_cookies, | 
 |         subresource_cookies, | 
 |         storage::test::GetFrameContent(render_frame_host), | 
 |     }; | 
 |   } | 
 |  | 
 |   content::RenderFrameHost* GetPrimaryMainFrame() { | 
 |     content::WebContents* web_contents = | 
 |         browser()->tab_strip_model()->GetActiveWebContents(); | 
 |     return web_contents->GetPrimaryMainFrame(); | 
 |   } | 
 |  | 
 |   content::RenderFrameHost* GetFrame() { | 
 |     return ChildFrameAt(GetPrimaryMainFrame(), 0); | 
 |   } | 
 |  | 
 |   content::RenderFrameHost* GetNestedFrame() { | 
 |     return ChildFrameAt(GetFrame(), 0); | 
 |   } | 
 |  | 
 |   content::RenderFrameHost* GetFirstFrame() { return GetFrame(); } | 
 |  | 
 |   content::RenderFrameHost* GetSecondFrame() { | 
 |     return ChildFrameAt(GetPrimaryMainFrame(), 1); | 
 |   } | 
 |  | 
 |   net::test_server::EmbeddedTestServer& https_server() { return https_server_; } | 
 |  | 
 |   bool IsStoragePartitioned() const { return is_storage_partitioned_; } | 
 |  | 
 |   permissions::MockPermissionPromptFactory* prompt_factory() { | 
 |     return prompt_factory_.get(); | 
 |   } | 
 |  | 
 |  private: | 
 |   net::test_server::EmbeddedTestServer https_server_; | 
 |   base::test::ScopedFeatureList features_; | 
 |   bool is_storage_partitioned_; | 
 |   std::unique_ptr<permissions::MockPermissionPromptFactory> prompt_factory_; | 
 | }; | 
 |  | 
 | // Test fixture for core Storage Access API functionality, guaranteed by spec. | 
 | // This fixture should use the minimal set of features/params. | 
 | class StorageAccessAPIBrowserTest : public StorageAccessAPIBaseBrowserTest { | 
 |  public: | 
 |   StorageAccessAPIBrowserTest() | 
 |       : StorageAccessAPIBaseBrowserTest(/*is_storage_partitioned=*/false) {} | 
 | }; | 
 |  | 
 | // Check default values for permissions.query on storage-access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryDefault) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/echoheader?cookie"); | 
 |  | 
 |   EXPECT_EQ(QueryPermission(GetPrimaryMainFrame()), "granted"); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "prompt"); | 
 | } | 
 |  | 
 | // Check default values for permissions.query on storage-access when 3p cookie | 
 | // is allowed. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        PermissionQueryDefault_AllowCrossSiteCookie) { | 
 |   SetBlockThirdPartyCookies(false); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/echoheader?cookie"); | 
 |  | 
 |   EXPECT_EQ(QueryPermission(GetPrimaryMainFrame()), "granted"); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "prompt"); | 
 | } | 
 |  | 
 | // Test that permissions.query changes to "granted" when a storage access | 
 | // request was successful. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryGranted) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/echoheader?cookie"); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "prompt"); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   // Grant initial permission. | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "granted"); | 
 |  | 
 |   // Ensure that after a navigation the permission state is preserved. | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/echoheader?cookie"); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "granted"); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, PermissionQueryCrossSite) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostB); | 
 |   NavigateFrameTo(kHostA, "/echoheader?cookie"); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "prompt"); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   // Grant initial permission. | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "granted"); | 
 |  | 
 |   // Ensure that the scope of the permission grant is for the entire site. | 
 |   NavigateFrameTo(kHostASubdomain, "/echoheader?cookie"); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "granted"); | 
 |  | 
 |   // The permission should not be available cross-site. | 
 |   NavigateFrameTo(kHostC, "/echoheader?cookie"); | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "prompt"); | 
 | } | 
 |  | 
 | // When 3p cookie is allowed, check that in a A(B) frame tree, the embedded | 
 | // B iframe can access cookie without requesting, but the prompt is still shown | 
 | // if the iframe makes the request. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     ThirdPartyCookiesAccess_CrossSiteIframe_AllowCrossSiteCookie) { | 
 |   SetBlockThirdPartyCookies(false); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   // The cross-site iframe has cookie access since 3p cookies are allowed. | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), | 
 |             CookieBundleWithContent("cross-site=b.test")); | 
 |  | 
 |   // TODO(https://crbug.com/1441133): We should either make sure there is way to | 
 |   // let developer check whether they need to call rSA(), or no prompt is shown | 
 |   // when 3p cookie is allowed. | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(1, prompt_factory()->TotalRequestCount()); | 
 | } | 
 |  | 
 | // Validate that a cross-site iframe can bypass third-party cookie blocking via | 
 | // the Storage Access API. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        ThirdPartyCookiesIFrameRequestsAccess_CrossSiteIframe) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), CookieBundle("cross-site=b.test")); | 
 | } | 
 |  | 
 | // Validate that if an iframe obtains access, then cookies become unblocked for | 
 | // just that top-level/third-party combination and are still blocked for other | 
 | // combinations. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     ThirdPartyCookiesIFrameRequestsAccess_CrossSiteIframe_UnrelatedSites) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |   base::HistogramTester histogram_tester; | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateFrameTo(EchoCookiesURL(kHostC)); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostC), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   content::FetchHistogramsFromChildProcesses(); | 
 |  | 
 |   EXPECT_THAT( | 
 |       histogram_tester.GetBucketCount( | 
 |           kUseCounterHistogram, | 
 |           blink::mojom::WebFeature::kStorageAccessAPI_HasStorageAccess_Method), | 
 |       Gt(0)); | 
 |   EXPECT_THAT(histogram_tester.GetBucketCount( | 
 |                   kUseCounterHistogram, | 
 |                   blink::mojom::WebFeature:: | 
 |                       kStorageAccessAPI_requestStorageAccess_Method), | 
 |               Gt(0)); | 
 | } | 
 |  | 
 | // Validate that a nested A(B(B)) iframe can obtain cookie access, and that that | 
 | // access is not shared with the "middle" B iframe. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     ThirdPartyCookiesIFrameRequestsAccess_NestedCrossSiteIframe_InnerRequestsAccess) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostB), | 
 |             NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), NoCookies()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostB), | 
 |             CookieBundle("cross-site=b.test")); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), NoCookies()); | 
 | } | 
 |  | 
 | // Validate that in a A(B(B)) frame tree, the middle B iframe can obtain access, | 
 | // and that access is not shared with the leaf B iframe. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     ThirdPartyCookiesIFrameRequestsAccess_NestedCrossSiteIframe_MiddleRequestsAccess) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), NoCookies()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), CookieBundle("cross-site=b.test")); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostB), NoCookies()); | 
 |  | 
 |   // Subresource request from the cross-site iframe to an end point that's | 
 |   // same-origin with the top-level does not enable cookie access. | 
 |   EXPECT_EQ(CookiesFromFetch(GetFrame(), kHostA), "None"); | 
 |   EXPECT_EQ(CookiesFromFetch(GetNestedFrame(), kHostA), "None"); | 
 | } | 
 |  | 
 | // Validate that in a A(B(C)) frame tree, the C leaf iframe can obtain cookie | 
 | // access. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     ThirdPartyCookiesIFrameRequestsAccess_NestedCrossSiteIframe_DistinctSites) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostC)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostC), | 
 |             NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostC), | 
 |             CookieBundle("cross-site=c.test")); | 
 | } | 
 |  | 
 | // Validate that cross-site sibling iframes cannot take advantage of each | 
 | // other's granted permission. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        ThirdPartyCookiesCrossSiteSiblingIFrameRequestsAccess) { | 
 |   NavigateToPageWithTwoFrames(kHostA); | 
 |   NavigateFirstFrameTo(EchoCookiesURL(kHostB)); | 
 |   NavigateSecondFrameTo(EchoCookiesURL(kHostC)); | 
 |  | 
 |   // Verify that both same-origin subresource request and cross-origin | 
 |   // subresource request can access cookies for the kHostB iframe. | 
 |   ASSERT_EQ(CookiesFromFetch(GetFirstFrame(), kHostB), "cross-site=b.test"); | 
 |   ASSERT_EQ(CookiesFromFetch(GetFirstFrame(), kHostC), "cross-site=c.test"); | 
 |  | 
 |   // Verify that both same-origin subresource request and cross-origin | 
 |   // subresource request can access cookies for the kHostC iframe. | 
 |   ASSERT_EQ(CookiesFromFetch(GetSecondFrame(), kHostC), "cross-site=c.test"); | 
 |   ASSERT_EQ(CookiesFromFetch(GetSecondFrame(), kHostB), "cross-site=b.test"); | 
 |  | 
 |   SetBlockThirdPartyCookies(true); | 
 |   // Navigate the first iframe to kHostB and grant Storage Access. | 
 |   NavigateFirstFrameTo(EchoCookiesURL(kHostB)); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFirstFrame(), kHostB), | 
 |             NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFirstFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetFirstFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetFirstFrame(), kHostB), | 
 |             CookieBundle("cross-site=b.test")); | 
 |  | 
 |   // Navigate the second iframe to kHostC and grant Storage Access. | 
 |   NavigateSecondFrameTo(EchoCookiesURL(kHostC)); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetSecondFrame(), kHostC), | 
 |             NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetSecondFrame())); | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetSecondFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetSecondFrame(), kHostC), | 
 |             CookieBundle("cross-site=c.test")); | 
 |  | 
 |   // Verify same-origin subresource request has cookie access whereas the | 
 |   // cross-origin subresource request does not for the kHostB iframe. | 
 |   EXPECT_EQ(CookiesFromFetch(GetFirstFrame(), kHostB), "cross-site=b.test"); | 
 |   EXPECT_EQ(CookiesFromFetch(GetFirstFrame(), kHostC), "None"); | 
 |  | 
 |   // Verify same-origin subresource request has cookie access whereas the | 
 |   // cross-origin subresource request does not for the kHostC iframe. | 
 |   EXPECT_EQ(CookiesFromFetch(GetSecondFrame(), kHostC), "cross-site=c.test"); | 
 |   EXPECT_EQ(CookiesFromFetch(GetSecondFrame(), kHostB), "None"); | 
 | } | 
 |  | 
 | // Validate that the Storage Access API does not override any explicit user | 
 | // settings to block storage access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        ThirdPartyCookiesIFrameThirdPartyExceptions) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |   BlockAllCookiesOnHost(kHostB); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), NoCookies()); | 
 | } | 
 |  | 
 | // Validate that user settings take precedence for the leaf in a A(B(B)) frame | 
 | // tree. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     ThirdPartyCookiesIFrameThirdPartyExceptions_NestedSameSite) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |   BlockAllCookiesOnHost(kHostB); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostB), | 
 |             NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |  | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostB), NoCookies()); | 
 | } | 
 |  | 
 | // Validate that user settings take precedence for the leaf in a A(B(C)) frame | 
 | // tree. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     ThirdPartyCookiesIFrameThirdPartyExceptions_NestedCrossSite) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |   BlockAllCookiesOnHost(kHostC); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostC)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostC), | 
 |             NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |  | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostC), NoCookies()); | 
 | } | 
 |  | 
 | // Validates that once a grant is removed access is also removed. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        ThirdPartyGrantsDeletedAccess) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), CookieBundle("cross-site=b.test")); | 
 |  | 
 |   // Manually delete all our grants. | 
 |   HostContentSettingsMap* settings_map = | 
 |       HostContentSettingsMapFactory::GetForProfile(browser()->profile()); | 
 |   settings_map->ClearSettingsForOneType(ContentSettingsType::STORAGE_ACCESS); | 
 |   // Verify cookie cannot be accessed. | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), NoCookies()); | 
 |  | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 | } | 
 |  | 
 | // Validate that if the iframe's origin is opaque, it cannot obtain storage | 
 | // access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, OpaqueOriginRejects) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   ASSERT_TRUE( | 
 |       ExecJs(GetPrimaryMainFrame(), | 
 |              "document.querySelector('iframe').sandbox='allow-scripts';")); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_FALSE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 | } | 
 |  | 
 | // Validate that if the iframe is sandboxed and allows scripts but is missing | 
 | // the Storage Access sandbox tag, the iframe cannot obtain storage access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        MissingSandboxTokenRejects) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   ASSERT_TRUE(ExecJs(GetPrimaryMainFrame(), | 
 |                      "document.querySelector('iframe').sandbox='allow-" | 
 |                      "scripts allow-same-origin';")); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_FALSE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 | } | 
 |  | 
 | // Validate that if the iframe is sandboxed and has the Storage Access sandbox | 
 | // tag, the iframe can obtain storage access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, SandboxTokenResolves) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   ASSERT_TRUE( | 
 |       ExecJs(GetPrimaryMainFrame(), | 
 |              "document.querySelector('iframe').sandbox='allow-scripts " | 
 |              "allow-same-origin allow-storage-access-by-user-activation';")); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 | } | 
 |  | 
 | // Validates that expired grants don't get reused. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, ThirdPartyGrantsExpiry) { | 
 |   base::HistogramTester histogram_tester; | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostC)); | 
 |  | 
 |   // Manually create a pre-expired grant and ensure it doesn't grant access for | 
 |   // HostB. | 
 |   base::Time expiration_time = base::Time::Now() - base::Minutes(5); | 
 |   HostContentSettingsMap* settings_map = | 
 |       HostContentSettingsMapFactory::GetForProfile(browser()->profile()); | 
 |   settings_map->SetContentSettingDefaultScope( | 
 |       GetURL(kHostB), GetURL(kHostA), ContentSettingsType::STORAGE_ACCESS, | 
 |       CONTENT_SETTING_ALLOW, | 
 |       {expiration_time, content_settings::SessionModel::UserSession}); | 
 |   settings_map->SetContentSettingDefaultScope( | 
 |       GetURL(kHostC), GetURL(kHostA), ContentSettingsType::STORAGE_ACCESS, | 
 |       CONTENT_SETTING_ALLOW); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   // The iframe should request for new grant since the existing one is expired. | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   // Validate that only one permission was newly granted. | 
 |   histogram_tester.ExpectUniqueSample(kRequestOutcomeHistogram, | 
 |                                       RequestOutcome::kGrantedByUser, 1); | 
 |  | 
 |   // The nested iframe reuses the existing grant without requesting. | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostC), | 
 |             CookieBundle("cross-site=c.test")); | 
 |  | 
 |   // We don't get to record a sample for the "reuse" case, so that histogram | 
 |   // still only has 1 sample in total. | 
 |   histogram_tester.ExpectTotalCount(kRequestOutcomeHistogram, 1); | 
 |  | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostC)); | 
 |   // Only when the initiator is the frame that's been navigated can inherit | 
 |   // per-frame storage access. | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostC), | 
 |             NoCookiesWithContent()); | 
 | } | 
 |  | 
 | // Validate that if an iframe navigates itself to a same-origin endpoint, and | 
 | // that navigation does not include any cross-origin redirects, the new document | 
 | // can inherit storage access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        Navigation_SelfInitiated_SameOrigin_Preserves) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   ASSERT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       content::NavigateToURLFromRenderer(GetFrame(), EchoCookiesURL(kHostB))); | 
 |  | 
 |   EXPECT_TRUE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), | 
 |             CookieBundleWithContent("cross-site=b.test")); | 
 | } | 
 |  | 
 | // Validate that if an iframe is navigated (by a cross-site initiator) to a | 
 | // same-origin endpoint, and that navigation does not include any cross-origin | 
 | // redirects, the new document cannot inherit storage access. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     Navigation_NonSelfInitiated_SameOriginDestination_CrossSiteInitiator) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   ASSERT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   // The navigation for this frame does not include cookies, since the initiator | 
 |   // is cross-site from the destination, and the initiator did not have storage | 
 |   // access. | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 | } | 
 |  | 
 | // Validate that if an iframe is navigated (by a same-site initiator) to a | 
 | // same-origin endpoint (even if the navigation does not include any | 
 | // cross-origin redirects), the new document cannot inherit storage access. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     Navigation_NonSelfInitiated_SameOriginDestination_SameSiteInitiator) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostBSubdomain)); | 
 |  | 
 |   ASSERT_EQ(ReadCookies(GetFrame(), kHostB), NoCookies()); | 
 |   ASSERT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostB), | 
 |             NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostBSubdomain)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |   // The navigation itself carried cookies due to the initiator's storage | 
 |   // access, but the new document did not inherit storage access, since the | 
 |   // navigation was not self-initiated. | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostB), | 
 |             std::make_tuple("", "None", "cross-site=b.test")); | 
 | } | 
 |  | 
 | // Validate that if an iframe is navigated (by a same-site initiator) to a | 
 | // same-origin endpoint (even if the navigation does not include any | 
 | // cross-origin redirects, and the navigated frame has obtained storage access | 
 | // already), the new document cannot inherit storage access. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     Navigation_NonSelfInitiated_SameOriginDestination_SameSiteInitiator_TargetHasStorageAccess) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostBSubdomain)); | 
 |  | 
 |   ASSERT_EQ(ReadCookies(GetFrame(), kHostB), NoCookies()); | 
 |   ASSERT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostB), | 
 |             NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |  | 
 |   NavigateNestedFrameTo(EchoCookiesURL(kHostBSubdomain)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |   // The navigation itself carried cookies due to the initiator's storage | 
 |   // access, but the new document did not inherit storage access, since the | 
 |   // navigation was not self-initiated. | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetNestedFrame(), kHostB), | 
 |             std::make_tuple("", "None", "cross-site=b.test")); | 
 | } | 
 |  | 
 | // Validate that if an iframe navigates itself to a same-site cross-origin | 
 | // endpoint, and that navigation does not include any cross-origin redirects, | 
 | // the new document cannot inherit storage access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        Navigation_SelfInitiated_SameSiteCrossOrigin) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   ASSERT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   EXPECT_TRUE(content::NavigateToURLFromRenderer( | 
 |       GetFrame(), EchoCookiesURL(kHostBSubdomain))); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   // The navigation itself carried cookies from the previous document's storage | 
 |   // access, but the new document did not inherit storage access. | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), | 
 |             std::make_tuple("", "None", "cross-site=b.test")); | 
 | } | 
 |  | 
 | // Validate that if an iframe navigates itself to a cross-site endpoint, and | 
 | // that navigation does not include any cross-origin redirects, the new document | 
 | // cannot inherit storage access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        Navigation_SelfInitiated_CrossSite) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   ASSERT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       content::NavigateToURLFromRenderer(GetFrame(), EchoCookiesURL(kHostC))); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostC), NoCookiesWithContent()); | 
 | } | 
 |  | 
 | // Validate that if an iframe navigates itself to a same-origin endpoint, but | 
 | // that navigation include a cross-origin redirect, the new document | 
 | // cannot inherit storage access. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     Navigation_SelfInitiated_SameOrigin_CrossOriginRedirect) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   ASSERT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   GURL dest = EchoCookiesURL(kHostB); | 
 |   EXPECT_TRUE(content::NavigateToURLFromRenderer( | 
 |       GetFrame(), | 
 |       /*url=*/ | 
 |       RedirectViaHosts({kHostBSubdomain}, dest), | 
 |       /*expected_commit_url=*/dest)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   // The navigation itself carried cookies from the previous document's storage | 
 |   // access, but the new document did not inherit storage access. | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostBSubdomain), | 
 |             std::make_tuple("", "None", "cross-site=b.test")); | 
 | } | 
 |  | 
 | // Validate that if an iframe navigates itself to a same-origin endpoint, and | 
 | // that navigation includes a cross-origin redirect (even if there's a | 
 | // subsequent same-origin redirect), the new document cannot inherit storage | 
 | // access. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIBrowserTest, | 
 |     Navigation_SelfInitiated_SameOrigin_CrossSiteAndSameSiteRedirects) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   ASSERT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   GURL dest = EchoCookiesURL(kHostB); | 
 |   EXPECT_TRUE(content::NavigateToURLFromRenderer( | 
 |       GetFrame(), | 
 |       /*url=*/ | 
 |       RedirectViaHosts({kHostBSubdomain, kHostB}, dest), | 
 |       /*expected_commit_url=*/dest)); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   // The navigation itself carried cookies from the previous document's storage | 
 |   // access, but the new document did not inherit storage access. | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostBSubdomain), | 
 |             std::make_tuple("", "None", "cross-site=b.test")); | 
 | } | 
 |  | 
 | // Validate that in a A(A) frame tree, the inner A iframe can obtain cookie | 
 | // access by default. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        EmbeddedSameOriginCookieAccess) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostA)); | 
 |  | 
 |   EXPECT_TRUE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(0, prompt_factory()->TotalRequestCount()); | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostA), | 
 |             CookieBundleWithContent("cross-site=a.test")); | 
 | } | 
 |  | 
 | // Validate that in a A(sub.A) frame tree, the inner A iframe can obtain cookie | 
 | // access by default. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        EmbeddedSameSiteCookieAccess) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostASubdomain)); | 
 |  | 
 |   EXPECT_TRUE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   EXPECT_EQ(0, prompt_factory()->TotalRequestCount()); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostA), | 
 |             CookieBundleWithContent("cross-site=a.test")); | 
 | } | 
 |  | 
 | // Validate that in a A(B(A)) frame tree, the inner A iframe can obtain cookie | 
 | // access after requesting access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        NestedSameOriginCookieAccess_CrossSiteAncestorChain) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(kHostA, "/empty.html"); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostA), NoCookies()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(0, prompt_factory()->TotalRequestCount()); | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostA), | 
 |             CookieBundle("cross-site=a.test")); | 
 | } | 
 |  | 
 | // Validate that in a A(B(sub.A)) frame tree, the inner iframe can obtain cookie | 
 | // access after requesting access. | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        NestedSameSiteCookieAccess_CrossSiteAncestorChain) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(kHostASubdomain, "/empty.html"); | 
 |  | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostA), NoCookies()); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |   EXPECT_EQ(0, prompt_factory()->TotalRequestCount()); | 
 |   EXPECT_EQ(ReadCookies(GetNestedFrame(), kHostASubdomain), | 
 |             CookieBundle("cross-site=a.test")); | 
 | } | 
 |  | 
 | class StorageAccessAPIPromptBrowserTest | 
 |     : public StorageAccessAPIBaseBrowserTest, | 
 |       public testing::WithParamInterface<bool> { | 
 |  public: | 
 |   StorageAccessAPIPromptBrowserTest() | 
 |       : StorageAccessAPIBaseBrowserTest( | 
 |             /*is_storage_partitioned=*/false), | 
 |         is_storage_access_new_UI_(GetParam()) {} | 
 |  | 
 |   std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { | 
 |     std::vector<base::test::FeatureRefAndParams> enabled = | 
 |         StorageAccessAPIBaseBrowserTest::GetEnabledFeatures(); | 
 |  | 
 |     if (is_storage_access_new_UI_) { | 
 |       enabled.push_back( | 
 |           {permissions::features::kPermissionStorageAccessAPI, {}}); | 
 |     } | 
 |  | 
 |     return enabled; | 
 |   } | 
 |  | 
 |   std::vector<base::test::FeatureRef> GetDisabledFeatures() override { | 
 |     std::vector<base::test::FeatureRef> disabled = | 
 |         StorageAccessAPIBaseBrowserTest::GetDisabledFeatures(); | 
 |  | 
 |     if (!is_storage_access_new_UI_) { | 
 |       disabled.push_back(permissions::features::kPermissionStorageAccessAPI); | 
 |     } | 
 |  | 
 |     return disabled; | 
 |   } | 
 |  | 
 |  private: | 
 |   const bool is_storage_access_new_UI_; | 
 | }; | 
 |  | 
 | // Validate that in a A(B) frame tree, the embedded B iframe can obtain cookie | 
 | // access if requested and got accepted. | 
 | IN_PROC_BROWSER_TEST_P(StorageAccessAPIPromptBrowserTest, | 
 |                        EmbeddedCrossSiteCookieAccess_Accept) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(1, prompt_factory()->TotalRequestCount()); | 
 |   EXPECT_EQ(1, prompt_factory()->RequestTypeSeen( | 
 |                    permissions::RequestType::kStorageAccess)); | 
 | } | 
 |  | 
 | // Validate that in a A(B) frame tree, the embedded B iframe can not obtain | 
 | // cookie access if requested and got denied. | 
 | IN_PROC_BROWSER_TEST_P(StorageAccessAPIPromptBrowserTest, | 
 |                        EmbeddedCrossSiteCookieAccess_Deny) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::DENY_ALL); | 
 |  | 
 |   EXPECT_FALSE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(1, prompt_factory()->TotalRequestCount()); | 
 |   EXPECT_EQ(1, prompt_factory()->RequestTypeSeen( | 
 |                    permissions::RequestType::kStorageAccess)); | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P(/*no prefix*/, | 
 |                          StorageAccessAPIPromptBrowserTest, | 
 |                          testing::Bool()); | 
 |  | 
 | class StorageAccessAPIStorageBrowserTest | 
 |     : public StorageAccessAPIBaseBrowserTest, | 
 |       public testing::WithParamInterface<std::tuple<TestType, bool>> { | 
 |  public: | 
 |   StorageAccessAPIStorageBrowserTest() | 
 |       : StorageAccessAPIBaseBrowserTest(std::get<1>(GetParam())) {} | 
 |  | 
 |   void ExpectStorage(content::RenderFrameHost* frame, bool expected) { | 
 |     switch (GetTestType()) { | 
 |       case TestType::kFrame: | 
 |         storage::test::ExpectStorageForFrame(frame, /*include_cookies=*/false, | 
 |                                              expected); | 
 |         return; | 
 |       case TestType::kWorker: | 
 |         storage::test::ExpectStorageForWorker(frame, expected); | 
 |         return; | 
 |     } | 
 |   } | 
 |  | 
 |   void SetStorage(content::RenderFrameHost* frame) { | 
 |     switch (GetTestType()) { | 
 |       case TestType::kFrame: | 
 |         storage::test::SetStorageForFrame(frame, /*include_cookies=*/false); | 
 |         return; | 
 |       case TestType::kWorker: | 
 |         storage::test::SetStorageForWorker(frame); | 
 |         return; | 
 |     } | 
 |   } | 
 |  | 
 |   bool DoesPermissionGrantStorage() const { return IsStoragePartitioned(); } | 
 |  | 
 |  private: | 
 |   TestType GetTestType() const { return std::get<0>(GetParam()); } | 
 | }; | 
 |  | 
 | // Validate that the Storage Access API will unblock other types of storage | 
 | // access when a grant is given and that it only applies to the top-level/third | 
 | // party pair requested on. | 
 | IN_PROC_BROWSER_TEST_P(StorageAccessAPIStorageBrowserTest, | 
 |                        ThirdPartyIFrameStorageRequestsAccess) { | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/browsing_data/site_data.html"); | 
 |  | 
 |   ExpectStorage(GetFrame(), false); | 
 |   SetStorage(GetFrame()); | 
 |   ExpectStorage(GetFrame(), true); | 
 |  | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/browsing_data/site_data.html"); | 
 |  | 
 |   ExpectStorage(GetFrame(), false); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/browsing_data/site_data.html"); | 
 |  | 
 |   ExpectStorage(GetFrame(), DoesPermissionGrantStorage()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_P(StorageAccessAPIStorageBrowserTest, | 
 |                        NestedThirdPartyIFrameStorage) { | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html"); | 
 |  | 
 |   ExpectStorage(GetNestedFrame(), false); | 
 |   SetStorage(GetNestedFrame()); | 
 |   ExpectStorage(GetNestedFrame(), true); | 
 |  | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html"); | 
 |  | 
 |   ExpectStorage(GetNestedFrame(), false); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE( | 
 |       storage::test::RequestAndCheckStorageAccessForFrame(GetNestedFrame())); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/iframe.html"); | 
 |   NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html"); | 
 |  | 
 |   ExpectStorage(GetNestedFrame(), DoesPermissionGrantStorage()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame())); | 
 | } | 
 |  | 
 | // Test third-party cookie blocking of features that allow to communicate | 
 | // between tabs such as SharedWorkers. | 
 | IN_PROC_BROWSER_TEST_P(StorageAccessAPIStorageBrowserTest, MultiTabTest) { | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/browsing_data/site_data.html"); | 
 |  | 
 |   storage::test::ExpectCrossTabInfoForFrame(GetFrame(), false); | 
 |   storage::test::SetCrossTabInfoForFrame(GetFrame()); | 
 |   storage::test::ExpectCrossTabInfoForFrame(GetFrame(), true); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   // Create a second tab to test communication between tabs. | 
 |   NavigateToNewTabWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/browsing_data/site_data.html"); | 
 |  | 
 |   permissions::PermissionRequestManager::FromWebContents( | 
 |       browser()->tab_strip_model()->GetActiveWebContents()) | 
 |       ->set_auto_response_for_test( | 
 |           permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   storage::test::ExpectCrossTabInfoForFrame(GetFrame(), true); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/browsing_data/site_data.html"); | 
 |  | 
 |   storage::test::ExpectCrossTabInfoForFrame(GetFrame(), false); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/browsing_data/site_data.html"); | 
 |  | 
 |   storage::test::ExpectCrossTabInfoForFrame(GetFrame(), | 
 |                                             DoesPermissionGrantStorage()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P(/*no prefix*/, | 
 |                          StorageAccessAPIStorageBrowserTest, | 
 |                          testing::Combine(testing::Values(TestType::kFrame, | 
 |                                                           TestType::kWorker), | 
 |                                           testing::Bool())); | 
 |  | 
 | class StorageAccessAPIWithFirstPartySetsBrowserTest | 
 |     : public StorageAccessAPIBaseBrowserTest { | 
 |  public: | 
 |   StorageAccessAPIWithFirstPartySetsBrowserTest() | 
 |       : StorageAccessAPIBaseBrowserTest(false) {} | 
 |  | 
 |   void SetUpCommandLine(base::CommandLine* command_line) override { | 
 |     StorageAccessAPIBaseBrowserTest::SetUpCommandLine(command_line); | 
 |     command_line->AppendSwitchASCII( | 
 |         network::switches::kUseFirstPartySet, | 
 |         base::StrCat({R"({"primary": "https://)", kHostA, | 
 |                       R"(", "associatedSites": ["https://)", kHostB, R"("])", | 
 |                       R"(, "serviceSites": ["https://)", kHostD, R"("]})"})); | 
 |   } | 
 |  | 
 |  protected: | 
 |   std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { | 
 |     return { | 
 |         {blink::features::kStorageAccessAPI, | 
 |          { | 
 |              { | 
 |                  blink::features::kStorageAccessAPIAutoGrantInFPS.name, | 
 |                  "true", | 
 |              }, | 
 |              { | 
 |                  blink::features::kStorageAccessAPIAutoDenyOutsideFPS.name, | 
 |                  "true", | 
 |              }, | 
 |              { | 
 |                  blink::features::kStorageAccessAPIImplicitGrantLimit.name, | 
 |                  "0", | 
 |              }, | 
 |          }}, | 
 |     }; | 
 |   } | 
 | }; | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, | 
 |                        Permission_AutograntedWithinFirstPartySet) { | 
 |   base::HistogramTester histogram_tester; | 
 |   // Note: kHostA and kHostB are considered same-party due to the use of | 
 |   // `network::switches::kUseFirstPartySet`. | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   content::FetchHistogramsFromChildProcesses(); | 
 |  | 
 |   EXPECT_THAT(histogram_tester.GetBucketCount( | 
 |                   kRequestOutcomeHistogram, | 
 |                   0 /*RequestOutcome::kGrantedByFirstPartySet*/), | 
 |               Gt(0)); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, | 
 |                        Permission_AutodeniedForServiceDomain) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |   base::HistogramTester histogram_tester; | 
 |  | 
 |   NavigateToPageWithFrame(kHostD); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostA)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostA), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   // The promise should be rejected; `khostD` is a service domain. | 
 |   EXPECT_FALSE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateFrameTo(EchoCookiesURL(kHostA)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostA), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   content::FetchHistogramsFromChildProcesses(); | 
 |   EXPECT_THAT(histogram_tester.GetBucketCount( | 
 |                   kRequestOutcomeHistogram, | 
 |                   5 /*RequestOutcome::kDeniedByPrerequisites*/), | 
 |               Gt(0)); | 
 |   // Ensure that the denied state is not exposed to developers, per the spec. | 
 |   EXPECT_EQ(QueryPermission(GetFrame()), "prompt"); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithFirstPartySetsBrowserTest, | 
 |                        Permission_AutodeniedOutsideFirstPartySet) { | 
 |   base::HistogramTester histogram_tester; | 
 |   // Note: kHostA and kHostC are considered cross-party, since kHostA's set does | 
 |   // not include kHostC. | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostC)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostC), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   // kHostC cannot request storage access. | 
 |   EXPECT_FALSE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateFrameTo(EchoCookiesURL(kHostC)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostC), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   content::FetchHistogramsFromChildProcesses(); | 
 |  | 
 |   EXPECT_THAT(histogram_tester.GetBucketCount( | 
 |                   kRequestOutcomeHistogram, | 
 |                   3 /*RequestOutcome::kDeniedByFirstPartySet*/), | 
 |               Gt(0)); | 
 | } | 
 |  | 
 | class StorageAccessAPIWithFirstPartySetsAndImplicitGrantsBrowserTest | 
 |     : public StorageAccessAPIBaseBrowserTest { | 
 |  public: | 
 |   StorageAccessAPIWithFirstPartySetsAndImplicitGrantsBrowserTest() | 
 |       : StorageAccessAPIBaseBrowserTest(false) {} | 
 |  | 
 |  protected: | 
 |   std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { | 
 |     return { | 
 |         {blink::features::kStorageAccessAPI, | 
 |          { | 
 |              { | 
 |                  blink::features::kStorageAccessAPIAutoGrantInFPS.name, | 
 |                  "true", | 
 |              }, | 
 |              { | 
 |                  blink::features::kStorageAccessAPIAutoDenyOutsideFPS.name, | 
 |                  "false", | 
 |              }, | 
 |              { | 
 |                  blink::features::kStorageAccessAPIImplicitGrantLimit.name, | 
 |                  "5", | 
 |              }, | 
 |          }}, | 
 |     }; | 
 |   } | 
 | }; | 
 |  | 
 | // Validate that when auto-deny-outside-fps is disabled (but auto-grant is | 
 | // enabled), implicit grants still work. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     StorageAccessAPIWithFirstPartySetsAndImplicitGrantsBrowserTest, | 
 |     ImplicitGrants) { | 
 |   // Note: kHostA and kHostC are considered cross-party, since kHostA's set does | 
 |   // not include kHostC. | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostC)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostC), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   // kHostC can request storage access, due to implicit grants. | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   NavigateToPageWithFrame(kHostB); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostC)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostC), NoCookiesWithContent()); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 | } | 
 |  | 
 | class StorageAccessAPIWithCHIPSBrowserTest | 
 |     : public StorageAccessAPIBaseBrowserTest { | 
 |  public: | 
 |   StorageAccessAPIWithCHIPSBrowserTest() | 
 |       : StorageAccessAPIBaseBrowserTest( | 
 |             /*is_storage_partitioned=*/false) {} | 
 |  | 
 |   std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { | 
 |     std::vector<base::test::FeatureRefAndParams> enabled = | 
 |         StorageAccessAPIBaseBrowserTest::GetEnabledFeatures(); | 
 |     enabled.push_back({net::features::kPartitionedCookies, {}}); | 
 |     return enabled; | 
 |   } | 
 | }; | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithCHIPSBrowserTest, | 
 |                        RequestStorageAccess_CoexistsWithCHIPS) { | 
 |   SetBlockThirdPartyCookies(true); | 
 |  | 
 |   SetPartitionedCookieInContext(/*top_level_host=*/kHostA, | 
 |                                 /*embedded_host=*/kHostB); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   EXPECT_EQ(ReadCookiesAndContent(GetFrame(), kHostB), | 
 |             CookieBundleWithContent("cross-site=b.test(partitioned)")); | 
 |   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame())); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), | 
 |             CookieBundle("cross-site=b.test; cross-site=b.test(partitioned)")); | 
 | } | 
 |  | 
 | class StorageAccessAPIEnterprisePolicyBrowserTest | 
 |     : public StorageAccessAPIBaseBrowserTest, | 
 |       public testing::WithParamInterface< | 
 |           /* (origin, content_setting, is_storage_partitioned) */ | 
 |           std::tuple<const char*, ContentSetting, bool>> { | 
 |  public: | 
 |   StorageAccessAPIEnterprisePolicyBrowserTest() | 
 |       : StorageAccessAPIBaseBrowserTest(std::get<2>(GetParam())) {} | 
 |  | 
 |   void SetUpInProcessBrowserTestFixture() override { | 
 |     policy::PolicyTest::SetUpInProcessBrowserTestFixture(); | 
 |     policy::PolicyMap policies; | 
 |     SetPolicy(&policies, | 
 |               policy::key::kDefaultThirdPartyStoragePartitioningSetting, | 
 |               base::Value(GetContentSetting())); | 
 |     base::Value::List origins; | 
 |     origins.Append(base::Value(GetContentOrigin())); | 
 |     SetPolicy(&policies, | 
 |               policy::key::kThirdPartyStoragePartitioningBlockedForOrigins, | 
 |               base::Value(std::move(origins))); | 
 |     UpdateProviderPolicy(policies); | 
 |   } | 
 |  | 
 |   bool ExpectPartitionedStorage() const { | 
 |     // We only expect storage to be partitioned if the base::Feature is enabled | 
 |     // and the default content setting isn't BLOCK and the origin block list | 
 |     // doesn't match a.test (paths are ignored) | 
 |     return IsStoragePartitioned() && | 
 |            GetContentSetting() != CONTENT_SETTING_BLOCK && | 
 |            GetContentOrigin() != kHostA && GetContentOrigin() != kOriginA && | 
 |            GetContentOrigin() != kUrlA; | 
 |   } | 
 |  | 
 |   // Derive a test name from parameter information. | 
 |   static std::string TestName(const ::testing::TestParamInfo<ParamType>& info) { | 
 |     const char* origin = std::get<0>(info.param); | 
 |     ContentSetting content_setting = std::get<1>(info.param); | 
 |     bool is_storage_partitioned = std::get<2>(info.param); | 
 |     return base::JoinString( | 
 |         { | 
 |             origin == kHostA            ? "kHostA" | 
 |             : origin == kOriginA        ? "kOriginA" | 
 |             : origin == kUrlA           ? "kUrlA" | 
 |             : origin == kHostASubdomain ? "kHostASubdomain" | 
 |             : origin == kHostB          ? "kHostB" | 
 |                                         : "empty", | 
 |             content_setting == CONTENT_SETTING_DEFAULT ? "DEFAULT" | 
 |             : content_setting == CONTENT_SETTING_ALLOW ? "ALLOW" | 
 |                                                        : "BLOCK", | 
 |             is_storage_partitioned ? "Partitioned" : "Unpartitioned", | 
 |         }, | 
 |         "_"); | 
 |   } | 
 |  | 
 |  private: | 
 |   ContentSetting GetContentSetting() const { return std::get<1>(GetParam()); } | 
 |   const char* GetContentOrigin() const { return std::get<0>(GetParam()); } | 
 | }; | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     /*no prefix*/, | 
 |     StorageAccessAPIEnterprisePolicyBrowserTest, | 
 |     testing::Combine( | 
 |         testing::Values(kHostA, kOriginA, kUrlA, kHostASubdomain, kHostB, ""), | 
 |         testing::Values(CONTENT_SETTING_DEFAULT, | 
 |                         CONTENT_SETTING_ALLOW, | 
 |                         CONTENT_SETTING_BLOCK), | 
 |         testing::Bool()), | 
 |     StorageAccessAPIEnterprisePolicyBrowserTest::TestName); | 
 |  | 
 | IN_PROC_BROWSER_TEST_P(StorageAccessAPIEnterprisePolicyBrowserTest, | 
 |                        PartitionedStorage) { | 
 |   // Navigate to Origin B, setup storage, and expect storage. | 
 |   NavigateToPage(kHostB, "/browsing_data/site_data.html"); | 
 |   storage::test::ExpectStorageForFrame(GetPrimaryMainFrame(), | 
 |                                        /*include_cookies=*/false, | 
 |                                        /*expected=*/false); | 
 |   storage::test::SetStorageForFrame(GetPrimaryMainFrame(), | 
 |                                     /*include_cookies=*/false); | 
 |   storage::test::ExpectStorageForFrame(GetPrimaryMainFrame(), | 
 |                                        /*include_cookies=*/false, | 
 |                                        /*expected=*/true); | 
 |  | 
 |   // Navigate to Origin A w/ Frame B and expect storage if not partitioned. | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(kHostB, "/browsing_data/site_data.html"); | 
 |   storage::test::ExpectStorageForFrame(GetFrame(), /*include_cookies=*/false, | 
 |                                        !ExpectPartitionedStorage()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        EnsureOnePromptDenialSuffices) { | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::DENY_ALL); | 
 |  | 
 |   { | 
 |     // The first request should show a prompt, which is denied. | 
 |     permissions::PermissionRequestObserver pre_observer( | 
 |         browser()->tab_strip_model()->GetActiveWebContents()); | 
 |     ASSERT_FALSE(pre_observer.request_shown()); | 
 |     ASSERT_FALSE( | 
 |         storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |     ASSERT_TRUE(pre_observer.request_shown()); | 
 |     ASSERT_EQ(prompt_factory()->TotalRequestCount(), 1); | 
 |   } | 
 |   { | 
 |     // However, subsequent requests should not re-prompt. | 
 |     permissions::PermissionRequestObserver post_observer( | 
 |         browser()->tab_strip_model()->GetActiveWebContents()); | 
 |     // Validate that there's no stale data. | 
 |     ASSERT_FALSE(post_observer.request_shown()); | 
 |  | 
 |     EXPECT_FALSE( | 
 |         storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |     // Verify no prompt was shown after the first one was already denied. | 
 |     EXPECT_FALSE(post_observer.request_shown()); | 
 |     EXPECT_EQ(prompt_factory()->TotalRequestCount(), 1); | 
 |   } | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIBrowserTest, | 
 |                        DismissalAllowsFuturePrompts) { | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::DISMISS); | 
 |  | 
 |   { | 
 |     // The first request should show a prompt, which is dismissed. | 
 |     permissions::PermissionRequestObserver observer( | 
 |         browser()->tab_strip_model()->GetActiveWebContents()); | 
 |     EXPECT_FALSE( | 
 |         content::ExecJs(GetFrame(), "document.requestStorageAccess()")); | 
 |     ASSERT_TRUE(observer.request_shown()); | 
 |     EXPECT_EQ(false, | 
 |               content::EvalJs(GetFrame(), "document.hasStorageAccess()")); | 
 |     ASSERT_EQ(prompt_factory()->TotalRequestCount(), 1); | 
 |   } | 
 |  | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::ACCEPT_ALL); | 
 |  | 
 |   { | 
 |     // However, subsequent requests should be able to re-prompt. | 
 |     permissions::PermissionRequestObserver observer( | 
 |         browser()->tab_strip_model()->GetActiveWebContents()); | 
 |  | 
 |     EXPECT_TRUE( | 
 |         storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |  | 
 |     // Verify a prompt was shown. | 
 |     EXPECT_TRUE(observer.request_shown()); | 
 |     EXPECT_EQ(prompt_factory()->TotalRequestCount(), 2); | 
 |   } | 
 | } | 
 |  | 
 | class StorageAccessAPIWithImplicitGrantsBrowserTest | 
 |     : public StorageAccessAPIBaseBrowserTest { | 
 |  public: | 
 |   StorageAccessAPIWithImplicitGrantsBrowserTest() | 
 |       : StorageAccessAPIBaseBrowserTest(/*is_storage_partitioned=*/false) {} | 
 |  | 
 |  protected: | 
 |   std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override { | 
 |     return { | 
 |         {blink::features::kStorageAccessAPI, | 
 |          { | 
 |              { | 
 |                  blink::features::kStorageAccessAPIAutoGrantInFPS.name, | 
 |                  "false", | 
 |              }, | 
 |              { | 
 |                  blink::features::kStorageAccessAPIAutoDenyOutsideFPS.name, | 
 |                  "false", | 
 |              }, | 
 |              { | 
 |                  // We use a low, but nonzero, number for the limit so that we | 
 |                  // can verify that implicit grants are usable, and verify what | 
 |                  // happens when we exceed the limit. | 
 |                  blink::features::kStorageAccessAPIImplicitGrantLimit.name, | 
 |                  "2", | 
 |              }, | 
 |          }}, | 
 |     }; | 
 |   } | 
 | }; | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(StorageAccessAPIWithImplicitGrantsBrowserTest, | 
 |                        ImplicitGrantsAllowAccess) { | 
 |   base::HistogramTester histogram_tester; | 
 |   SetBlockThirdPartyCookies(true); | 
 |   prompt_factory()->set_response_type( | 
 |       permissions::PermissionRequestManager::DENY_ALL); | 
 |  | 
 |   NavigateToPageWithFrame(kHostA); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |   // Access should be allowed by the first implicit grant. | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   histogram_tester.ExpectUniqueSample(kGrantIsImplicitHistogram, | 
 |                                       /*sample=*/true, 1); | 
 |  | 
 |   NavigateToPageWithFrame(kHostC); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |   // Access should be allowed by the second implicit grant. | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   histogram_tester.ExpectUniqueSample(kGrantIsImplicitHistogram, | 
 |                                       /*sample=*/true, 2); | 
 |  | 
 |   NavigateToPageWithFrame(kHostD); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostB)); | 
 |   // Access is denied since kHostB has already exhausted both of its implicit | 
 |   // grants, and we've set the prompt_factory to always deny. | 
 |   EXPECT_FALSE(content::ExecJs(GetFrame(), "document.requestStorageAccess()")); | 
 |   histogram_tester.ExpectBucketCount( | 
 |       kRequestOutcomeHistogram, /*sample=*/RequestOutcome::kDeniedByUser, 1); | 
 |  | 
 |   NavigateToPageWithFrame(kHostB); | 
 |   NavigateFrameTo(EchoCookiesURL(kHostA)); | 
 |   // Other embeds can still obtain access via their own implicit grants. | 
 |   EXPECT_TRUE(storage::test::RequestAndCheckStorageAccessForFrame(GetFrame())); | 
 |   histogram_tester.ExpectUniqueSample(kGrantIsImplicitHistogram, | 
 |                                       /*sample=*/true, 3); | 
 | } | 
 |  | 
 | }  // namespace |