Implement proper in-memory ephemeral localStorage support.

This commit is contained in:
Aleksey Khoroshilov
2021-07-23 17:52:26 +07:00
parent 1759dd51ad
commit 1a67fade4f
24 changed files with 526 additions and 154 deletions
-16
View File
@@ -1,18 +1,3 @@
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
@@ -22,7 +7,6 @@ if (!is_android) {
]
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
deps = [
":ephemeral_storage",
"//base",
"//brave/common",
"//brave/components/brave_shields/browser:browser",
@@ -706,6 +706,67 @@ IN_PROC_BROWSER_TEST_F(EphemeralStorageBrowserTest, NetworkCookiesAreSetIn3p) {
EXPECT_EQ("", site_b_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);
// Set values in the first tab. The second tab should see changes.
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,
FirstPartyNestedInThirdParty) {
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
@@ -6,12 +6,16 @@
#include "brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.h"
#include <map>
#include <memory>
#include <set>
#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"
@@ -31,7 +35,6 @@ namespace {
// TODO(bridiver) - share these constants with DOMWindowStorage
constexpr char kSessionStorageSuffix[] = "/ephemeral-session-storage";
constexpr char kLocalStorageSuffix[] = "/ephemeral-local-storage";
base::TimeDelta g_storage_keep_alive_for_testing = base::TimeDelta::Min();
@@ -47,6 +50,23 @@ std::string StringToSessionStorageId(const std::string& string,
return hash;
}
class EphemeralStorageOriginsSourceImpl
: public content::TLDEphemeralLifetime::EphemeralStorageOriginsSource {
public:
EphemeralStorageOriginsSourceImpl(
scoped_refptr<content_settings::CookieSettings> cookie_settings)
: cookie_settings_(std::move(cookie_settings)) {}
std::vector<url::Origin> TakeEphemeralStorageOpaqueOrigins(
const std::string& ephemeral_storage_domain) override {
return cookie_settings_->TakeEphemeralStorageOpaqueOrigins(
ephemeral_storage_domain);
}
private:
scoped_refptr<content_settings::CookieSettings> cookie_settings_;
};
} // namespace
// EphemeralStorageTabHelper helps to manage the lifetime of ephemeral storage.
@@ -68,7 +88,6 @@ EphemeralStorageTabHelper::~EphemeralStorageTabHelper() {}
void EphemeralStorageTabHelper::WebContentsDestroyed() {
keep_alive_tld_ephemeral_lifetime_list_.clear();
keep_alive_local_storage_list_.clear();
}
void EphemeralStorageTabHelper::ReadyToCommitNavigation(
@@ -91,9 +110,6 @@ void EphemeralStorageTabHelper::ReadyToCommitNavigation(
void EphemeralStorageTabHelper::ClearEphemeralLifetimeKeepalive(
const content::TLDEphemeralLifetimeKey& key) {
ClearLocalStorageKeepAlive(
StringToSessionStorageId(key.second, kLocalStorageSuffix));
auto it = base::ranges::find_if(keep_alive_tld_ephemeral_lifetime_list_,
[&key](const auto& tld_ephermal_liftime) {
return tld_ephermal_liftime->key() == key;
@@ -102,15 +118,6 @@ void EphemeralStorageTabHelper::ClearEphemeralLifetimeKeepalive(
keep_alive_tld_ephemeral_lifetime_list_.erase(it);
}
void EphemeralStorageTabHelper::ClearLocalStorageKeepAlive(
const std::string& id) {
auto it = base::ranges::find_if(
keep_alive_local_storage_list_,
[&id](const auto& local_storage) { return local_storage->id() == id; });
if (it != keep_alive_local_storage_list_.end())
keep_alive_local_storage_list_.erase(it);
}
void EphemeralStorageTabHelper::CreateEphemeralStorageAreasForDomainAndURL(
const std::string& new_domain,
const GURL& new_url) {
@@ -126,7 +133,6 @@ void EphemeralStorageTabHelper::CreateEphemeralStorageAreasForDomainAndURL(
net::features::kBraveEphemeralStorageKeepAlive) &&
tld_ephemeral_lifetime_) {
keep_alive_tld_ephemeral_lifetime_list_.push_back(tld_ephemeral_lifetime_);
keep_alive_local_storage_list_.push_back(local_storage_namespace_);
// keep the ephemeral storage alive for some time to handle redirects
// including meta refresh or other page driven "redirects" that end up back
@@ -143,15 +149,6 @@ void EphemeralStorageTabHelper::CreateEphemeralStorageAreasForDomainAndURL(
: g_storage_keep_alive_for_testing);
}
// This will fetch a session storage namespace for this storage partition
// and storage domain. If another tab helper is already using the same
// namespace, this will just give us a new reference. When the last tab helper
// drops the reference, the namespace should be deleted.
std::string local_partition_id =
StringToSessionStorageId(new_domain, kLocalStorageSuffix);
local_storage_namespace_ = content::CreateSessionStorageNamespace(
partition, local_partition_id, absl::nullopt);
// Session storage is always per-tab and never per-TLD, so we always delete
// and recreate the session storage when switching domains.
//
@@ -176,7 +173,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<EphemeralStorageOriginsSourceImpl>(
CookieSettingsFactory::GetForProfile(
Profile::FromBrowserContext(browser_context))));
}
// static
@@ -45,20 +45,16 @@ class EphemeralStorageTabHelper
void ClearEphemeralLifetimeKeepalive(
const content::TLDEphemeralLifetimeKey& key);
void ClearLocalStorageKeepAlive(const std::string& id);
void CreateEphemeralStorageAreasForDomainAndURL(const std::string& new_domain,
const GURL& new_url);
friend class content::WebContentsUserData<EphemeralStorageTabHelper>;
scoped_refptr<content::SessionStorageNamespace> local_storage_namespace_;
scoped_refptr<content::SessionStorageNamespace> session_storage_namespace_;
scoped_refptr<content::TLDEphemeralLifetime> tld_ephemeral_lifetime_;
std::vector<scoped_refptr<content::TLDEphemeralLifetime>>
keep_alive_tld_ephemeral_lifetime_list_;
std::vector<scoped_refptr<content::SessionStorageNamespace>>
keep_alive_local_storage_list_;
base::WeakPtrFactory<EphemeralStorageTabHelper> weak_factory_{this};
+14
View File
@@ -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",
]
-1
View File
@@ -13,7 +13,6 @@ if (!is_android) {
deps = [
"//brave/browser",
"//brave/browser/ephemeral_storage",
"//chrome/browser",
"//chrome/browser/ui",
"//chrome/test:test_support",
+3 -1
View File
@@ -14,6 +14,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")
@@ -93,7 +94,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",
@@ -340,6 +340,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
@@ -361,6 +362,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
@@ -15,9 +15,13 @@ void ContentSettingsManagerImpl::AllowEphemeralStorageAccess(
const url::Origin& origin,
const GURL& site_for_cookies,
const url::Origin& top_frame_origin,
base::OnceCallback<void(bool)> 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<url::Origin>(storage_origin)
: absl::nullopt);
}
} // namespace content_settings
@@ -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<void(bool)> callback) override; \
AllowEphemeralStorageAccessCallback callback) override; \
void OnContentBlocked
#include "../../../../../components/content_settings/browser/content_settings_manager_impl.h"
@@ -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);
};
@@ -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",
]
@@ -0,0 +1,77 @@
/* 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 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<url::Origin>& 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;
}
} else {
ephemeral_storage_origins_it =
ephemeral_storage_origins_
.emplace(ephemeral_storage_domain,
EphemeralStorageOrigins::mapped_type())
.first;
}
url::Origin opaque_origin = origin.DeriveNewOpaqueOrigin();
ephemeral_storage_origins_it->second[origin] = opaque_origin;
storage_origin = std::move(opaque_origin);
return true;
}
std::vector<url::Origin> CookieSettings::TakeEphemeralStorageOpaqueOrigins(
const std::string& ephemeral_storage_domain) {
std::vector<url::Origin> result;
auto ephemeral_storage_origins_it =
ephemeral_storage_origins_.find(ephemeral_storage_domain);
if (ephemeral_storage_origins_it != ephemeral_storage_origins_.end()) {
result.reserve(ephemeral_storage_origins_it->second.size());
for (auto& origins : ephemeral_storage_origins_it->second) {
result.push_back(std::move(origins.second));
}
ephemeral_storage_origins_.erase(ephemeral_storage_origins_it);
}
return result;
}
} // namespace content_settings
#undef BRAVE_COOKIE_SETTINGS_GET_COOKIES_SETTINGS_INTERNAL
@@ -0,0 +1,38 @@
/* 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 <vector>
#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<url::Origin>& top_frame_origin, \
url::Origin& storage_origin); \
std::vector<url::Origin> TakeEphemeralStorageOpaqueOrigins( \
const std::string& ephemeral_storage_domain); \
\
private: \
/* Ephemeral storage domain to non_opaque->opaque origins map. */ \
using EphemeralStorageOrigins = \
base::flat_map<std::string, base::flat_map<url::Origin, url::Origin>>; \
EphemeralStorageOrigins 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_
@@ -1,4 +1,5 @@
include_rules = [
"+../../../../../../components/content_settings/core/common",
"+components/content_settings/common",
"+components/content_settings/core/common",
]
@@ -0,0 +1,4 @@
include_rules = [
"+../../../../../../components/services/storage",
"+components/services/storage",
]
@@ -0,0 +1,119 @@
/* 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<base::SequencedTaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> legacy_task_runner,
mojo::PendingReceiver<mojom::LocalStorageControl> receiver)
: local_storage_(std::make_unique<LocalStorageImpl_ChromiumImpl>(
storage_root,
task_runner,
legacy_task_runner,
mojo::PendingReceiver<mojom::LocalStorageControl>())),
in_memory_local_storage_(std::make_unique<LocalStorageImpl_ChromiumImpl>(
base::FilePath(),
task_runner,
legacy_task_runner,
mojo::PendingReceiver<mojom::LocalStorageControl>())) {
if (receiver)
control_receiver_.Bind(std::move(receiver));
}
LocalStorageImpl::~LocalStorageImpl() = default;
void LocalStorageImpl::ShutDown(base::OnceClosure callback) {
local_storage_->ShutDown(
base::BindOnce(&LocalStorageImpl::ShutDownInMemoryStorage,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void LocalStorageImpl::BindStorageArea(
const url::Origin& origin,
mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
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<mojom::StoragePolicyUpdatePtr> policy_updates) {
local_storage_->ApplyPolicyUpdates(std::move(policy_updates));
}
void LocalStorageImpl::ForceKeepSessionState() {
local_storage_->ForceKeepSessionState();
}
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;
}
void LocalStorageImpl::ShutDownInMemoryStorage(base::OnceClosure callback) {
in_memory_local_storage_->ShutDown(std::move(callback));
}
} // namespace storage
@@ -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/. */
#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<base::SequencedTaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> legacy_task_runner,
mojo::PendingReceiver<mojom::LocalStorageControl> receiver);
~LocalStorageImpl() override;
void ShutDown(base::OnceClosure callback);
// mojom::LocalStorageControl implementation:
void BindStorageArea(
const url::Origin& origin,
mojo::PendingReceiver<blink::mojom::StorageArea> 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<mojom::StoragePolicyUpdatePtr> policy_updates) override;
void ForceKeepSessionState() override;
private:
const url::Origin* GetNonOpaqueOrigin(const url::Origin& origin, bool create);
void ShutDownInMemoryStorage(base::OnceClosure callback);
std::unique_ptr<LocalStorageImpl_ChromiumImpl> local_storage_;
std::unique_ptr<LocalStorageImpl_ChromiumImpl> in_memory_local_storage_;
mojo::Receiver<mojom::LocalStorageControl> control_receiver_{this};
// LocalStorageImpl works only with non-opaque origins, that's why we use an
// opaque->non-opaque origin map.
std::map<url::Origin, url::Origin> non_opaque_origins_;
base::WeakPtrFactory<LocalStorageImpl> weak_ptr_factory_{this};
};
} // namespace storage
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_LOCAL_STORAGE_IMPL_H_
@@ -8,6 +8,7 @@
#include <map>
#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<EphemeralStorageOriginsSource>
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& opaque_origin :
ephemeral_storage_origins_source_->TakeEphemeralStorageOpaqueOrigins(
key_.second)) {
storage_partition_->GetDOMStorageContext()->DeleteLocalStorage(
opaque_origin, 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> TLDEphemeralLifetime::GetOrCreate(
BrowserContext* browser_context,
StoragePartition* storage_partition,
const std::string& storage_domain) {
const std::string& storage_domain,
std::unique_ptr<EphemeralStorageOriginsSource>
ephemeral_storage_origins_source) {
const TLDEphemeralLifetimeKey key(browser_context, storage_domain);
if (scoped_refptr<TLDEphemeralLifetime> existing = Get(key)) {
return existing;
}
return base::MakeRefCounted<TLDEphemeralLifetime>(key, storage_partition);
return base::MakeRefCounted<TLDEphemeralLifetime>(
key, storage_partition, std::move(ephemeral_storage_origins_source));
}
// static
@@ -11,9 +11,11 @@
#include <vector>
#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,26 @@ class CONTENT_EXPORT TLDEphemeralLifetime
public:
using OnDestroyCallback = base::OnceCallback<void(const std::string&)>;
class EphemeralStorageOriginsSource {
public:
virtual ~EphemeralStorageOriginsSource() = default;
virtual std::vector<url::Origin> TakeEphemeralStorageOpaqueOrigins(
const std::string& ephemeral_storage_domain) = 0;
};
TLDEphemeralLifetime(const TLDEphemeralLifetimeKey& key,
StoragePartition* storage_partition);
StoragePartition* storage_partition,
std::unique_ptr<EphemeralStorageOriginsSource>
ephemeral_storage_origins_source);
static TLDEphemeralLifetime* Get(BrowserContext* browser_context,
const std::string& storage_domain);
static scoped_refptr<TLDEphemeralLifetime> GetOrCreate(
BrowserContext* browser_context,
StoragePartition* storage_partition,
const std::string& storage_domain);
const std::string& storage_domain,
std::unique_ptr<EphemeralStorageOriginsSource>
ephemeral_storage_origins_source);
// Add a callback to a callback list to be called on destruction.
void RegisterOnDestroyCallback(OnDestroyCallback callback);
@@ -63,6 +77,8 @@ class CONTENT_EXPORT TLDEphemeralLifetime
TLDEphemeralLifetimeKey key_;
StoragePartition* storage_partition_;
std::unique_ptr<EphemeralStorageOriginsSource>
ephemeral_storage_origins_source_;
std::vector<OnDestroyCallback> on_destroy_callbacks_;
base::WeakPtrFactory<TLDEphemeralLifetime> weak_factory_{this};
@@ -7,6 +7,7 @@
#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 "third_party/blink/public/platform/web_security_origin.h"
#define AllowStorageAccessSync \
AllowAutoplay(bool play_requested) { return true; } \
@@ -16,8 +17,9 @@
virtual BraveFarblingLevel GetBraveFarblingLevel() { \
return BraveFarblingLevel::OFF; \
} \
virtual bool UseEphemeralStorageSync(StorageType storageType) { \
return false; \
virtual blink::WebSecurityOrigin GetEphemeralStorageOriginSync( \
StorageType storageType) { \
return blink::WebSecurityOrigin(); \
} \
virtual bool AllowStorageAccessSync
@@ -33,7 +33,8 @@ class BraveDOMWindowStorage final
private:
StorageArea* ephemeralSessionStorage();
StorageArea* ephemeralLocalStorage();
StorageArea* ephemeralLocalStorage(
const SecurityOrigin* ephemeral_storage_origin);
mutable Member<StorageArea> ephemeral_session_storage_;
mutable Member<StorageArea> ephemeral_local_storage_;
@@ -20,7 +20,6 @@ namespace blink {
namespace {
constexpr char kSessionStorageSuffix[] = "/ephemeral-session-storage";
constexpr char kLocalStorageSuffix[] = "/ephemeral-local-storage";
// This replicates the conversion of a string into a session storage namespace
// id that is found in the implementation of EphemeralStorageTabHelper.
@@ -32,80 +31,73 @@ String StringToSessionStorageId(const String& string,
return String(hash.c_str());
}
bool ShouldUseEphemeralStorage(
const SecurityOrigin* GetEphemeralStorageOrigin(
LocalDOMWindow* window,
WebContentSettingsClient::StorageType storage_type) {
auto* frame = window->GetFrame();
if (!frame)
return false;
return nullptr;
if (auto* settings_client = frame->GetContentSettingsClient())
return settings_client->UseEphemeralStorageSync(storage_type);
return settings_client->GetEphemeralStorageOriginSync(storage_type).Get();
return false;
return nullptr;
}
} // namespace
// EphemeralStorageNamespace manage ephemeral storage namespaces for a
// particular Page object. The namespaces are instantiated on the Page lazily,
// as soon as a third-party frame needs ephemeral storage. They are then shared
// by all third-party frames that are embedded in this Page.
// EphemeralSessionStorageNamespace manages ephemeral sessionStorage namespace
// for a particular Page object. The namespace is instantiated on the Page
// lazily, as soon as a third-party frame needs ephemeral sessionStorage. It's
// then shared by all third-party frames that are embedded in this Page.
//
// These namespaces are created in the browser process ahead of time. We ensure
// The namespace is created in the browser process ahead of time. We ensure
// that we are using the same namespace by using a common naming scheme.
class EphemeralStorageNamespaces
: public GarbageCollected<EphemeralStorageNamespaces>,
class EphemeralSessionStorageNamespace
: public GarbageCollected<EphemeralSessionStorageNamespace>,
public Supplement<Page> {
public:
EphemeralStorageNamespaces(StorageController* controller,
const String& session_storage_id,
const String& local_storage_id);
virtual ~EphemeralStorageNamespaces() = default;
EphemeralSessionStorageNamespace(StorageController* controller,
const String& session_storage_id);
virtual ~EphemeralSessionStorageNamespace() = default;
static const char kSupplementName[];
static EphemeralStorageNamespaces* From(Page* page, LocalDOMWindow* window);
static EphemeralSessionStorageNamespace* From(Page* page,
LocalDOMWindow* window);
StorageNamespace* session_storage() { return session_storage_.Get(); }
StorageNamespace* local_storage() { return local_storage_.Get(); }
void Trace(Visitor* visitor) const override;
private:
Member<StorageNamespace> session_storage_;
Member<StorageNamespace> local_storage_;
};
const char EphemeralStorageNamespaces::kSupplementName[] =
"EphemeralStorageNamespaces";
const char EphemeralSessionStorageNamespace::kSupplementName[] =
"EphemeralSessionStorageNamespace";
EphemeralStorageNamespaces::EphemeralStorageNamespaces(
EphemeralSessionStorageNamespace::EphemeralSessionStorageNamespace(
StorageController* controller,
const String& session_storage_id,
const String& local_storage_id)
const String& session_storage_id)
: Supplement(nullptr),
session_storage_(
MakeGarbageCollected<StorageNamespace>(controller,
session_storage_id)),
local_storage_(MakeGarbageCollected<StorageNamespace>(controller,
local_storage_id)) {
}
session_storage_id)) {}
void EphemeralStorageNamespaces::Trace(Visitor* visitor) const {
void EphemeralSessionStorageNamespace::Trace(Visitor* visitor) const {
visitor->Trace(session_storage_);
visitor->Trace(local_storage_);
Supplement<Page>::Trace(visitor);
}
// static
EphemeralStorageNamespaces* EphemeralStorageNamespaces::From(
EphemeralSessionStorageNamespace* EphemeralSessionStorageNamespace::From(
Page* page,
LocalDOMWindow* window) {
DCHECK(window);
if (!page)
return nullptr;
EphemeralStorageNamespaces* supplement =
Supplement<Page>::From<EphemeralStorageNamespaces>(page);
EphemeralSessionStorageNamespace* supplement =
Supplement<Page>::From<EphemeralSessionStorageNamespace>(page);
if (supplement)
return supplement;
@@ -117,20 +109,8 @@ EphemeralStorageNamespaces* EphemeralStorageNamespaces::From(
String::FromUTF8(webview->GetSessionStorageNamespaceId()),
kSessionStorageSuffix);
auto* security_origin =
page->MainFrame()->GetSecurityContext()->GetSecurityOrigin();
String domain = security_origin->RegistrableDomain();
// RegistrableDomain might return an empty string if this host is an IP
// address or a file URL.
if (domain.IsEmpty()) {
url::Origin origin = url::Origin::Create(GURL(window->Url()).GetOrigin());
domain = String::FromUTF8(origin.Serialize());
}
String local_storage_id =
StringToSessionStorageId(domain, kLocalStorageSuffix);
supplement = MakeGarbageCollected<EphemeralStorageNamespaces>(
StorageController::GetInstance(), session_storage_id, local_storage_id);
supplement = MakeGarbageCollected<EphemeralSessionStorageNamespace>(
StorageController::GetInstance(), session_storage_id);
ProvideTo(*page, supplement);
return supplement;
@@ -173,7 +153,7 @@ StorageArea* BraveDOMWindowStorage::sessionStorage(
auto* storage =
DOMWindowStorage::From(*window).sessionStorage(exception_state);
if (!ShouldUseEphemeralStorage(
if (!GetEphemeralStorageOrigin(
window, WebContentSettingsClient::StorageType::kSessionStorage))
return storage;
@@ -186,13 +166,13 @@ StorageArea* BraveDOMWindowStorage::ephemeralSessionStorage() {
LocalDOMWindow* window = GetSupplementable();
Page* page = window->GetFrame()->GetDocument()->GetPage();
EphemeralStorageNamespaces* namespaces =
EphemeralStorageNamespaces::From(page, window);
if (!namespaces)
EphemeralSessionStorageNamespace* ephemeral_namespace =
EphemeralSessionStorageNamespace::From(page, window);
if (!ephemeral_namespace)
return nullptr;
auto storage_area =
namespaces->session_storage()->GetCachedArea(window->GetSecurityOrigin());
auto storage_area = ephemeral_namespace->session_storage()->GetCachedArea(
window->GetSecurityOrigin());
ephemeral_session_storage_ =
StorageArea::Create(window, std::move(storage_area),
@@ -205,38 +185,26 @@ StorageArea* BraveDOMWindowStorage::localStorage(
LocalDOMWindow* window = GetSupplementable();
auto* storage = DOMWindowStorage::From(*window).localStorage(exception_state);
if (!ShouldUseEphemeralStorage(
window, WebContentSettingsClient::StorageType::kLocalStorage))
const SecurityOrigin* ephemeral_storage_origin = GetEphemeralStorageOrigin(
window, WebContentSettingsClient::StorageType::kLocalStorage);
if (!ephemeral_storage_origin)
return storage;
return ephemeralLocalStorage();
return ephemeralLocalStorage(ephemeral_storage_origin);
}
StorageArea* BraveDOMWindowStorage::ephemeralLocalStorage() {
StorageArea* BraveDOMWindowStorage::ephemeralLocalStorage(
const SecurityOrigin* ephemeral_storage_origin) {
DCHECK(ephemeral_storage_origin);
if (ephemeral_local_storage_)
return ephemeral_local_storage_;
LocalDOMWindow* window = GetSupplementable();
Page* page = window->GetFrame()->GetDocument()->GetPage();
EphemeralStorageNamespaces* namespaces =
EphemeralStorageNamespaces::From(page, window);
if (!namespaces)
return nullptr;
auto* controller = StorageController::GetInstance();
controller->ClearAreasIfNeeded();
auto storage_area = base::MakeRefCounted<CachedStorageArea>(
CachedStorageArea::AreaType::kSessionStorage, window->GetSecurityOrigin(),
controller->TaskRunner(), namespaces->local_storage(),
/*is_session_storage_for_prerendering=*/false);
// 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);
auto storage_area = StorageController::GetInstance()->GetLocalStorageArea(
ephemeral_storage_origin);
ephemeral_local_storage_ = StorageArea::Create(
window, std::move(storage_area), StorageArea::StorageType::kLocalStorage);
return ephemeral_local_storage_;
}
@@ -133,46 +133,53 @@ void BraveContentSettingsAgentImpl::DidNotAllowScript() {
ContentSettingsAgentImpl::DidNotAllowScript();
}
bool BraveContentSettingsAgentImpl::UseEphemeralStorageSync(
blink::WebSecurityOrigin
BraveContentSettingsAgentImpl::GetEphemeralStorageOriginSync(
StorageType storage_type) {
if (!base::FeatureList::IsEnabled(net::features::kBraveEphemeralStorage))
return false;
return blink::WebSecurityOrigin();
if (storage_type != StorageType::kLocalStorage &&
storage_type != StorageType::kSessionStorage)
return false;
return blink::WebSecurityOrigin();
blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
if (!frame || IsFrameWithOpaqueOrigin(frame))
return false;
return blink::WebSecurityOrigin();
auto frame_origin = url::Origin(frame->GetSecurityOrigin());
StoragePermissionsKey key(frame_origin, storage_type);
const auto permissions = cached_storage_permissions_.find(key);
if (permissions != cached_storage_permissions_.end())
return permissions->second;
const auto ephemeral_storage_origin_it =
cached_ephemeral_storage_origins_.find(key);
if (ephemeral_storage_origin_it != cached_ephemeral_storage_origins_.end())
return ephemeral_storage_origin_it->second;
auto top_origin = url::Origin(frame->Top()->GetSecurityOrigin());
if (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 blink::WebSecurityOrigin();
}
bool result = false;
absl::optional<url::Origin> optional_ephemeral_storage_origin;
GetContentSettingsManager().AllowEphemeralStorageAccess(
routing_id(), ConvertToMojoStorageType(storage_type), frame_origin,
frame->GetDocument().SiteForCookies().RepresentativeUrl(), top_origin,
&result);
cached_storage_permissions_[key] = result;
return result;
&optional_ephemeral_storage_origin);
blink::WebSecurityOrigin ephemeral_storage_origin(
optional_ephemeral_storage_origin
? blink::WebSecurityOrigin(*optional_ephemeral_storage_origin)
: blink::WebSecurityOrigin());
cached_ephemeral_storage_origins_[key] = ephemeral_storage_origin;
return ephemeral_storage_origin;
}
bool BraveContentSettingsAgentImpl::AllowStorageAccessSync(
StorageType storage_type) {
bool result = ContentSettingsAgentImpl::AllowStorageAccessSync(storage_type);
if (result || UseEphemeralStorageSync(storage_type))
if (result || !GetEphemeralStorageOriginSync(storage_type).IsNull())
return true;
return false;
@@ -46,7 +46,8 @@ class BraveContentSettingsAgentImpl
const blink::WebURL& script_url) override;
void DidNotAllowScript() override;
bool UseEphemeralStorageSync(StorageType storage_type) override;
blink::WebSecurityOrigin GetEphemeralStorageOriginSync(
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<std::string> preloaded_temporarily_allowed_scripts_;
using StoragePermissionsKey = std::pair<url::Origin, StorageType>;
base::flat_map<StoragePermissionsKey, bool> cached_storage_permissions_;
base::flat_map<StoragePermissionsKey, blink::WebSecurityOrigin>
cached_ephemeral_storage_origins_;
mojo::AssociatedRemote<brave_shields::mojom::BraveShieldsHost>
brave_shields_remote_;