Commit bdbd0663 authored by Daniel Murphy's avatar Daniel Murphy Committed by Commit Bot

[SessionStorageS13N] SessionStorageNamespace mojo implementation.

Creates the browser-side implementation of the SessionStorageNamespace
mojo interface. The namespace holds the per-origin storage area and
provides ways to both connect to the data and shallow-clone the data.

TBR: jam@chromium.org for content/browser/BUILD.gn
Bug: 716490
Change-Id: I9df5812afdacf4330728c590d47ecfdf93e320bd
Reviewed-on: https://chromium-review.googlesource.com/1015894
Commit-Queue: Daniel Murphy <dmurph@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarMarijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553792}
parent f35aeeb0
...@@ -704,6 +704,8 @@ jumbo_source_set("browser") { ...@@ -704,6 +704,8 @@ jumbo_source_set("browser") {
"dom_storage/session_storage_metadata.h", "dom_storage/session_storage_metadata.h",
"dom_storage/session_storage_namespace_impl.cc", "dom_storage/session_storage_namespace_impl.cc",
"dom_storage/session_storage_namespace_impl.h", "dom_storage/session_storage_namespace_impl.h",
"dom_storage/session_storage_namespace_impl_mojo.cc",
"dom_storage/session_storage_namespace_impl_mojo.h",
"download/blob_download_url_loader_factory_getter.cc", "download/blob_download_url_loader_factory_getter.cc",
"download/blob_download_url_loader_factory_getter.h", "download/blob_download_url_loader_factory_getter.h",
"download/byte_stream_input_stream.cc", "download/byte_stream_input_stream.cc",
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "components/services/leveldb/public/cpp/util.h" #include "components/services/leveldb/public/cpp/util.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/test/fake_leveldb_database.h" #include "content/test/fake_leveldb_database.h"
#include "mojo/public/cpp/bindings/strong_associated_binding.h" #include "mojo/public/cpp/bindings/strong_associated_binding.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
......
...@@ -42,14 +42,14 @@ class CONTENT_EXPORT SessionStorageLevelDBWrapper ...@@ -42,14 +42,14 @@ class CONTENT_EXPORT SessionStorageLevelDBWrapper
const url::Origin& origin)>; const url::Origin& origin)>;
// Creates a wrapper for the given |namespace_entry|-|origin| data area. All // Creates a wrapper for the given |namespace_entry|-|origin| data area. All
// LevelDBWrapper calls are delegated to the |data_map|. The |map_id_callback| // LevelDBWrapper calls are delegated to the |data_map|. The
// is called when a shared |data_map| needs to be forked for the copy-on-write // |register_new_map_callback| is called when a shared |data_map| needs to be
// behavior and a new map needs to be registered. // forked for the copy-on-write behavior and a new map needs to be registered.
SessionStorageLevelDBWrapper( SessionStorageLevelDBWrapper(
SessionStorageMetadata::NamespaceEntry namespace_entry, SessionStorageMetadata::NamespaceEntry namespace_entry,
url::Origin origin, url::Origin origin,
scoped_refptr<SessionStorageDataMap> data_map, scoped_refptr<SessionStorageDataMap> data_map,
RegisterNewAreaMap map_id_callback); RegisterNewAreaMap register_new_map_callback);
~SessionStorageLevelDBWrapper() override; ~SessionStorageLevelDBWrapper() override;
// Creates a shallow copy clone for the new namespace entry. // Creates a shallow copy clone for the new namespace entry.
......
...@@ -82,9 +82,10 @@ class SessionStorageLevelDBWrapperTest : public testing::Test { ...@@ -82,9 +82,10 @@ class SessionStorageLevelDBWrapperTest : public testing::Test {
metadata_.SetupNewDatabase(1); metadata_.SetupNewDatabase(1);
std::vector<leveldb::mojom::BatchedOperationPtr> save_operations; std::vector<leveldb::mojom::BatchedOperationPtr> save_operations;
metadata_.RegisterNewMap( auto map_id = metadata_.RegisterNewMap(
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), test_origin1_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), test_origin1_,
&save_operations); &save_operations);
DCHECK(map_id->KeyPrefix() == StdStringToUint8Vector("map-0-"));
leveldb_database_->Write(std::move(save_operations), base::DoNothing()); leveldb_database_->Write(std::move(save_operations), base::DoNothing());
} }
~SessionStorageLevelDBWrapperTest() override = default; ~SessionStorageLevelDBWrapperTest() override = default;
......
// 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 "components/services/leveldb/public/cpp/util.h"
#include "content/public/browser/child_process_security_policy.h"
namespace content {
SessionStorageNamespaceImplMojo::SessionStorageNamespaceImplMojo(
std::string namespace_id,
leveldb::mojom::LevelDBDatabase* database,
SessionStorageDataMap::Listener* data_map_listener,
RegisterShallowClonedNamespace add_namespace_callback,
SessionStorageLevelDBWrapper::RegisterNewAreaMap register_new_map_callback)
: namespace_id_(std::move(namespace_id)),
database_(database),
data_map_listener_(data_map_listener),
add_namespace_callback_(std::move(add_namespace_callback)),
register_new_map_callback_(std::move(register_new_map_callback)),
binding_(this) {}
SessionStorageNamespaceImplMojo::~SessionStorageNamespaceImplMojo() = default;
void SessionStorageNamespaceImplMojo::PopulateFromMetadata(
SessionStorageMetadata::NamespaceEntry namespace_metadata) {
DCHECK(!IsPopulated());
DCHECK(!waiting_on_clone_population());
populated_ = true;
namespace_entry_ = namespace_metadata;
for (const auto& pair : namespace_entry_->second) {
origin_areas_[pair.first] = std::make_unique<SessionStorageLevelDBWrapper>(
namespace_entry_, pair.first,
SessionStorageDataMap::Create(data_map_listener_, pair.second.get(),
database_),
register_new_map_callback_);
}
}
void SessionStorageNamespaceImplMojo::PopulateAsClone(
SessionStorageMetadata::NamespaceEntry namespace_metadata,
const OriginAreas& areas_to_clone) {
DCHECK(!IsPopulated());
populated_ = true;
waiting_on_clone_population_ = false;
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_clone_population_.empty()) {
for (base::OnceClosure& callback : run_after_clone_population_)
std::move(callback).Run();
run_after_clone_population_.clear();
}
}
void SessionStorageNamespaceImplMojo::Bind(
mojom::SessionStorageNamespaceRequest request,
int process_id) {
if (waiting_on_clone_population_) {
bind_waiting_on_clone_population_ = true;
run_after_clone_population_.push_back(
base::BindOnce(&SessionStorageNamespaceImplMojo::Bind,
base::Unretained(this), std::move(request), process_id));
return;
}
DCHECK(IsPopulated());
process_id_ = process_id;
binding_.Bind(std::move(request));
bind_waiting_on_clone_population_ = false;
}
void SessionStorageNamespaceImplMojo::RemoveOriginData(
const url::Origin& origin) {
if (waiting_on_clone_population_) {
run_after_clone_population_.push_back(
base::BindOnce(&SessionStorageNamespaceImplMojo::RemoveOriginData,
base::Unretained(this), origin));
return;
}
DCHECK(IsPopulated());
auto it = origin_areas_.find(origin);
if (it == origin_areas_.end())
return;
// Renderer process expects |source| to always be two newline separated
// strings.
it->second->DeleteAll("\n", base::DoNothing());
it->second->data_map()->level_db_wrapper()->ScheduleImmediateCommit();
}
void SessionStorageNamespaceImplMojo::OpenArea(
const url::Origin& origin,
mojom::LevelDBWrapperAssociatedRequest database) {
DCHECK(IsPopulated());
DCHECK(binding_.is_bound());
DCHECK_NE(process_id_, ChildProcessHost::kInvalidUniqueID);
if (!ChildProcessSecurityPolicy::GetInstance()->CanAccessDataForOrigin(
process_id_, origin.GetURL())) {
binding_.ReportBadMessage("Access denied for sessionStorage request");
return;
}
auto it = origin_areas_.find(origin);
if (it == origin_areas_.end()) {
it = origin_areas_
.emplace(std::make_pair(
origin, std::make_unique<SessionStorageLevelDBWrapper>(
namespace_entry_, origin,
SessionStorageDataMap::Create(
data_map_listener_,
register_new_map_callback_.Run(
namespace_entry_, origin),
database_),
register_new_map_callback_)))
.first;
}
it->second->Bind(std::move(database));
}
void SessionStorageNamespaceImplMojo::Clone(
const std::string& clone_to_namespace) {
add_namespace_callback_.Run(namespace_entry_, clone_to_namespace,
origin_areas_);
}
LevelDBWrapperImpl*
SessionStorageNamespaceImplMojo::GetWrapperForOriginForTesting(
const url::Origin& origin) const {
auto it = origin_areas_.find(origin);
if (it == origin_areas_.end())
return nullptr;
return it->second->data_map()->level_db_wrapper();
}
} // namespace content
// 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.
#ifndef CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_NAMESPACE_IMPL_MOJO_H_
#define CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_NAMESPACE_IMPL_MOJO_H_
#include <memory>
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/memory/ref_counted.h"
#include "content/browser/dom_storage/session_storage_data_map.h"
#include "content/browser/dom_storage/session_storage_leveldb_wrapper.h"
#include "content/browser/dom_storage/session_storage_metadata.h"
#include "content/browser/leveldb_wrapper_impl.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/leveldb_wrapper.mojom.h"
#include "content/common/storage_partition_service.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "url/origin.h"
namespace content {
class LevelDBWrapperImpl;
// Implements the mojo interface SessionStorageNamespace. Stores data maps per
// origin, which are accessible using the LevelDBWrapper interface with the
// |OpenArea| call. Supports cloning (shallow cloning with copy-on-write
// behavior) from another SessionStorageNamespaceImplMojo.
//
// This class is populated & bound in the following patterns:
// 1. The namespace is new or being populated from data on disk, and
// |PopulateFromMetadata| is called. Afterwards |Bind| can be called.
// 2. The namespace is being created as a clone from a |Clone| call on another
// SessionStorageNamespaceImplMojo. PopulateFromMetadata is called with the
// data from the other namespace, and then |Bind| can be called afterwards.
// 3. The namespace is being created as a clone, but the |Clone| call from the
// source namespace hasn't been called yet. |SetWaitingForClonePopulation| is
// called first, after which |Bind| can be called. The actually binding
// doesn't happen until |PopulateAsClone| is finally called with the source
// namespace data.
// Note: The reason for cases 2 and 3 is because there are two ways the Session
// Storage system knows about clones. First, it gets the |Clone| call on the
// source namespace, coming from the renderer doing the navigation, and in the
// correct order with any session storage modifications from that source
// renderer. Second, the RenderViewHostImpl of the navigated-to-frame will
// create the cloned namespace and expect to manage it's lifetime that way, and
// this can happen before the first case, as they are on different task runners.
class CONTENT_EXPORT SessionStorageNamespaceImplMojo final
: public mojom::SessionStorageNamespace {
public:
using OriginAreas =
std::map<url::Origin, std::unique_ptr<SessionStorageLevelDBWrapper>>;
using RegisterShallowClonedNamespace = base::RepeatingCallback<void(
SessionStorageMetadata::NamespaceEntry source_namespace,
const std::string& destination_namespace,
const OriginAreas& areas_to_clone)>;
// Constructs a namespace with the given |namespace_id|, expecting to be
// populated and bound later (see class comment). The |database| and
// |data_map_listener| are given to any data maps constructed for this
// namespace. The |add_namespace_callback| is called when the |Clone| method
// is called by mojo. The |register_new_map_callback| is given to the the
// SessionStorageLevelDBWrapper's, used per-origin, that are bound to in
// OpenArea.
SessionStorageNamespaceImplMojo(
std::string namespace_id,
leveldb::mojom::LevelDBDatabase* database,
SessionStorageDataMap::Listener* data_map_listener,
RegisterShallowClonedNamespace add_namespace_callback,
SessionStorageLevelDBWrapper::RegisterNewAreaMap
register_new_map_callback);
~SessionStorageNamespaceImplMojo() override;
void SetWaitingForClonePopulation() { waiting_on_clone_population_ = true; }
bool waiting_on_clone_population() { return waiting_on_clone_population_; }
// Called when this is a new namespace, or when the namespace was loaded from
// disk. Should be called before |Bind|.
void PopulateFromMetadata(
SessionStorageMetadata::NamespaceEntry namespace_metadata);
// Can either be called before |Bind|, or if the source namespace isn't
// available yet, |SetWaitingForClonePopulation| can be called. Then |Bind|
// will work, and hold onto the request until after this method is called.
void PopulateAsClone(
SessionStorageMetadata::NamespaceEntry namespace_metadata,
const OriginAreas& areas_to_clone);
SessionStorageMetadata::NamespaceEntry namespace_entry() {
return namespace_entry_;
}
bool IsPopulated() const { return populated_; }
// Must be preceded by a call to |PopulateFromMetadata|, |PopulateAsClone|, or
// |SetWaitingForClonePopulation|. For the later case, |PopulateAsClone| must
// eventually be called before the SessionStorageNamespaceRequest can be
// bound.
void Bind(mojom::SessionStorageNamespaceRequest request, int process_id);
bool IsBound() const {
return binding_.is_bound() || bind_waiting_on_clone_population_;
}
// Removes data for the given origin from this namespace. If there is no data
// map for that given origin, this does nothing.
void RemoveOriginData(const url::Origin& origin);
// SessionStorageNamespace:
// Connects the given database mojo request to the data map for the given
// origin. Before connection, it checks to make sure the |process_id| given to
// the |Bind| method can access the given origin.
void OpenArea(const url::Origin& origin,
mojom::LevelDBWrapperAssociatedRequest database) override;
// Simply calls the |add_namespace_callback_| callback with this namespace's
// data.
void Clone(const std::string& clone_to_namespace) override;
// Because LevelDBWrapper::GetAll is a sync call, for testing it's easier for
// us to call that directly on the wrapper. Unfortunate, but better than
// spinning up 3 threads.
LevelDBWrapperImpl* GetWrapperForOriginForTesting(
const url::Origin& origin) const;
private:
const std::string namespace_id_;
SessionStorageMetadata::NamespaceEntry namespace_entry_;
int process_id_ = ChildProcessHost::kInvalidUniqueID;
leveldb::mojom::LevelDBDatabase* database_;
SessionStorageDataMap::Listener* data_map_listener_;
RegisterShallowClonedNamespace add_namespace_callback_;
SessionStorageLevelDBWrapper::RegisterNewAreaMap register_new_map_callback_;
bool waiting_on_clone_population_ = false;
bool bind_waiting_on_clone_population_ = false;
std::vector<base::OnceClosure> run_after_clone_population_;
bool populated_ = false;
OriginAreas origin_areas_;
mojo::Binding<mojom::SessionStorageNamespace> binding_;
};
} // namespace content
#endif // CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_NAMESPACE_IMPL_MOJO_H_
...@@ -1280,6 +1280,7 @@ test("content_unittests") { ...@@ -1280,6 +1280,7 @@ test("content_unittests") {
"../browser/dom_storage/session_storage_database_unittest.cc", "../browser/dom_storage/session_storage_database_unittest.cc",
"../browser/dom_storage/session_storage_leveldb_wrapper_unittest.cc", "../browser/dom_storage/session_storage_leveldb_wrapper_unittest.cc",
"../browser/dom_storage/session_storage_metadata_unittest.cc", "../browser/dom_storage/session_storage_metadata_unittest.cc",
"../browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc",
"../browser/download/download_manager_impl_unittest.cc", "../browser/download/download_manager_impl_unittest.cc",
"../browser/download/download_request_core_unittest.cc", "../browser/download/download_request_core_unittest.cc",
"../browser/download/save_package_unittest.cc", "../browser/download/save_package_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment