| // Copyright 2013 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "extensions/browser/blob_reader.h" | 
 |  | 
 | #include <limits> | 
 | #include <utility> | 
 |  | 
 | #include "base/containers/span.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/strings/string_view_util.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 |  | 
 | // static | 
 | void BlobReader::Read(mojo::PendingRemote<blink::mojom::Blob> blob, | 
 |                       BlobReadCallback callback, | 
 |                       uint64_t offset, | 
 |                       uint64_t length) { | 
 |   DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
 |   CHECK_GE(offset, 0U); | 
 |   CHECK_GT(length, 0U); | 
 |   CHECK_LE(offset, std::numeric_limits<uint64_t>::max() - length); | 
 |  | 
 |   Read(std::move(blob), std::move(callback), Range{offset, length}); | 
 | } | 
 |  | 
 | // static | 
 | void BlobReader::Read(mojo::PendingRemote<blink::mojom::Blob> blob, | 
 |                       BlobReadCallback callback) { | 
 |   DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
 |   Read(std::move(blob), std::move(callback), /*range=*/std::nullopt); | 
 | } | 
 |  | 
 | BlobReader::~BlobReader() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } | 
 |  | 
 | // static | 
 | void BlobReader::Read(mojo::PendingRemote<blink::mojom::Blob> blob, | 
 |                       BlobReadCallback callback, | 
 |                       std::optional<BlobReader::Range> range) { | 
 |   auto reader = | 
 |       base::WrapUnique(new BlobReader(std::move(blob), std::move(range))); | 
 |  | 
 |   // Move the reader to be owned by the callback, so hold onto a temporary | 
 |   // pointer to it so we can still call Start on it. | 
 |   BlobReader* raw_reader = reader.get(); | 
 |   base::OnceClosure wrapped = base::BindOnce( | 
 |       [](BlobReadCallback callback, std::unique_ptr<BlobReader> reader) { | 
 |         std::move(callback).Run(std::move(reader->blob_data_), | 
 |                                 *reader->blob_length_); | 
 |       }, | 
 |       std::move(callback), std::move(reader)); | 
 |   raw_reader->Start(std::move(wrapped)); | 
 | } | 
 |  | 
 | BlobReader::BlobReader(mojo::PendingRemote<blink::mojom::Blob> blob, | 
 |                        std::optional<Range> range) | 
 |     : blob_(std::move(blob)), read_range_(std::move(range)) { | 
 |   DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
 |   blob_.set_disconnect_handler( | 
 |       base::BindOnce(&BlobReader::Failed, base::Unretained(this))); | 
 | } | 
 |  | 
 | void BlobReader::Start(base::OnceClosure callback) { | 
 |   DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
 |   callback_ = std::move(callback); | 
 |  | 
 |   mojo::ScopedDataPipeProducerHandle producer_handle; | 
 |   mojo::ScopedDataPipeConsumerHandle consumer_handle; | 
 |   MojoResult result = CreateDataPipe(nullptr, producer_handle, consumer_handle); | 
 |   if (result != MOJO_RESULT_OK) { | 
 |     Failed(); | 
 |     return; | 
 |   } | 
 |   if (read_range_) { | 
 |     blob_->ReadRange(read_range_->offset, read_range_->length, | 
 |                      std::move(producer_handle), | 
 |                      receiver_.BindNewPipeAndPassRemote()); | 
 |   } else { | 
 |     blob_->ReadAll(std::move(producer_handle), | 
 |                    receiver_.BindNewPipeAndPassRemote()); | 
 |   } | 
 |   data_pipe_drainer_ = | 
 |       std::make_unique<mojo::DataPipeDrainer>(this, std::move(consumer_handle)); | 
 | } | 
 |  | 
 | void BlobReader::OnCalculatedSize(uint64_t total_size, | 
 |                                   uint64_t expected_content_size) { | 
 |   blob_length_ = total_size; | 
 |   if (data_complete_) { | 
 |     Succeeded(); | 
 |   } | 
 | } | 
 |  | 
 | void BlobReader::OnDataAvailable(base::span<const uint8_t> data) { | 
 |   blob_data_.append(base::as_string_view(data)); | 
 | } | 
 |  | 
 | void BlobReader::OnDataComplete() { | 
 |   data_complete_ = true; | 
 |   if (blob_length_) { | 
 |     Succeeded(); | 
 |   } | 
 | } | 
 |  | 
 | void BlobReader::Failed() { | 
 |   blob_length_ = 0; | 
 |   std::move(callback_).Run(); | 
 | } | 
 |  | 
 | void BlobReader::Succeeded() { | 
 |   std::move(callback_).Run(); | 
 | } |