diff --git a/browser/ephemeral_storage/BUILD.gn b/browser/ephemeral_storage/BUILD.gn index 041831746aad..e0863757e859 100644 --- a/browser/ephemeral_storage/BUILD.gn +++ b/browser/ephemeral_storage/BUILD.gn @@ -1,28 +1,14 @@ -source_set("ephemeral_storage") { - sources = [ - "ephemeral_storage_tab_helper.cc", - "ephemeral_storage_tab_helper.h", - ] - - deps = [ - "//base", - "//chrome/browser/ui", - "//content/public/browser", - "//net", - "//third_party/blink/public/common", - ] -} - if (!is_android) { source_set("ephemeral_storage_tests") { testonly = true sources = [ + "ephemeral_storage_1p_browsertest.cc", "ephemeral_storage_browsertest.cc", + "ephemeral_storage_browsertest.h", "ephemeral_storage_qa_browsertest.cc", ] defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] deps = [ - ":ephemeral_storage", "//base", "//brave/components/brave_shields/browser:browser", "//brave/components/brave_shields/common:common", diff --git a/browser/ephemeral_storage/ephemeral_storage_1p_browsertest.cc b/browser/ephemeral_storage/ephemeral_storage_1p_browsertest.cc new file mode 100644 index 000000000000..7f1cfa765ef6 --- /dev/null +++ b/browser/ephemeral_storage/ephemeral_storage_1p_browsertest.cc @@ -0,0 +1,364 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/ephemeral_storage/ephemeral_storage_browsertest.h" + +#include "chrome/browser/content_settings/cookie_settings_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "content/public/test/browser_test.h" +#include "net/base/features.h" + +using content::RenderFrameHost; +using content::WebContents; + +class EphemeralStorage1pBrowserTest : public EphemeralStorageBrowserTest { + public: + EphemeralStorage1pBrowserTest() { + scoped_feature_list_.InitAndEnableFeature( + net::features::kBraveFirstPartyEphemeralStorage); + } + ~EphemeralStorage1pBrowserTest() override {} + + void SetCookieSetting(const GURL& url, ContentSetting content_setting) { + auto cookie_settings = + CookieSettingsFactory::GetForProfile(browser()->profile()); + cookie_settings->SetCookieSetting(url, content_setting); + } + + // Helper to load easy-to-use Indexed DB API. + void LoadIndexedDbHelper(RenderFrameHost* host) { + const char kLoadIndexMinScript[] = + "new Promise((resolve) => {" + " const script = document.createElement('script');" + " script.onload = () => {" + " resolve(true);" + " };" + " script.onerror = () => {" + " resolve(false);" + " };" + " script.src = '/ephemeral-storage/static/js/libs/index-min.js';" + " document.body.appendChild(script);" + "});"; + + ASSERT_EQ(true, content::EvalJs(host, kLoadIndexMinScript)); + } + + bool SetIDBValue(RenderFrameHost* host) { + LoadIndexedDbHelper(host); + content::EvalJsResult eval_js_result = content::EvalJs( + host, "(async () => { await window.idbKeyval.set('a', 'a'); })()"); + return eval_js_result.error.empty(); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(EphemeralStorage1pBrowserTest, FirstPartyIsEphemeral) { + SetCookieSetting(a_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + + WebContents* first_party_tab = LoadURLInNewTab(a_site_ephemeral_storage_url_); + + // We set a value in the page where all the frames are first-party. + SetValuesInFrames(first_party_tab, "a.com", "from=a.com"); + + { + ValuesFromFrames first_party_values = GetValuesFromFrames(first_party_tab); + EXPECT_EQ("a.com", first_party_values.main_frame.local_storage); + EXPECT_EQ("a.com", first_party_values.iframe_1.local_storage); + EXPECT_EQ("a.com", first_party_values.iframe_2.local_storage); + + EXPECT_EQ("a.com", first_party_values.main_frame.session_storage); + EXPECT_EQ("a.com", first_party_values.iframe_1.session_storage); + EXPECT_EQ("a.com", first_party_values.iframe_2.session_storage); + + EXPECT_EQ("from=a.com", first_party_values.main_frame.cookies); + EXPECT_EQ("from=a.com", first_party_values.iframe_1.cookies); + EXPECT_EQ("from=a.com", first_party_values.iframe_2.cookies); + } + + // After keepalive values should be cleared. + ui_test_utils::NavigateToURL(browser(), b_site_ephemeral_storage_url_); + WaitForCleanupAfterKeepAlive(); + ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_); + + ExpectValuesFromFramesAreEmpty(FROM_HERE, + GetValuesFromFrames(first_party_tab)); +} + +IN_PROC_BROWSER_TEST_F(EphemeralStorage1pBrowserTest, + StorageIsPartitionedAndCleared) { + SetCookieSetting(a_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + SetCookieSetting(b_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + SetCookieSetting(c_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + + WebContents* first_party_tab = LoadURLInNewTab(b_site_ephemeral_storage_url_); + WebContents* site_a_tab1 = LoadURLInNewTab(a_site_ephemeral_storage_url_); + WebContents* site_a_tab2 = LoadURLInNewTab(a_site_ephemeral_storage_url_); + WebContents* site_c_tab = LoadURLInNewTab(c_site_ephemeral_storage_url_); + + EXPECT_EQ(browser()->tab_strip_model()->count(), 5); + + // We set a value in the page where all the frames are first-party. + SetValuesInFrames(first_party_tab, "b.com - first party", "from=b.com"); + + // The page this tab is loaded via a.com and has two b.com third-party + // iframes. The third-party iframes should have ephemeral storage. That means + // that their values should be shared by third-party b.com iframes loaded from + // a.com. + SetValuesInFrames(site_a_tab1, "a.com", "from=a.com"); + ValuesFromFrames site_a_tab1_values = GetValuesFromFrames(site_a_tab1); + EXPECT_EQ("a.com", site_a_tab1_values.main_frame.local_storage); + EXPECT_EQ("a.com", site_a_tab1_values.iframe_1.local_storage); + EXPECT_EQ("a.com", site_a_tab1_values.iframe_2.local_storage); + + EXPECT_EQ("a.com", site_a_tab1_values.main_frame.session_storage); + EXPECT_EQ("a.com", site_a_tab1_values.iframe_1.session_storage); + EXPECT_EQ("a.com", site_a_tab1_values.iframe_2.session_storage); + + EXPECT_EQ("from=a.com", site_a_tab1_values.main_frame.cookies); + EXPECT_EQ("from=a.com", site_a_tab1_values.iframe_1.cookies); + EXPECT_EQ("from=a.com", site_a_tab1_values.iframe_2.cookies); + + // The second tab is loaded on the same domain, so should see the same + // storage for the third-party iframes. + ValuesFromFrames site_a_tab2_values = GetValuesFromFrames(site_a_tab2); + EXPECT_EQ("a.com", site_a_tab2_values.main_frame.local_storage); + EXPECT_EQ("a.com", site_a_tab2_values.iframe_1.local_storage); + EXPECT_EQ("a.com", site_a_tab2_values.iframe_2.local_storage); + + EXPECT_EQ(nullptr, site_a_tab2_values.main_frame.session_storage); + EXPECT_EQ(nullptr, site_a_tab2_values.iframe_1.session_storage); + EXPECT_EQ(nullptr, site_a_tab2_values.iframe_2.session_storage); + + EXPECT_EQ("from=a.com", site_a_tab2_values.main_frame.cookies); + EXPECT_EQ("from=a.com", site_a_tab2_values.iframe_1.cookies); + EXPECT_EQ("from=a.com", site_a_tab2_values.iframe_2.cookies); + + // The storage in the first-party iframes should still reflect the + // original value that was written in the non-ephemeral storage area. + ValuesFromFrames first_party_values = GetValuesFromFrames(first_party_tab); + EXPECT_EQ("b.com - first party", first_party_values.main_frame.local_storage); + EXPECT_EQ("b.com - first party", first_party_values.iframe_1.local_storage); + EXPECT_EQ("b.com - first party", first_party_values.iframe_2.local_storage); + + EXPECT_EQ("b.com - first party", + first_party_values.main_frame.session_storage); + EXPECT_EQ("b.com - first party", first_party_values.iframe_1.session_storage); + EXPECT_EQ("b.com - first party", first_party_values.iframe_2.session_storage); + + EXPECT_EQ("from=b.com", first_party_values.main_frame.cookies); + EXPECT_EQ("from=b.com", first_party_values.iframe_1.cookies); + EXPECT_EQ("from=b.com", first_party_values.iframe_2.cookies); + + // Even though this page loads b.com iframes as third-party iframes, the TLD + // differs, so it should get an entirely different ephemeral storage area. + ValuesFromFrames site_c_tab_values = GetValuesFromFrames(site_c_tab); + ExpectValuesFromFramesAreEmpty(FROM_HERE, site_c_tab_values); + + // Close 4 tabs. + for (int i = 0; i < 4; ++i) { + browser()->tab_strip_model()->CloseWebContentsAt(1, + TabStripModel::CLOSE_NONE); + } + + WaitForCleanupAfterKeepAlive(); + + ExpectValuesFromFramesAreEmpty( + FROM_HERE, + GetValuesFromFrames(LoadURLInNewTab(a_site_ephemeral_storage_url_))); + ExpectValuesFromFramesAreEmpty( + FROM_HERE, + GetValuesFromFrames(LoadURLInNewTab(b_site_ephemeral_storage_url_))); + ExpectValuesFromFramesAreEmpty( + FROM_HERE, + GetValuesFromFrames(LoadURLInNewTab(c_site_ephemeral_storage_url_))); +} + +IN_PROC_BROWSER_TEST_F(EphemeralStorage1pBrowserTest, + IndexedDbUnavailableInES) { + SetCookieSetting(a_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + SetCookieSetting(b_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + + WebContents* site_a = LoadURLInNewTab(a_site_ephemeral_storage_url_); + WebContents* site_b = LoadURLInNewTab(b_site_ephemeral_storage_url_); + + // Main frame and 1p frame. + EXPECT_FALSE(SetIDBValue(site_a->GetMainFrame())); + EXPECT_FALSE(SetIDBValue(content::ChildFrameAt(site_a->GetMainFrame(), 2))); + // 3p frames. + EXPECT_FALSE(SetIDBValue(content::ChildFrameAt(site_a->GetMainFrame(), 0))); + EXPECT_FALSE(SetIDBValue(content::ChildFrameAt(site_a->GetMainFrame(), 1))); + + // 3p frame. + EXPECT_FALSE(SetIDBValue(content::ChildFrameAt(site_b->GetMainFrame(), 2))); +} + +IN_PROC_BROWSER_TEST_F(EphemeralStorage1pBrowserTest, + LocalStorageWorksBetweenFrames) { + SetCookieSetting(b_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + + WebContents* site_b_tab1 = LoadURLInNewTab(b_site_ephemeral_storage_url_); + WebContents* site_b_tab2 = LoadURLInNewTab(b_site_ephemeral_storage_url_); + + // Set values in the first tab. + SetValuesInFrames(site_b_tab1, "b.com", "from=b.com"); + + // Expect values are seen by the second tab in all frames. + { + ValuesFromFrames first_party_values = GetValuesFromFrames(site_b_tab2); + EXPECT_EQ("b.com", first_party_values.main_frame.local_storage); + EXPECT_EQ("b.com", first_party_values.iframe_1.local_storage); + EXPECT_EQ("b.com", first_party_values.iframe_2.local_storage); + + EXPECT_EQ(nullptr, first_party_values.main_frame.session_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_1.session_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_2.session_storage); + + EXPECT_EQ("from=b.com", first_party_values.main_frame.cookies); + EXPECT_EQ("from=b.com", first_party_values.iframe_1.cookies); + EXPECT_EQ("from=b.com", first_party_values.iframe_2.cookies); + } + + // Update values in the first tab. + SetValuesInFrames(site_b_tab1, "b2.com", "from=b.com"); + + { + ValuesFromFrames first_party_values = GetValuesFromFrames(site_b_tab2); + EXPECT_EQ("b2.com", first_party_values.main_frame.local_storage); + EXPECT_EQ("b2.com", first_party_values.iframe_1.local_storage); + EXPECT_EQ("b2.com", first_party_values.iframe_2.local_storage); + + EXPECT_EQ(nullptr, first_party_values.main_frame.session_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_1.session_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_2.session_storage); + + EXPECT_EQ("from=b.com", first_party_values.main_frame.cookies); + EXPECT_EQ("from=b.com", first_party_values.iframe_1.cookies); + EXPECT_EQ("from=b.com", first_party_values.iframe_2.cookies); + } +} + +IN_PROC_BROWSER_TEST_F(EphemeralStorage1pBrowserTest, + NavigationCookiesAreCleared) { + SetCookieSetting(a_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + SetCookieSetting(b_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + + GURL a_site_set_cookie_url = https_server_.GetURL( + "a.com", "/set-cookie?name=acom;path=/;SameSite=None;Secure"); + GURL b_site_set_cookie_url = https_server_.GetURL( + "b.com", "/set-cookie?name=bcom;path=/;SameSite=None;Secure"); + + WebContents* site_a_set_cookies = LoadURLInNewTab(a_site_set_cookie_url); + WebContents* site_b_set_cookies = LoadURLInNewTab(b_site_set_cookie_url); + WebContents* site_a = LoadURLInNewTab(a_site_ephemeral_storage_url_); + WebContents* site_b = LoadURLInNewTab(b_site_ephemeral_storage_url_); + + // Default cookie storage request should return empty results. + EXPECT_TRUE(content::GetCookies(browser()->profile(), GURL("https://a.com/")) + .empty()); + EXPECT_TRUE(content::GetCookies(browser()->profile(), GURL("https://b.com/")) + .empty()); + + // JS cookie request should return valid results. + EXPECT_EQ("name=acom", GetCookiesInFrame(site_a_set_cookies->GetMainFrame())); + EXPECT_EQ("name=bcom", GetCookiesInFrame(site_b_set_cookies->GetMainFrame())); + EXPECT_EQ("name=acom", GetCookiesInFrame(site_a->GetMainFrame())); + + // The third-party iframe should not have the b.com cookie that was set on the + // main frame. + RenderFrameHost* main_frame = site_a->GetMainFrame(); + RenderFrameHost* iframe_a = content::ChildFrameAt(main_frame, 0); + RenderFrameHost* iframe_b = content::ChildFrameAt(main_frame, 1); + EXPECT_EQ("", GetCookiesInFrame(iframe_a)); + EXPECT_EQ("", GetCookiesInFrame(iframe_b)); + + // Setting the cookie directly on the third-party iframe should only set the + // cookie in the ephemeral storage area for that frame. + GURL b_site_set_ephemeral_cookie_url = https_server_.GetURL( + "b.com", "/set-cookie?name=bcom_ephemeral;path=/;SameSite=None;Secure"); + NavigateIframeToURL(site_a, "third_party_iframe_a", + b_site_set_ephemeral_cookie_url); + ASSERT_EQ("name=bcom_ephemeral", GetCookiesInFrame(iframe_a)); + ASSERT_EQ("name=bcom_ephemeral", GetCookiesInFrame(iframe_b)); + + // The cookie set in the ephemeral area should not visible in the main + // cookie storage. + EXPECT_TRUE(content::GetCookies(browser()->profile(), GURL("https://b.com/")) + .empty()); + EXPECT_EQ("name=bcom", GetCookiesInFrame(site_b->GetMainFrame())); + + // Navigating to a new TLD should clear all ephemeral cookies after keep-alive + // timeout. + ASSERT_TRUE(content::NavigateToURL(site_a_set_cookies, + c_site_ephemeral_storage_url_)); + ASSERT_TRUE(content::NavigateToURL(site_b_set_cookies, + c_site_ephemeral_storage_url_)); + ASSERT_TRUE(content::NavigateToURL(site_a, c_site_ephemeral_storage_url_)); + ASSERT_TRUE(content::NavigateToURL(site_b, c_site_ephemeral_storage_url_)); + WaitForCleanupAfterKeepAlive(); + ASSERT_TRUE(content::NavigateToURL(site_a, a_site_ephemeral_storage_url_)); + ASSERT_TRUE(content::NavigateToURL(site_b, b_site_ephemeral_storage_url_)); + + ValuesFromFrames values_site_a = GetValuesFromFrames(site_a); + EXPECT_EQ("", values_site_a.main_frame.cookies); + EXPECT_EQ("", values_site_a.iframe_1.cookies); + EXPECT_EQ("", values_site_a.iframe_2.cookies); + + ValuesFromFrames values_site_b = GetValuesFromFrames(site_b); + EXPECT_EQ("", values_site_b.main_frame.cookies); + EXPECT_EQ("", values_site_b.iframe_1.cookies); + EXPECT_EQ("", values_site_b.iframe_2.cookies); +} + +IN_PROC_BROWSER_TEST_F(EphemeralStorage1pBrowserTest, + FirstPartyNestedInThirdParty) { + SetCookieSetting(a_site_ephemeral_storage_url_, CONTENT_SETTING_SESSION_ONLY); + + auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); + + GURL a_site_set_cookie_url = https_server_.GetURL( + "a.com", "/set-cookie?name=acom;path=/;SameSite=None;Secure"); + ui_test_utils::NavigateToURL(browser(), a_site_set_cookie_url); + ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_); + + RenderFrameHost* site_a_main_frame = web_contents->GetMainFrame(); + RenderFrameHost* nested_frames_tab = + content::ChildFrameAt(site_a_main_frame, 3); + ASSERT_NE(nested_frames_tab, nullptr); + RenderFrameHost* first_party_nested_acom = + content::ChildFrameAt(nested_frames_tab, 2); + ASSERT_NE(first_party_nested_acom, nullptr); + + WebContents* site_b_tab = LoadURLInNewTab(b_site_ephemeral_storage_url_); + RenderFrameHost* site_b_main_frame = site_b_tab->GetMainFrame(); + RenderFrameHost* third_party_nested_acom = + content::ChildFrameAt(site_b_main_frame, 2); + ASSERT_NE(first_party_nested_acom, nullptr); + + ASSERT_EQ("name=acom", GetCookiesInFrame(site_a_main_frame)); + ASSERT_EQ("name=acom", GetCookiesInFrame(first_party_nested_acom)); + ASSERT_EQ("", GetCookiesInFrame(third_party_nested_acom)); + + SetValuesInFrame(site_a_main_frame, "first-party-a.com", + "name=first-party-a.com"); + SetValuesInFrame(third_party_nested_acom, "third-party-a.com", + "name=third-party-a.com"); + + ValuesFromFrame first_party_values = + GetValuesFromFrame(first_party_nested_acom); + EXPECT_EQ("first-party-a.com", first_party_values.local_storage); + EXPECT_EQ("first-party-a.com", first_party_values.session_storage); + EXPECT_EQ("name=first-party-a.com", first_party_values.cookies); + + ValuesFromFrame third_party_values = + GetValuesFromFrame(third_party_nested_acom); + EXPECT_EQ("third-party-a.com", third_party_values.local_storage); + EXPECT_EQ("third-party-a.com", third_party_values.session_storage); + EXPECT_EQ("name=third-party-a.com", third_party_values.cookies); +} diff --git a/browser/ephemeral_storage/ephemeral_storage_browsertest.cc b/browser/ephemeral_storage/ephemeral_storage_browsertest.cc index bc9b9980bed3..4fb67bccb1fb 100644 --- a/browser/ephemeral_storage/ephemeral_storage_browsertest.cc +++ b/browser/ephemeral_storage/ephemeral_storage_browsertest.cc @@ -3,9 +3,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include +#include "brave/browser/ephemeral_storage/ephemeral_storage_browsertest.h" + +#include -#include "base/memory/weak_ptr.h" #include "base/path_service.h" #include "base/strings/strcat.h" #include "base/strings/stringprintf.h" @@ -18,26 +19,23 @@ #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/network_session_configurator/common/network_switches.h" #include "components/prefs/pref_service.h" #include "content/public/browser/notification_types.h" -#include "content/public/browser/render_frame_host.h" #include "content/public/browser/storage_partition.h" -#include "content/public/browser/web_contents.h" +#include "content/public/common/content_switches.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_navigation_observer.h" #include "net/base/features.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/default_handlers.h" -#include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" #include "net/test/embedded_test_server/request_handler_util.h" -#include "url/gurl.h" using content::RenderFrameHost; using content::WebContents; +using HttpRequestMonitor = EphemeralStorageBrowserTest::HttpRequestMonitor; using net::test_server::BasicHttpResponse; using net::test_server::EmbeddedTestServer; using net::test_server::HttpRequest; @@ -47,75 +45,20 @@ namespace { const int kKeepAliveInterval = 2; -enum StorageType { Session, Local }; - -class HttpRequestMonitor : public base::SupportsWeakPtr { - public: - void OnHttpRequest(const HttpRequest& request) { - http_requests_.push_back(request); - } - - bool HasHttpRequestWithCookie(const GURL& url, - const std::string& cookie_value) const { - for (const auto& http_request : http_requests_) { - if (GetHttpRequestURL(http_request) != url) - continue; - for (const auto& header : http_request.headers) { - if (header.first == net::HttpRequestHeaders::kCookie && - header.second == cookie_value) { - return true; - } - } - } - return false; - } - - void Clear() { http_requests_.clear(); } - - private: - GURL GetHttpRequestURL(const HttpRequest& http_request) const { - return GURL(base::StrCat( - {http_request.base_url.scheme_piece(), "://", - http_request.headers.at(net::HttpRequestHeaders::kHost).c_str(), - http_request.relative_url.c_str()})); - } - - std::vector http_requests_; -}; - -const char* ToString(StorageType storage_type) { +const char* ToString(EphemeralStorageBrowserTest::StorageType storage_type) { switch (storage_type) { - case StorageType::Session: + case EphemeralStorageBrowserTest::StorageType::Session: return "session"; - case StorageType::Local: + case EphemeralStorageBrowserTest::StorageType::Local: return "local"; } } -void SetStorageValueInFrame(RenderFrameHost* host, - std::string value, - StorageType storage_type) { - std::string script = - base::StringPrintf("%sStorage.setItem('storage_key', '%s');", - ToString(storage_type), value.c_str()); - ASSERT_TRUE(content::ExecuteScript(host, script)); -} - -content::EvalJsResult GetStorageValueInFrame(RenderFrameHost* host, - StorageType storage_type) { - std::string script = base::StringPrintf("%sStorage.getItem('storage_key');", - ToString(storage_type)); - return content::EvalJs(host, script); -} - -void SetCookieInFrame(RenderFrameHost* host, std::string cookie) { - std::string script = base::StringPrintf( - "document.cookie='%s; path=/; SameSite=None; Secure'", cookie.c_str()); - ASSERT_TRUE(content::ExecuteScript(host, script)); -} - -content::EvalJsResult GetCookiesInFrame(RenderFrameHost* host) { - return content::EvalJs(host, "document.cookie"); +GURL GetHttpRequestURL(const HttpRequest& http_request) { + return GURL(base::StrCat( + {http_request.base_url.scheme_piece(), "://", + http_request.headers.at(net::HttpRequestHeaders::kHost).c_str(), + http_request.relative_url.c_str()})); } std::unique_ptr HandleFileRequestWithNetworkCookies( @@ -145,120 +88,179 @@ std::unique_ptr HandleFileRequestWithNetworkCookies( } // namespace -class EphemeralStorageBrowserTest : public InProcessBrowserTest { - public: - EphemeralStorageBrowserTest() - : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {} +HttpRequestMonitor::HttpRequestMonitor() = default; +HttpRequestMonitor::~HttpRequestMonitor() = + default; - void SetUpOnMainThread() override { - InProcessBrowserTest::SetUpOnMainThread(); +void HttpRequestMonitor::OnHttpRequest( + const HttpRequest& request) { + http_requests_.push_back(request); +} - host_resolver()->AddRule("*", "127.0.0.1"); +bool HttpRequestMonitor::HasHttpRequestWithCookie( + const GURL& url, + const std::string& cookie_value) const { + for (const auto& http_request : http_requests_) { + if (GetHttpRequestURL(http_request) != url) + continue; + for (const auto& header : http_request.headers) { + if (header.first == net::HttpRequestHeaders::kCookie && + header.second == cookie_value) { + return true; + } + } + } + return false; +} - brave::RegisterPathProvider(); - SetUpHttpsServer(); +EphemeralStorageBrowserTest::EphemeralStorageBrowserTest() + : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {} - a_site_ephemeral_storage_url_ = - https_server_.GetURL("a.com", "/ephemeral_storage.html"); - b_site_ephemeral_storage_url_ = - https_server_.GetURL("b.com", "/ephemeral_storage.html"); - c_site_ephemeral_storage_url_ = - https_server_.GetURL("c.com", "/ephemeral_storage.html"); - a_site_ephemeral_storage_with_network_cookies_url_ = https_server_.GetURL( - "a.com", "/ephemeral_storage_with_network_cookies.html"); +EphemeralStorageBrowserTest::~EphemeralStorageBrowserTest() = default; - ephemeral_storage::EphemeralStorageTabHelper:: - SetKeepAliveTimeDelayForTesting( - base::TimeDelta::FromSeconds(kKeepAliveInterval)); - } +void EphemeralStorageBrowserTest::SetUpOnMainThread() { + InProcessBrowserTest::SetUpOnMainThread(); - void SetUpHttpsServer() { - base::FilePath test_data_dir; - base::PathService::Get(brave::DIR_TEST_DATA, &test_data_dir); - - https_server_.RegisterDefaultHandler( - base::BindRepeating(&HandleFileRequestWithNetworkCookies, - base::SequencedTaskRunnerHandle::Get(), - http_request_monitor_.AsWeakPtr(), test_data_dir)); - https_server_.AddDefaultHandlers(GetChromeTestDataDir()); - content::SetupCrossSiteRedirector(&https_server_); - ASSERT_TRUE(https_server_.Start()); - } + host_resolver()->AddRule("*", "127.0.0.1"); - void SetUpCommandLine(base::CommandLine* command_line) override { - InProcessBrowserTest::SetUpCommandLine(command_line); + brave::RegisterPathProvider(); + SetUpHttpsServer(); - // This is needed to load pages from "domain.com" without an interstitial. - command_line->AppendSwitch(switches::kIgnoreCertificateErrors); - } + a_site_ephemeral_storage_url_ = + https_server_.GetURL("a.com", "/ephemeral_storage.html"); + b_site_ephemeral_storage_url_ = + https_server_.GetURL("b.com", "/ephemeral_storage.html"); + c_site_ephemeral_storage_url_ = + https_server_.GetURL("c.com", "/ephemeral_storage.html"); + a_site_ephemeral_storage_with_network_cookies_url_ = https_server_.GetURL( + "a.com", "/ephemeral_storage_with_network_cookies.html"); - void SetValuesInFrame(RenderFrameHost* frame, - std::string storage_value, - std::string cookie_value) { - SetStorageValueInFrame(frame, storage_value, StorageType::Local); - SetStorageValueInFrame(frame, storage_value, StorageType::Session); - SetCookieInFrame(frame, cookie_value); - } + ephemeral_storage::EphemeralStorageTabHelper::SetKeepAliveTimeDelayForTesting( + base::TimeDelta::FromSeconds(kKeepAliveInterval)); +} - void SetValuesInFrames(WebContents* web_contents, - std::string storage_value, - std::string cookie_value) { - RenderFrameHost* main = web_contents->GetMainFrame(); - SetValuesInFrame(main, storage_value, cookie_value); - SetValuesInFrame(content::ChildFrameAt(main, 0), storage_value, - cookie_value); - SetValuesInFrame(content::ChildFrameAt(main, 1), storage_value, - cookie_value); - } +void EphemeralStorageBrowserTest::SetUpHttpsServer() { + base::FilePath test_data_dir; + base::PathService::Get(brave::DIR_TEST_DATA, &test_data_dir); + + https_server_.RegisterDefaultHandler( + base::BindRepeating(&HandleFileRequestWithNetworkCookies, + base::SequencedTaskRunnerHandle::Get(), + http_request_monitor_.AsWeakPtr(), test_data_dir)); + https_server_.AddDefaultHandlers(GetChromeTestDataDir()); + content::SetupCrossSiteRedirector(&https_server_); + ASSERT_TRUE(https_server_.Start()); +} - struct ValuesFromFrame { - content::EvalJsResult local_storage; - content::EvalJsResult session_storage; - content::EvalJsResult cookies; - }; +void EphemeralStorageBrowserTest::SetUpCommandLine( + base::CommandLine* command_line) { + InProcessBrowserTest::SetUpCommandLine(command_line); - ValuesFromFrame GetValuesFromFrame(RenderFrameHost* frame) { - return { - GetStorageValueInFrame(frame, StorageType::Local), - GetStorageValueInFrame(frame, StorageType::Session), - GetCookiesInFrame(frame), - }; - } + // This is needed to load pages from "domain.com" without an interstitial. + command_line->AppendSwitch(switches::kIgnoreCertificateErrors); +} + +void EphemeralStorageBrowserTest::SetValuesInFrame(RenderFrameHost* frame, + std::string storage_value, + std::string cookie_value) { + SetStorageValueInFrame(frame, storage_value, StorageType::Local); + SetStorageValueInFrame(frame, storage_value, StorageType::Session); + SetCookieInFrame(frame, cookie_value); +} + +void EphemeralStorageBrowserTest::SetValuesInFrames(WebContents* web_contents, + std::string storage_value, + std::string cookie_value) { + RenderFrameHost* main = web_contents->GetMainFrame(); + SetValuesInFrame(main, storage_value, cookie_value); + SetValuesInFrame(content::ChildFrameAt(main, 0), storage_value, cookie_value); + SetValuesInFrame(content::ChildFrameAt(main, 1), storage_value, cookie_value); +} - struct ValuesFromFrames { - ValuesFromFrame main_frame; - ValuesFromFrame iframe_1; - ValuesFromFrame iframe_2; +EphemeralStorageBrowserTest::ValuesFromFrame +EphemeralStorageBrowserTest::GetValuesFromFrame(RenderFrameHost* frame) { + return { + GetStorageValueInFrame(frame, StorageType::Local), + GetStorageValueInFrame(frame, StorageType::Session), + GetCookiesInFrame(frame), }; +} - ValuesFromFrames GetValuesFromFrames(WebContents* web_contents) { - RenderFrameHost* main_frame = web_contents->GetMainFrame(); - return { - GetValuesFromFrame(main_frame), - GetValuesFromFrame(content::ChildFrameAt(main_frame, 0)), - GetValuesFromFrame(content::ChildFrameAt(main_frame, 1)), - }; - } +EphemeralStorageBrowserTest::ValuesFromFrames +EphemeralStorageBrowserTest::GetValuesFromFrames(WebContents* web_contents) { + RenderFrameHost* main_frame = web_contents->GetMainFrame(); + return { + GetValuesFromFrame(main_frame), + GetValuesFromFrame(content::ChildFrameAt(main_frame, 0)), + GetValuesFromFrame(content::ChildFrameAt(main_frame, 1)), + }; +} - WebContents* LoadURLInNewTab(GURL url) { - ui_test_utils::AllBrowserTabAddedWaiter add_tab; - ui_test_utils::NavigateToURLWithDisposition( - browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, - ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); - return add_tab.Wait(); - } +WebContents* EphemeralStorageBrowserTest::LoadURLInNewTab(GURL url) { + ui_test_utils::AllBrowserTabAddedWaiter add_tab; + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); + return add_tab.Wait(); +} - protected: - net::test_server::EmbeddedTestServer https_server_; - GURL a_site_ephemeral_storage_url_; - GURL b_site_ephemeral_storage_url_; - GURL c_site_ephemeral_storage_url_; - GURL a_site_ephemeral_storage_with_network_cookies_url_; - HttpRequestMonitor http_request_monitor_; +void EphemeralStorageBrowserTest::SetStorageValueInFrame( + RenderFrameHost* host, + std::string value, + StorageType storage_type) { + std::string script = + base::StringPrintf("%sStorage.setItem('storage_key', '%s');", + ToString(storage_type), value.c_str()); + ASSERT_TRUE(content::ExecuteScript(host, script)); +} - private: - DISALLOW_COPY_AND_ASSIGN(EphemeralStorageBrowserTest); -}; +content::EvalJsResult EphemeralStorageBrowserTest::GetStorageValueInFrame( + RenderFrameHost* host, + StorageType storage_type) { + std::string script = base::StringPrintf("%sStorage.getItem('storage_key');", + ToString(storage_type)); + return content::EvalJs(host, script); +} + +void EphemeralStorageBrowserTest::SetCookieInFrame(RenderFrameHost* host, + std::string cookie) { + std::string script = base::StringPrintf( + "document.cookie='%s; path=/; SameSite=None; Secure'", cookie.c_str()); + ASSERT_TRUE(content::ExecuteScript(host, script)); +} + +content::EvalJsResult EphemeralStorageBrowserTest::GetCookiesInFrame( + RenderFrameHost* host) { + return content::EvalJs(host, "document.cookie"); +} + +void EphemeralStorageBrowserTest::WaitForCleanupAfterKeepAlive() { + base::RunLoop run_loop; + base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), + base::TimeDelta::FromSeconds(kKeepAliveInterval)); + run_loop.Run(); +} + +void EphemeralStorageBrowserTest::ExpectValuesFromFramesAreEmpty( + const base::Location& location, + const ValuesFromFrames& values) { + testing::ScopedTrace scoped_trace(location.file_name(), + location.line_number(), + "Some values are not empty"); + + EXPECT_EQ(nullptr, values.main_frame.local_storage); + EXPECT_EQ(nullptr, values.iframe_1.local_storage); + EXPECT_EQ(nullptr, values.iframe_2.local_storage); + + EXPECT_EQ(nullptr, values.main_frame.session_storage); + EXPECT_EQ(nullptr, values.iframe_1.session_storage); + EXPECT_EQ(nullptr, values.iframe_2.session_storage); + + EXPECT_EQ("", values.main_frame.cookies); + EXPECT_EQ("", values.iframe_1.cookies); + EXPECT_EQ("", values.iframe_2.cookies); +} IN_PROC_BROWSER_TEST_F(EphemeralStorageBrowserTest, StorageIsPartitioned) { WebContents* first_party_tab = LoadURLInNewTab(b_site_ephemeral_storage_url_); @@ -343,6 +345,66 @@ IN_PROC_BROWSER_TEST_F(EphemeralStorageBrowserTest, StorageIsPartitioned) { EXPECT_EQ("", site_c_tab_values.iframe_2.cookies); } +IN_PROC_BROWSER_TEST_F(EphemeralStorageBrowserTest, LocalStorageIsShared) { + WebContents* site_a_tab1 = + LoadURLInNewTab(a_site_ephemeral_storage_with_network_cookies_url_); + WebContents* site_a_tab2 = LoadURLInNewTab(a_site_ephemeral_storage_url_); + + SetValuesInFrames(site_a_tab1, "a.com", "from=a.com"); + ValuesFromFrames site_a_tab1_values = GetValuesFromFrames(site_a_tab1); + EXPECT_EQ("a.com", site_a_tab1_values.main_frame.local_storage); + EXPECT_EQ("a.com", site_a_tab1_values.iframe_1.local_storage); + EXPECT_EQ("a.com", site_a_tab1_values.iframe_2.local_storage); + + EXPECT_EQ("a.com", site_a_tab1_values.main_frame.session_storage); + EXPECT_EQ("a.com", site_a_tab1_values.iframe_1.session_storage); + EXPECT_EQ("a.com", site_a_tab1_values.iframe_2.session_storage); + + EXPECT_EQ("name=acom_simple; from=a.com", + site_a_tab1_values.main_frame.cookies); + EXPECT_EQ("name=bcom_simple; from=a.com", + site_a_tab1_values.iframe_1.cookies); + EXPECT_EQ("name=bcom_simple; from=a.com", + site_a_tab1_values.iframe_2.cookies); + + // The second tab is loaded on the same domain, so should see the same + // storage for the third-party iframes. + ValuesFromFrames site_a_tab2_values = GetValuesFromFrames(site_a_tab2); + EXPECT_EQ("a.com", site_a_tab2_values.main_frame.local_storage); + EXPECT_EQ("a.com", site_a_tab2_values.iframe_1.local_storage); + EXPECT_EQ("a.com", site_a_tab2_values.iframe_2.local_storage); + + EXPECT_EQ(nullptr, site_a_tab2_values.main_frame.session_storage); + EXPECT_EQ(nullptr, site_a_tab2_values.iframe_1.session_storage); + EXPECT_EQ(nullptr, site_a_tab2_values.iframe_2.session_storage); + + EXPECT_EQ("name=acom_simple; from=a.com", + site_a_tab2_values.main_frame.cookies); + EXPECT_EQ("name=bcom_simple; from=a.com", + site_a_tab2_values.iframe_1.cookies); + EXPECT_EQ("name=bcom_simple; from=a.com", + site_a_tab2_values.iframe_2.cookies); + + SetValuesInFrames(site_a_tab1, "a.com-modify", "from=a.com-modify"); + { + ValuesFromFrames site_a_tab2_values = GetValuesFromFrames(site_a_tab2); + EXPECT_EQ("a.com-modify", site_a_tab2_values.main_frame.local_storage); + EXPECT_EQ("a.com-modify", site_a_tab2_values.iframe_1.local_storage); + EXPECT_EQ("a.com-modify", site_a_tab2_values.iframe_2.local_storage); + + EXPECT_EQ(nullptr, site_a_tab2_values.main_frame.session_storage); + EXPECT_EQ(nullptr, site_a_tab2_values.iframe_1.session_storage); + EXPECT_EQ(nullptr, site_a_tab2_values.iframe_2.session_storage); + + EXPECT_EQ("name=acom_simple; from=a.com-modify", + site_a_tab2_values.main_frame.cookies); + EXPECT_EQ("name=bcom_simple; from=a.com-modify", + site_a_tab2_values.iframe_1.cookies); + EXPECT_EQ("name=bcom_simple; from=a.com-modify", + site_a_tab2_values.iframe_2.cookies); + } +} + IN_PROC_BROWSER_TEST_F(EphemeralStorageBrowserTest, NavigatingClearsEphemeralStorageAfterKeepAlive) { ui_test_utils::NavigateToURL( @@ -385,13 +447,7 @@ IN_PROC_BROWSER_TEST_F(EphemeralStorageBrowserTest, // after keepalive values should be cleared ui_test_utils::NavigateToURL(browser(), b_site_ephemeral_storage_url_); - - base::RunLoop run_loop; - base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, run_loop.QuitClosure(), - base::TimeDelta::FromSeconds(kKeepAliveInterval)); - run_loop.Run(); - + WaitForCleanupAfterKeepAlive(); ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_); ValuesFromFrames after_timeout = GetValuesFromFrames(web_contents); @@ -606,13 +662,7 @@ IN_PROC_BROWSER_TEST_F(EphemeralStorageBrowserTest, // Navigating to a new TLD should clear all ephemeral cookies after keep-alive // timeout. ui_test_utils::NavigateToURL(browser(), b_site_ephemeral_storage_url_); - - base::RunLoop run_loop; - base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, run_loop.QuitClosure(), - base::TimeDelta::FromSeconds(kKeepAliveInterval)); - run_loop.Run(); - + WaitForCleanupAfterKeepAlive(); ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_); ValuesFromFrames values_after = GetValuesFromFrames(web_contents); @@ -802,3 +852,59 @@ IN_PROC_BROWSER_TEST_F(EphemeralStorageKeepAliveDisabledBrowserTest, EXPECT_EQ("", values_after.iframe_1.cookies); EXPECT_EQ("", values_after.iframe_2.cookies); } + +class EphemeralStorageNoSiteIsolationAndKeepAliveDisabledBrowserTest + : public EphemeralStorageKeepAliveDisabledBrowserTest { + public: + EphemeralStorageNoSiteIsolationAndKeepAliveDisabledBrowserTest() {} + + void SetUpCommandLine(base::CommandLine* command_line) override { + EphemeralStorageKeepAliveDisabledBrowserTest::SetUpCommandLine( + command_line); + command_line->AppendSwitch(switches::kDisableSiteIsolation); + } +}; + +// Test for Android-specific bug when a renderer caches localStorage proxy in +// the same process without a proper cleanup. +IN_PROC_BROWSER_TEST_F( + EphemeralStorageNoSiteIsolationAndKeepAliveDisabledBrowserTest, + RenderInitiatedNavigationClearsEphemeralStorage) { + ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_); + auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); + + SetValuesInFrames(web_contents, "a.com value", "from=a.com"); + + ValuesFromFrames values_before = GetValuesFromFrames(web_contents); + EXPECT_EQ("a.com value", values_before.main_frame.local_storage); + EXPECT_EQ("a.com value", values_before.iframe_1.local_storage); + EXPECT_EQ("a.com value", values_before.iframe_2.local_storage); + + EXPECT_EQ("a.com value", values_before.main_frame.session_storage); + EXPECT_EQ("a.com value", values_before.iframe_1.session_storage); + EXPECT_EQ("a.com value", values_before.iframe_2.session_storage); + + EXPECT_EQ("from=a.com", values_before.main_frame.cookies); + EXPECT_EQ("from=a.com", values_before.iframe_1.cookies); + EXPECT_EQ("from=a.com", values_before.iframe_2.cookies); + + // Navigate away and then navigate back to the original site using + // renderer-initiated navigations. + ASSERT_TRUE(content::NavigateToURLFromRenderer( + web_contents, b_site_ephemeral_storage_url_)); + ASSERT_TRUE(content::NavigateToURLFromRenderer( + web_contents, a_site_ephemeral_storage_url_)); + + ValuesFromFrames values_after = GetValuesFromFrames(web_contents); + EXPECT_EQ("a.com value", values_after.main_frame.local_storage); + EXPECT_EQ(nullptr, values_after.iframe_1.local_storage); + EXPECT_EQ(nullptr, values_after.iframe_2.local_storage); + + EXPECT_EQ("a.com value", values_after.main_frame.session_storage); + EXPECT_EQ(nullptr, values_after.iframe_1.session_storage); + EXPECT_EQ(nullptr, values_after.iframe_2.session_storage); + + EXPECT_EQ("from=a.com", values_after.main_frame.cookies); + EXPECT_EQ("", values_after.iframe_1.cookies); + EXPECT_EQ("", values_after.iframe_2.cookies); +} diff --git a/browser/ephemeral_storage/ephemeral_storage_browsertest.h b/browser/ephemeral_storage/ephemeral_storage_browsertest.h new file mode 100644 index 000000000000..b12616df52cb --- /dev/null +++ b/browser/ephemeral_storage/ephemeral_storage_browsertest.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_EPHEMERAL_STORAGE_EPHEMERAL_STORAGE_BROWSERTEST_H_ +#define BRAVE_BROWSER_EPHEMERAL_STORAGE_EPHEMERAL_STORAGE_BROWSERTEST_H_ + +#include +#include + +#include "base/memory/weak_ptr.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" +#include "net/test/embedded_test_server/http_request.h" +#include "url/gurl.h" + +class EphemeralStorageBrowserTest : public InProcessBrowserTest { + public: + enum StorageType { Session, Local }; + + struct ValuesFromFrame { + content::EvalJsResult local_storage; + content::EvalJsResult session_storage; + content::EvalJsResult cookies; + }; + + struct ValuesFromFrames { + ValuesFromFrame main_frame; + ValuesFromFrame iframe_1; + ValuesFromFrame iframe_2; + }; + + class HttpRequestMonitor : public base::SupportsWeakPtr { + public: + HttpRequestMonitor(); + ~HttpRequestMonitor(); + + void OnHttpRequest(const net::test_server::HttpRequest& request); + bool HasHttpRequestWithCookie(const GURL& url, + const std::string& cookie_value) const; + void Clear() { http_requests_.clear(); } + + private: + std::vector http_requests_; + }; + + EphemeralStorageBrowserTest(); + EphemeralStorageBrowserTest(const EphemeralStorageBrowserTest&) = delete; + EphemeralStorageBrowserTest& operator=(const EphemeralStorageBrowserTest&) = + delete; + ~EphemeralStorageBrowserTest() override; + + void SetUpOnMainThread() override; + void SetUpCommandLine(base::CommandLine* command_line) override; + + void SetValuesInFrame(content::RenderFrameHost* frame, + std::string storage_value, + std::string cookie_value); + + void SetValuesInFrames(content::WebContents* web_contents, + std::string storage_value, + std::string cookie_value); + + ValuesFromFrame GetValuesFromFrame(content::RenderFrameHost* frame); + ValuesFromFrames GetValuesFromFrames(content::WebContents* web_contents); + + content::WebContents* LoadURLInNewTab(GURL url); + + void SetStorageValueInFrame(content::RenderFrameHost* host, + std::string value, + StorageType storage_type); + content::EvalJsResult GetStorageValueInFrame(content::RenderFrameHost* host, + StorageType storage_type); + void SetCookieInFrame(content::RenderFrameHost* host, std::string cookie); + content::EvalJsResult GetCookiesInFrame(content::RenderFrameHost* host); + void WaitForCleanupAfterKeepAlive(); + void ExpectValuesFromFramesAreEmpty(const base::Location& location, + const ValuesFromFrames& values); + + protected: + void SetUpHttpsServer(); + + net::test_server::EmbeddedTestServer https_server_; + GURL a_site_ephemeral_storage_url_; + GURL b_site_ephemeral_storage_url_; + GURL c_site_ephemeral_storage_url_; + GURL a_site_ephemeral_storage_with_network_cookies_url_; + HttpRequestMonitor http_request_monitor_; +}; + +#endif // BRAVE_BROWSER_EPHEMERAL_STORAGE_EPHEMERAL_STORAGE_BROWSERTEST_H_ diff --git a/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc b/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc index 8d7ed1c920ed..f6a3f6d45645 100644 --- a/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc +++ b/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc @@ -6,12 +6,16 @@ #include "brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.h" #include +#include #include #include "base/feature_list.h" #include "base/hash/md5.h" #include "base/ranges/ranges.h" #include "base/threading/sequenced_task_runner_handle.h" +#include "chrome/browser/content_settings/cookie_settings_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "components/content_settings/core/browser/cookie_settings.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/session_storage_namespace.h" #include "content/public/browser/storage_partition.h" @@ -47,6 +51,23 @@ std::string StringToSessionStorageId(const std::string& string, return hash; } +class EphemeralStorageOriginsSourceImpl + : public content::TLDEphemeralLifetime::EphemeralStorageOriginsSource { + public: + EphemeralStorageOriginsSourceImpl( + scoped_refptr cookie_settings) + : cookie_settings_(std::move(cookie_settings)) {} + + base::flat_map TakeEphemeralStorageOrigins( + const std::string& ephemeral_storage_domain) override { + return cookie_settings_->TakeEphemeralStorageOrigins( + ephemeral_storage_domain); + } + + private: + scoped_refptr cookie_settings_; +}; + } // namespace // EphemeralStorageTabHelper helps to manage the lifetime of ephemeral storage. @@ -176,7 +197,10 @@ void EphemeralStorageTabHelper::CreateEphemeralStorageAreasForDomainAndURL( : absl::nullopt); tld_ephemeral_lifetime_ = content::TLDEphemeralLifetime::GetOrCreate( - browser_context, partition, new_domain); + browser_context, partition, new_domain, + std::make_unique( + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)))); } // static diff --git a/browser/ephemeral_storage/sources.gni b/browser/ephemeral_storage/sources.gni new file mode 100644 index 000000000000..b3c454a21b38 --- /dev/null +++ b/browser/ephemeral_storage/sources.gni @@ -0,0 +1,14 @@ +brave_browser_ephemeral_storage_sources = [ + "//brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc", + "//brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.h", +] + +brave_browser_ephemeral_storage_deps = [ + "//base", + "//chrome/browser/profiles", + "//chrome/browser/ui", + "//components/content_settings/core/browser", + "//content/public/browser", + "//net", + "//third_party/blink/public/common", +] diff --git a/browser/permissions/BUILD.gn b/browser/permissions/BUILD.gn index 0e849dfbc427..1680d824710b 100644 --- a/browser/permissions/BUILD.gn +++ b/browser/permissions/BUILD.gn @@ -13,7 +13,6 @@ if (!is_android) { deps = [ "//brave/browser", - "//brave/browser/ephemeral_storage", "//chrome/browser", "//chrome/browser/ui", "//chrome/test:test_support", diff --git a/browser/sources.gni b/browser/sources.gni index b1f41eece09e..c009fe0f78ee 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -13,6 +13,7 @@ import("//brave/browser/component_updater/sources.gni") import("//brave/browser/crypto_dot_com/sources.gni") import("//brave/browser/decentralized_dns/sources.gni") import("//brave/browser/download/sources.gni") +import("//brave/browser/ephemeral_storage/sources.gni") import("//brave/browser/ethereum_remote_client/buildflags/buildflags.gni") import("//brave/browser/gemini/sources.gni") import("//brave/browser/greaselion/sources.gni") @@ -92,7 +93,6 @@ brave_chrome_browser_deps = [ "//brave/browser/brave_ads/notifications", "//brave/browser/component_updater", "//brave/browser/content_settings", - "//brave/browser/ephemeral_storage", "//brave/browser/gcm_driver", "//brave/browser/net", "//brave/browser/ntp_background_images", @@ -338,6 +338,7 @@ brave_chrome_browser_sources += brave_browser_component_updater_sources brave_chrome_browser_sources += brave_browser_crypto_dot_com_sources brave_chrome_browser_sources += brave_browser_decentralized_dns_sources brave_chrome_browser_sources += brave_browser_download_sources +brave_chrome_browser_sources += brave_browser_ephemeral_storage_sources brave_chrome_browser_sources += brave_browser_gemini_sources brave_chrome_browser_sources += brave_browser_greaselion_sources brave_chrome_browser_sources += brave_browser_importer_sources @@ -358,6 +359,7 @@ brave_chrome_browser_deps += brave_browser_component_updater_deps brave_chrome_browser_deps += brave_browser_crypto_dot_com_deps brave_chrome_browser_deps += brave_browser_decentralized_dns_deps brave_chrome_browser_deps += brave_browser_download_deps +brave_chrome_browser_deps += brave_browser_ephemeral_storage_deps brave_chrome_browser_deps += brave_browser_gemini_deps brave_chrome_browser_deps += brave_browser_greaselion_deps brave_chrome_browser_deps += brave_browser_importer_deps diff --git a/chromium_src/components/content_settings/browser/content_settings_manager_impl.cc b/chromium_src/components/content_settings/browser/content_settings_manager_impl.cc index a9e6b909890e..7985c93fd86d 100644 --- a/chromium_src/components/content_settings/browser/content_settings_manager_impl.cc +++ b/chromium_src/components/content_settings/browser/content_settings_manager_impl.cc @@ -5,8 +5,16 @@ #include "components/content_settings/browser/content_settings_manager_impl.h" +#define BRAVE_CONTENT_SETTINGS_MANAGER_IMPL_ALLOW_STORAGE_ACCESS \ + if (allowed) { \ + allowed &= cookie_settings_->IsStorageAccessAllowed( \ + url, site_for_cookies, top_frame_origin, storage_type); \ + } + #include "../../../../../components/content_settings/browser/content_settings_manager_impl.cc" +#undef BRAVE_CONTENT_SETTINGS_MANAGER_IMPL_ALLOW_STORAGE_ACCESS + namespace content_settings { void ContentSettingsManagerImpl::AllowEphemeralStorageAccess( @@ -15,9 +23,13 @@ void ContentSettingsManagerImpl::AllowEphemeralStorageAccess( const url::Origin& origin, const GURL& site_for_cookies, const url::Origin& top_frame_origin, - base::OnceCallback callback) { - std::move(callback).Run(cookie_settings_->ShouldUseEphemeralStorage( - origin.GetURL(), site_for_cookies, top_frame_origin)); + AllowEphemeralStorageAccessCallback callback) { + url::Origin storage_origin; + const bool should_use = cookie_settings_->ShouldUseEphemeralStorage( + origin, site_for_cookies, top_frame_origin, storage_origin); + std::move(callback).Run(should_use + ? absl::make_optional(storage_origin) + : absl::nullopt); } } // namespace content_settings diff --git a/chromium_src/components/content_settings/browser/content_settings_manager_impl.h b/chromium_src/components/content_settings/browser/content_settings_manager_impl.h index 3594fef03061..7dd11fb48d73 100644 --- a/chromium_src/components/content_settings/browser/content_settings_manager_impl.h +++ b/chromium_src/components/content_settings/browser/content_settings_manager_impl.h @@ -6,6 +6,7 @@ #ifndef BRAVE_CHROMIUM_SRC_COMPONENTS_CONTENT_SETTINGS_BROWSER_CONTENT_SETTINGS_MANAGER_IMPL_H_ #define BRAVE_CHROMIUM_SRC_COMPONENTS_CONTENT_SETTINGS_BROWSER_CONTENT_SETTINGS_MANAGER_IMPL_H_ +#include "base/containers/flat_map.h" #include "components/content_settings/common/content_settings_manager.mojom.h" #define OnContentBlocked \ @@ -14,7 +15,7 @@ int32_t render_frame_id, StorageType storage_type, \ const url::Origin& origin, const GURL& site_for_cookies, \ const url::Origin& top_frame_origin, \ - base::OnceCallback callback) override; \ + AllowEphemeralStorageAccessCallback callback) override; \ void OnContentBlocked #include "../../../../../components/content_settings/browser/content_settings_manager_impl.h" diff --git a/chromium_src/components/content_settings/common/content_settings_manager.mojom b/chromium_src/components/content_settings/common/content_settings_manager.mojom index ac2ef8f5605e..edc1911275c2 100644 --- a/chromium_src/components/content_settings/common/content_settings_manager.mojom +++ b/chromium_src/components/content_settings/common/content_settings_manager.mojom @@ -11,5 +11,5 @@ interface ContentSettingsManager { StorageType storage_type, url.mojom.Origin origin, url.mojom.Url site_for_cookies, - url.mojom.Origin top_frame_origin) => (bool ephemeral_storage_allowed); + url.mojom.Origin top_frame_origin) => (url.mojom.Origin? storage_origin); }; diff --git a/chromium_src/components/content_settings/core/browser/DEPS b/chromium_src/components/content_settings/core/browser/DEPS index b8f40ff23a2b..1a42642015ca 100644 --- a/chromium_src/components/content_settings/core/browser/DEPS +++ b/chromium_src/components/content_settings/core/browser/DEPS @@ -2,4 +2,5 @@ include_rules = [ "+../../../../../../components/content_settings/core/browser", "+components/content_settings/core/browser", "+components/content_settings/core/common", + "+components/keyed_service/core", ] diff --git a/chromium_src/components/content_settings/core/browser/cookie_settings.cc b/chromium_src/components/content_settings/core/browser/cookie_settings.cc new file mode 100644 index 000000000000..39ccaa887722 --- /dev/null +++ b/chromium_src/components/content_settings/core/browser/cookie_settings.cc @@ -0,0 +1,76 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "components/content_settings/core/browser/cookie_settings.h" + +#include "net/base/url_util.h" + +#define BRAVE_COOKIE_SETTINGS_GET_COOKIES_SETTINGS_INTERNAL \ + if (setting == CONTENT_SETTING_SESSION_ONLY && !block_third && \ + ShouldBlockThirdPartyCookies() && \ + !first_party_url.SchemeIs(extension_scheme_)) { \ + block_third = true; \ + } + +#define ShutdownOnUIThread ShutdownOnUIThread_ChromiumImpl + +#include "../../../../../../components/content_settings/core/browser/cookie_settings.cc" + +#undef ShutdownOnUIThread + +namespace content_settings { + +void CookieSettings::ShutdownOnUIThread() { + ShutdownOnUIThread_ChromiumImpl(); + ephemeral_storage_origins_.clear(); +} + +bool CookieSettings::ShouldUseEphemeralStorage( + const url::Origin& origin, + const GURL& site_for_cookies, + const absl::optional& top_frame_origin, + url::Origin& storage_origin) { + const bool should_use = CookieSettingsBase::ShouldUseEphemeralStorage( + origin.GetURL(), site_for_cookies, top_frame_origin); + if (!should_use) { + return false; + } + DCHECK(top_frame_origin); + const std::string ephemeral_storage_domain = + net::URLToEphemeralStorageDomain(top_frame_origin->GetURL()); + + auto ephemeral_storage_origins_it = + ephemeral_storage_origins_.find(ephemeral_storage_domain); + if (ephemeral_storage_origins_it != ephemeral_storage_origins_.end()) { + const auto& storage_origins = ephemeral_storage_origins_it->second; + auto storage_origin_it = storage_origins.find(origin); + if (storage_origin_it != storage_origins.end()) { + storage_origin = storage_origin_it->second; + return true; + } + } + + url::Origin opaque_origin = origin.DeriveNewOpaqueOrigin(); + ephemeral_storage_origins_[ephemeral_storage_domain][origin] = opaque_origin; + storage_origin = std::move(opaque_origin); + return true; +} + +base::flat_map +CookieSettings::TakeEphemeralStorageOrigins( + const std::string& ephemeral_storage_domain) { + base::flat_map result; + auto ephemeral_storage_origins_it = + ephemeral_storage_origins_.find(ephemeral_storage_domain); + if (ephemeral_storage_origins_it != ephemeral_storage_origins_.end()) { + result = std::move(ephemeral_storage_origins_it->second); + ephemeral_storage_origins_.erase(ephemeral_storage_origins_it); + } + return result; +} + +} // namespace content_settings + +#undef BRAVE_COOKIE_SETTINGS_GET_COOKIES_SETTINGS_INTERNAL diff --git a/chromium_src/components/content_settings/core/browser/cookie_settings.h b/chromium_src/components/content_settings/core/browser/cookie_settings.h new file mode 100644 index 000000000000..4ab09e336643 --- /dev/null +++ b/chromium_src/components/content_settings/core/browser/cookie_settings.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_CHROMIUM_SRC_COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_COOKIE_SETTINGS_H_ +#define BRAVE_CHROMIUM_SRC_COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_COOKIE_SETTINGS_H_ + +#include "base/containers/flat_map.h" +#include "components/content_settings/core/browser/content_settings_provider.h" +#include "components/keyed_service/core/refcounted_keyed_service.h" +#include "url/origin.h" + +#define ShutdownOnUIThread \ + ShutdownOnUIThread_ChromiumImpl(); \ + bool ShouldUseEphemeralStorage( \ + const url::Origin& origin, const GURL& site_for_cookies, \ + const absl::optional& top_frame_origin, \ + url::Origin& storage_origin); \ + base::flat_map TakeEphemeralStorageOrigins( \ + const std::string& ephemeral_storage_domain); \ + \ + private: \ + base::flat_map> \ + ephemeral_storage_origins_; \ + \ + public: \ + void ShutdownOnUIThread + +#include "../../../../../../components/content_settings/core/browser/cookie_settings.h" + +#undef ShutdownOnUIThread + +#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_COOKIE_SETTINGS_H_ diff --git a/chromium_src/components/content_settings/core/common/DEPS b/chromium_src/components/content_settings/core/common/DEPS index 585d3f206d6d..56b791513a1f 100644 --- a/chromium_src/components/content_settings/core/common/DEPS +++ b/chromium_src/components/content_settings/core/common/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+../../../../../../components/content_settings/core/common", + "+components/content_settings/common", "+components/content_settings/core/common", ] diff --git a/chromium_src/components/content_settings/core/common/cookie_settings_base.cc b/chromium_src/components/content_settings/core/common/cookie_settings_base.cc index 24c2dc4268a1..c5b3240f7908 100644 --- a/chromium_src/components/content_settings/core/common/cookie_settings_base.cc +++ b/chromium_src/components/content_settings/core/common/cookie_settings_base.cc @@ -111,6 +111,12 @@ bool CookieSettingsBase::ShouldUseEphemeralStorage( if (!first_party_url.is_valid()) return false; + if (base::FeatureList::IsEnabled( + net::features::kBraveFirstPartyEphemeralStorage) && + IsCookieSessionOnly(first_party_url)) { + return true; + } + if (net::registry_controlled_domains::SameDomainOrHost( first_party_url, url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) @@ -184,6 +190,22 @@ bool CookieSettingsBase::IsCookieAccessAllowedImpl( return false; } +bool CookieSettingsBase::IsStorageAccessAllowed( + const GURL& url, + const GURL& site_for_cookies, + const absl::optional& top_frame_origin, + StorageType storage_type) const { + if (storage_type != StorageType::INDEXED_DB || + !base::FeatureList::IsEnabled( + net::features::kBraveFirstPartyEphemeralStorage)) { + return true; + } + + // Allow INDEXED_DB only for non session-only (1pES) cookie sites. + return !IsCookieSessionOnly( + GetFirstPartyURL(site_for_cookies, top_frame_origin)); +} + } // namespace content_settings #define IsCookieAccessAllowed IsChromiumCookieAccessAllowed diff --git a/chromium_src/components/content_settings/core/common/cookie_settings_base.h b/chromium_src/components/content_settings/core/common/cookie_settings_base.h index 42147aeeda1e..74ae5d5a5d67 100644 --- a/chromium_src/components/content_settings/core/common/cookie_settings_base.h +++ b/chromium_src/components/content_settings/core/common/cookie_settings_base.h @@ -7,6 +7,8 @@ #define BRAVE_CHROMIUM_SRC_COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_COOKIE_SETTINGS_BASE_H_ #include "base/auto_reset.h" +#include "base/optional.h" +#include "components/content_settings/common/content_settings_manager.mojom.h" namespace content_settings { @@ -25,6 +27,7 @@ class ScopedEphemeralStorageAwareness { } // namespace content_settings #define BRAVE_COOKIE_SETTINGS_BASE_H \ + using StorageType = mojom::ContentSettingsManager::StorageType; \ bool ShouldUseEphemeralStorage( \ const GURL& url, const GURL& site_for_cookies, \ const absl::optional& top_frame_origin) const; \ @@ -40,6 +43,10 @@ class ScopedEphemeralStorageAwareness { bool IsChromiumCookieAccessAllowed( \ const GURL& url, const GURL& site_for_cookies, \ const absl::optional& top_frame_origin) const; \ + bool IsStorageAccessAllowed( \ + const GURL& url, const GURL& site_for_cookies, \ + const absl::optional& top_frame_origin, \ + StorageType storage_type) const; \ \ private: \ bool IsCookieAccessAllowedImpl( \ diff --git a/chromium_src/components/services/storage/dom_storage/DEPS b/chromium_src/components/services/storage/dom_storage/DEPS new file mode 100644 index 000000000000..a9161458ba7a --- /dev/null +++ b/chromium_src/components/services/storage/dom_storage/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+../../../../../../components/services/storage", + "+components/services/storage", +] diff --git a/chromium_src/components/services/storage/dom_storage/local_storage_impl.cc b/chromium_src/components/services/storage/dom_storage/local_storage_impl.cc new file mode 100644 index 000000000000..5400dec20df6 --- /dev/null +++ b/chromium_src/components/services/storage/dom_storage/local_storage_impl.cc @@ -0,0 +1,114 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "components/services/storage/dom_storage/local_storage_impl.h" + +#define LocalStorageImpl LocalStorageImpl_ChromiumImpl + +#include "../../../../../../components/services/storage/dom_storage/local_storage_impl.cc" + +#undef LocalStorageImpl + +namespace storage { + +LocalStorageImpl::LocalStorageImpl( + const base::FilePath& storage_root, + scoped_refptr task_runner, + scoped_refptr legacy_task_runner, + mojo::PendingReceiver receiver) + : local_storage_(std::make_unique( + storage_root, + task_runner, + legacy_task_runner, + mojo::PendingReceiver())), + in_memory_local_storage_(std::make_unique( + base::FilePath(), + task_runner, + legacy_task_runner, + mojo::PendingReceiver())) { + if (receiver) + control_receiver_.Bind(std::move(receiver)); +} + +LocalStorageImpl::~LocalStorageImpl() = default; + +void LocalStorageImpl::ShutDown(base::OnceClosure callback) { + local_storage_->ShutDown(std::move(callback)); +} + +const url::Origin* LocalStorageImpl::GetNonOpaqueOrigin( + const url::Origin& origin, + bool create) { + DCHECK(origin.opaque()); + auto non_opaque_origin_it = non_opaque_origins_.find(origin); + if (non_opaque_origin_it != non_opaque_origins_.end()) { + return &non_opaque_origin_it->second; + } + if (!create) { + return nullptr; + } + const auto& origin_scheme_host_port = + origin.GetTupleOrPrecursorTupleIfOpaque(); + auto emplaced_pair = non_opaque_origins_.emplace( + origin, + url::Origin::CreateFromNormalizedTuple( + origin_scheme_host_port.scheme(), + base::ToLowerASCII(base::UnguessableToken::Create().ToString()), + origin_scheme_host_port.port())); + return &emplaced_pair.first->second; +} + +// mojom::LocalStorageControl implementation: +void LocalStorageImpl::BindStorageArea( + const url::Origin& origin, + mojo::PendingReceiver receiver) { + LOG(ERROR) << "LocalStorageImpl::BindStorageArea " << origin; + if (origin.opaque()) { + in_memory_local_storage_->BindStorageArea(*GetNonOpaqueOrigin(origin, true), + std::move(receiver)); + } else { + local_storage_->BindStorageArea(origin, std::move(receiver)); + } +} + +void LocalStorageImpl::GetUsage(GetUsageCallback callback) { + local_storage_->GetUsage(std::move(callback)); +} + +void LocalStorageImpl::DeleteStorage(const url::Origin& origin, + DeleteStorageCallback callback) { + if (origin.opaque()) { + if (const auto* non_opaque_origin = GetNonOpaqueOrigin(origin, false)) { + in_memory_local_storage_->DeleteStorage(*non_opaque_origin, + std::move(callback)); + non_opaque_origins_.erase(origin); + } + } else { + local_storage_->DeleteStorage(origin, std::move(callback)); + } +} + +void LocalStorageImpl::CleanUpStorage(CleanUpStorageCallback callback) { + local_storage_->CleanUpStorage(std::move(callback)); +} + +void LocalStorageImpl::Flush(FlushCallback callback) { + local_storage_->Flush(std::move(callback)); +} + +void LocalStorageImpl::PurgeMemory() { + local_storage_->PurgeMemory(); +} + +void LocalStorageImpl::ApplyPolicyUpdates( + std::vector policy_updates) { + local_storage_->ApplyPolicyUpdates(std::move(policy_updates)); +} + +void LocalStorageImpl::ForceKeepSessionState() { + local_storage_->ForceKeepSessionState(); +} + +} // namespace storage diff --git a/chromium_src/components/services/storage/dom_storage/local_storage_impl.h b/chromium_src/components/services/storage/dom_storage/local_storage_impl.h new file mode 100644 index 000000000000..11565f10de6d --- /dev/null +++ b/chromium_src/components/services/storage/dom_storage/local_storage_impl.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_CHROMIUM_SRC_COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_LOCAL_STORAGE_IMPL_H_ +#define BRAVE_CHROMIUM_SRC_COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_LOCAL_STORAGE_IMPL_H_ + +#define LocalStorageImpl LocalStorageImpl_ChromiumImpl + +#include "../../../../../../components/services/storage/dom_storage/local_storage_impl.h" + +#undef LocalStorageImpl + +namespace storage { + +class LocalStorageImpl : public mojom::LocalStorageControl { + public: + LocalStorageImpl(const base::FilePath& storage_root, + scoped_refptr task_runner, + scoped_refptr legacy_task_runner, + mojo::PendingReceiver receiver); + ~LocalStorageImpl() override; + + void ShutDown(base::OnceClosure callback); + + // mojom::LocalStorageControl implementation: + void BindStorageArea( + const url::Origin& origin, + mojo::PendingReceiver receiver) override; + void GetUsage(GetUsageCallback callback) override; + void DeleteStorage(const url::Origin& origin, + DeleteStorageCallback callback) override; + void CleanUpStorage(CleanUpStorageCallback callback) override; + void Flush(FlushCallback callback) override; + void PurgeMemory() override; + void ApplyPolicyUpdates( + std::vector policy_updates) override; + void ForceKeepSessionState() override; + + private: + const url::Origin* GetNonOpaqueOrigin(const url::Origin& origin, bool create); + + std::unique_ptr local_storage_; + std::unique_ptr in_memory_local_storage_; + std::map non_opaque_origins_; + mojo::Receiver control_receiver_{this}; + + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +} // namespace storage + +#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_LOCAL_STORAGE_IMPL_H_ diff --git a/chromium_src/content/browser/devtools/protocol/DEPS b/chromium_src/content/browser/devtools/protocol/DEPS new file mode 100644 index 000000000000..0789dae23999 --- /dev/null +++ b/chromium_src/content/browser/devtools/protocol/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+../../../../../../content/browser/devtools/protocol", + "+content/browser/devtools/protocol", +] diff --git a/chromium_src/content/browser/devtools/protocol/network_handler.cc b/chromium_src/content/browser/devtools/protocol/network_handler.cc new file mode 100644 index 000000000000..c720aaa714d9 --- /dev/null +++ b/chromium_src/content/browser/devtools/protocol/network_handler.cc @@ -0,0 +1,28 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "content/browser/devtools/protocol/network_handler.h" + +#define BRAVE_COOKIE_RETRIEVER_NETWORK_SERVICE_RETRIEVE_ARGS \ + const url::Origin &top_frame_origin, + +#define BRAVE_COOKIE_RETRIEVER_NETWORK_SERVICE_RETRIEVE_BODY \ + cookie_options.set_top_frame_origin(top_frame_origin); + +#define BRAVE_NETWORK_HANDLER_GET_COOKIES_RETREIVE_CALL_ARGS \ + host_->ComputeTopFrameOrigin(host_->GetLastCommittedOrigin()), + +#define BRAVE_NETWORK_HANDLER_SET_COOKIES_SET_COOKIE_OPTIONS \ + options.set_top_frame_origin( \ + host_ ? base::make_optional(host_->ComputeTopFrameOrigin( \ + host_->GetLastCommittedOrigin())) \ + : base::nullopt); + +#include "../../../../../../content/browser/devtools/protocol/network_handler.cc" + +#undef BRAVE_NETWORK_HANDLER_SET_COOKIES_SET_COOKIE_OPTIONS +#undef BRAVE_NETWORK_HANDLER_GET_COOKIES_RETREIVE_CALL_ARGS +#undef BRAVE_COOKIE_RETRIEVER_NETWORK_SERVICE_RETRIEVE_BODY +#undef BRAVE_COOKIE_RETRIEVER_NETWORK_SERVICE_RETRIEVE_ARGS diff --git a/chromium_src/content/browser/tld_ephemeral_lifetime.cc b/chromium_src/content/browser/tld_ephemeral_lifetime.cc index c677fcc519bf..79e76cca1291 100644 --- a/chromium_src/content/browser/tld_ephemeral_lifetime.cc +++ b/chromium_src/content/browser/tld_ephemeral_lifetime.cc @@ -8,6 +8,7 @@ #include #include "base/no_destructor.h" +#include "content/browser/dom_storage/dom_storage_context_wrapper.h" #include "services/network/public/mojom/cookie_manager.mojom.h" namespace content { @@ -28,12 +29,19 @@ TLDEphemeralLifetimeMap& active_tld_storage_areas() { } // namespace -TLDEphemeralLifetime::TLDEphemeralLifetime(const TLDEphemeralLifetimeKey& key, - StoragePartition* storage_partition) - : key_(key), storage_partition_(storage_partition) { +TLDEphemeralLifetime::TLDEphemeralLifetime( + const TLDEphemeralLifetimeKey& key, + StoragePartition* storage_partition, + std::unique_ptr + ephemeral_storage_origins_source) + : key_(key), + storage_partition_(storage_partition), + ephemeral_storage_origins_source_( + std::move(ephemeral_storage_origins_source)) { DCHECK(active_tld_storage_areas().find(key_) == active_tld_storage_areas().end()); DCHECK(storage_partition_); + DCHECK(ephemeral_storage_origins_source_); active_tld_storage_areas().emplace(key_, weak_factory_.GetWeakPtr()); } @@ -42,6 +50,12 @@ TLDEphemeralLifetime::~TLDEphemeralLifetime() { filter->ephemeral_storage_domain = key_.second; storage_partition_->GetCookieManagerForBrowserProcess()->DeleteCookies( std::move(filter), base::NullCallback()); + for (const auto& storage_origins : + ephemeral_storage_origins_source_->TakeEphemeralStorageOrigins( + key_.second)) { + storage_partition_->GetDOMStorageContext()->DeleteLocalStorage( + storage_origins.second, base::DoNothing()); + } if (!on_destroy_callbacks_.empty()) { auto on_destroy_callbacks = std::move(on_destroy_callbacks_); @@ -65,13 +79,16 @@ TLDEphemeralLifetime* TLDEphemeralLifetime::Get( scoped_refptr TLDEphemeralLifetime::GetOrCreate( BrowserContext* browser_context, StoragePartition* storage_partition, - const std::string& storage_domain) { + const std::string& storage_domain, + std::unique_ptr + ephemeral_storage_origins_source) { const TLDEphemeralLifetimeKey key(browser_context, storage_domain); if (scoped_refptr existing = Get(key)) { return existing; } - return base::MakeRefCounted(key, storage_partition); + return base::MakeRefCounted( + key, storage_partition, std::move(ephemeral_storage_origins_source)); } // static diff --git a/chromium_src/content/public/browser/tld_ephemeral_lifetime.h b/chromium_src/content/public/browser/tld_ephemeral_lifetime.h index d7839ff4e5c1..3496e90efa7f 100644 --- a/chromium_src/content/public/browser/tld_ephemeral_lifetime.h +++ b/chromium_src/content/public/browser/tld_ephemeral_lifetime.h @@ -11,9 +11,11 @@ #include #include "base/callback.h" +#include "base/containers/flat_map.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" +#include "url/origin.h" namespace content { @@ -41,14 +43,27 @@ class CONTENT_EXPORT TLDEphemeralLifetime public: using OnDestroyCallback = base::OnceCallback; + class EphemeralStorageOriginsSource { + public: + virtual ~EphemeralStorageOriginsSource() = default; + + virtual base::flat_map + TakeEphemeralStorageOrigins( + const std::string& ephemeral_storage_domain) = 0; + }; + TLDEphemeralLifetime(const TLDEphemeralLifetimeKey& key, - StoragePartition* storage_partition); + StoragePartition* storage_partition, + std::unique_ptr + ephemeral_storage_origins_source); static TLDEphemeralLifetime* Get(BrowserContext* browser_context, const std::string& storage_domain); static scoped_refptr GetOrCreate( BrowserContext* browser_context, StoragePartition* storage_partition, - const std::string& storage_domain); + const std::string& storage_domain, + std::unique_ptr + ephemeral_storage_origins_source); // Add a callback to a callback list to be called on destruction. void RegisterOnDestroyCallback(OnDestroyCallback callback); @@ -63,6 +78,8 @@ class CONTENT_EXPORT TLDEphemeralLifetime TLDEphemeralLifetimeKey key_; StoragePartition* storage_partition_; + std::unique_ptr + ephemeral_storage_origins_source_; std::vector on_destroy_callbacks_; base::WeakPtrFactory weak_factory_{this}; diff --git a/chromium_src/net/base/features.cc b/chromium_src/net/base/features.cc index fd64d56390d7..7ccef4dd56c0 100644 --- a/chromium_src/net/base/features.cc +++ b/chromium_src/net/base/features.cc @@ -17,5 +17,8 @@ const base::FeatureParam kBraveEphemeralStorageKeepAliveTimeInSeconds = { &kBraveEphemeralStorageKeepAlive, "BraveEphemeralStorageKeepAliveTimeInSeconds", 30}; +const base::Feature kBraveFirstPartyEphemeralStorage{ + "BraveFirstPartyEphemeralStorage", base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features } // namespace net diff --git a/chromium_src/net/base/features.h b/chromium_src/net/base/features.h index 25a4404671ac..8c77aa754dcd 100644 --- a/chromium_src/net/base/features.h +++ b/chromium_src/net/base/features.h @@ -16,6 +16,7 @@ NET_EXPORT extern const base::Feature kBraveEphemeralStorage; NET_EXPORT extern const base::Feature kBraveEphemeralStorageKeepAlive; NET_EXPORT extern const base::FeatureParam kBraveEphemeralStorageKeepAliveTimeInSeconds; +NET_EXPORT extern const base::Feature kBraveFirstPartyEphemeralStorage; } // namespace features } // namespace net diff --git a/chromium_src/services/network/cookie_manager.cc b/chromium_src/services/network/cookie_manager.cc index b445b9f1b7e9..700d3ddc9eb0 100644 --- a/chromium_src/services/network/cookie_manager.cc +++ b/chromium_src/services/network/cookie_manager.cc @@ -3,10 +3,58 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "services/network/cookie_manager.h" #include "services/network/restricted_cookie_manager.h" #define BRAVE_DELETIONFILTERTOINFO \ delete_info.ephemeral_storage_domain = \ std::move(filter->ephemeral_storage_domain); +#define GetCookieList GetCookieList_ChromiumImpl +#define SetCanonicalCookie SetCanonicalCookie_ChromiumImpl + #include "../../../../services/network/cookie_manager.cc" + +#undef GetCookieList +#undef SetCanonicalCookie + +namespace network { + +void CookieManager::GetCookieList(const GURL& url, + const net::CookieOptions& cookie_options, + GetCookieListCallback callback) { + if (!cookie_options.should_use_ephemeral_storage() && + cookie_settings_.ShouldUseEphemeralStorage( + url, cookie_options.site_for_cookies().RepresentativeUrl(), + cookie_options.top_frame_origin())) { + auto ephemeral_cookie_options = cookie_options; + ephemeral_cookie_options.set_should_use_ephemeral_storage(true); + cookie_store_->GetCookieListWithOptionsAsync(url, ephemeral_cookie_options, + std::move(callback)); + return; + } + + GetCookieList_ChromiumImpl(url, cookie_options, std::move(callback)); +} + +void CookieManager::SetCanonicalCookie(const net::CanonicalCookie& cookie, + const GURL& source_url, + const net::CookieOptions& cookie_options, + SetCanonicalCookieCallback callback) { + if (!cookie_options.should_use_ephemeral_storage() && + cookie_settings_.ShouldUseEphemeralStorage( + source_url, cookie_options.site_for_cookies().RepresentativeUrl(), + cookie_options.top_frame_origin())) { + auto ephemeral_cookie_options = cookie_options; + ephemeral_cookie_options.set_should_use_ephemeral_storage(true); + cookie_store_->SetCanonicalCookieAsync( + std::make_unique(cookie), source_url, + ephemeral_cookie_options, std::move(callback)); + return; + } + + SetCanonicalCookie_ChromiumImpl(cookie, source_url, cookie_options, + std::move(callback)); +} + +} // namespace network diff --git a/chromium_src/services/network/cookie_manager.h b/chromium_src/services/network/cookie_manager.h new file mode 100644 index 000000000000..aca48d61d99b --- /dev/null +++ b/chromium_src/services/network/cookie_manager.h @@ -0,0 +1,29 @@ +/* Copyright 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_CHROMIUM_SRC_SERVICES_NETWORK_COOKIE_MANAGER_H_ +#define BRAVE_CHROMIUM_SRC_SERVICES_NETWORK_COOKIE_MANAGER_H_ + +#include "services/network/public/mojom/cookie_manager.mojom.h" + +#define GetCookieList \ + GetCookieList_ChromiumImpl(const GURL& url, \ + const net::CookieOptions& cookie_options, \ + GetCookieListCallback callback); \ + void GetCookieList + +#define SetCanonicalCookie \ + SetCanonicalCookie_ChromiumImpl(const net::CanonicalCookie& cookie, \ + const GURL& source_url, \ + const net::CookieOptions& cookie_options, \ + SetCanonicalCookieCallback callback); \ + void SetCanonicalCookie + +#include "../../../../services/network/cookie_manager.h" + +#undef SetCanonicalCookie +#undef GetCookieList + +#endif // BRAVE_CHROMIUM_SRC_SERVICES_NETWORK_COOKIE_MANAGER_H_ diff --git a/chromium_src/services/network/cookie_settings.cc b/chromium_src/services/network/cookie_settings.cc new file mode 100644 index 000000000000..72e733b0d838 --- /dev/null +++ b/chromium_src/services/network/cookie_settings.cc @@ -0,0 +1,13 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define BRAVE_COOKIE_SETTINGS_GET_COOKIE_SETTINGS_INTERNAL \ + if (cookie_setting == CONTENT_SETTING_SESSION_ONLY) { \ + /* Do nothing */ \ + } else // NOLINT + +#include "../../../../services/network/cookie_settings.cc" + +#undef BRAVE_COOKIE_SETTINGS_GET_COOKIE_SETTINGS_INTERNAL diff --git a/chromium_src/third_party/blink/public/platform/web_content_settings_client.h b/chromium_src/third_party/blink/public/platform/web_content_settings_client.h index dc6cc7451242..f5cd8dcd4dfd 100644 --- a/chromium_src/third_party/blink/public/platform/web_content_settings_client.h +++ b/chromium_src/third_party/blink/public/platform/web_content_settings_client.h @@ -7,18 +7,20 @@ #define BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_CONTENT_SETTINGS_CLIENT_H_ #include "brave/third_party/blink/renderer/brave_farbling_constants.h" +#include "url/origin.h" -#define AllowStorageAccessSync \ - AllowAutoplay(bool play_requested) { return true; } \ - virtual bool AllowFingerprinting(bool enabled_per_settings) { \ - return enabled_per_settings; \ - } \ - virtual BraveFarblingLevel GetBraveFarblingLevel() { \ - return BraveFarblingLevel::OFF; \ - } \ - virtual bool UseEphemeralStorageSync(StorageType storageType) { \ - return false; \ - } \ +#define AllowStorageAccessSync \ + AllowAutoplay(bool play_requested) { return true; } \ + virtual bool AllowFingerprinting(bool enabled_per_settings) { \ + return enabled_per_settings; \ + } \ + virtual BraveFarblingLevel GetBraveFarblingLevel() { \ + return BraveFarblingLevel::OFF; \ + } \ + virtual absl::optional UseEphemeralStorageSync( \ + StorageType storageType) { \ + return absl::nullopt; \ + } \ virtual bool AllowStorageAccessSync #include "../../../../../../third_party/blink/public/platform/web_content_settings_client.h" diff --git a/chromium_src/third_party/blink/renderer/modules/storage/brave_dom_window_storage.h b/chromium_src/third_party/blink/renderer/modules/storage/brave_dom_window_storage.h index 8dd246028b7a..78f6adfdc115 100644 --- a/chromium_src/third_party/blink/renderer/modules/storage/brave_dom_window_storage.h +++ b/chromium_src/third_party/blink/renderer/modules/storage/brave_dom_window_storage.h @@ -10,6 +10,7 @@ namespace blink { +class EphemeralStorageNamespaces; class ExceptionState; class LocalDOMWindow; class StorageArea; @@ -33,10 +34,13 @@ class BraveDOMWindowStorage final private: StorageArea* ephemeralSessionStorage(); - StorageArea* ephemeralLocalStorage(); + StorageArea* ephemeralLocalStorage(const SecurityOrigin* es_security_origin); + mutable Member ephemeral_storage_namespaces_; mutable Member ephemeral_session_storage_; mutable Member ephemeral_local_storage_; + base::flat_map> + es_security_origins_; }; } // namespace blink diff --git a/chromium_src/third_party/blink/renderer/modules/storage/cached_storage_area.cc b/chromium_src/third_party/blink/renderer/modules/storage/cached_storage_area.cc new file mode 100644 index 000000000000..436ca381f6fc --- /dev/null +++ b/chromium_src/third_party/blink/renderer/modules/storage/cached_storage_area.cc @@ -0,0 +1,22 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define BRAVE_CACHED_STORAGE_AREA_ENSURE_LOADED \ + if (remote_area_.is_bound() && !remote_area_.is_connected()) { \ + ResetConnection(); \ + is_disconnect_handler_registered_ = false; \ + } \ + \ + if (!is_disconnect_handler_registered_ && remote_area_.is_bound() && \ + remote_area_.is_connected()) { \ + remote_area_.set_disconnect_handler( \ + base::BindOnce([](CachedStorageArea* area) { area->map_.reset(); }, \ + base::Unretained(this))); \ + is_disconnect_handler_registered_ = true; \ + } + +#include "../../../../../../../third_party/blink/renderer/modules/storage/cached_storage_area.cc" + +#undef BRAVE_CACHED_STORAGE_AREA_ENSURE_LOADED diff --git a/chromium_src/third_party/blink/renderer/modules/storage/cached_storage_area.h b/chromium_src/third_party/blink/renderer/modules/storage/cached_storage_area.h new file mode 100644 index 000000000000..1b576ebec330 --- /dev/null +++ b/chromium_src/third_party/blink/renderer/modules/storage/cached_storage_area.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_CACHED_STORAGE_AREA_H_ +#define BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_CACHED_STORAGE_AREA_H_ + +#define pending_mutations_by_key_ \ + pending_mutations_by_key_; \ + bool is_disconnect_handler_registered_ = false + +#include "../../../../../../../third_party/blink/renderer/modules/storage/cached_storage_area.h" + +#undef pending_mutations_by_key_ + +#endif // BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_CACHED_STORAGE_AREA_H_ diff --git a/chromium_src/third_party/blink/renderer/modules/storage/dom_window_storage.cc b/chromium_src/third_party/blink/renderer/modules/storage/dom_window_storage.cc index b89068288f34..213442ec1697 100644 --- a/chromium_src/third_party/blink/renderer/modules/storage/dom_window_storage.cc +++ b/chromium_src/third_party/blink/renderer/modules/storage/dom_window_storage.cc @@ -32,17 +32,17 @@ String StringToSessionStorageId(const String& string, return String(hash.c_str()); } -bool ShouldUseEphemeralStorage( +absl::optional ShouldUseEphemeralStorage( LocalDOMWindow* window, WebContentSettingsClient::StorageType storage_type) { auto* frame = window->GetFrame(); if (!frame) - return false; + return absl::nullopt; if (auto* settings_client = frame->GetContentSettingsClient()) return settings_client->UseEphemeralStorageSync(storage_type); - return false; + return absl::nullopt; } } // namespace @@ -70,7 +70,23 @@ class EphemeralStorageNamespaces StorageNamespace* local_storage() { return local_storage_.Get(); } void Trace(Visitor* visitor) const override; + const SecurityOrigin* GetLocalStorageUniqueSecurityOrigin( + const SecurityOrigin* origin) { + auto origin_it = origins_.find(origin); + if (origin_it != origins_.end()) { + return origin_it->value.get(); + } + auto result = SecurityOrigin::CreateWithReferenceOrigin( + KURL(String("data:ephemeral")), origin); + origins_.insert(origin, result); + return result.get(); + } + private: + HashMap, + scoped_refptr, + SecurityOriginHash> + origins_; Member session_storage_; Member local_storage_; }; @@ -86,9 +102,7 @@ EphemeralStorageNamespaces::EphemeralStorageNamespaces( session_storage_( MakeGarbageCollected(controller, session_storage_id)), - local_storage_(MakeGarbageCollected(controller, - local_storage_id)) { -} + local_storage_(MakeGarbageCollected(controller)) {} void EphemeralStorageNamespaces::Trace(Visitor* visitor) const { visitor->Trace(session_storage_); @@ -173,17 +187,15 @@ StorageArea* BraveDOMWindowStorage::sessionStorage( auto* storage = DOMWindowStorage::From(*window).sessionStorage(exception_state); - if (!ShouldUseEphemeralStorage( - window, WebContentSettingsClient::StorageType::kSessionStorage)) + auto es_origin = ShouldUseEphemeralStorage( + window, WebContentSettingsClient::StorageType::kSessionStorage); + if (!es_origin) return storage; return ephemeralSessionStorage(); } StorageArea* BraveDOMWindowStorage::ephemeralSessionStorage() { - if (ephemeral_session_storage_) - return ephemeral_session_storage_; - LocalDOMWindow* window = GetSupplementable(); Page* page = window->GetFrame()->GetDocument()->GetPage(); EphemeralStorageNamespaces* namespaces = @@ -191,6 +203,13 @@ StorageArea* BraveDOMWindowStorage::ephemeralSessionStorage() { if (!namespaces) return nullptr; + if (ephemeral_session_storage_ && + ephemeral_storage_namespaces_ == namespaces) { + return ephemeral_session_storage_; + } + + ephemeral_storage_namespaces_ = namespaces; + auto storage_area = namespaces->session_storage()->GetCachedArea(window->GetSecurityOrigin()); @@ -205,17 +224,26 @@ StorageArea* BraveDOMWindowStorage::localStorage( LocalDOMWindow* window = GetSupplementable(); auto* storage = DOMWindowStorage::From(*window).localStorage(exception_state); - if (!ShouldUseEphemeralStorage( - window, WebContentSettingsClient::StorageType::kLocalStorage)) + auto es_origin = ShouldUseEphemeralStorage( + window, WebContentSettingsClient::StorageType::kLocalStorage); + if (!es_origin) return storage; - return ephemeralLocalStorage(); -} + const SecurityOrigin* es_security_origin = nullptr; + auto es_security_origin_it = es_security_origins_.find(*es_origin); + if (es_security_origin_it != es_security_origins_.end()) { + es_security_origin = es_security_origin_it->second.get(); + } else { + auto sec_origin = SecurityOrigin::CreateFromUrlOrigin(*es_origin); + es_security_origin = sec_origin.get(); + es_security_origins_.emplace(*es_origin, std::move(sec_origin)); + } -StorageArea* BraveDOMWindowStorage::ephemeralLocalStorage() { - if (ephemeral_local_storage_) - return ephemeral_local_storage_; + return ephemeralLocalStorage(es_security_origin); +} +StorageArea* BraveDOMWindowStorage::ephemeralLocalStorage( + const SecurityOrigin* es_security_origin) { LocalDOMWindow* window = GetSupplementable(); Page* page = window->GetFrame()->GetDocument()->GetPage(); EphemeralStorageNamespaces* namespaces = @@ -223,24 +251,26 @@ StorageArea* BraveDOMWindowStorage::ephemeralLocalStorage() { if (!namespaces) return nullptr; - auto* controller = StorageController::GetInstance(); - controller->ClearAreasIfNeeded(); - auto storage_area = base::MakeRefCounted( - CachedStorageArea::AreaType::kSessionStorage, window->GetSecurityOrigin(), - controller->TaskRunner(), namespaces->local_storage(), - /*is_session_storage_for_prerendering=*/false); + if (ephemeral_local_storage_ && ephemeral_storage_namespaces_ == namespaces) { + return ephemeral_local_storage_; + } + + ephemeral_storage_namespaces_ = namespaces; + + auto storage_area = + namespaces->local_storage()->GetCachedArea(es_security_origin); // Ephemeral localStorage never persists stored data, which is also how // sessionStorage works. Due to this, when opening up a new ephemeral // localStorage area, we use the sessionStorage infrastructure. - ephemeral_local_storage_ = - StorageArea::Create(window, std::move(storage_area), - StorageArea::StorageType::kSessionStorage); + ephemeral_local_storage_ = StorageArea::Create( + window, std::move(storage_area), StorageArea::StorageType::kLocalStorage); return ephemeral_local_storage_; } void BraveDOMWindowStorage::Trace(Visitor* visitor) const { + visitor->Trace(ephemeral_storage_namespaces_); visitor->Trace(ephemeral_session_storage_); visitor->Trace(ephemeral_local_storage_); Supplement::Trace(visitor); diff --git a/components/content_settings/renderer/brave_content_settings_agent_impl.cc b/components/content_settings/renderer/brave_content_settings_agent_impl.cc index cf9d2975e2dc..fba3d5ecd08c 100644 --- a/components/content_settings/renderer/brave_content_settings_agent_impl.cc +++ b/components/content_settings/renderer/brave_content_settings_agent_impl.cc @@ -133,19 +133,20 @@ void BraveContentSettingsAgentImpl::DidNotAllowScript() { ContentSettingsAgentImpl::DidNotAllowScript(); } -bool BraveContentSettingsAgentImpl::UseEphemeralStorageSync( +absl::optional +BraveContentSettingsAgentImpl::UseEphemeralStorageSync( StorageType storage_type) { if (!base::FeatureList::IsEnabled(net::features::kBraveEphemeralStorage)) - return false; + return absl::nullopt; if (storage_type != StorageType::kLocalStorage && storage_type != StorageType::kSessionStorage) - return false; + return absl::nullopt; blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); if (!frame || IsFrameWithOpaqueOrigin(frame)) - return false; + return absl::nullopt; auto frame_origin = url::Origin(frame->GetSecurityOrigin()); StoragePermissionsKey key(frame_origin, storage_type); @@ -154,12 +155,15 @@ bool BraveContentSettingsAgentImpl::UseEphemeralStorageSync( return permissions->second; auto top_origin = url::Origin(frame->Top()->GetSecurityOrigin()); - if (net::registry_controlled_domains::SameDomainOrHost( + if (!base::FeatureList::IsEnabled( + net::features::kBraveFirstPartyEphemeralStorage) && + net::registry_controlled_domains::SameDomainOrHost( top_origin, frame_origin, - net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) - return false; + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) { + return absl::nullopt; + } - bool result = false; + absl::optional result; GetContentSettingsManager().AllowEphemeralStorageAccess( routing_id(), ConvertToMojoStorageType(storage_type), frame_origin, frame->GetDocument().SiteForCookies().RepresentativeUrl(), top_origin, diff --git a/components/content_settings/renderer/brave_content_settings_agent_impl.h b/components/content_settings/renderer/brave_content_settings_agent_impl.h index ccdc7620c1d7..2d43c3541018 100644 --- a/components/content_settings/renderer/brave_content_settings_agent_impl.h +++ b/components/content_settings/renderer/brave_content_settings_agent_impl.h @@ -46,7 +46,8 @@ class BraveContentSettingsAgentImpl const blink::WebURL& script_url) override; void DidNotAllowScript() override; - bool UseEphemeralStorageSync(StorageType storage_type) override; + absl::optional UseEphemeralStorageSync( + StorageType storage_type) override; bool AllowStorageAccessSync(StorageType storage_type) override; void BraveSpecificDidBlockJavaScript(const std::u16string& details); @@ -96,7 +97,8 @@ class BraveContentSettingsAgentImpl base::flat_set preloaded_temporarily_allowed_scripts_; using StoragePermissionsKey = std::pair; - base::flat_map cached_storage_permissions_; + base::flat_map> + cached_storage_permissions_; mojo::AssociatedRemote brave_shields_remote_; diff --git a/patches/components-content_settings-browser-content_settings_manager_impl.cc.patch b/patches/components-content_settings-browser-content_settings_manager_impl.cc.patch new file mode 100644 index 000000000000..8b167df6a094 --- /dev/null +++ b/patches/components-content_settings-browser-content_settings_manager_impl.cc.patch @@ -0,0 +1,12 @@ +diff --git a/components/content_settings/browser/content_settings_manager_impl.cc b/components/content_settings/browser/content_settings_manager_impl.cc +index 59fb64215055341548f1131d394678485ca9fce3..ea8f23ffceedbc0b1b53f048859ce17f599b3bc9 100644 +--- a/components/content_settings/browser/content_settings_manager_impl.cc ++++ b/components/content_settings/browser/content_settings_manager_impl.cc +@@ -87,6 +87,7 @@ void ContentSettingsManagerImpl::AllowStorageAccess( + + bool allowed = cookie_settings_->IsCookieAccessAllowed(url, site_for_cookies, + top_frame_origin); ++ BRAVE_CONTENT_SETTINGS_MANAGER_IMPL_ALLOW_STORAGE_ACCESS + if (delegate_->AllowStorageAccess(render_process_id_, render_frame_id, + storage_type, url, allowed, &callback)) { + DCHECK(!callback); diff --git a/patches/components-content_settings-core-browser-cookie_settings.cc.patch b/patches/components-content_settings-core-browser-cookie_settings.cc.patch new file mode 100644 index 000000000000..616b66b678e6 --- /dev/null +++ b/patches/components-content_settings-core-browser-cookie_settings.cc.patch @@ -0,0 +1,12 @@ +diff --git a/components/content_settings/core/browser/cookie_settings.cc b/components/content_settings/core/browser/cookie_settings.cc +index b1c017a14bbf260770cd55178aa2376b921d7e1f..80122ae257764851ff2c02be4f834eef77bbbdae 100644 +--- a/components/content_settings/core/browser/cookie_settings.cc ++++ b/components/content_settings/core/browser/cookie_settings.cc +@@ -190,6 +190,7 @@ ContentSetting CookieSettings::GetCookieSettingInternal( + // We should always have a value, at least from the default provider. + DCHECK(value); + ContentSetting setting = ValueToContentSetting(value.get()); ++ BRAVE_COOKIE_SETTINGS_GET_COOKIES_SETTINGS_INTERNAL + bool block = block_third && is_third_party_request; + + if (!block) { diff --git a/patches/content-browser-devtools-protocol-network_handler.cc.patch b/patches/content-browser-devtools-protocol-network_handler.cc.patch new file mode 100644 index 000000000000..b0081fe493f7 --- /dev/null +++ b/patches/content-browser-devtools-protocol-network_handler.cc.patch @@ -0,0 +1,41 @@ +diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc +index 54b80ef80f263a1f131d5a0e8beaaf90db318d4d..06add0fd8b3c6043ecb84135fadee9a26f8f2bc6 100644 +--- a/content/browser/devtools/protocol/network_handler.cc ++++ b/content/browser/devtools/protocol/network_handler.cc +@@ -198,10 +198,12 @@ class CookieRetrieverNetworkService + public: + static void Retrieve(network::mojom::CookieManager* cookie_manager, + const std::vector urls, ++ BRAVE_COOKIE_RETRIEVER_NETWORK_SERVICE_RETRIEVE_ARGS + std::unique_ptr callback) { + scoped_refptr self = + new CookieRetrieverNetworkService(std::move(callback)); + net::CookieOptions cookie_options = net::CookieOptions::MakeAllInclusive(); ++ BRAVE_COOKIE_RETRIEVER_NETWORK_SERVICE_RETRIEVE_BODY + for (const auto& url : urls) { + cookie_manager->GetCookieList( + url, cookie_options, +@@ -1308,7 +1310,6 @@ void NetworkHandler::ClearBrowserCookies( + callback->sendFailure(Response::InternalError()); + return; + } +- + storage_partition_->GetCookieManagerForBrowserProcess()->DeleteCookies( + network::mojom::CookieDeletionFilter::New(), + base::BindOnce([](std::unique_ptr callback, +@@ -1326,6 +1327,7 @@ void NetworkHandler::GetCookies(Maybe> protocol_urls, + + CookieRetrieverNetworkService::Retrieve( + storage_partition_->GetCookieManagerForBrowserProcess(), urls, ++ BRAVE_NETWORK_HANDLER_GET_COOKIES_RETREIVE_CALL_ARGS + std::move(callback)); + } + +@@ -1382,6 +1384,7 @@ void NetworkHandler::SetCookie(const std::string& name, + options.set_same_site_cookie_context( + net::CookieOptions::SameSiteCookieContext::MakeInclusive()); + options.set_include_httponly(); ++ BRAVE_NETWORK_HANDLER_SET_COOKIES_SET_COOKIE_OPTIONS + storage_partition_->GetCookieManagerForBrowserProcess()->SetCanonicalCookie( + *cookie, net::cookie_util::SimulatedCookieSource(*cookie, "https"), + options, diff --git a/patches/services-network-cookie_settings.cc.patch b/patches/services-network-cookie_settings.cc.patch new file mode 100644 index 000000000000..3407d3043088 --- /dev/null +++ b/patches/services-network-cookie_settings.cc.patch @@ -0,0 +1,12 @@ +diff --git a/services/network/cookie_settings.cc b/services/network/cookie_settings.cc +index 3cb2c78b4934db3415e72be8401c1e92e0b678b4..3cd1fdc9a767e6dd1cb06a85e1624f9f526dd7af 100644 +--- a/services/network/cookie_settings.cc ++++ b/services/network/cookie_settings.cc +@@ -130,6 +130,7 @@ ContentSetting CookieSettings::GetCookieSettingInternal( + // Site-specific settings override the global "block third-party cookies" + // setting. + // Note: global settings are implemented as a catch-all (*, *) pattern. ++ BRAVE_COOKIE_SETTINGS_GET_COOKIE_SETTINGS_INTERNAL + if (IsExplicitSetting(*entry)) + blocked_by_third_party_setting = false; + } diff --git a/patches/third_party-blink-renderer-modules-storage-cached_storage_area.cc.patch b/patches/third_party-blink-renderer-modules-storage-cached_storage_area.cc.patch new file mode 100644 index 000000000000..3a338680d45f --- /dev/null +++ b/patches/third_party-blink-renderer-modules-storage-cached_storage_area.cc.patch @@ -0,0 +1,12 @@ +diff --git a/third_party/blink/renderer/modules/storage/cached_storage_area.cc b/third_party/blink/renderer/modules/storage/cached_storage_area.cc +index 4b98ae5eb00fa84a42133be5a77e38adb636cceb..5fd6b337b66b52f9234e307aeb1c2d8edb9d2468 100644 +--- a/third_party/blink/renderer/modules/storage/cached_storage_area.cc ++++ b/third_party/blink/renderer/modules/storage/cached_storage_area.cc +@@ -566,6 +566,7 @@ void CachedStorageArea::MaybeApplyNonLocalMutationForKey( + } + + void CachedStorageArea::EnsureLoaded() { ++ BRAVE_CACHED_STORAGE_AREA_ENSURE_LOADED + if (map_) + return; +