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

[SessionStorageS13N] Custom LevelDBWrapper implementation for cloning

Adds the SessionStorageLevelDBWrapper class, which works with the
SessionStorageMetadata and SessionStorageDataMap classes to support
shallow cloning for SessionStorage data maps.

TBR-ing jam for content/browser/BUILD.gn

TBR: jam@chromium.org
Bug: 716490
Change-Id: I1a3ad1a79f39ef001bd4489591d6e99ab0f6347f
Reviewed-on: https://chromium-review.googlesource.com/954410
Commit-Queue: Daniel Murphy <dmurph@chromium.org>
Reviewed-by: default avatarMarijn Kruisselbrink <mek@chromium.org>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553288}
parent 03d58e81
......@@ -696,6 +696,8 @@ jumbo_source_set("browser") {
"dom_storage/session_storage_database.h",
"dom_storage/session_storage_database_adapter.cc",
"dom_storage/session_storage_database_adapter.h",
"dom_storage/session_storage_leveldb_wrapper.cc",
"dom_storage/session_storage_leveldb_wrapper.h",
"dom_storage/session_storage_metadata.cc",
"dom_storage/session_storage_metadata.h",
"dom_storage/session_storage_namespace_impl.cc",
......
// 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_leveldb_wrapper.h"
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "content/browser/dom_storage/session_storage_data_map.h"
#include "content/common/dom_storage/dom_storage_types.h"
#include "third_party/leveldatabase/env_chromium.h"
namespace content {
SessionStorageLevelDBWrapper::SessionStorageLevelDBWrapper(
SessionStorageMetadata::NamespaceEntry namespace_entry,
url::Origin origin,
scoped_refptr<SessionStorageDataMap> data_map,
RegisterNewAreaMap register_new_map_callback)
: namespace_entry_(namespace_entry),
origin_(std::move(origin)),
shared_data_map_(std::move(data_map)),
register_new_map_callback_(std::move(register_new_map_callback)),
binding_(this) {}
SessionStorageLevelDBWrapper::~SessionStorageLevelDBWrapper() {
if (binding_.is_bound())
shared_data_map_->RemoveBindingReference();
}
void SessionStorageLevelDBWrapper::Bind(
mojom::LevelDBWrapperAssociatedRequest request) {
DCHECK(!IsBound());
shared_data_map_->AddBindingReference();
binding_.Bind(std::move(request));
binding_.set_connection_error_handler(
base::BindOnce(&SessionStorageLevelDBWrapper::OnConnectionError,
base::Unretained(this)));
}
std::unique_ptr<SessionStorageLevelDBWrapper>
SessionStorageLevelDBWrapper::Clone(
SessionStorageMetadata::NamespaceEntry namespace_entry) {
DCHECK(namespace_entry_ != namespace_entry);
return base::WrapUnique(new SessionStorageLevelDBWrapper(
namespace_entry, origin_, shared_data_map_, register_new_map_callback_));
}
// LevelDBWrapper:
void SessionStorageLevelDBWrapper::AddObserver(
mojom::LevelDBObserverAssociatedPtrInfo observer) {
mojom::LevelDBObserverAssociatedPtr observer_ptr;
observer_ptr.Bind(std::move(observer));
mojo::InterfacePtrSetElementId ptr_id =
shared_data_map_->level_db_wrapper()->AddObserver(
std::move(observer_ptr));
observer_ptrs_.push_back(ptr_id);
}
void SessionStorageLevelDBWrapper::Put(
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source,
PutCallback callback) {
DCHECK(IsBound());
DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
if (shared_data_map_->map_data()->ReferenceCount() > 1)
CreateNewMap(NewMapType::FORKED, base::nullopt);
shared_data_map_->level_db_wrapper()->Put(key, value, client_old_value,
source, std::move(callback));
}
void SessionStorageLevelDBWrapper::Delete(
const std::vector<uint8_t>& key,
const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source,
DeleteCallback callback) {
DCHECK(IsBound());
DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
if (shared_data_map_->map_data()->ReferenceCount() > 1)
CreateNewMap(NewMapType::FORKED, base::nullopt);
shared_data_map_->level_db_wrapper()->Delete(key, client_old_value, source,
std::move(callback));
}
void SessionStorageLevelDBWrapper::DeleteAll(const std::string& source,
DeleteAllCallback callback) {
DCHECK(IsBound());
DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
if (shared_data_map_->map_data()->ReferenceCount() > 1) {
CreateNewMap(NewMapType::EMPTY_FROM_DELETE_ALL, source);
return;
}
shared_data_map_->level_db_wrapper()->DeleteAll(source, std::move(callback));
}
void SessionStorageLevelDBWrapper::Get(const std::vector<uint8_t>& key,
GetCallback callback) {
DCHECK(IsBound());
DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
shared_data_map_->level_db_wrapper()->Get(key, std::move(callback));
}
void SessionStorageLevelDBWrapper::GetAll(
mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback,
GetAllCallback callback) {
DCHECK(IsBound());
DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
shared_data_map_->level_db_wrapper()->GetAll(std::move(complete_callback),
std::move(callback));
}
// Note: this can be called after invalidation of the |namespace_entry_|.
void SessionStorageLevelDBWrapper::OnConnectionError() {
shared_data_map_->RemoveBindingReference();
// Make sure we totally unbind the binding - this doesn't seem to happen
// automatically on connection error. The bound status is used in the
// destructor to know if |RemoveBindingReference| was already called.
if (binding_.is_bound())
binding_.Unbind();
}
void SessionStorageLevelDBWrapper::CreateNewMap(
NewMapType map_type,
const base::Optional<std::string>& delete_all_source) {
std::vector<mojom::LevelDBObserverAssociatedPtr> ptrs_to_move;
for (const mojo::InterfacePtrSetElementId& ptr_id : observer_ptrs_) {
DCHECK(shared_data_map_->level_db_wrapper()->HasObserver(ptr_id));
ptrs_to_move.push_back(
shared_data_map_->level_db_wrapper()->RemoveObserver(ptr_id));
}
observer_ptrs_.clear();
shared_data_map_->RemoveBindingReference();
switch (map_type) {
case NewMapType::FORKED:
shared_data_map_ = SessionStorageDataMap::CreateClone(
shared_data_map_->listener(),
register_new_map_callback_.Run(namespace_entry_, origin_),
shared_data_map_->level_db_wrapper());
break;
case NewMapType::EMPTY_FROM_DELETE_ALL: {
// The code optimizes the 'delete all' for shared maps by just creating
// a new map instead of forking. However, we still need the observers to
// be correctly called. To do that, we manually call them here.
shared_data_map_ = SessionStorageDataMap::Create(
shared_data_map_->listener(),
register_new_map_callback_.Run(namespace_entry_, origin_),
shared_data_map_->level_db_wrapper()->database());
for (auto& ptr : ptrs_to_move) {
ptr->AllDeleted(delete_all_source.value_or("\n"));
}
break;
}
}
shared_data_map_->AddBindingReference();
for (auto& observer_ptr : ptrs_to_move) {
observer_ptrs_.push_back(shared_data_map_->level_db_wrapper()->AddObserver(
std::move(observer_ptr)));
}
}
} // 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_LEVELDB_WRAPPER_H_
#define CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_LEVELDB_WRAPPER_H_
#include <vector>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/optional.h"
#include "content/browser/dom_storage/session_storage_metadata.h"
#include "content/browser/leveldb_wrapper_impl.h"
#include "content/common/content_export.h"
#include "content/common/leveldb_wrapper.mojom.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "url/origin.h"
namespace content {
class SessionStorageDataMap;
// This class provides session storage access to the renderer by binding to the
// LevelDBWrapper mojom interface. It represents the data stored for a
// namespace-origin area.
//
// This class delegates calls to SessionStorageDataMap objects, and can share
// them with other SessionStorageLevelDBImpl instances to support shallow
// cloning (copy-on-write). This should be done through the |Clone()| method and
// not manually.
//
// During forking, this class is responsible for dealing with moving its
// observers from the SessionStorageDataMap's LevelDBWrapper to the new forked
// SessionStorageDataMap's LevelDBWrapper.
class CONTENT_EXPORT SessionStorageLevelDBWrapper
: public mojom::LevelDBWrapper {
public:
using RegisterNewAreaMap =
base::RepeatingCallback<scoped_refptr<SessionStorageMetadata::MapData>(
SessionStorageMetadata::NamespaceEntry namespace_entry,
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.
SessionStorageLevelDBWrapper(
SessionStorageMetadata::NamespaceEntry namespace_entry,
url::Origin origin,
scoped_refptr<SessionStorageDataMap> data_map,
RegisterNewAreaMap map_id_callback);
~SessionStorageLevelDBWrapper() override;
// Creates a shallow copy clone for the new namespace entry.
// This doesn't change the refcount of the underlying map - that operation
// must be done using SessionStorageMetadata::RegisterShallowClonedNamespace.
std::unique_ptr<SessionStorageLevelDBWrapper> Clone(
SessionStorageMetadata::NamespaceEntry namespace_entry);
void Bind(mojom::LevelDBWrapperAssociatedRequest request);
bool IsBound() const { return binding_.is_bound(); }
SessionStorageDataMap* data_map() { return shared_data_map_.get(); }
// LevelDBWrapper:
void AddObserver(mojom::LevelDBObserverAssociatedPtrInfo observer) override;
void Put(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source,
PutCallback callback) override;
void Delete(const std::vector<uint8_t>& key,
const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source,
DeleteCallback callback) override;
void DeleteAll(const std::string& source,
DeleteAllCallback callback) override;
void Get(const std::vector<uint8_t>& key, GetCallback callback) override;
void GetAll(
mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback,
GetAllCallback callback) override;
private:
void OnConnectionError();
enum class NewMapType { FORKED, EMPTY_FROM_DELETE_ALL };
void CreateNewMap(NewMapType map_type,
const base::Optional<std::string>& delete_all_source);
SessionStorageMetadata::NamespaceEntry namespace_entry_;
url::Origin origin_;
scoped_refptr<SessionStorageDataMap> shared_data_map_;
RegisterNewAreaMap register_new_map_callback_;
std::vector<mojo::InterfacePtrSetElementId> observer_ptrs_;
mojo::AssociatedBinding<mojom::LevelDBWrapper> binding_;
DISALLOW_COPY_AND_ASSIGN(SessionStorageLevelDBWrapper);
};
} // namespace content
#endif // CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_LEVELDB_WRAPPER_H_
......@@ -206,7 +206,8 @@ std::vector<uint8_t> SessionStorageMetadata::DatabaseVersionAsVector() const {
return NumberToValue(database_version_);
}
SessionStorageMetadata::MapData* SessionStorageMetadata::RegisterNewMap(
scoped_refptr<SessionStorageMetadata::MapData>
SessionStorageMetadata::RegisterNewMap(
NamespaceEntry namespace_entry,
const url::Origin& origin,
std::vector<leveldb::mojom::BatchedOperationPtr>* save_operations) {
......@@ -242,7 +243,7 @@ SessionStorageMetadata::MapData* SessionStorageMetadata::RegisterNewMap(
BatchOperationType::PUT_KEY, GetAreaKey(namespace_entry->first, origin),
new_map_data->MapNumberAsBytes()));
return new_map_data.get();
return new_map_data;
}
void SessionStorageMetadata::RegisterShallowClonedNamespace(
......
......@@ -101,7 +101,7 @@ class CONTENT_EXPORT SessionStorageMetadata {
// the next available map id.
// Note: It is invalid to call this method for an area that has a map with
// only one reference.
MapData* RegisterNewMap(
scoped_refptr<MapData> RegisterNewMap(
NamespaceEntry namespace_entry,
const url::Origin& origin,
std::vector<leveldb::mojom::BatchedOperationPtr>* save_operations);
......
......@@ -179,7 +179,7 @@ TEST_F(SessionStorageMetadataTest, SaveNewMap) {
std::vector<leveldb::mojom::BatchedOperationPtr> operations;
auto ns1_entry = metadata.GetOrCreateNamespaceEntry(test_namespace1_id_);
auto* map_data =
auto map_data =
metadata.RegisterNewMap(ns1_entry, test_origin1_, &operations);
ASSERT_TRUE(map_data);
......
......@@ -17,6 +17,7 @@
#include "components/services/leveldb/public/interfaces/leveldb.mojom.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/test/fake_leveldb_database.h"
#include "content/test/leveldb_wrapper_test_util.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/strong_associated_binding.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
......@@ -24,8 +25,10 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
using test::MakeSuccessCallback;
using test::MakeGetAllCallback;
using test::GetAllCallback;
using CacheMode = LevelDBWrapperImpl::CacheMode;
using DatabaseError = leveldb::mojom::DatabaseError;
......@@ -87,32 +90,6 @@ class IncrementalBarrier {
DISALLOW_COPY_AND_ASSIGN(IncrementalBarrier);
};
class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback {
public:
static mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo CreateAndBind(
bool* result,
base::OnceClosure callback) {
mojom::LevelDBWrapperGetAllCallbackAssociatedPtr ptr;
auto request = mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr);
mojo::MakeStrongAssociatedBinding(
base::WrapUnique(new GetAllCallback(result, std::move(callback))),
std::move(request));
return ptr.PassInterface();
}
private:
GetAllCallback(bool* result, base::OnceClosure callback)
: result_(result), callback_(std::move(callback)) {}
void Complete(bool success) override {
*result_ = success;
if (callback_)
std::move(callback_).Run();
}
bool* result_;
base::OnceClosure callback_;
};
class MockDelegate : public LevelDBWrapperImpl::Delegate {
public:
MockDelegate() {}
......@@ -167,33 +144,6 @@ base::OnceCallback<void(bool, const std::vector<uint8_t>&)> MakeGetCallback(
value_out);
}
void GetAllDataCallback(leveldb::mojom::DatabaseError* status_out,
std::vector<mojom::KeyValuePtr>* data_out,
leveldb::mojom::DatabaseError status,
std::vector<mojom::KeyValuePtr> data) {
*status_out = status;
*data_out = std::move(data);
}
base::OnceCallback<void(leveldb::mojom::DatabaseError status,
std::vector<mojom::KeyValuePtr> data)>
MakeGetAllCallback(leveldb::mojom::DatabaseError* status_out,
std::vector<mojom::KeyValuePtr>* data_out) {
return base::BindOnce(&GetAllDataCallback, status_out, data_out);
}
void SuccessCallback(base::OnceClosure callback,
bool* success_out,
bool success) {
*success_out = success;
std::move(callback).Run();
}
base::OnceCallback<void(bool)> MakeSuccessCallback(base::OnceClosure callback,
bool* success_out) {
return base::BindOnce(&SuccessCallback, std::move(callback), success_out);
}
LevelDBWrapperImpl::Options GetDefaultTestingOptions(CacheMode cache_mode) {
LevelDBWrapperImpl::Options options;
options.max_size = kTestSizeLimit;
......@@ -271,38 +221,15 @@ class LevelDBWrapperImplTest : public testing::Test,
return success;
}
bool PutSync(mojom::LevelDBWrapper* wrapper,
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
const base::Optional<std::vector<uint8_t>>& client_old_value,
std::string source = kTestSource) {
bool success = false;
base::RunLoop loop;
wrapper->Put(key, value, client_old_value, source,
MakeSuccessCallback(loop.QuitClosure(), &success));
loop.Run();
return success;
}
bool DeleteSync(
mojom::LevelDBWrapper* wrapper,
const std::vector<uint8_t>& key,
const base::Optional<std::vector<uint8_t>>& client_old_value) {
bool success = false;
base::RunLoop loop;
wrapper->Delete(key, client_old_value, test_source_,
MakeSuccessCallback(loop.QuitClosure(), &success));
loop.Run();
return success;
return test::DeleteSync(wrapper, key, client_old_value, test_source_);
}
bool DeleteAllSync(mojom::LevelDBWrapper* wrapper) {
bool success = false;
base::RunLoop loop;
wrapper->DeleteAll(test_source_,
MakeSuccessCallback(loop.QuitClosure(), &success));
loop.Run();
return success;
return test::DeleteAllSync(wrapper, test_source_);
}
bool GetSync(const std::vector<uint8_t>& key, std::vector<uint8_t>* result) {
......@@ -313,7 +240,7 @@ class LevelDBWrapperImplTest : public testing::Test,
const std::vector<uint8_t>& value,
const base::Optional<std::vector<uint8_t>>& client_old_value,
std::string source = kTestSource) {
return PutSync(wrapper(), key, value, client_old_value, source);
return test::PutSync(wrapper(), key, value, client_old_value, source);
}
bool DeleteSync(
......@@ -326,20 +253,9 @@ class LevelDBWrapperImplTest : public testing::Test,
std::string GetSyncStrUsingGetAll(LevelDBWrapperImpl* wrapper_impl,
const std::string& key) {
leveldb::mojom::DatabaseError status;
std::vector<mojom::KeyValuePtr> data;
bool done = false;
base::RunLoop loop;
// Testing the 'Sync' mojo version is a big headache involving 3 threads, so
// just test the async version.
wrapper_impl->GetAll(
GetAllCallback::CreateAndBind(&done, loop.QuitClosure()),
MakeGetAllCallback(&status, &data));
loop.Run();
if (!done)
return "";
leveldb::mojom::DatabaseError status =
test::GetAllSync(wrapper_impl, &data);
if (status != leveldb::mojom::DatabaseError::OK)
return "";
......@@ -516,20 +432,13 @@ TEST_F(LevelDBWrapperImplTest, PutLoadsValuesAfterCacheModeUpgrade) {
TEST_P(LevelDBWrapperImplParamTest, GetAll) {
wrapper_impl()->SetCacheModeForTesting(GetParam());
DatabaseError status;
std::vector<mojom::KeyValuePtr> data;
bool result = false;
leveldb::mojom::DatabaseError status =
test::GetAllSync(wrapper_impl(), &data);
base::RunLoop loop;
// Testing the 'Sync' mojo version is a big headache involving 3 threads, so
// just test the async version.
wrapper_impl()->GetAll(
GetAllCallback::CreateAndBind(&result, loop.QuitClosure()),
MakeGetAllCallback(&status, &data));
loop.Run();
EXPECT_EQ(leveldb::mojom::DatabaseError::OK, status);
EXPECT_EQ(2u, data.size());
EXPECT_TRUE(result);
}
TEST_P(LevelDBWrapperImplParamTest, CommitPutToDB) {
......@@ -829,17 +738,9 @@ TEST_P(LevelDBWrapperImplParamTest, FixUpData) {
changes.push_back(std::make_pair(test_prefix_bytes_, ToBytes("bla")));
delegate()->set_mock_changes(std::move(changes));
leveldb::mojom::DatabaseError status;
std::vector<mojom::KeyValuePtr> data;
bool result = false;
base::RunLoop loop;
// Testing the 'Sync' mojo version is a big headache involving 3 threads, so
// just test the async version.
wrapper_impl()->GetAll(
GetAllCallback::CreateAndBind(&result, loop.QuitClosure()),
MakeGetAllCallback(&status, &data));
loop.Run();
leveldb::mojom::DatabaseError status =
test::GetAllSync(wrapper_impl(), &data);
EXPECT_EQ(leveldb::mojom::DatabaseError::OK, status);
ASSERT_EQ(2u, data.size());
......
......@@ -194,8 +194,11 @@ jumbo_static_library("test_support") {
"fake_plugin_service.h",
"fake_renderer_compositor_frame_sink.cc",
"fake_renderer_compositor_frame_sink.h",
"gmock_util.h",
"gpu_browsertest_helpers.cc",
"gpu_browsertest_helpers.h",
"leveldb_wrapper_test_util.cc",
"leveldb_wrapper_test_util.h",
"mock_background_sync_controller.cc",
"mock_background_sync_controller.h",
"mock_keyboard.cc",
......@@ -1275,6 +1278,7 @@ test("content_unittests") {
"../browser/dom_storage/local_storage_context_mojo_unittest.cc",
"../browser/dom_storage/session_storage_data_map_unittest.cc",
"../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/download/download_manager_impl_unittest.cc",
"../browser/download/download_request_core_unittest.cc",
......@@ -1673,6 +1677,7 @@ test("content_unittests") {
"//components/offline_pages/buildflags",
"//components/payments/mojom",
"//components/rappor:test_support",
"//components/services/leveldb:lib",
"//components/services/leveldb/public/cpp",
"//components/ukm:test_support",
"//components/viz/client",
......
// 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 "testing/gmock/include/gmock/gmock.h"
#ifndef CONTENT_TEST_GMOCK_UTIL_H_
#define CONTENT_TEST_GMOCK_UTIL_H_
// This file contains gmock actions and matchers that integrate with concepts in
// /base.
namespace base {
namespace test {
// Defines a gmock action that runs a given closure.
ACTION_P(RunClosure, closure) {
closure.Run();
}
} // namespace test
} // namespace base
#endif // CONTENT_TEST_GMOCK_UTIL_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/test/leveldb_wrapper_test_util.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
#include "mojo/public/cpp/bindings/strong_associated_binding.h"
namespace content {
namespace test {
namespace {
void SuccessCallback(base::OnceClosure callback,
bool* success_out,
bool success) {
*success_out = success;
std::move(callback).Run();
}
} // namespace
base::OnceCallback<void(bool)> MakeSuccessCallback(base::OnceClosure callback,
bool* success_out) {
return base::BindOnce(&SuccessCallback, std::move(callback), success_out);
}
bool PutSync(mojom::LevelDBWrapper* wrapper,
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
const base::Optional<std::vector<uint8_t>>& old_value,
const std::string& source) {
bool success = false;
base::RunLoop loop;
wrapper->Put(key, value, old_value, source,
base::BindLambdaForTesting([&](bool success_in) {
success = success_in;
loop.Quit();
}));
loop.Run();
return success;
}
leveldb::mojom::DatabaseError GetAllSync(
content::LevelDBWrapperImpl* wrapper,
std::vector<mojom::KeyValuePtr>* data_out) {
DCHECK(data_out);
base::RunLoop loop;
bool complete = false;
leveldb::mojom::DatabaseError status =
leveldb::mojom::DatabaseError::INVALID_ARGUMENT;
wrapper->GetAll(
GetAllCallback::CreateAndBind(&complete, loop.QuitClosure()),
base::BindLambdaForTesting([&](leveldb::mojom::DatabaseError status_in,
std::vector<mojom::KeyValuePtr> data_in) {
status = status_in;
*data_out = std::move(data_in);
}));
loop.Run();
DCHECK(complete);
return status;
}
bool DeleteSync(mojom::LevelDBWrapper* wrapper,
const std::vector<uint8_t>& key,
const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source) {
bool success = false;
base::RunLoop loop;
wrapper->Delete(key, client_old_value, source,
base::BindLambdaForTesting([&](bool success_in) {
success = success_in;
loop.Quit();
}));
loop.Run();
return success;
}
bool DeleteAllSync(mojom::LevelDBWrapper* wrapper, const std::string& source) {
bool success = false;
base::RunLoop loop;
wrapper->DeleteAll(source, base::BindLambdaForTesting([&](bool success_in) {
success = success_in;
loop.Quit();
}));
loop.Run();
return success;
}
base::OnceCallback<void(leveldb::mojom::DatabaseError,
std::vector<mojom::KeyValuePtr>)>
MakeGetAllCallback(leveldb::mojom::DatabaseError* status_out,
std::vector<mojom::KeyValuePtr>* data_out) {
DCHECK(status_out);
DCHECK(data_out);
return base::BindLambdaForTesting(
[status_out, data_out](leveldb::mojom::DatabaseError status_in,
std::vector<mojom::KeyValuePtr> data_in) {
*status_out = status_in;
*data_out = std::move(data_in);
});
}
// static
mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo
GetAllCallback::CreateAndBind(bool* result, base::OnceClosure callback) {
mojom::LevelDBWrapperGetAllCallbackAssociatedPtr ptr;
auto request = mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr);
mojo::MakeStrongAssociatedBinding(
base::WrapUnique(new GetAllCallback(result, std::move(callback))),
std::move(request));
return ptr.PassInterface();
}
GetAllCallback::GetAllCallback(bool* result, base::OnceClosure callback)
: result_(result), callback_(std::move(callback)) {}
GetAllCallback::~GetAllCallback() = default;
void GetAllCallback::Complete(bool success) {
*result_ = success;
if (callback_)
std::move(callback_).Run();
}
MockLevelDBObserver::MockLevelDBObserver() = default;
MockLevelDBObserver::~MockLevelDBObserver() = default;
} // namespace test
} // 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_TEST_LEVELDB_WRAPPER_TEST_UTIL_H_
#define CONTENT_TEST_LEVELDB_WRAPPER_TEST_UTIL_H_
#include <stdint.h>
#include <vector>
#include "base/callback.h"
#include "base/optional.h"
#include "components/services/leveldb/public/interfaces/leveldb.mojom.h"
#include "content/browser/leveldb_wrapper_impl.h"
#include "content/common/leveldb_wrapper.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
// Utility functions and classes for testing LevelDBWrapper implementations.
namespace content {
namespace test {
// Creates a callback that sets the given |success_out| to the boolean argument,
// and then calls |callback|.
base::OnceCallback<void(bool)> MakeSuccessCallback(base::OnceClosure callback,
bool* success_out);
// Does a |Put| call on the given |wrapper| and waits until the response is
// received. Returns if the call was successful.
bool PutSync(mojom::LevelDBWrapper* wrapper,
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
const base::Optional<std::vector<uint8_t>>& old_value,
const std::string& source);
// This only accepts the implementation, as calling the mojo interface is a
// synchronous call which freezes the thread. If the leveldb implementation
// is asynchrounous, this requires 3 threads.
leveldb::mojom::DatabaseError GetAllSync(
content::LevelDBWrapperImpl* wrapper,
std::vector<mojom::KeyValuePtr>* data_out);
// Does a |Delete| call on the wrapper and waits until the response is
// received. Returns if the call was successful.
bool DeleteSync(mojom::LevelDBWrapper* wrapper,
const std::vector<uint8_t>& key,
const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source);
// Does a |DeleteAll| call on the wrapper and waits until the response is
// received. Returns if the call was successful.
bool DeleteAllSync(mojom::LevelDBWrapper* wrapper, const std::string& source);
// Creates a callback that simply sets the |*_out| variables to the arguments.
base::OnceCallback<void(leveldb::mojom::DatabaseError,
std::vector<mojom::KeyValuePtr>)>
MakeGetAllCallback(leveldb::mojom::DatabaseError* status_out,
std::vector<mojom::KeyValuePtr>* data_out);
// Utility class to help using the LevelDBWrapper::GetAll method. Use
// |CreateAndBind| to create the PtrInfo to send to the |GetAll| method.
// When the call is complete, the |callback| will be called.
class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback {
public:
static mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo CreateAndBind(
bool* result,
base::OnceClosure callback);
~GetAllCallback() override;
private:
GetAllCallback(bool* result, base::OnceClosure callback);
void Complete(bool success) override;
bool* result_;
base::OnceClosure callback_;
};
// Mock observer implementation for use with LevelDBWrapper.
class MockLevelDBObserver : public content::mojom::LevelDBObserver {
public:
MockLevelDBObserver();
~MockLevelDBObserver() override;
MOCK_METHOD3(KeyAdded,
void(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
const std::string& source));
MOCK_METHOD4(KeyChanged,
void(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& new_value,
const std::vector<uint8_t>& old_value,
const std::string& source));
MOCK_METHOD3(KeyDeleted,
void(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& old_value,
const std::string& source));
MOCK_METHOD1(AllDeleted, void(const std::string& source));
MOCK_METHOD1(ShouldSendOldValueOnMutations, void(bool value));
};
} // namespace test
} // namespace content
#endif // CONTENT_TEST_LEVELDB_WRAPPER_TEST_UTIL_H_
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