blob: 80d4391b41c4f5d6f2c8fa61f91fca1fe5342902 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/dom_storage/session_storage_namespace_impl_mojo.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "components/services/leveldb/public/cpp/util.h"
#include "content/browser/child_process_security_policy_impl.h"
namespace content {
namespace {
void SessionStorageResponse(base::OnceClosure callback, bool success) {
std::move(callback).Run();
}
} // namespace
SessionStorageNamespaceImplMojo::SessionStorageNamespaceImplMojo(
std::string namespace_id,
SessionStorageDataMap::Listener* data_map_listener,
SessionStorageAreaImpl::RegisterNewAreaMap register_new_map_callback,
Delegate* delegate)
: namespace_id_(std::move(namespace_id)),
data_map_listener_(data_map_listener),
register_new_map_callback_(std::move(register_new_map_callback)),
delegate_(delegate) {}
SessionStorageNamespaceImplMojo::~SessionStorageNamespaceImplMojo() {
DCHECK(child_namespaces_waiting_for_clone_call_.empty());
}
void SessionStorageNamespaceImplMojo::SetPendingPopulationFromParentNamespace(
const std::string& from_namespace) {
pending_population_from_parent_namespace_ = from_namespace;
state_ = State::kNotPopulatedAndPendingClone;
}
void SessionStorageNamespaceImplMojo::AddChildNamespaceWaitingForClone(
const std::string& namespace_id) {
child_namespaces_waiting_for_clone_call_.insert(namespace_id);
}
bool SessionStorageNamespaceImplMojo::HasChildNamespacesWaitingForClone()
const {
return !child_namespaces_waiting_for_clone_call_.empty();
}
void SessionStorageNamespaceImplMojo::ClearChildNamespacesWaitingForClone() {
child_namespaces_waiting_for_clone_call_.clear();
}
bool SessionStorageNamespaceImplMojo::HasAreaForOrigin(
const url::Origin& origin) const {
return origin_areas_.find(origin) != origin_areas_.end();
}
void SessionStorageNamespaceImplMojo::PopulateFromMetadata(
leveldb::mojom::LevelDBDatabase* database,
SessionStorageMetadata::NamespaceEntry namespace_metadata) {
DCHECK(!IsPopulated());
database_ = database;
state_ = State::kPopulated;
pending_population_from_parent_namespace_.clear();
namespace_entry_ = namespace_metadata;
for (const auto& pair : namespace_entry_->second) {
scoped_refptr<SessionStorageDataMap> data_map =
delegate_->MaybeGetExistingDataMapForId(
pair.second->MapNumberAsBytes());
if (!data_map) {
data_map = SessionStorageDataMap::CreateFromDisk(data_map_listener_,
pair.second, database_);
}
origin_areas_[pair.first] = std::make_unique<SessionStorageAreaImpl>(
namespace_entry_, pair.first, std::move(data_map),
register_new_map_callback_);
}
if (!run_after_population_.empty()) {
for (base::OnceClosure& callback : run_after_population_)
std::move(callback).Run();
run_after_population_.clear();
}
}
void SessionStorageNamespaceImplMojo::PopulateAsClone(
leveldb::mojom::LevelDBDatabase* database,
SessionStorageMetadata::NamespaceEntry namespace_metadata,
const OriginAreas& areas_to_clone) {
DCHECK(!IsPopulated());
database_ = database;
state_ = State::kPopulated;
pending_population_from_parent_namespace_.clear();
namespace_entry_ = namespace_metadata;
std::transform(areas_to_clone.begin(), areas_to_clone.end(),
std::inserter(origin_areas_, origin_areas_.begin()),
[namespace_metadata](const auto& source) {
return std::make_pair(
source.first, source.second->Clone(namespace_metadata));
});
if (!run_after_population_.empty()) {
for (base::OnceClosure& callback : run_after_population_)
std::move(callback).Run();
run_after_population_.clear();
}
}
void SessionStorageNamespaceImplMojo::Reset() {
namespace_entry_ = SessionStorageMetadata::NamespaceEntry();
database_ = nullptr;
pending_population_from_parent_namespace_.clear();
bind_waiting_on_population_ = false;
run_after_population_.clear();
state_ = State::kNotPopulated;
child_namespaces_waiting_for_clone_call_.clear();
origin_areas_.clear();
receivers_.Clear();
}
void SessionStorageNamespaceImplMojo::Bind(
mojo::PendingReceiver<blink::mojom::SessionStorageNamespace> receiver,
int process_id) {
if (!IsPopulated()) {
bind_waiting_on_population_ = true;
run_after_population_.push_back(base::BindOnce(
&SessionStorageNamespaceImplMojo::Bind, base::Unretained(this),
std::move(receiver), process_id));
return;
}
DCHECK(IsPopulated());
receivers_.Add(this, std::move(receiver), process_id);
bind_waiting_on_population_ = false;
}
void SessionStorageNamespaceImplMojo::PurgeUnboundAreas() {
auto it = origin_areas_.begin();
while (it != origin_areas_.end()) {
if (!it->second->IsBound())
it = origin_areas_.erase(it);
else
++it;
}
}
void SessionStorageNamespaceImplMojo::RemoveOriginData(
const url::Origin& origin,
base::OnceClosure callback) {
DCHECK_NE(state_, State::kNotPopulated);
if (!IsPopulated()) {
run_after_population_.push_back(
base::BindOnce(&SessionStorageNamespaceImplMojo::RemoveOriginData,
base::Unretained(this), origin, std::move(callback)));
return;
}
DCHECK(IsPopulated());
auto it = origin_areas_.find(origin);
if (it == origin_areas_.end()) {
std::move(callback).Run();
return;
}
// Renderer process expects |source| to always be two newline separated
// strings.
it->second->DeleteAll(
"\n", base::BindOnce(&SessionStorageResponse, std::move(callback)));
it->second->NotifyObserversAllDeleted();
it->second->data_map()->storage_area()->ScheduleImmediateCommit();
}
void SessionStorageNamespaceImplMojo::OpenArea(
const url::Origin& origin,
mojo::PendingAssociatedReceiver<blink::mojom::StorageArea> receiver) {
DCHECK(IsPopulated());
DCHECK(!receivers_.empty());
int process_id = receivers_.current_context();
// TODO(943887): Replace HasSecurityState() call with something that can
// preserve security state after process shutdown. The security state check
// is a temporary solution to avoid crashes when this method is run after the
// process associated with |process_id| has been destroyed.
// It temporarily restores the old behavior of always allowing access if the
// process is gone.
auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
if (!policy->CanAccessDataForOrigin(process_id, origin) &&
policy->HasSecurityState(process_id)) {
receivers_.ReportBadMessage("Access denied for sessionStorage request");
return;
}
auto it = origin_areas_.find(origin);
if (it == origin_areas_.end()) {
// The area may have been purged due to lack of bindings, so check the
// metadata for the map.
scoped_refptr<SessionStorageDataMap> data_map;
auto map_data_it = namespace_entry_->second.find(origin);
if (map_data_it != namespace_entry_->second.end()) {
// The map exists already, either on disk or being used by another
// namespace.
scoped_refptr<SessionStorageMetadata::MapData> map_data =
map_data_it->second;
data_map =
delegate_->MaybeGetExistingDataMapForId(map_data->MapNumberAsBytes());
if (!data_map) {
data_map = SessionStorageDataMap::CreateFromDisk(data_map_listener_,
map_data, database_);
}
} else {
// The map doesn't exist yet.
data_map = SessionStorageDataMap::CreateEmpty(
data_map_listener_,
register_new_map_callback_.Run(namespace_entry_, origin), database_);
}
it = origin_areas_
.emplace(std::make_pair(
origin, std::make_unique<SessionStorageAreaImpl>(
namespace_entry_, origin, std::move(data_map),
register_new_map_callback_)))
.first;
}
it->second->Bind(std::move(receiver));
}
void SessionStorageNamespaceImplMojo::Clone(
const std::string& clone_to_namespace) {
DCHECK(IsPopulated());
child_namespaces_waiting_for_clone_call_.erase(clone_to_namespace);
delegate_->RegisterShallowClonedNamespace(namespace_entry_,
clone_to_namespace, origin_areas_);
}
void SessionStorageNamespaceImplMojo::CloneAllNamespacesWaitingForClone(
leveldb::mojom::LevelDBDatabase* database,
SessionStorageMetadata* metadata,
const std::map<std::string,
std::unique_ptr<SessionStorageNamespaceImplMojo>>&
namespaces_map) {
SessionStorageNamespaceImplMojo* parent = this;
// If the current state is kNotPopulatedAndPendingClone, then the children can
// all be cloned from our parent instead of us.
if (state() ==
SessionStorageNamespaceImplMojo::State::kNotPopulatedAndPendingClone) {
auto parent_it =
namespaces_map.find(pending_population_from_parent_namespace_);
// The parent must be in the map, because the only way to remove something
// from the map is to call DeleteSessionNamespace, which would have called
// this method on the parent if there were children, and resolved our clone
// dependency.
DCHECK(parent_it != namespaces_map.end());
parent = parent_it->second.get();
}
if (parent->state() ==
SessionStorageNamespaceImplMojo::State::kNotPopulated) {
// Populate the namespace to prepare for copy.
parent->PopulateFromMetadata(
database, metadata->GetOrCreateNamespaceEntry(parent->namespace_id_));
}
auto* delegate = parent->delegate_;
for (const std::string& destination_namespace :
child_namespaces_waiting_for_clone_call_) {
if (parent->IsPopulated()) {
delegate->RegisterShallowClonedNamespace(parent->namespace_entry(),
destination_namespace,
parent->origin_areas_);
} else {
parent->AddChildNamespaceWaitingForClone(destination_namespace);
parent->run_after_population_.push_back(
base::BindOnce(&SessionStorageNamespaceImplMojo::Clone,
base::Unretained(parent), destination_namespace));
auto child_it = namespaces_map.find(destination_namespace);
// The child must be in the map, as the only way to add it to
// |child_namespaces_waiting_for_clone_call_| is to call
// CloneSessionNamespace, which always adds it to the map.
DCHECK(child_it != namespaces_map.end());
child_it->second->SetPendingPopulationFromParentNamespace(
parent->namespace_id_);
}
}
child_namespaces_waiting_for_clone_call_.clear();
}
void SessionStorageNamespaceImplMojo::FlushOriginForTesting(
const url::Origin& origin) {
if (!IsPopulated())
return;
auto it = origin_areas_.find(origin);
if (it == origin_areas_.end())
return;
it->second->data_map()->storage_area()->ScheduleImmediateCommit();
}
} // namespace content