Commit b31800d9 authored by Marijn Kruisselbrink's avatar Marijn Kruisselbrink Committed by Commit Bot

[Blobs] Add support for progress events to BlobBuilderFromStream.

And use that in the XHR code.

Bug: 754493, 712693, 791702
Change-Id: Ic0904558d43131cc2240805eaa794359f6a3f1d7
Reviewed-on: https://chromium-review.googlesource.com/957600
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#547471}
parent 05ce6db9
......@@ -117,6 +117,7 @@ void SyncLoadContext::OnStartLoadingResponseBody(
download_to_blob_registry_->RegisterFromStream(
response_->info.mime_type, "",
std::max<int64_t>(0, response_->info.content_length), std::move(body),
nullptr,
base::BindOnce(&SyncLoadContext::OnFinishCreatingBlob,
base::Unretained(this)));
}
......
......@@ -323,6 +323,8 @@ static_library("test_support") {
sources = [
"test/async_file_test_helper.cc",
"test/async_file_test_helper.h",
"test/fake_progress_client.cc",
"test/fake_progress_client.h",
"test/fileapi_test_file_set.cc",
"test/fileapi_test_file_set.h",
"test/mock_blob_registry_delegate.cc",
......
......@@ -11,6 +11,7 @@
#include "storage/browser/blob/blob_data_handle.h"
#include "storage/browser/blob/shareable_blob_data_item.h"
#include "storage/browser/storage_browser_export.h"
#include "third_party/WebKit/public/mojom/blob/blob_registry.mojom.h"
namespace storage {
......@@ -49,22 +50,27 @@ class STORAGE_EXPORT BlobBuilderFromStream {
base::OnceCallback<void(BlobBuilderFromStream*,
std::unique_ptr<BlobDataHandle>)>;
BlobBuilderFromStream(base::WeakPtr<BlobStorageContext> context,
std::string content_type,
std::string content_disposition,
uint64_t length_hint,
mojo::ScopedDataPipeConsumerHandle data,
ResultCallback callback);
BlobBuilderFromStream(
base::WeakPtr<BlobStorageContext> context,
std::string content_type,
std::string content_disposition,
uint64_t length_hint,
mojo::ScopedDataPipeConsumerHandle data,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
ResultCallback callback);
~BlobBuilderFromStream();
private:
class WritePipeToFileHelper;
class WritePipeToFutureDataHelper;
void AllocateMoreMemorySpace(uint64_t length_hint,
mojo::ScopedDataPipeConsumerHandle pipe);
void AllocateMoreMemorySpace(
uint64_t length_hint,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
mojo::ScopedDataPipeConsumerHandle pipe);
void MemoryQuotaAllocated(
mojo::ScopedDataPipeConsumerHandle pipe,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
std::vector<scoped_refptr<ShareableBlobDataItem>> chunk_items,
size_t item_to_populate,
bool success);
......@@ -72,12 +78,16 @@ class STORAGE_EXPORT BlobBuilderFromStream {
std::vector<scoped_refptr<ShareableBlobDataItem>> chunk_items,
size_t populated_item_index,
uint64_t bytes_written,
mojo::ScopedDataPipeConsumerHandle pipe);
mojo::ScopedDataPipeConsumerHandle pipe,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client);
void AllocateMoreFileSpace(uint64_t length_hint,
mojo::ScopedDataPipeConsumerHandle pipe);
void AllocateMoreFileSpace(
uint64_t length_hint,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
mojo::ScopedDataPipeConsumerHandle pipe);
void FileQuotaAllocated(
mojo::ScopedDataPipeConsumerHandle pipe,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
std::vector<scoped_refptr<ShareableBlobDataItem>> chunk_items,
size_t item_to_populate,
std::vector<BlobMemoryController::FileCreationInfo> info,
......@@ -89,6 +99,7 @@ class STORAGE_EXPORT BlobBuilderFromStream {
bool success,
uint64_t bytes_written,
mojo::ScopedDataPipeConsumerHandle pipe,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
const base::Time& modification_time);
void DidWriteToExtendedFile(
scoped_refptr<ShareableFileReference> file_reference,
......@@ -96,6 +107,7 @@ class STORAGE_EXPORT BlobBuilderFromStream {
bool success,
uint64_t bytes_written,
mojo::ScopedDataPipeConsumerHandle pipe,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
const base::Time& modification_time);
// These values are persisted to logs. Entries should not be renumbered and
......
......@@ -12,10 +12,12 @@
#include "base/task_scheduler/task_scheduler.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_data_item.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/test/fake_progress_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace storage {
......@@ -87,7 +89,7 @@ class BlobBuilderFromStreamTest
BlobBuilderFromStream* finished_builder = nullptr;
BlobBuilderFromStream builder(
context_->AsWeakPtr(), kContentType, kContentDisposition, length_hint,
std::move(pipe.consumer_handle),
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting([&](BlobBuilderFromStream* result_builder,
std::unique_ptr<BlobDataHandle> blob) {
finished_builder = result_builder;
......@@ -175,7 +177,7 @@ TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnDeletion) {
BlobBuilderFromStream* builder_ptr = nullptr;
auto builder = std::make_unique<BlobBuilderFromStream>(
context_->AsWeakPtr(), "", "", GetLengthHint(16),
std::move(pipe.consumer_handle),
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting([&](BlobBuilderFromStream* result_builder,
std::unique_ptr<BlobDataHandle> blob) {
EXPECT_EQ(builder_ptr, result_builder);
......@@ -340,7 +342,7 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuota) {
std::unique_ptr<BlobDataHandle> result;
BlobBuilderFromStream builder(
context_->AsWeakPtr(), "", "", kLengthHint,
std::move(pipe.consumer_handle),
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting(
[&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) {
result = std::move(blob);
......@@ -363,7 +365,7 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuotaAndNoDisk) {
std::unique_ptr<BlobDataHandle> result;
BlobBuilderFromStream builder(
context_->AsWeakPtr(), "", "", kLengthHint,
std::move(pipe.consumer_handle),
std::move(pipe.consumer_handle), nullptr,
base::BindLambdaForTesting(
[&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) {
result = std::move(blob);
......@@ -377,6 +379,37 @@ TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuotaAndNoDisk) {
EXPECT_EQ(0u, context_->memory_controller().disk_usage());
}
TEST_P(BlobBuilderFromStreamTest, ProgressEvents) {
const std::string kData =
base::RandBytesAsString(kTestBlobStorageMaxBytesDataItemSize + 5);
FakeProgressClient progress_client;
blink::mojom::ProgressClientAssociatedPtr progress_client_ptr;
mojo::AssociatedBinding<blink::mojom::ProgressClient> progress_binding(
&progress_client,
MakeRequestAssociatedWithDedicatedPipe(&progress_client_ptr));
mojo::DataPipe pipe;
base::RunLoop loop;
std::unique_ptr<BlobDataHandle> result;
BlobBuilderFromStream builder(
context_->AsWeakPtr(), "", "", GetLengthHint(kData.size()),
std::move(pipe.consumer_handle), progress_client_ptr.PassInterface(),
base::BindLambdaForTesting(
[&](BlobBuilderFromStream*, std::unique_ptr<BlobDataHandle> blob) {
result = std::move(blob);
loop.Quit();
}));
mojo::BlockingCopyFromString(kData, pipe.producer_handle);
pipe.producer_handle.reset();
loop.Run();
progress_binding.FlushForTesting();
EXPECT_EQ(kData.size(), progress_client.total_size);
EXPECT_GE(progress_client.call_count, 2);
}
INSTANTIATE_TEST_CASE_P(BlobBuilderFromStreamTest,
BlobBuilderFromStreamTest,
::testing::Values(LengthHintTestType::kUnknownSize,
......
......@@ -550,6 +550,7 @@ void BlobRegistryImpl::RegisterFromStream(
const std::string& content_disposition,
uint64_t expected_length,
mojo::ScopedDataPipeConsumerHandle data,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
RegisterFromStreamCallback callback) {
if (!context_) {
std::move(callback).Run(nullptr);
......@@ -558,7 +559,7 @@ void BlobRegistryImpl::RegisterFromStream(
blobs_being_streamed_.insert(std::make_unique<BlobBuilderFromStream>(
context_, content_type, content_disposition, expected_length,
std::move(data),
std::move(data), std::move(progress_client),
base::BindOnce(&BlobRegistryImpl::StreamingBlobDone,
base::Unretained(this), std::move(callback))));
}
......
......@@ -46,11 +46,13 @@ class STORAGE_EXPORT BlobRegistryImpl : public blink::mojom::BlobRegistry {
const std::string& content_disposition,
std::vector<blink::mojom::DataElementPtr> elements,
RegisterCallback callback) override;
void RegisterFromStream(const std::string& content_type,
const std::string& content_disposition,
uint64_t expected_length,
mojo::ScopedDataPipeConsumerHandle data,
RegisterFromStreamCallback callback) override;
void RegisterFromStream(
const std::string& content_type,
const std::string& content_disposition,
uint64_t expected_length,
mojo::ScopedDataPipeConsumerHandle data,
blink::mojom::ProgressClientAssociatedPtrInfo progress_client,
RegisterFromStreamCallback callback) override;
void GetBlobFromUUID(blink::mojom::BlobRequest blob,
const std::string& uuid,
GetBlobFromUUIDCallback callback) override;
......
......@@ -20,6 +20,7 @@
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_data_handle.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/test/fake_progress_client.h"
#include "storage/browser/test/mock_blob_registry_delegate.h"
#include "storage/browser/test/mock_bytes_provider.h"
#include "storage/browser/test/mock_special_storage_policy.h"
......@@ -1037,12 +1038,17 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream) {
const std::string kContentType = "content/type";
const std::string kContentDisposition = "disposition";
FakeProgressClient progress_client;
blink::mojom::ProgressClientAssociatedPtrInfo progress_client_ptr;
mojo::AssociatedBinding<blink::mojom::ProgressClient> progress_binding(
&progress_client, MakeRequest(&progress_client_ptr));
mojo::DataPipe pipe;
blink::mojom::SerializedBlobPtr blob;
base::RunLoop loop;
registry_->RegisterFromStream(
kContentType, kContentDisposition, kData.length(),
std::move(pipe.consumer_handle),
std::move(pipe.consumer_handle), std::move(progress_client_ptr),
base::BindLambdaForTesting([&](blink::mojom::SerializedBlobPtr result) {
blob = std::move(result);
loop.Quit();
......@@ -1058,6 +1064,9 @@ TEST_F(BlobRegistryImplTest, RegisterFromStream) {
ASSERT_TRUE(blob->blob);
blink::mojom::BlobPtr blob_ptr(std::move(blob->blob));
EXPECT_EQ(blob->uuid, UUIDFromBlob(blob_ptr.get()));
EXPECT_EQ(kData.length(), progress_client.total_size);
EXPECT_GE(progress_client.call_count, 1);
}
} // namespace storage
// 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 "storage/browser/test/fake_progress_client.h"
namespace storage {
void FakeProgressClient::OnProgress(uint64_t delta) {
total_size += delta;
call_count++;
}
} // namespace storage
// 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 STORAGE_BROWSER_TEST_FAKE_PROGRESS_CLIENT_H_
#define STORAGE_BROWSER_TEST_FAKE_PROGRESS_CLIENT_H_
#include "third_party/WebKit/public/mojom/blob/blob_registry.mojom.h"
namespace storage {
class FakeProgressClient : public blink::mojom::ProgressClient {
public:
void OnProgress(uint64_t delta) override;
int call_count = 0;
uint64_t total_size = 0;
};
} // namespace storage
#endif // STORAGE_BROWSER_TEST_FAKE_PROGRESS_CLIENT_H_
......@@ -26,6 +26,7 @@ void FakeBlobRegistry::RegisterFromStream(
const String& content_disposition,
uint64_t expected_length,
mojo::ScopedDataPipeConsumerHandle data,
mojom::blink::ProgressClientAssociatedPtrInfo,
RegisterFromStreamCallback) {
NOTREACHED();
}
......
......@@ -25,6 +25,7 @@ class FakeBlobRegistry : public mojom::blink::BlobRegistry {
const String& content_disposition,
uint64_t expected_length,
mojo::ScopedDataPipeConsumerHandle,
mojom::blink::ProgressClientAssociatedPtrInfo,
RegisterFromStreamCallback) override;
void GetBlobFromUUID(mojom::blink::BlobRequest,
......
......@@ -73,6 +73,7 @@ ResourceLoader::ResourceLoader(ResourceFetcher* fetcher,
resource_(resource),
inflight_keepalive_bytes_(inflight_keepalive_bytes),
is_cache_aware_loading_activated_(false),
progress_binding_(this),
cancel_timer_(Context().GetLoadingTaskRunner(),
this,
&ResourceLoader::CancelTimerFired) {
......@@ -619,10 +620,14 @@ void ResourceLoader::DidStartLoadingResponseBody(
const ResourceResponse& response = resource_->GetResponse();
AtomicString mime_type = response.MimeType();
mojom::blink::ProgressClientAssociatedPtrInfo progress_client_ptr;
progress_binding_.Bind(MakeRequest(&progress_client_ptr));
mojom::blink::BlobRegistry* blob_registry = BlobDataHandle::GetBlobRegistry();
blob_registry->RegisterFromStream(
mime_type.IsNull() ? g_empty_string : mime_type.LowerASCII(), "",
std::max(0ll, response.ExpectedContentLength()), std::move(body),
std::move(progress_client_ptr),
WTF::Bind(&ResourceLoader::FinishedCreatingBlob, WrapPersistent(this)));
}
......@@ -827,6 +832,16 @@ ResourceLoader::GetLoadingTaskRunner() {
return Context().GetLoadingTaskRunner();
}
void ResourceLoader::OnProgress(uint64_t delta) {
DCHECK(!blob_finished_);
if (scheduler_client_id_ == ResourceLoadScheduler::kInvalidClientId)
return;
Context().DispatchDidReceiveData(resource_->Identifier(), nullptr, delta);
resource_->DidDownloadData(delta);
}
void ResourceLoader::FinishedCreatingBlob(
const scoped_refptr<BlobDataHandle>& blob) {
DCHECK(!blob_finished_);
......@@ -834,13 +849,6 @@ void ResourceLoader::FinishedCreatingBlob(
if (scheduler_client_id_ == ResourceLoadScheduler::kInvalidClientId)
return;
// TODO(mek): Implement proper progress events once streaming data to a blob
// supports reporting progress.
if (blob) {
Context().DispatchDidReceiveData(resource_->Identifier(), nullptr,
blob->size());
resource_->DidDownloadData(blob->size());
}
Context().DispatchDidDownloadToBlob(resource_->Identifier(), blob.get());
resource_->DidDownloadToBlob(blob);
......
......@@ -31,6 +31,7 @@
#include <memory>
#include "base/gtest_prod_util.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "platform/PlatformExport.h"
#include "platform/heap/Handle.h"
#include "platform/loader/fetch/Resource.h"
......@@ -40,6 +41,7 @@
#include "platform/wtf/Forward.h"
#include "public/platform/WebURLLoader.h"
#include "public/platform/WebURLLoaderClient.h"
#include "third_party/WebKit/public/mojom/blob/blob_registry.mojom-blink.h"
namespace blink {
......@@ -54,7 +56,8 @@ class ResourceFetcher;
class PLATFORM_EXPORT ResourceLoader final
: public GarbageCollectedFinalized<ResourceLoader>,
public ResourceLoadSchedulerClient,
protected WebURLLoaderClient {
protected WebURLLoaderClient,
protected mojom::blink::ProgressClient {
USING_GARBAGE_COLLECTED_MIXIN(ResourceLoader);
USING_PRE_FINALIZER(ResourceLoader, Dispose);
......@@ -168,6 +171,7 @@ class PLATFORM_EXPORT ResourceLoader final
void CancelTimerFired(TimerBase*);
void OnProgress(uint64_t delta) override;
void FinishedCreatingBlob(const scoped_refptr<BlobDataHandle>&);
std::unique_ptr<WebURLLoader> loader_;
......@@ -180,6 +184,7 @@ class PLATFORM_EXPORT ResourceLoader final
bool is_cache_aware_loading_activated_;
bool is_downloading_to_blob_ = false;
mojo::AssociatedBinding<mojom::blink::ProgressClient> progress_binding_;
bool blob_finished_ = false;
bool blob_response_started_ = false;
// If DidFinishLoading is called while downloading to a blob before the blob
......
......@@ -10,8 +10,19 @@ import "third_party/WebKit/public/mojom/blob/data_element.mojom";
import "third_party/WebKit/public/mojom/blob/serialized_blob.mojom";
import "url/mojom/origin.mojom";
// This interface is the primary access point from renderer to the browser's blob system.
// This interface provides methods to register new blobs and get references to existing blobs.
// This interface is used to inform a client of progress, in particular when
// creating a blob from a data stream.
interface ProgressClient {
// Called everytime a new chunk of data has been read from the stream, and
// written to a blob being built. |delta| is the size of the latest chunk,
// so when the blob is finished the sum of all the |delta| values will be
// equal to the size of the created blob.
OnProgress(uint64 delta);
};
// This interface is the primary access point from renderer to the browser's
// blob system. This interface provides methods to register new blobs and get
// references to existing blobs.
interface BlobRegistry {
// Registers a new blob with the blob registry.
// TODO(mek): Make this method non-sync and get rid of the UUID parameter once
......@@ -31,7 +42,9 @@ interface BlobRegistry {
// returned.
RegisterFromStream(string content_type, string content_disposition,
uint64 length_hint,
handle<data_pipe_consumer> data) => (SerializedBlob? blob);
handle<data_pipe_consumer> data,
associated ProgressClient? progress_client)
=> (SerializedBlob? blob);
// Returns a reference to an existing blob. Should not be used by new code,
// is only exposed to make converting existing blob using code easier.
......
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