diff --git a/browser/brave_content_browser_client.cc b/browser/brave_content_browser_client.cc index 7e0526485253..77ac85f0d40b 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -19,6 +19,7 @@ #include "brave/browser/brave_browser_process.h" #include "brave/browser/brave_shields/brave_shields_web_contents_observer.h" #include "brave/browser/debounce/debounce_service_factory.h" +#include "brave/browser/ephemeral_storage/ephemeral_storage_service_factory.h" #include "brave/browser/ethereum_remote_client/buildflags/buildflags.h" #include "brave/browser/net/brave_proxying_url_loader_factory.h" #include "brave/browser/net/brave_proxying_web_socket.h" @@ -816,6 +817,7 @@ BraveContentBrowserClient::CreateThrottlesForNavigation( brave_shields::DomainBlockNavigationThrottle::MaybeCreateThrottleFor( handle, g_brave_browser_process->ad_block_service(), g_brave_browser_process->ad_block_custom_filters_service(), + EphemeralStorageServiceFactory::GetForContext(context), HostContentSettingsMapFactory::GetForProfile( Profile::FromBrowserContext(context)), g_browser_process->GetApplicationLocale())) diff --git a/browser/ephemeral_storage/BUILD.gn b/browser/ephemeral_storage/BUILD.gn deleted file mode 100644 index 1fbe70385137..000000000000 --- a/browser/ephemeral_storage/BUILD.gn +++ /dev/null @@ -1,28 +0,0 @@ -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 = [ - "//base", - "//brave/common", - "//brave/components/brave_shields/browser:browser", - "//brave/components/brave_shields/common:common", - "//chrome/browser", - "//chrome/browser/profiles:profile", - "//chrome/browser/ui", - "//chrome/test:test_support_ui", - "//content/public/common", - "//content/test:test_support", - "//net", - "//services/network:test_support", - "//third_party/blink/public/common", - "//url", - ] - } -} diff --git a/browser/ephemeral_storage/ephemeral_storage_1p_browsertest.cc b/browser/ephemeral_storage/ephemeral_storage_1p_browsertest.cc index a264c09f84f6..b4a044ab2e41 100644 --- a/browser/ephemeral_storage/ephemeral_storage_1p_browsertest.cc +++ b/browser/ephemeral_storage/ephemeral_storage_1p_browsertest.cc @@ -6,7 +6,6 @@ #include "brave/browser/ephemeral_storage/ephemeral_storage_browsertest.h" #include "base/strings/strcat.h" -#include "base/test/bind.h" #include "brave/components/brave_shields/browser/brave_shields_util.h" #include "chrome/browser/content_settings/cookie_settings_factory.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" @@ -32,64 +31,6 @@ class EphemeralStorage1pBrowserTest : public EphemeralStorageBrowserTest { } ~EphemeralStorage1pBrowserTest() override {} - void SetCookieSetting(const GURL& url, ContentSetting content_setting) { - auto* host_content_settings_map = - HostContentSettingsMapFactory::GetForProfile(browser()->profile()); - host_content_settings_map->SetContentSettingCustomScope( - ContentSettingsPattern::FromString( - base::StrCat({"[*.]", url.host_piece(), ":*"})), - ContentSettingsPattern::Wildcard(), ContentSettingsType::COOKIES, - 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(); - } - - HostContentSettingsMap* content_settings() { - return HostContentSettingsMapFactory::GetForProfile(browser()->profile()); - } - - network::mojom::CookieManager* CookieManager() { - return browser() - ->profile() - ->GetDefaultStoragePartition() - ->GetCookieManagerForBrowserProcess(); - } - - std::vector GetAllCookies() { - base::RunLoop run_loop; - std::vector cookies_out; - CookieManager()->GetAllCookies(base::BindLambdaForTesting( - [&](const std::vector& cookies) { - cookies_out = cookies; - run_loop.Quit(); - })); - run_loop.Run(); - return cookies_out; - } - private: base::test::ScopedFeatureList scoped_feature_list_; }; diff --git a/browser/ephemeral_storage/ephemeral_storage_1p_domain_block_browsertest.cc b/browser/ephemeral_storage/ephemeral_storage_1p_domain_block_browsertest.cc new file mode 100644 index 000000000000..ce5d508f08d8 --- /dev/null +++ b/browser/ephemeral_storage/ephemeral_storage_1p_domain_block_browsertest.cc @@ -0,0 +1,190 @@ +/* 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 "base/test/bind.h" +#include "base/test/thread_test_helper.h" +#include "brave/browser/brave_browser_process.h" +#include "brave/components/brave_component_updater/browser/local_data_files_service.h" +#include "brave/components/brave_shields/browser/ad_block_service.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" +#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/test_navigation_observer.h" +#include "net/base/features.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" + +using content::RenderFrameHost; +using content::WebContents; + +class EphemeralStorage1pDomainBlockBrowserTest + : public EphemeralStorageBrowserTest { + public: + EphemeralStorage1pDomainBlockBrowserTest() { + scoped_feature_list_.InitAndEnableFeature( + net::features::kBraveFirstPartyEphemeralStorage); + } + ~EphemeralStorage1pDomainBlockBrowserTest() override {} + + void SetUpOnMainThread() override { + EphemeralStorageBrowserTest::SetUpOnMainThread(); + } + + void UpdateAdBlockInstanceWithRules(const std::string& rules, + const std::string& resources = "") { + brave_shields::AdBlockService* ad_block_service = + g_brave_browser_process->ad_block_service(); + ad_block_service->GetTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&brave_shields::AdBlockService::ResetForTest, + base::Unretained(ad_block_service), rules, resources)); + WaitForAdBlockServiceThreads(); + } + + void WaitForAdBlockServiceThreads() { + scoped_refptr tr_helper(new base::ThreadTestHelper( + g_brave_browser_process->local_data_files_service()->GetTaskRunner())); + ASSERT_TRUE(tr_helper->Run()); + } + + void BlockDomainByURL(const GURL& url) { + UpdateAdBlockInstanceWithRules("||" + url.host() + "^"); + } + + bool IsShowingInterstitial(WebContents* web_contents) { + return chrome_browser_interstitials::IsShowingInterstitial(web_contents); + } + + void Click(WebContents* web_contents, const std::string& id) { + content::RenderFrameHost* frame = web_contents->GetMainFrame(); + frame->ExecuteJavaScriptForTests( + base::ASCIIToUTF16("document.getElementById('" + id + "').click();\n"), + base::NullCallback()); + } + + void ClickAndWaitForNavigation(WebContents* web_contents, + const std::string& id) { + content::TestNavigationObserver observer( + web_contents, 1, content::MessageLoopRunner::QuitMode::DEFERRED); + Click(web_contents, id); + observer.Wait(); + } + + WebContents* NavigateToBlockedDomain() { + brave_shields::SetCosmeticFilteringControlType( + content_settings(), brave_shields::ControlType::BLOCK, + a_site_ephemeral_storage_url_); + BlockDomainByURL(a_site_ephemeral_storage_url_); + + WebContents* first_party_tab = + LoadURLInNewTab(a_site_ephemeral_storage_url_); + EXPECT_TRUE(IsShowingInterstitial(first_party_tab)); + Click(first_party_tab, "dont-warn-again-checkbox"); + ClickAndWaitForNavigation(first_party_tab, "primary-button"); + + // 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); + } + + return first_party_tab; + } + + void NavigateToBlockedDomainAndExpectEphemeralEnabled() { + WebContents* first_party_tab = NavigateToBlockedDomain(); + + // After keepalive values should be cleared. + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), b_site_ephemeral_storage_url_)); + WaitForCleanupAfterKeepAlive(); + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_)); + + ExpectValuesFromFramesAreEmpty(FROM_HERE, + GetValuesFromFrames(first_party_tab)); + } + + void NavigateToBlockedDomainAndExpectNotEphemeral() { + WebContents* first_party_tab = NavigateToBlockedDomain(); + + // After keepalive main frame values should not be cleared. + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), b_site_ephemeral_storage_url_)); + WaitForCleanupAfterKeepAlive(); + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_)); + + { + ValuesFromFrames first_party_values = + GetValuesFromFrames(first_party_tab); + EXPECT_EQ("a.com", first_party_values.main_frame.local_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_1.local_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_2.local_storage); + + EXPECT_EQ("a.com", 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=a.com", first_party_values.main_frame.cookies); + EXPECT_EQ("", first_party_values.iframe_1.cookies); + EXPECT_EQ("", first_party_values.iframe_2.cookies); + } + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F( + EphemeralStorage1pDomainBlockBrowserTest, + FirstPartyEphemeralIsEnabledAfterIntersisitalProcessing) { + brave_shields::SetCosmeticFilteringControlType( + content_settings(), brave_shields::ControlType::BLOCK, + a_site_ephemeral_storage_url_); + BlockDomainByURL(a_site_ephemeral_storage_url_); + + NavigateToBlockedDomainAndExpectEphemeralEnabled(); +} + +IN_PROC_BROWSER_TEST_F(EphemeralStorage1pDomainBlockBrowserTest, + FirstPartyEphemeralIsNotEnabledIfCookiesStored) { + ASSERT_TRUE(content::SetCookie(browser()->profile(), + a_site_ephemeral_storage_url_, + "from=a.com;SameSite=None;Secure")); + + NavigateToBlockedDomainAndExpectNotEphemeral(); +} + +IN_PROC_BROWSER_TEST_F( + EphemeralStorage1pDomainBlockBrowserTest, + FirstPartyEphemeralIsNotEnabledIfLocalStorageDataStored) { + // Store local storage value in a.com. + WebContents* first_party_tab = LoadURLInNewTab(a_site_ephemeral_storage_url_); + SetStorageValueInFrame(first_party_tab->GetMainFrame(), "a.com", + StorageType::Local); + // Navigate away to b.com. + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), b_site_ephemeral_storage_url_)); + + NavigateToBlockedDomainAndExpectNotEphemeral(); +} diff --git a/browser/ephemeral_storage/ephemeral_storage_browsertest.cc b/browser/ephemeral_storage/ephemeral_storage_browsertest.cc index 40ffbda3f3a3..72815ea76f6f 100644 --- a/browser/ephemeral_storage/ephemeral_storage_browsertest.cc +++ b/browser/ephemeral_storage/ephemeral_storage_browsertest.cc @@ -10,16 +10,20 @@ #include "base/path_service.h" #include "base/strings/strcat.h" #include "base/strings/stringprintf.h" +#include "base/test/bind.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/time/time.h" #include "brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.h" #include "brave/common/brave_paths.h" #include "brave/components/brave_shields/browser/brave_shields_util.h" #include "brave/components/brave_shields/common/brave_shield_constants.h" +#include "chrome/browser/content_settings/cookie_settings_factory.h" #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/ui_test_utils.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/network_session_configurator/common/network_switches.h" #include "components/prefs/pref_service.h" #include "content/public/browser/notification_types.h" @@ -304,6 +308,66 @@ content::EvalJsResult EphemeralStorageBrowserTest::GetBroadcastMessage( return content::EvalJs(frame, "self.bc_message"); } +void EphemeralStorageBrowserTest::SetCookieSetting( + const GURL& url, + ContentSetting content_setting) { + auto* host_content_settings_map = + HostContentSettingsMapFactory::GetForProfile(browser()->profile()); + host_content_settings_map->SetContentSettingCustomScope( + ContentSettingsPattern::FromString( + base::StrCat({"[*.]", url.host_piece(), ":*"})), + ContentSettingsPattern::Wildcard(), ContentSettingsType::COOKIES, + content_setting); +} + +// Helper to load easy-to-use Indexed DB API. +void EphemeralStorageBrowserTest::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 EphemeralStorageBrowserTest::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(); +} + +HostContentSettingsMap* EphemeralStorageBrowserTest::content_settings() { + return HostContentSettingsMapFactory::GetForProfile(browser()->profile()); +} + +network::mojom::CookieManager* EphemeralStorageBrowserTest::CookieManager() { + return browser() + ->profile() + ->GetDefaultStoragePartition() + ->GetCookieManagerForBrowserProcess(); +} + +std::vector EphemeralStorageBrowserTest::GetAllCookies() { + base::RunLoop run_loop; + std::vector cookies_out; + CookieManager()->GetAllCookies(base::BindLambdaForTesting( + [&](const std::vector& cookies) { + cookies_out = cookies; + run_loop.Quit(); + })); + run_loop.Run(); + return cookies_out; +} + IN_PROC_BROWSER_TEST_F(EphemeralStorageBrowserTest, StorageIsPartitioned) { WebContents* first_party_tab = LoadURLInNewTab(b_site_ephemeral_storage_url_); WebContents* site_a_tab1 = diff --git a/browser/ephemeral_storage/ephemeral_storage_browsertest.h b/browser/ephemeral_storage/ephemeral_storage_browsertest.h index 58721250c2f8..3ff0e6f77fc1 100644 --- a/browser/ephemeral_storage/ephemeral_storage_browsertest.h +++ b/browser/ephemeral_storage/ephemeral_storage_browsertest.h @@ -14,8 +14,11 @@ #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 "services/network/public/mojom/cookie_manager.mojom.h" #include "url/gurl.h" +class HostContentSettingsMap; + class EphemeralStorageBrowserTest : public InProcessBrowserTest { public: enum StorageType { Session, Local }; @@ -86,6 +89,16 @@ class EphemeralStorageBrowserTest : public InProcessBrowserTest { void ClearBroadcastMessage(content::RenderFrameHost* frame); content::EvalJsResult GetBroadcastMessage(content::RenderFrameHost* frame); + void SetCookieSetting(const GURL& url, ContentSetting content_setting); + + // Helper to load easy-to-use Indexed DB API. + void LoadIndexedDbHelper(content::RenderFrameHost* host); + bool SetIDBValue(content::RenderFrameHost* host); + + HostContentSettingsMap* content_settings(); + network::mojom::CookieManager* CookieManager(); + std::vector GetAllCookies(); + protected: void SetUpHttpsServer(); diff --git a/browser/ephemeral_storage/ephemeral_storage_service_factory.cc b/browser/ephemeral_storage/ephemeral_storage_service_factory.cc new file mode 100644 index 000000000000..ed0224c924ed --- /dev/null +++ b/browser/ephemeral_storage/ephemeral_storage_service_factory.cc @@ -0,0 +1,52 @@ +/* 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_service_factory.h" + +#include + +#include "base/feature_list.h" +#include "brave/components/ephemeral_storage/ephemeral_storage_service.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "net/base/features.h" + +// static +EphemeralStorageServiceFactory* EphemeralStorageServiceFactory::GetInstance() { + return base::Singleton::get(); +} + +// static +ephemeral_storage::EphemeralStorageService* +EphemeralStorageServiceFactory::GetForContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +EphemeralStorageServiceFactory::EphemeralStorageServiceFactory() + : BrowserContextKeyedServiceFactory( + "EphemeralStorageService", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(HostContentSettingsMapFactory::GetInstance()); +} + +EphemeralStorageServiceFactory::~EphemeralStorageServiceFactory() {} + +KeyedService* EphemeralStorageServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + if (!base::FeatureList::IsEnabled( + net::features::kBraveFirstPartyEphemeralStorage)) { + return nullptr; + } + return new ephemeral_storage::EphemeralStorageService( + context, HostContentSettingsMapFactory::GetForProfile(context)); +} + +content::BrowserContext* EphemeralStorageServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextOwnInstanceInIncognito(context); +} diff --git a/browser/ephemeral_storage/ephemeral_storage_service_factory.h b/browser/ephemeral_storage/ephemeral_storage_service_factory.h new file mode 100644 index 000000000000..ad9cb4ca6c07 --- /dev/null +++ b/browser/ephemeral_storage/ephemeral_storage_service_factory.h @@ -0,0 +1,35 @@ +/* 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_SERVICE_FACTORY_H_ +#define BRAVE_BROWSER_EPHEMERAL_STORAGE_EPHEMERAL_STORAGE_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace ephemeral_storage { +class EphemeralStorageService; +} + +class EphemeralStorageServiceFactory + : public BrowserContextKeyedServiceFactory { + public: + static ephemeral_storage::EphemeralStorageService* GetForContext( + content::BrowserContext* context); + static EphemeralStorageServiceFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits; + + EphemeralStorageServiceFactory(); + ~EphemeralStorageServiceFactory() override; + + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +#endif // BRAVE_BROWSER_EPHEMERAL_STORAGE_EPHEMERAL_STORAGE_SERVICE_FACTORY_H_ diff --git a/browser/ephemeral_storage/sources.gni b/browser/ephemeral_storage/sources.gni index b3c454a21b38..71c5aa62175f 100644 --- a/browser/ephemeral_storage/sources.gni +++ b/browser/ephemeral_storage/sources.gni @@ -1,10 +1,13 @@ brave_browser_ephemeral_storage_sources = [ + "//brave/browser/ephemeral_storage/ephemeral_storage_service_factory.cc", + "//brave/browser/ephemeral_storage/ephemeral_storage_service_factory.h", "//brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.cc", "//brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.h", ] brave_browser_ephemeral_storage_deps = [ "//base", + "//brave/components/ephemeral_storage", "//chrome/browser/profiles", "//chrome/browser/ui", "//components/content_settings/core/browser", diff --git a/components/brave_shields/browser/BUILD.gn b/components/brave_shields/browser/BUILD.gn index 965bcbf974bf..f893b448ea29 100644 --- a/components/brave_shields/browser/BUILD.gn +++ b/components/brave_shields/browser/BUILD.gn @@ -56,6 +56,7 @@ static_library("browser") { "//brave/components/content_settings/core/browser", "//brave/components/content_settings/core/common", "//brave/components/debounce/common", + "//brave/components/ephemeral_storage", "//brave/components/p3a", "//brave/components/resources:static_resources_grit", "//brave/components/resources:strings_grit", diff --git a/components/brave_shields/browser/ad_block_base_service.h b/components/brave_shields/browser/ad_block_base_service.h index 207a7f458920..a6ecc919e3fb 100644 --- a/components/brave_shields/browser/ad_block_base_service.h +++ b/components/brave_shields/browser/ad_block_base_service.h @@ -24,6 +24,7 @@ class AdBlockServiceTest; class BraveAdBlockTPNetworkDelegateHelperTest; +class EphemeralStorage1pDomainBlockBrowserTest; class PerfPredictorTabHelperTest; using brave_component_updater::BraveComponent; @@ -69,6 +70,7 @@ class AdBlockBaseService : public BaseBraveShieldsService { protected: friend class ::AdBlockServiceTest; friend class ::BraveAdBlockTPNetworkDelegateHelperTest; + friend class ::EphemeralStorage1pDomainBlockBrowserTest; friend class ::PerfPredictorTabHelperTest; bool Init() override; diff --git a/components/brave_shields/browser/domain_block_controller_client.cc b/components/brave_shields/browser/domain_block_controller_client.cc index 58f354ca597a..c5c0956ca272 100644 --- a/components/brave_shields/browser/domain_block_controller_client.cc +++ b/components/brave_shields/browser/domain_block_controller_client.cc @@ -7,6 +7,7 @@ #include "brave/components/brave_shields/browser/ad_block_custom_filters_service.h" #include "brave/components/brave_shields/browser/domain_block_tab_storage.h" +#include "brave/components/ephemeral_storage/ephemeral_storage_service.h" #include "components/prefs/pref_service.h" #include "components/security_interstitials/content/settings_page_helper.h" #include "components/security_interstitials/core/metrics_helper.h" @@ -32,6 +33,7 @@ DomainBlockControllerClient::DomainBlockControllerClient( content::WebContents* web_contents, const GURL& request_url, AdBlockCustomFiltersService* ad_block_custom_filters_service, + ephemeral_storage::EphemeralStorageService* ephemeral_storage_service, PrefService* prefs, const std::string& locale) : security_interstitials::SecurityInterstitialControllerClient( @@ -43,8 +45,11 @@ DomainBlockControllerClient::DomainBlockControllerClient( nullptr /* settings_page_helper */), request_url_(request_url), ad_block_custom_filters_service_(ad_block_custom_filters_service), + ephemeral_storage_service_(ephemeral_storage_service), dont_warn_again_(false) {} +DomainBlockControllerClient::~DomainBlockControllerClient() = default; + void DomainBlockControllerClient::GoBack() { SecurityInterstitialControllerClient::GoBackAfterNavigationCommitted(); } @@ -59,9 +64,27 @@ void DomainBlockControllerClient::Proceed() { ad_block_custom_filters_service_->UpdateCustomFilters( "@@||" + request_url_.host() + "^\n" + custom_filters); } + if (ephemeral_storage_service_) { + ephemeral_storage_service_->CanEnable1PESForUrl( + request_url_, + base::BindOnce(&DomainBlockControllerClient::OnCanEnable1PESForUrl, + weak_ptr_factory_.GetWeakPtr())); + } else { + ReloadPage(); + } +} + +void DomainBlockControllerClient::ReloadPage() { web_contents_->GetController().Reload(content::ReloadType::NORMAL, false); } +void DomainBlockControllerClient::OnCanEnable1PESForUrl(bool can_enable_1pes) { + if (can_enable_1pes) { + ephemeral_storage_service_->Set1PESEnabledForUrl(request_url_, true); + } + ReloadPage(); +} + void DomainBlockControllerClient::SetDontWarnAgain(bool value) { dont_warn_again_ = value; } diff --git a/components/brave_shields/browser/domain_block_controller_client.h b/components/brave_shields/browser/domain_block_controller_client.h index 27403c8eb039..531866ecefa3 100644 --- a/components/brave_shields/browser/domain_block_controller_client.h +++ b/components/brave_shields/browser/domain_block_controller_client.h @@ -9,6 +9,7 @@ #include #include +#include "base/memory/weak_ptr.h" #include "components/security_interstitials/content/security_interstitial_controller_client.h" #include "url/gurl.h" @@ -20,6 +21,10 @@ namespace security_interstitials { class MetricsHelper; } // namespace security_interstitials +namespace ephemeral_storage { +class EphemeralStorageService; +} // namespace ephemeral_storage + namespace brave_shields { class AdBlockCustomFiltersService; @@ -34,9 +39,10 @@ class DomainBlockControllerClient content::WebContents* web_contents, const GURL& request_url, AdBlockCustomFiltersService* ad_block_custom_filters_service, + ephemeral_storage::EphemeralStorageService* ephemeral_storage_service, PrefService* prefs, const std::string& locale); - ~DomainBlockControllerClient() override = default; + ~DomainBlockControllerClient() override; DomainBlockControllerClient(const DomainBlockControllerClient&) = delete; DomainBlockControllerClient& operator=(const DomainBlockControllerClient&) = @@ -49,9 +55,15 @@ class DomainBlockControllerClient void Proceed() override; private: + void ReloadPage(); + void OnCanEnable1PESForUrl(bool can_enable_1pes); + const GURL request_url_; AdBlockCustomFiltersService* ad_block_custom_filters_service_; + ephemeral_storage::EphemeralStorageService* ephemeral_storage_service_; bool dont_warn_again_; + + base::WeakPtrFactory weak_ptr_factory_{this}; }; } // namespace brave_shields diff --git a/components/brave_shields/browser/domain_block_navigation_throttle.cc b/components/brave_shields/browser/domain_block_navigation_throttle.cc index 397e35985dd5..463f5f3c18e3 100644 --- a/components/brave_shields/browser/domain_block_navigation_throttle.cc +++ b/components/brave_shields/browser/domain_block_navigation_throttle.cc @@ -62,6 +62,7 @@ DomainBlockNavigationThrottle::MaybeCreateThrottleFor( content::NavigationHandle* navigation_handle, AdBlockService* ad_block_service, AdBlockCustomFiltersService* ad_block_custom_filters_service, + ephemeral_storage::EphemeralStorageService* ephemeral_storage_service, HostContentSettingsMap* content_settings, const std::string& locale) { if (!ad_block_service || !ad_block_custom_filters_service) @@ -70,18 +71,20 @@ DomainBlockNavigationThrottle::MaybeCreateThrottleFor( return nullptr; return std::make_unique( navigation_handle, ad_block_service, ad_block_custom_filters_service, - content_settings, locale); + ephemeral_storage_service, content_settings, locale); } DomainBlockNavigationThrottle::DomainBlockNavigationThrottle( content::NavigationHandle* navigation_handle, AdBlockService* ad_block_service, AdBlockCustomFiltersService* ad_block_custom_filters_service, + ephemeral_storage::EphemeralStorageService* ephemeral_storage_service, HostContentSettingsMap* content_settings, const std::string& locale) : content::NavigationThrottle(navigation_handle), ad_block_service_(ad_block_service), ad_block_custom_filters_service_(ad_block_custom_filters_service), + ephemeral_storage_service_(ephemeral_storage_service), content_settings_(content_settings), locale_(locale) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -170,8 +173,8 @@ void DomainBlockNavigationThrottle::ShowInterstitial() { // The controller client implements the actual logic to "go back" or "proceed" // from the interstitial. auto controller_client = std::make_unique( - web_contents, request_url, ad_block_custom_filters_service_, pref_service, - locale_); + web_contents, request_url, ad_block_custom_filters_service_, + ephemeral_storage_service_, pref_service, locale_); // This handles populating the HTML template of the interstitial page with // localized strings and other information we only know at runtime, diff --git a/components/brave_shields/browser/domain_block_navigation_throttle.h b/components/brave_shields/browser/domain_block_navigation_throttle.h index b5256c6933bf..d409be89b0eb 100644 --- a/components/brave_shields/browser/domain_block_navigation_throttle.h +++ b/components/brave_shields/browser/domain_block_navigation_throttle.h @@ -21,6 +21,10 @@ class NavigationHandle; class WebContents; } // namespace content +namespace ephemeral_storage { +class EphemeralStorageService; +} // namespace ephemeral_storage + namespace brave_shields { class AdBlockService; @@ -32,6 +36,7 @@ class DomainBlockNavigationThrottle : public content::NavigationThrottle { content::NavigationHandle* navigation_handle, AdBlockService* ad_block_service, AdBlockCustomFiltersService* ad_block_custom_filters_service, + ephemeral_storage::EphemeralStorageService* ephemeral_storage_service, HostContentSettingsMap* content_settings, const std::string& locale); ~DomainBlockNavigationThrottle() override; @@ -44,6 +49,7 @@ class DomainBlockNavigationThrottle : public content::NavigationThrottle { content::NavigationHandle* navigation_handle, AdBlockService* ad_block_service, AdBlockCustomFiltersService* ad_block_custom_filters_service, + ephemeral_storage::EphemeralStorageService* ephemeral_storage_service, HostContentSettingsMap* content_settings, const std::string& locale); @@ -61,6 +67,8 @@ class DomainBlockNavigationThrottle : public content::NavigationThrottle { AdBlockService* ad_block_service_ = nullptr; AdBlockCustomFiltersService* ad_block_custom_filters_service_ = nullptr; + ephemeral_storage::EphemeralStorageService* ephemeral_storage_service_ = + nullptr; HostContentSettingsMap* content_settings_ = nullptr; std::string locale_; base::WeakPtrFactory weak_ptr_factory_{this}; diff --git a/components/ephemeral_storage/BUILD.gn b/components/ephemeral_storage/BUILD.gn new file mode 100644 index 000000000000..99e029714dea --- /dev/null +++ b/components/ephemeral_storage/BUILD.gn @@ -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/. */ + +static_library("ephemeral_storage") { + sources = [ + "ephemeral_storage_service.cc", + "ephemeral_storage_service.h", + "url_storage_checker.cc", + "url_storage_checker.h", + ] + + deps = [ + "//base", + "//components/content_settings/core/browser", + "//components/keyed_service/core", + "//content/public/browser", + "//net", + "//url", + ] +} diff --git a/components/ephemeral_storage/DEPS b/components/ephemeral_storage/DEPS new file mode 100644 index 000000000000..b13bede23099 --- /dev/null +++ b/components/ephemeral_storage/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+content/public/browser", + "+services/network/public", + "+third_party/blink/public/mojom/dom_storage/storage_area.mojom.h", +] diff --git a/components/ephemeral_storage/ephemeral_storage_service.cc b/components/ephemeral_storage/ephemeral_storage_service.cc new file mode 100644 index 000000000000..aaadef74025d --- /dev/null +++ b/components/ephemeral_storage/ephemeral_storage_service.cc @@ -0,0 +1,58 @@ +/* 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/components/ephemeral_storage/ephemeral_storage_service.h" + +#include + +#include "base/strings/strcat.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "brave/components/ephemeral_storage/url_storage_checker.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/content_settings/core/common/content_settings.h" +#include "content/public/browser/browser_context.h" +#include "net/base/features.h" + +namespace ephemeral_storage { + +EphemeralStorageService::EphemeralStorageService( + content::BrowserContext* context, + HostContentSettingsMap* host_content_settings_map) + : context_(context), host_content_settings_map_(host_content_settings_map) { + DCHECK(base::FeatureList::IsEnabled( + net::features::kBraveFirstPartyEphemeralStorage)); + DCHECK(context_); + DCHECK(host_content_settings_map_); +} + +EphemeralStorageService::~EphemeralStorageService() {} + +void EphemeralStorageService::CanEnable1PESForUrl( + const GURL& url, + base::OnceCallback callback) const { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&UrlStorageChecker::StartCheck, + base::MakeRefCounted( + context_->GetStoragePartitionForUrl(url, true), url, + std::move(callback)))); +} + +void EphemeralStorageService::Set1PESEnabledForUrl(const GURL& url, + bool enable) { + host_content_settings_map_->SetContentSettingCustomScope( + ContentSettingsPattern::FromString( + base::StrCat({"[*.]", url.host_piece(), ":*"})), + ContentSettingsPattern::Wildcard(), ContentSettingsType::COOKIES, + enable ? CONTENT_SETTING_SESSION_ONLY : CONTENT_SETTING_DEFAULT); +} + +bool EphemeralStorageService::Is1PESEnabledForUrl(const GURL& url) const { + return host_content_settings_map_->GetContentSetting( + url, url, ContentSettingsType::COOKIES) == + CONTENT_SETTING_SESSION_ONLY; +} + +} // namespace ephemeral_storage diff --git a/components/ephemeral_storage/ephemeral_storage_service.h b/components/ephemeral_storage/ephemeral_storage_service.h new file mode 100644 index 000000000000..399d5407ba9d --- /dev/null +++ b/components/ephemeral_storage/ephemeral_storage_service.h @@ -0,0 +1,50 @@ +/* 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_COMPONENTS_EPHEMERAL_STORAGE_EPHEMERAL_STORAGE_SERVICE_H_ +#define BRAVE_COMPONENTS_EPHEMERAL_STORAGE_EPHEMERAL_STORAGE_SERVICE_H_ + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "components/keyed_service/core/keyed_service.h" +#include "url/gurl.h" + +class HostContentSettingsMap; + +namespace content { +class BrowserContext; +class StoragePartition; +} // namespace content + +namespace ephemeral_storage { + +// Service to enable or disable first party ephemeral storage from external +// actors. +class EphemeralStorageService + : public KeyedService, + public base::SupportsWeakPtr { + public: + EphemeralStorageService(content::BrowserContext* context, + HostContentSettingsMap* host_content_settings_map); + ~EphemeralStorageService() override; + + // Performs storage check (cookies, localStorage) and callbacks `true` if + // nothing is stored in all of these storages. + void CanEnable1PESForUrl( + const GURL& url, + base::OnceCallback callback) const; + // Enables/disables first party ephemeral storage for |url|. + void Set1PESEnabledForUrl(const GURL& url, bool enable); + // Returns current state of first party ephemeral storage mode for |url|. + bool Is1PESEnabledForUrl(const GURL& url) const; + + private: + content::BrowserContext* context_ = nullptr; + HostContentSettingsMap* host_content_settings_map_ = nullptr; +}; + +} // namespace ephemeral_storage + +#endif // BRAVE_COMPONENTS_EPHEMERAL_STORAGE_EPHEMERAL_STORAGE_SERVICE_H_ diff --git a/components/ephemeral_storage/url_storage_checker.cc b/components/ephemeral_storage/url_storage_checker.cc new file mode 100644 index 000000000000..4827c61a6c19 --- /dev/null +++ b/components/ephemeral_storage/url_storage_checker.cc @@ -0,0 +1,64 @@ +/* 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/components/ephemeral_storage/url_storage_checker.h" + +#include + +#include "components/services/storage/public/mojom/local_storage_control.mojom.h" +#include "content/public/browser/storage_partition.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" + +namespace ephemeral_storage { + +UrlStorageChecker::UrlStorageChecker( + content::StoragePartition* storage_partition, + const GURL& url, + Callback callback) + : storage_partition_(storage_partition), + url_(url), + callback_(std::move(callback)) { + DCHECK(storage_partition_); + DCHECK(url_.is_valid()); + DCHECK(callback_); +} + +UrlStorageChecker::~UrlStorageChecker() = default; + +void UrlStorageChecker::StartCheck() { + storage_partition_->GetCookieManagerForBrowserProcess()->GetCookieList( + url_, net::CookieOptions::MakeAllInclusive(), + base::BindOnce(&UrlStorageChecker::OnGetCookieList, this)); +} + +void UrlStorageChecker::OnGetCookieList( + const std::vector& included_cookies, + const std::vector& excluded_cookies) { + if (!included_cookies.empty()) { + std::move(callback_).Run(false); + return; + } + + storage_partition_->GetLocalStorageControl()->BindStorageArea( + blink::StorageKey(url::Origin::Create(url_)), + local_storage_area_.BindNewPipeAndPassReceiver()); + local_storage_area_->GetAll( + {}, base::BindOnce(&UrlStorageChecker::OnGetLocalStorageData, this)); +} + +void UrlStorageChecker::OnGetLocalStorageData( + std::vector local_storage_data) { + if (!local_storage_data.empty()) { + std::move(callback_).Run(false); + return; + } + + std::move(callback_).Run(true); +} + +} // namespace ephemeral_storage diff --git a/components/ephemeral_storage/url_storage_checker.h b/components/ephemeral_storage/url_storage_checker.h new file mode 100644 index 000000000000..6f560a218dd5 --- /dev/null +++ b/components/ephemeral_storage/url_storage_checker.h @@ -0,0 +1,57 @@ +/* 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_COMPONENTS_EPHEMERAL_STORAGE_URL_STORAGE_CHECKER_H_ +#define BRAVE_COMPONENTS_EPHEMERAL_STORAGE_URL_STORAGE_CHECKER_H_ + +#include + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_refptr.h" +#include "components/services/storage/public/mojom/local_storage_control.mojom.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/cookies/canonical_cookie.h" +#include "third_party/blink/public/mojom/dom_storage/storage_area.mojom.h" +#include "url/gurl.h" + +namespace content { +class StoragePartition; +} // namespace content + +namespace ephemeral_storage { + +// Performs cookies and localStorage data existence check for a URL. +class UrlStorageChecker : public base::RefCounted { + public: + using Callback = base::OnceCallback; + + UrlStorageChecker(content::StoragePartition* storage_partition, + const GURL& url, + Callback callback); + + void StartCheck(); + + private: + friend class base::RefCounted; + ~UrlStorageChecker(); + + void OnGetCookieList( + const std::vector& included_cookies, + const std::vector& excluded_cookies); + + void OnGetLocalStorageData( + std::vector local_storage_data); + + content::StoragePartition* storage_partition_ = nullptr; + GURL url_; + Callback callback_; + + mojo::Remote local_storage_area_; +}; + +} // namespace ephemeral_storage + +#endif // BRAVE_COMPONENTS_EPHEMERAL_STORAGE_URL_STORAGE_CHECKER_H_ diff --git a/test/BUILD.gn b/test/BUILD.gn index 92178d3cbc34..e6202e2f1630 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -612,6 +612,11 @@ if (!is_android) { "//brave/browser/brave_stats/brave_stats_updater_browsertest.cc", "//brave/browser/debounce/debounce_browsertest.cc", "//brave/browser/devtools/brave_devtools_ui_bindings_browsertest.cc", + "//brave/browser/ephemeral_storage/ephemeral_storage_1p_browsertest.cc", + "//brave/browser/ephemeral_storage/ephemeral_storage_1p_domain_block_browsertest.cc", + "//brave/browser/ephemeral_storage/ephemeral_storage_browsertest.cc", + "//brave/browser/ephemeral_storage/ephemeral_storage_browsertest.h", + "//brave/browser/ephemeral_storage/ephemeral_storage_qa_browsertest.cc", "//brave/browser/extensions/api/brave_shields_api_browsertest.cc", "//brave/browser/extensions/api/brave_theme_api_browsertest.cc", "//brave/browser/extensions/brave_base_local_data_files_browsertest.cc", @@ -892,7 +897,6 @@ if (!is_android) { "//brave/app/theme:brave_unscaled_resources_grit", "//brave/browser", "//brave/browser/devtools", - "//brave/browser/ephemeral_storage:ephemeral_storage_tests", "//brave/browser/ethereum_remote_client/buildflags", "//brave/browser/extensions", "//brave/browser/net",