Commit 2d263e6d authored by Marijn Kruisselbrink's avatar Marijn Kruisselbrink Committed by Commit Bot

Implement byte transportation for mojo blob service.

Also turn on the BlobStorageBrowserTest for the new mojo implementation
now that transportation mostly works. This required a couple of minor
other changes:
- Adding BlobRegistry to the browser process' manifest
- Turning off some BlobDispatcherHost IsInUseInProcess checks. These
  checks don't really make sense anyway when it will become possible
  to transfer blobs between renderers directly, and this never was a
  security boundary anyway, so removing the check shouldn't hurt.

Bug: 611935
Change-Id: If8bfc2d8351aecc72a3818d0cbb013a55efc6175
Reviewed-on: https://chromium-review.googlesource.com/558192Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#487290}
parent 7f82c887
...@@ -7,12 +7,14 @@ ...@@ -7,12 +7,14 @@
#include <algorithm> #include <algorithm>
#include "base/bind.h" #include "base/bind.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "content/browser/bad_message.h" #include "content/browser/bad_message.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/child_process_security_policy_impl.h" #include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/fileapi/browser_file_system_helper.h" #include "content/browser/fileapi/browser_file_system_helper.h"
#include "content/common/fileapi/webblob_messages.h" #include "content/common/fileapi/webblob_messages.h"
#include "content/public/common/content_features.h"
#include "ipc/ipc_platform_file.h" #include "ipc/ipc_platform_file.h"
#include "storage/browser/blob/blob_data_handle.h" #include "storage/browser/blob/blob_data_handle.h"
#include "storage/browser/blob/blob_entry.h" #include "storage/browser/blob/blob_entry.h"
...@@ -355,7 +357,12 @@ void BlobDispatcherHost::SendFinalBlobStatus(const std::string& uuid, ...@@ -355,7 +357,12 @@ void BlobDispatcherHost::SendFinalBlobStatus(const std::string& uuid,
} }
bool BlobDispatcherHost::IsInUseInHost(const std::string& uuid) { bool BlobDispatcherHost::IsInUseInHost(const std::string& uuid) {
return base::ContainsKey(blobs_inuse_map_, uuid); // IsInUseInHost is not a security check, as renderers can arbitrarily start
// using blobs by sending an IncrementRefCount IPC. Furthermore with mojo
// blobs it doesn't make sense anymore to try to decide if a blob is in use in
// a process, so just always return true in that case.
return base::FeatureList::IsEnabled(features::kMojoBlobs) ||
base::ContainsKey(blobs_inuse_map_, uuid);
} }
bool BlobDispatcherHost::IsUrlRegisteredInHost(const GURL& blob_url) { bool BlobDispatcherHost::IsUrlRegisteredInHost(const GURL& blob_url) {
......
...@@ -3,11 +3,13 @@ ...@@ -3,11 +3,13 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/sequenced_worker_pool.h" #include "base/threading/sequenced_worker_pool.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test_utils.h" #include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h"
...@@ -103,4 +105,36 @@ IN_PROC_BROWSER_TEST_F(BlobStorageBrowserTest, BlobCombinations) { ...@@ -103,4 +105,36 @@ IN_PROC_BROWSER_TEST_F(BlobStorageBrowserTest, BlobCombinations) {
RunAllBlockingPoolTasksUntilIdle(); RunAllBlockingPoolTasksUntilIdle();
} }
class MojoBlobStorageBrowserTest : public BlobStorageBrowserTest {
public:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(features::kMojoBlobs);
BlobStorageBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(MojoBlobStorageBrowserTest, BlobCombinations) {
SetBlobLimits();
SimpleTest(GetTestUrl("blob_storage", "blob_creation_and_slicing.html"));
storage::BlobMemoryController* memory_controller = GetMemoryController();
ASSERT_TRUE(memory_controller);
// Our exact usages depend on IPC message ordering & garbage collection.
// Since this is basically random, we just check bounds.
EXPECT_LT(0u, memory_controller->memory_usage());
EXPECT_LT(0ul, memory_controller->disk_usage());
EXPECT_GT(memory_controller->disk_usage(),
static_cast<uint64_t>(memory_controller->memory_usage()));
EXPECT_GT(limits_.max_blob_in_memory_space,
memory_controller->memory_usage());
EXPECT_GT(limits_.effective_max_disk_space, memory_controller->disk_usage());
shell()->Close();
// Make sure we run all file / io tasks.
base::RunLoop().RunUntilIdle();
RunAllBlockingPoolTasksUntilIdle();
}
} // namespace content } // namespace content
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
"shape_detection::mojom::FaceDetectionProvider", "shape_detection::mojom::FaceDetectionProvider",
"resource_coordinator::mojom::CoordinationUnit", "resource_coordinator::mojom::CoordinationUnit",
"shape_detection::mojom::TextDetection", "shape_detection::mojom::TextDetection",
"storage::mojom::BlobRegistry",
"ui::mojom::Gpu" "ui::mojom::Gpu"
], ],
"geolocation_config": [ "geolocation_config": [
......
...@@ -32,6 +32,8 @@ component("browser") { ...@@ -32,6 +32,8 @@ component("browser") {
"blob/blob_transport_host.h", "blob/blob_transport_host.h",
"blob/blob_transport_request_builder.cc", "blob/blob_transport_request_builder.cc",
"blob/blob_transport_request_builder.h", "blob/blob_transport_request_builder.h",
"blob/blob_transport_strategy.cc",
"blob/blob_transport_strategy.h",
"blob/blob_url_request_job.cc", "blob/blob_url_request_job.cc",
"blob/blob_url_request_job.h", "blob/blob_url_request_job.h",
"blob/blob_url_request_job_factory.cc", "blob/blob_url_request_job_factory.cc",
...@@ -243,6 +245,7 @@ source_set("unittests") { ...@@ -243,6 +245,7 @@ source_set("unittests") {
"blob/blob_storage_context_unittest.cc", "blob/blob_storage_context_unittest.cc",
"blob/blob_storage_registry_unittest.cc", "blob/blob_storage_registry_unittest.cc",
"blob/blob_transport_request_builder_unittest.cc", "blob/blob_transport_request_builder_unittest.cc",
"blob/blob_transport_strategy_unittest.cc",
"database/database_quota_client_unittest.cc", "database/database_quota_client_unittest.cc",
"database/database_tracker_unittest.cc", "database/database_tracker_unittest.cc",
"database/database_util_unittest.cc", "database/database_util_unittest.cc",
...@@ -309,6 +312,8 @@ static_library("test_support") { ...@@ -309,6 +312,8 @@ static_library("test_support") {
"test/fileapi_test_file_set.h", "test/fileapi_test_file_set.h",
"test/mock_blob_url_request_context.cc", "test/mock_blob_url_request_context.cc",
"test/mock_blob_url_request_context.h", "test/mock_blob_url_request_context.h",
"test/mock_bytes_provider.cc",
"test/mock_bytes_provider.h",
"test/mock_file_change_observer.cc", "test/mock_file_change_observer.cc",
"test/mock_file_change_observer.h", "test/mock_file_change_observer.h",
"test/mock_file_update_observer.cc", "test/mock_file_update_observer.cc",
...@@ -336,6 +341,7 @@ static_library("test_support") { ...@@ -336,6 +341,7 @@ static_library("test_support") {
deps = [ deps = [
":browser", ":browser",
"//base/test:test_support", "//base/test:test_support",
"//mojo/common",
"//net:test_support", "//net:test_support",
"//testing/gtest", "//testing/gtest",
"//third_party/leveldatabase", "//third_party/leveldatabase",
......
...@@ -111,8 +111,19 @@ bool BlobDataBuilder::PopulateFutureData(size_t index, ...@@ -111,8 +111,19 @@ bool BlobDataBuilder::PopulateFutureData(size_t index,
const char* data, const char* data,
size_t offset, size_t offset,
size_t length) { size_t length) {
DCHECK_LT(index, items_.size());
DCHECK(data); DCHECK(data);
char* target = GetFutureDataPointerToPopulate(index, offset, length);
if (!target)
return false;
std::memcpy(target, data, length);
return true;
}
char* BlobDataBuilder::GetFutureDataPointerToPopulate(size_t index,
size_t offset,
size_t length) {
DCHECK_LT(index, items_.size());
DataElement* element = items_[index]->data_element_ptr(); DataElement* element = items_[index]->data_element_ptr();
// We lazily allocate our data buffer by waiting until the first // We lazily allocate our data buffer by waiting until the first
...@@ -128,16 +139,15 @@ bool BlobDataBuilder::PopulateFutureData(size_t index, ...@@ -128,16 +139,15 @@ bool BlobDataBuilder::PopulateFutureData(size_t index,
} }
if (element->type() != DataElement::TYPE_BYTES) { if (element->type() != DataElement::TYPE_BYTES) {
DVLOG(1) << "Invalid item type."; DVLOG(1) << "Invalid item type.";
return false; return nullptr;
} }
base::CheckedNumeric<size_t> checked_end = offset; base::CheckedNumeric<size_t> checked_end = offset;
checked_end += length; checked_end += length;
if (!checked_end.IsValid() || checked_end.ValueOrDie() > element->length()) { if (!checked_end.IsValid() || checked_end.ValueOrDie() > element->length()) {
DVLOG(1) << "Invalid offset or length."; DVLOG(1) << "Invalid offset or length.";
return false; return nullptr;
} }
std::memcpy(element->mutable_bytes() + offset, data, length); return element->mutable_bytes() + offset;
return true;
} }
size_t BlobDataBuilder::AppendFutureFile(uint64_t offset, size_t BlobDataBuilder::AppendFutureFile(uint64_t offset,
......
...@@ -81,6 +81,16 @@ class STORAGE_EXPORT BlobDataBuilder { ...@@ -81,6 +81,16 @@ class STORAGE_EXPORT BlobDataBuilder {
size_t offset, size_t offset,
size_t length); size_t length);
// Same as PopulateFutureData, but rather than passing in the data to be
// copied, this method returns a pointer where the caller can copy |length|
// bytes of data to.
// Returns nullptr if:
// * The item was not created by using AppendFutureData, or
// * The offset and length are not valid.
char* GetFutureDataPointerToPopulate(size_t index,
size_t offset,
size_t length);
// Adds an item that is flagged for future data population. Use // Adds an item that is flagged for future data population. Use
// 'PopulateFutureFile' to set the file path and expected modification time // 'PopulateFutureFile' to set the file path and expected modification time
// of this file. Returns the index of the item (to be used in // of this file. Returns the index of the item (to be used in
......
...@@ -9,9 +9,44 @@ ...@@ -9,9 +9,44 @@
#include "storage/browser/blob/blob_data_builder.h" #include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_impl.h" #include "storage/browser/blob/blob_impl.h"
#include "storage/browser/blob/blob_storage_context.h" #include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/blob/blob_transport_strategy.h"
namespace storage { namespace storage {
namespace {
using MemoryStrategy = BlobMemoryController::Strategy;
bool CalculateBlobMemorySize(const std::vector<mojom::DataElementPtr>& elements,
size_t* shortcut_bytes,
uint64_t* total_bytes) {
DCHECK(shortcut_bytes);
DCHECK(total_bytes);
base::CheckedNumeric<uint64_t> total_size_checked = 0;
base::CheckedNumeric<size_t> shortcut_size_checked = 0;
for (const auto& e : elements) {
if (e->is_bytes()) {
const auto& bytes = e->get_bytes();
total_size_checked += bytes->length;
if (bytes->embedded_data) {
if (bytes->embedded_data->size() != bytes->length)
return false;
shortcut_size_checked += bytes->length;
}
} else {
continue;
}
if (!total_size_checked.IsValid() || !shortcut_size_checked.IsValid())
return false;
}
*shortcut_bytes = shortcut_size_checked.ValueOrDie();
*total_bytes = total_size_checked.ValueOrDie();
return true;
}
} // namespace
class BlobRegistryImpl::BlobUnderConstruction { class BlobRegistryImpl::BlobUnderConstruction {
public: public:
BlobUnderConstruction(BlobRegistryImpl* blob_registry, BlobUnderConstruction(BlobRegistryImpl* blob_registry,
...@@ -33,7 +68,7 @@ class BlobRegistryImpl::BlobUnderConstruction { ...@@ -33,7 +68,7 @@ class BlobRegistryImpl::BlobUnderConstruction {
// referenced by this new blob. This (and any further methods) could end up // referenced by this new blob. This (and any further methods) could end up
// deleting |this| by removing it from the blobs_under_construction_ // deleting |this| by removing it from the blobs_under_construction_
// collection in the blob service. // collection in the blob service.
void StartFetchingBlobUUIDs(); void StartTransportation();
~BlobUnderConstruction() {} ~BlobUnderConstruction() {}
...@@ -48,9 +83,18 @@ class BlobRegistryImpl::BlobUnderConstruction { ...@@ -48,9 +83,18 @@ class BlobRegistryImpl::BlobUnderConstruction {
// Also deletes |this| by removing it from the blobs_under_construction_ list. // Also deletes |this| by removing it from the blobs_under_construction_ list.
void MarkAsBroken(BlobStatus reason, void MarkAsBroken(BlobStatus reason,
const std::string& bad_message_reason = "") { const std::string& bad_message_reason = "") {
context()->CancelBuildingBlob(uuid(), reason); DCHECK(BlobStatusIsError(reason));
DCHECK_EQ(bad_message_reason.empty(), !BlobStatusIsBadIPC(reason));
// The blob might no longer have any references, in which case it may no
// longer exist. If that happens just skip calling cancel.
if (context()->registry().HasEntry(uuid()))
context()->CancelBuildingBlob(uuid(), reason);
if (!bad_message_reason.empty()) if (!bad_message_reason.empty())
std::move(bad_message_callback_).Run(bad_message_reason); std::move(bad_message_callback_).Run(bad_message_reason);
MarkAsFinishedAndDeleteSelf();
}
void MarkAsFinishedAndDeleteSelf() {
blob_registry_->blobs_under_construction_.erase(uuid()); blob_registry_->blobs_under_construction_.erase(uuid());
} }
...@@ -75,6 +119,16 @@ class BlobRegistryImpl::BlobUnderConstruction { ...@@ -75,6 +119,16 @@ class BlobRegistryImpl::BlobUnderConstruction {
// transporting. // transporting.
void ResolvedAllBlobDependencies(); void ResolvedAllBlobDependencies();
// Called when memory has been reserved for this blob and transport can begin.
// Could also be called if something caused the blob to become invalid before
// transportation began, in which case we just give up.
void OnReadyForTransport(
BlobStatus status,
std::vector<BlobMemoryController::FileCreationInfo> file_infos);
// Called when all data has been transported, or transport has failed.
void TransportComplete(BlobStatus result);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
// Returns true if the DAG made up by this blob and any other blobs that // Returns true if the DAG made up by this blob and any other blobs that
// are currently being built by BlobRegistryImpl contains any cycles. // are currently being built by BlobRegistryImpl contains any cycles.
...@@ -99,6 +153,9 @@ class BlobRegistryImpl::BlobUnderConstruction { ...@@ -99,6 +153,9 @@ class BlobRegistryImpl::BlobUnderConstruction {
// called. // called.
mojo::ReportBadMessageCallback bad_message_callback_; mojo::ReportBadMessageCallback bad_message_callback_;
// Transport strategy to use when transporting data.
std::unique_ptr<BlobTransportStrategy> transport_strategy_;
// List of UUIDs for referenced blobs. Same size as |elements_|. All entries // List of UUIDs for referenced blobs. Same size as |elements_|. All entries
// for non-blob elements will remain empty strings. // for non-blob elements will remain empty strings.
std::vector<std::string> referenced_blob_uuids_; std::vector<std::string> referenced_blob_uuids_;
...@@ -113,17 +170,11 @@ class BlobRegistryImpl::BlobUnderConstruction { ...@@ -113,17 +170,11 @@ class BlobRegistryImpl::BlobUnderConstruction {
DISALLOW_COPY_AND_ASSIGN(BlobUnderConstruction); DISALLOW_COPY_AND_ASSIGN(BlobUnderConstruction);
}; };
void BlobRegistryImpl::BlobUnderConstruction::StartFetchingBlobUUIDs() { void BlobRegistryImpl::BlobUnderConstruction::StartTransportation() {
size_t blob_count = 0; size_t blob_count = 0;
for (size_t i = 0; i < elements_.size(); ++i) { for (size_t i = 0; i < elements_.size(); ++i) {
const auto& element = elements_[i]; const auto& element = elements_[i];
if (element->is_blob()) { if (element->is_blob()) {
if (element->get_blob()->blob.encountered_error()) {
// Will delete |this|.
MarkAsBroken(BlobStatus::ERR_REFERENCED_BLOB_BROKEN);
return;
}
// If connection to blob is broken, something bad happened, so mark this // If connection to blob is broken, something bad happened, so mark this
// new blob as broken, which will delete |this| and keep it from doing // new blob as broken, which will delete |this| and keep it from doing
// unneeded extra work. // unneeded extra work.
...@@ -134,6 +185,10 @@ void BlobRegistryImpl::BlobUnderConstruction::StartFetchingBlobUUIDs() { ...@@ -134,6 +185,10 @@ void BlobRegistryImpl::BlobUnderConstruction::StartFetchingBlobUUIDs() {
element->get_blob()->blob->GetInternalUUID( element->get_blob()->blob->GetInternalUUID(
base::BindOnce(&BlobUnderConstruction::ReceivedBlobUUID, base::BindOnce(&BlobUnderConstruction::ReceivedBlobUUID,
weak_ptr_factory_.GetWeakPtr(), blob_count++)); weak_ptr_factory_.GetWeakPtr(), blob_count++));
} else if (element->is_bytes()) {
element->get_bytes()->data.set_connection_error_handler(base::BindOnce(
&BlobUnderConstruction::MarkAsBroken, weak_ptr_factory_.GetWeakPtr(),
BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, ""));
} }
} }
referenced_blob_uuids_.resize(blob_count); referenced_blob_uuids_.resize(blob_count);
...@@ -142,6 +197,32 @@ void BlobRegistryImpl::BlobUnderConstruction::StartFetchingBlobUUIDs() { ...@@ -142,6 +197,32 @@ void BlobRegistryImpl::BlobUnderConstruction::StartFetchingBlobUUIDs() {
// Without it a blob could forever remaing pending if a renderer sends us // Without it a blob could forever remaing pending if a renderer sends us
// a BlobPtr connected to a (malicious) non-responding implementation. // a BlobPtr connected to a (malicious) non-responding implementation.
// Do some basic validation of bytes to transport, and determine memory
// transport strategy to use later.
uint64_t transport_memory_size = 0;
size_t shortcut_size = 0;
if (!CalculateBlobMemorySize(elements_, &shortcut_size,
&transport_memory_size)) {
MarkAsBroken(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
"Invalid byte element sizes in BlobRegistry::Register");
return;
}
const BlobMemoryController& memory_controller =
context()->memory_controller();
MemoryStrategy memory_strategy =
memory_controller.DetermineStrategy(shortcut_size, transport_memory_size);
if (memory_strategy == MemoryStrategy::TOO_LARGE) {
MarkAsBroken(BlobStatus::ERR_OUT_OF_MEMORY);
return;
}
transport_strategy_ = BlobTransportStrategy::Create(
memory_strategy, &builder_,
base::BindOnce(&BlobUnderConstruction::TransportComplete,
weak_ptr_factory_.GetWeakPtr()),
memory_controller.limits());
// If there were no unresolved blobs, immediately proceed to the next step. // If there were no unresolved blobs, immediately proceed to the next step.
// Currently this will only happen if there are no blobs referenced // Currently this will only happen if there are no blobs referenced
// whatsoever, but hopefully in the future blob UUIDs will be cached in the // whatsoever, but hopefully in the future blob UUIDs will be cached in the
...@@ -212,10 +293,11 @@ void BlobRegistryImpl::BlobUnderConstruction::ResolvedAllBlobDependencies() { ...@@ -212,10 +293,11 @@ void BlobRegistryImpl::BlobUnderConstruction::ResolvedAllBlobDependencies() {
DCHECK_EQ(resolved_blob_uuid_count_, referenced_blob_uuids_.size()); DCHECK_EQ(resolved_blob_uuid_count_, referenced_blob_uuids_.size());
DCHECK_EQ(ready_dependent_blob_count_, referenced_blob_uuids_.size()); DCHECK_EQ(ready_dependent_blob_count_, referenced_blob_uuids_.size());
// TODO(mek): Fill BlobDataBuilder with elements_ other than blobs.
auto blob_uuid_it = referenced_blob_uuids_.begin(); auto blob_uuid_it = referenced_blob_uuids_.begin();
for (const auto& element : elements_) { for (const auto& element : elements_) {
if (element->is_file()) { if (element->is_bytes()) {
transport_strategy_->AddBytesElement(element->get_bytes().get());
} else if (element->is_file()) {
const auto& f = element->get_file(); const auto& f = element->get_file();
builder_.AppendFile(f->path, f->offset, f->length, builder_.AppendFile(f->path, f->offset, f->length,
f->expected_modification_time.value_or(base::Time())); f->expected_modification_time.value_or(base::Time()));
...@@ -231,14 +313,49 @@ void BlobRegistryImpl::BlobUnderConstruction::ResolvedAllBlobDependencies() { ...@@ -231,14 +313,49 @@ void BlobRegistryImpl::BlobUnderConstruction::ResolvedAllBlobDependencies() {
element->get_blob()->length); element->get_blob()->length);
} }
} }
// OnReadyForTransport can be called synchronously, which can call
// MarkAsFinishedAndDeleteSelf synchronously, so don't access any members
// after this call.
std::unique_ptr<BlobDataHandle> new_handle = std::unique_ptr<BlobDataHandle> new_handle =
context()->BuildPreregisteredBlob( context()->BuildPreregisteredBlob(
builder_, BlobStorageContext::TransportAllowedCallback()); builder_, base::Bind(&BlobUnderConstruction::OnReadyForTransport,
weak_ptr_factory_.GetWeakPtr()));
// TODO(mek): Update BlobImpl with new BlobDataHandle. Although handles // TODO(mek): Update BlobImpl with new BlobDataHandle. Although handles
// only differ in their size() attribute, which is currently not used by // only differ in their size() attribute, which is currently not used by
// BlobImpl. // BlobImpl.
DCHECK(!BlobStatusIsPending(new_handle->GetBlobStatus())); }
void BlobRegistryImpl::BlobUnderConstruction::OnReadyForTransport(
BlobStatus status,
std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
if (!BlobStatusIsPending(status)) {
// Done or error.
MarkAsFinishedAndDeleteSelf();
return;
}
transport_strategy_->BeginTransport(std::move(file_infos));
}
void BlobRegistryImpl::BlobUnderConstruction::TransportComplete(
BlobStatus result) {
// The blob might no longer have any references, in which case it may no
// longer exist. If that happens just skip calling Complete.
// TODO(mek): Stop building sooner if a blob is no longer referenced.
if (context()->registry().HasEntry(uuid())) {
if (result == BlobStatus::DONE)
context()->NotifyTransportComplete(uuid());
else
context()->CancelBuildingBlob(uuid(), result);
}
if (BlobStatusIsBadIPC(result)) {
// BlobTransportStrategy might have already reported a BadMessage on the
// BytesProvider binding, but just to be safe, also report one on the
// BlobRegistry binding itself.
std::move(bad_message_callback_)
.Run("Received invalid data while transporting blob");
}
MarkAsFinishedAndDeleteSelf();
} }
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
...@@ -331,7 +448,7 @@ void BlobRegistryImpl::Register(mojom::BlobRequest blob, ...@@ -331,7 +448,7 @@ void BlobRegistryImpl::Register(mojom::BlobRequest blob,
context_->AddFutureBlob(uuid, content_type, content_disposition); context_->AddFutureBlob(uuid, content_type, content_disposition);
BlobImpl::Create(std::move(handle), std::move(blob)); BlobImpl::Create(std::move(handle), std::move(blob));
blobs_under_construction_[uuid]->StartFetchingBlobUUIDs(); blobs_under_construction_[uuid]->StartTransportation();
std::move(callback).Run(); std::move(callback).Run();
} }
......
...@@ -149,6 +149,7 @@ class STORAGE_EXPORT BlobStorageContext { ...@@ -149,6 +149,7 @@ class STORAGE_EXPORT BlobStorageContext {
friend class BlobDataHandle; friend class BlobDataHandle;
friend class BlobDataHandle::BlobDataHandleShared; friend class BlobDataHandle::BlobDataHandleShared;
friend class BlobFlattenerTest; friend class BlobFlattenerTest;
friend class BlobRegistryImplTest;
friend class BlobSliceTest; friend class BlobSliceTest;
friend class BlobStorageContextTest; friend class BlobStorageContextTest;
......
This diff is collapsed.
// Copyright 2017 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 STORAGE_BROWSER_BLOB_BLOB_TRANSPORT_STRATEGY_H_
#define STORAGE_BROWSER_BLOB_BLOB_TRANSPORT_STRATEGY_H_
#include "base/callback.h"
#include "storage/browser/blob/blob_memory_controller.h"
#include "storage/browser/storage_browser_export.h"
namespace storage {
class BlobDataBuilder;
namespace mojom {
class DataElementBytes;
}
// This class is responsible for transporting bytes for an under-construction
// blob, using a specified transport strategy. This is used by BlobRegistryImpl
// for the actual transportation of bytes.
class STORAGE_EXPORT BlobTransportStrategy {
public:
using ResultCallback = base::OnceCallback<void(BlobStatus)>;
// Creates a BlobTransportStrategy instance for the specified memory strategy.
// The BlobDataBuilder and BlobStorageLimits must outlive the returned
// BlobTransportStrategy.
static std::unique_ptr<BlobTransportStrategy> Create(
BlobMemoryController::Strategy strategy,
BlobDataBuilder* builder,
ResultCallback result_callback,
const BlobStorageLimits& limits);
virtual ~BlobTransportStrategy();
// Called once for each DataElementBytes in a blob. The |bytes| passed in must
// outlive the BlobTransportStrategy instance.
virtual void AddBytesElement(mojom::DataElementBytes* bytes) = 0;
// Called when quota has been allocated and transportation should begin.
// Implementations will call the |result_callback_| when transportation has
// completed, or failed.
virtual void BeginTransport(
std::vector<BlobMemoryController::FileCreationInfo> file_infos) = 0;
protected:
BlobTransportStrategy(BlobDataBuilder* builder,
ResultCallback result_callback);
BlobDataBuilder* builder_;
ResultCallback result_callback_;
};
} // namespace storage
#endif // STORAGE_BROWSER_BLOB_BLOB_TRANSPORT_STRATEGY_H_
This diff is collapsed.
// Copyright 2017 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 "storage/browser/test/mock_bytes_provider.h"
#include "mojo/common/data_pipe_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace storage {
MockBytesProvider::MockBytesProvider(
std::vector<uint8_t> data,
size_t* reply_request_count,
size_t* stream_request_count,
size_t* file_request_count,
base::Optional<base::Time> file_modification_time)
: data_(std::move(data)),
reply_request_count_(reply_request_count),
stream_request_count_(stream_request_count),
file_request_count_(file_request_count),
file_modification_time_(file_modification_time) {}
MockBytesProvider::~MockBytesProvider() {}
void MockBytesProvider::RequestAsReply(RequestAsReplyCallback callback) {
if (reply_request_count_)
++*reply_request_count_;
std::move(callback).Run(data_);
}
void MockBytesProvider::RequestAsStream(
mojo::ScopedDataPipeProducerHandle pipe) {
if (stream_request_count_)
++*stream_request_count_;
mojo::common::BlockingCopyFromString(
std::string(reinterpret_cast<const char*>(data_.data()), data_.size()),
pipe);
}
void MockBytesProvider::RequestAsFile(uint64_t source_offset,
uint64_t source_size,
base::File file,
uint64_t file_offset,
RequestAsFileCallback callback) {
if (file_request_count_)
++*file_request_count_;
EXPECT_LE(source_offset + source_size, data_.size());
EXPECT_EQ(source_size,
static_cast<uint64_t>(file.Write(
file_offset,
reinterpret_cast<const char*>(data_.data() + source_offset),
source_size)));
EXPECT_TRUE(file.Flush());
std::move(callback).Run(file_modification_time_);
}
} // namespace storage
// Copyright 2017 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 STORAGE_BROWSER_TEST_MOCK_BYTES_PROVIDER_H_
#define STORAGE_BROWSER_TEST_MOCK_BYTES_PROVIDER_H_
#include "storage/public/interfaces/blobs.mojom.h"
namespace storage {
// Mock BytesProvider implementation. RequestAsStream blocks, so make sure to
// bind this implementation to a pipe on a separate sequence from where the
// bytes are consumed.
class MockBytesProvider : public mojom::BytesProvider {
public:
explicit MockBytesProvider(
std::vector<uint8_t> data,
size_t* reply_request_count = nullptr,
size_t* stream_request_count = nullptr,
size_t* file_request_count = nullptr,
base::Optional<base::Time> file_modification_time = base::Time());
~MockBytesProvider() override;
// BytesProvider implementation:
void RequestAsReply(RequestAsReplyCallback callback) override;
void RequestAsStream(mojo::ScopedDataPipeProducerHandle pipe) override;
void RequestAsFile(uint64_t source_offset,
uint64_t source_size,
base::File file,
uint64_t file_offset,
RequestAsFileCallback callback) override;
private:
std::vector<uint8_t> data_;
size_t* reply_request_count_;
size_t* stream_request_count_;
size_t* file_request_count_;
base::Optional<base::Time> file_modification_time_;
};
} // namespace storage
#endif // STORAGE_BROWSER_TEST_MOCK_BYTES_PROVIDER_H_
...@@ -17,6 +17,7 @@ static_assert(kDefaultMinPageFileSize < kDefaultMaxBlobInMemorySpace, ...@@ -17,6 +17,7 @@ static_assert(kDefaultMinPageFileSize < kDefaultMaxBlobInMemorySpace,
bool BlobStorageLimits::IsValid() const { bool BlobStorageLimits::IsValid() const {
return max_ipc_memory_size < max_shared_memory_size && return max_ipc_memory_size < max_shared_memory_size &&
max_ipc_memory_size < max_bytes_data_item_size &&
min_page_file_size < max_file_size && min_page_file_size < max_file_size &&
min_page_file_size < max_blob_in_memory_space && min_page_file_size < max_blob_in_memory_space &&
effective_max_disk_space <= desired_max_disk_space; effective_max_disk_space <= desired_max_disk_space;
......
...@@ -52,6 +52,12 @@ struct STORAGE_COMMON_EXPORT BlobStorageLimits { ...@@ -52,6 +52,12 @@ struct STORAGE_COMMON_EXPORT BlobStorageLimits {
// This is the maximum size of a shared memory handle. // This is the maximum size of a shared memory handle.
size_t max_shared_memory_size = kDefaultSharedMemorySize; size_t max_shared_memory_size = kDefaultSharedMemorySize;
// This is the maximum size of a bytes BlobDataItem. Only used for mojo
// based blob transportation, as the old IPC/shared memory based
// implementation doesn't support different values for this and
// max_shared_memory_size.
size_t max_bytes_data_item_size = kDefaultSharedMemorySize;
// This is the maximum amount of memory we can use to store blobs. // This is the maximum amount of memory we can use to store blobs.
size_t max_blob_in_memory_space = kDefaultMaxBlobInMemorySpace; size_t max_blob_in_memory_space = kDefaultMaxBlobInMemorySpace;
// The ratio applied to |max_blob_in_memory_space| to reduce memory usage // The ratio applied to |max_blob_in_memory_space| to reduce memory usage
......
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