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") {
"dom_storage/session_storage_metadata.h",
"dom_storage/session_storage_namespace_impl.cc",
"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.h",
"download/byte_stream_input_stream.cc",
......
......@@ -11,7 +11,6 @@
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.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 "mojo/public/cpp/bindings/strong_associated_binding.h"
#include "testing/gmock/include/gmock/gmock.h"
......
......@@ -42,14 +42,14 @@ class CONTENT_EXPORT SessionStorageLevelDBWrapper
const url::Origin& origin)>;
// Creates a wrapper for the given |namespace_entry|-|origin| data area. All
// LevelDBWrapper calls are delegated to the |data_map|. The |map_id_callback|
// is called when a shared |data_map| needs to be forked for the copy-on-write
// behavior and a new map needs to be registered.
// LevelDBWrapper calls are delegated to the |data_map|. The
// |register_new_map_callback| is called when a shared |data_map| needs to be
// forked for the copy-on-write behavior and a new map needs to be registered.
SessionStorageLevelDBWrapper(
SessionStorageMetadata::NamespaceEntry namespace_entry,
url::Origin origin,
scoped_refptr<SessionStorageDataMap> data_map,
RegisterNewAreaMap map_id_callback);
RegisterNewAreaMap register_new_map_callback);
~SessionStorageLevelDBWrapper() override;
// Creates a shallow copy clone for the new namespace entry.
......
......@@ -82,9 +82,10 @@ class SessionStorageLevelDBWrapperTest : public testing::Test {
metadata_.SetupNewDatabase(1);
std::vector<leveldb::mojom::BatchedOperationPtr> save_operations;
metadata_.RegisterNewMap(
auto map_id = metadata_.RegisterNewMap(
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), test_origin1_,
&save_operations);
DCHECK(map_id->KeyPrefix() == StdStringToUint8Vector("map-0-"));
leveldb_database_->Write(std::move(save_operations), base::DoNothing());
}
~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_
// 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 "base/guid.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "components/services/leveldb/public/cpp/util.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/dom_storage/session_storage_data_map.h"
#include "content/browser/dom_storage/session_storage_metadata.h"
#include "content/test/fake_leveldb_database.h"
#include "content/test/leveldb_wrapper_test_util.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/public/cpp/bindings/strong_associated_binding.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace content {
namespace {
using leveldb::StdStringToUint8Vector;
using leveldb::mojom::DatabaseError;
using NamespaceEntry = SessionStorageMetadata::NamespaceEntry;
constexpr const int kTestProcessIdOrigin1 = 11;
constexpr const int kTestProcessIdOrigin2 = 12;
constexpr const int kTestProcessIdOrigin3 = 13;
class MockListener : public SessionStorageDataMap::Listener {
public:
MockListener() {}
~MockListener() override {}
MOCK_METHOD2(OnDataMapCreation,
void(const std::vector<uint8_t>& map_id,
SessionStorageDataMap* map));
MOCK_METHOD1(OnDataMapDestruction, void(const std::vector<uint8_t>& map_id));
MOCK_METHOD1(OnCommitResult, void(leveldb::mojom::DatabaseError error));
};
class SessionStorageNamespaceImplMojoTest : public testing::Test {
public:
SessionStorageNamespaceImplMojoTest()
: test_namespace_id1_(base::GenerateGUID()),
test_namespace_id2_(base::GenerateGUID()),
test_origin1_(url::Origin::Create(GURL("https://host1.com:1/"))),
test_origin2_(url::Origin::Create(GURL("https://host2.com:2/"))),
test_origin3_(url::Origin::Create(GURL("https://host3.com:3/"))),
database_(&mock_data_) {}
~SessionStorageNamespaceImplMojoTest() override = default;
void SetUp() override {
// Create a database that already has a namespace saved.
metadata_.SetupNewDatabase(1);
std::vector<leveldb::mojom::BatchedOperationPtr> save_operations;
NamespaceEntry entry =
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_);
auto map_id =
metadata_.RegisterNewMap(entry, test_origin1_, &save_operations);
DCHECK(map_id->KeyPrefix() == StdStringToUint8Vector("map-0-"));
database_.Write(std::move(save_operations), base::DoNothing());
// Put some data in one of the maps.
mock_data_[StdStringToUint8Vector("map-0-key1")] =
StdStringToUint8Vector("data1");
auto* security_policy = ChildProcessSecurityPolicyImpl::GetInstance();
security_policy->Add(kTestProcessIdOrigin1);
security_policy->Add(kTestProcessIdOrigin2);
security_policy->Add(kTestProcessIdOrigin3);
security_policy->AddIsolatedOrigins(
{test_origin1_, test_origin2_, test_origin3_});
security_policy->LockToOrigin(kTestProcessIdOrigin1,
test_origin1_.GetURL());
security_policy->LockToOrigin(kTestProcessIdOrigin2,
test_origin2_.GetURL());
security_policy->LockToOrigin(kTestProcessIdOrigin3,
test_origin3_.GetURL());
mojo::edk::SetDefaultProcessErrorCallback(
base::BindRepeating(&SessionStorageNamespaceImplMojoTest::OnBadMessage,
base::Unretained(this)));
}
void OnBadMessage(const std::string& reason) { bad_message_called_ = true; }
void TearDown() override {
auto* security_policy = ChildProcessSecurityPolicyImpl::GetInstance();
security_policy->Remove(kTestProcessIdOrigin1);
security_policy->Remove(kTestProcessIdOrigin2);
security_policy->Remove(kTestProcessIdOrigin3);
mojo::edk::SetDefaultProcessErrorCallback(
mojo::edk::ProcessErrorCallback());
}
// Creates a SessionStorageNamespaceImplMojo, saves it in the namespaces_ map,
// and returns a pointer to the object.
SessionStorageNamespaceImplMojo* CreateSessionStorageNamespaceImplMojo(
const std::string& namespace_id) {
DCHECK(namespaces_.find(namespace_id) == namespaces_.end());
SessionStorageNamespaceImplMojo::RegisterShallowClonedNamespace
add_namespace_callback =
base::BindRepeating(&SessionStorageNamespaceImplMojoTest::
RegisterShallowClonedNamespace,
base::Unretained(this));
SessionStorageLevelDBWrapper::RegisterNewAreaMap map_id_callback =
base::BindRepeating(
&SessionStorageNamespaceImplMojoTest::RegisterNewAreaMap,
base::Unretained(this));
auto namespace_impl = std::make_unique<SessionStorageNamespaceImplMojo>(
namespace_id, &database_, &listener_, std::move(add_namespace_callback),
std::move(map_id_callback));
auto* namespace_impl_ptr = namespace_impl.get();
namespaces_[namespace_id] = std::move(namespace_impl);
return namespace_impl_ptr;
}
scoped_refptr<SessionStorageMetadata::MapData> RegisterNewAreaMap(
NamespaceEntry namespace_entry,
const url::Origin& origin) {
std::vector<leveldb::mojom::BatchedOperationPtr> save_operations;
auto map_data =
metadata_.RegisterNewMap(namespace_entry, origin, &save_operations);
database_.Write(std::move(save_operations), base::DoNothing());
return map_data;
}
void RegisterShallowClonedNamespace(
NamespaceEntry source_namespace,
const std::string& destination_namespace,
const SessionStorageNamespaceImplMojo::OriginAreas& areas_to_clone) {
std::vector<leveldb::mojom::BatchedOperationPtr> save_operations;
NamespaceEntry namespace_entry =
metadata_.GetOrCreateNamespaceEntry(destination_namespace);
metadata_.RegisterShallowClonedNamespace(source_namespace, namespace_entry,
&save_operations);
database_.Write(std::move(save_operations), base::DoNothing());
auto it = namespaces_.find(destination_namespace);
if (it == namespaces_.end()) {
auto* namespace_impl =
CreateSessionStorageNamespaceImplMojo(destination_namespace);
namespace_impl->PopulateAsClone(namespace_entry, areas_to_clone);
return;
}
it->second->PopulateAsClone(namespace_entry, areas_to_clone);
}
protected:
base::test::ScopedTaskEnvironment task_environment_;
const std::string test_namespace_id1_;
const std::string test_namespace_id2_;
const url::Origin test_origin1_;
const url::Origin test_origin2_;
const url::Origin test_origin3_;
SessionStorageMetadata metadata_;
bool bad_message_called_ = false;
std::map<std::string, std::unique_ptr<SessionStorageNamespaceImplMojo>>
namespaces_;
testing::StrictMock<MockListener> listener_;
std::map<std::vector<uint8_t>, std::vector<uint8_t>> mock_data_;
FakeLevelDBDatabase database_;
};
TEST_F(SessionStorageNamespaceImplMojoTest, MetadataLoad) {
// Exercises creation, population, binding, and getting all data.
SessionStorageNamespaceImplMojo* namespace_impl =
CreateSessionStorageNamespaceImplMojo(test_namespace_id1_);
EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("0"), testing::_))
.Times(1);
namespace_impl->PopulateFromMetadata(
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
mojom::SessionStorageNamespacePtr ss_namespace;
namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
mojom::LevelDBWrapperAssociatedPtr leveldb_1;
ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
LevelDBWrapperImpl* wrapper_impl =
namespace_impl->GetWrapperForOriginForTesting(test_origin1_);
std::vector<mojom::KeyValuePtr> data;
DatabaseError status = test::GetAllSync(wrapper_impl, &data);
EXPECT_EQ(DatabaseError::OK, status);
EXPECT_EQ(1ul, data.size());
EXPECT_TRUE(base::ContainsValue(
data, mojom::KeyValue::New(StdStringToUint8Vector("key1"),
StdStringToUint8Vector("data1"))));
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0")))
.Times(1);
namespaces_.clear();
}
TEST_F(SessionStorageNamespaceImplMojoTest, MetadataLoadWithMapOperations) {
// Exercises creation, population, binding, and a map operation, and then
// getting all the data.
SessionStorageNamespaceImplMojo* namespace_impl =
CreateSessionStorageNamespaceImplMojo(test_namespace_id1_);
EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("0"), testing::_))
.Times(1);
namespace_impl->PopulateFromMetadata(
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
mojom::SessionStorageNamespacePtr ss_namespace;
namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
mojom::LevelDBWrapperAssociatedPtr leveldb_1;
ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(1);
test::PutSync(leveldb_1.get(), StdStringToUint8Vector("key2"),
StdStringToUint8Vector("data2"), base::nullopt, "");
LevelDBWrapperImpl* wrapper_impl =
namespace_impl->GetWrapperForOriginForTesting(test_origin1_);
std::vector<mojom::KeyValuePtr> data;
DatabaseError status = test::GetAllSync(wrapper_impl, &data);
EXPECT_EQ(DatabaseError::OK, status);
EXPECT_EQ(2ul, data.size());
EXPECT_TRUE(base::ContainsValue(
data, mojom::KeyValue::New(StdStringToUint8Vector("key1"),
StdStringToUint8Vector("data1"))));
EXPECT_TRUE(base::ContainsValue(
data, mojom::KeyValue::New(StdStringToUint8Vector("key2"),
StdStringToUint8Vector("data2"))));
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0")))
.Times(1);
namespaces_.clear();
}
TEST_F(SessionStorageNamespaceImplMojoTest, CloneBeforeBind) {
// Exercises cloning the namespace before we bind to the new cloned namespace.
SessionStorageNamespaceImplMojo* namespace_impl1 =
CreateSessionStorageNamespaceImplMojo(test_namespace_id1_);
SessionStorageNamespaceImplMojo* namespace_impl2 =
CreateSessionStorageNamespaceImplMojo(test_namespace_id2_);
EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("0"), testing::_))
.Times(1);
namespace_impl1->PopulateFromMetadata(
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
mojom::SessionStorageNamespacePtr ss_namespace1;
namespace_impl1->Bind(mojo::MakeRequest(&ss_namespace1),
kTestProcessIdOrigin1);
ss_namespace1->Clone(test_namespace_id2_);
ss_namespace1.FlushForTesting();
ASSERT_TRUE(namespace_impl2->IsPopulated());
mojom::SessionStorageNamespacePtr ss_namespace2;
namespace_impl2->Bind(mojo::MakeRequest(&ss_namespace2),
kTestProcessIdOrigin1);
mojom::LevelDBWrapperAssociatedPtr leveldb_2;
ss_namespace2->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_2));
// Do a put in the cloned namespace.
EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(2);
EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("1"), testing::_))
.Times(1);
test::PutSync(leveldb_2.get(), StdStringToUint8Vector("key2"),
StdStringToUint8Vector("data2"), base::nullopt, "");
LevelDBWrapperImpl* wrapper_impl2 =
namespace_impl2->GetWrapperForOriginForTesting(test_origin1_);
std::vector<mojom::KeyValuePtr> data;
DatabaseError status = test::GetAllSync(wrapper_impl2, &data);
EXPECT_EQ(DatabaseError::OK, status);
EXPECT_EQ(2ul, data.size());
EXPECT_TRUE(base::ContainsValue(
data, mojom::KeyValue::New(StdStringToUint8Vector("key1"),
StdStringToUint8Vector("data1"))));
EXPECT_TRUE(base::ContainsValue(
data, mojom::KeyValue::New(StdStringToUint8Vector("key2"),
StdStringToUint8Vector("data2"))));
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0")))
.Times(1);
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1")))
.Times(1);
namespaces_.clear();
}
TEST_F(SessionStorageNamespaceImplMojoTest, CloneAfterBind) {
// Exercises cloning the namespace before we bind to the new cloned namespace.
// Unlike the test above, we create a new area for the test_origin2_ in the
// new namespace.
SessionStorageNamespaceImplMojo* namespace_impl1 =
CreateSessionStorageNamespaceImplMojo(test_namespace_id1_);
SessionStorageNamespaceImplMojo* namespace_impl2 =
CreateSessionStorageNamespaceImplMojo(test_namespace_id2_);
EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("0"), testing::_))
.Times(1);
namespace_impl1->PopulateFromMetadata(
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
mojom::SessionStorageNamespacePtr ss_namespace1;
namespace_impl1->Bind(mojo::MakeRequest(&ss_namespace1),
kTestProcessIdOrigin1);
// Set that we are waiting for clone, so binding is possible.
namespace_impl2->SetWaitingForClonePopulation();
EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("1"), testing::_))
.Times(1);
// Get a new area.
mojom::SessionStorageNamespacePtr ss_namespace2;
namespace_impl2->Bind(mojo::MakeRequest(&ss_namespace2),
kTestProcessIdOrigin2);
mojom::LevelDBWrapperAssociatedPtr leveldb_2;
ss_namespace2->OpenArea(test_origin2_, mojo::MakeRequest(&leveldb_2));
// Finally do the clone.
ss_namespace1->Clone(test_namespace_id2_);
ss_namespace1.FlushForTesting();
ASSERT_TRUE(namespace_impl2->IsPopulated());
// Do a put in the cloned namespace.
EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(1);
test::PutSync(leveldb_2.get(), StdStringToUint8Vector("key2"),
StdStringToUint8Vector("data2"), base::nullopt, "");
LevelDBWrapperImpl* wrapper_impl2_o1 =
namespace_impl2->GetWrapperForOriginForTesting(test_origin1_);
std::vector<mojom::KeyValuePtr> data;
DatabaseError status = test::GetAllSync(wrapper_impl2_o1, &data);
EXPECT_EQ(DatabaseError::OK, status);
EXPECT_EQ(1ul, data.size());
EXPECT_TRUE(base::ContainsValue(
data, mojom::KeyValue::New(StdStringToUint8Vector("key1"),
StdStringToUint8Vector("data1"))));
LevelDBWrapperImpl* wrapper_impl2_o2 =
namespace_impl2->GetWrapperForOriginForTesting(test_origin2_);
data.clear();
status = test::GetAllSync(wrapper_impl2_o2, &data);
EXPECT_EQ(DatabaseError::OK, status);
EXPECT_EQ(1ul, data.size());
EXPECT_TRUE(base::ContainsValue(
data, mojom::KeyValue::New(StdStringToUint8Vector("key2"),
StdStringToUint8Vector("data2"))));
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0")))
.Times(1);
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1")))
.Times(1);
namespaces_.clear();
}
TEST_F(SessionStorageNamespaceImplMojoTest, RemoveOriginData) {
SessionStorageNamespaceImplMojo* namespace_impl =
CreateSessionStorageNamespaceImplMojo(test_namespace_id1_);
EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("0"), testing::_))
.Times(1);
namespace_impl->PopulateFromMetadata(
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
mojom::SessionStorageNamespacePtr ss_namespace;
namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
mojom::LevelDBWrapperAssociatedPtr leveldb_1;
ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1));
ss_namespace.FlushForTesting();
EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(1);
namespace_impl->RemoveOriginData(test_origin1_);
LevelDBWrapperImpl* wrapper_impl =
namespace_impl->GetWrapperForOriginForTesting(test_origin1_);
std::vector<mojom::KeyValuePtr> data;
DatabaseError status = test::GetAllSync(wrapper_impl, &data);
EXPECT_EQ(DatabaseError::OK, status);
EXPECT_EQ(0ul, data.size());
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0")))
.Times(1);
namespaces_.clear();
}
TEST_F(SessionStorageNamespaceImplMojoTest, ProcessLockedToOtherOrigin) {
// Tries to open an area with a process that is locked to a different origin
// and verifies the bad message callback.
SessionStorageNamespaceImplMojo* namespace_impl =
CreateSessionStorageNamespaceImplMojo(test_namespace_id1_);
EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("0"), testing::_))
.Times(1);
namespace_impl->PopulateFromMetadata(
metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_));
mojom::SessionStorageNamespacePtr ss_namespace;
namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1);
mojom::LevelDBWrapperAssociatedPtr leveldb_1;
ss_namespace->OpenArea(test_origin3_, mojo::MakeRequest(&leveldb_1));
ss_namespace.FlushForTesting();
EXPECT_TRUE(bad_message_called_);
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0")))
.Times(1);
namespaces_.clear();
}
} // namespace
} // namespace content
......@@ -1280,6 +1280,7 @@ test("content_unittests") {
"../browser/dom_storage/session_storage_database_unittest.cc",
"../browser/dom_storage/session_storage_leveldb_wrapper_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_request_core_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