summaryrefslogtreecommitdiff
path: root/chromium/content/browser/devtools/devtools_io_context.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/devtools/devtools_io_context.cc')
-rw-r--r--chromium/content/browser/devtools/devtools_io_context.cc523
1 files changed, 34 insertions, 489 deletions
diff --git a/chromium/content/browser/devtools/devtools_io_context.cc b/chromium/content/browser/devtools/devtools_io_context.cc
index 1e0ae3d9eee..02bc1faddb1 100644
--- a/chromium/content/browser/devtools/devtools_io_context.cc
+++ b/chromium/content/browser/devtools/devtools_io_context.cc
@@ -4,257 +4,65 @@
#include "content/browser/devtools/devtools_io_context.h"
-#include "base/base64.h"
-#include "base/containers/queue.h"
-#include "base/files/file.h"
-#include "base/files/file_util.h"
+#include "base/bind.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/task_scheduler/lazy_task_runner.h"
-#include "base/task_scheduler/post_task.h"
-#include "base/third_party/icu/icu_utf.h"
-#include "base/threading/thread_restrictions.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/storage_partition.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "storage/browser/blob/blob_data_handle.h"
-#include "storage/browser/blob/blob_reader.h"
-#include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/fileapi/file_system_context.h"
-#include "storage/common/blob_storage/blob_storage_constants.h"
-
-#include <queue>
+#include "content/browser/devtools/devtools_stream_blob.h"
+#include "content/browser/devtools/devtools_stream_file.h"
namespace content {
-namespace {
+DevToolsIOContext::Stream::Stream(
+ scoped_refptr<base::SequencedTaskRunner> task_runner)
+ : RefCountedDeleteOnSequence<DevToolsIOContext::Stream>(
+ std::move(task_runner)) {}
-base::SequencedTaskRunner* impl_task_runner() {
- constexpr base::TaskTraits kBlockingTraits = {base::MayBlock(),
- base::TaskPriority::BACKGROUND};
- static base::LazySequencedTaskRunner s_sequenced_task_unner =
- LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(kBlockingTraits);
- return s_sequenced_task_unner.Get().get();
+std::string DevToolsIOContext::Stream::Register(DevToolsIOContext* context) {
+ static unsigned s_last_stream_handle = 0;
+ const std::string handle = base::UintToString(++s_last_stream_handle);
+ Register(context, handle);
+ return handle;
}
-using storage::BlobReader;
-
-unsigned s_last_stream_handle = 0;
-
-class TempFileStream : public DevToolsIOContext::RWStream {
- public:
- explicit TempFileStream(bool binary);
-
- void Read(off_t position, size_t max_size, ReadCallback callback) override;
- void Close(bool invoke_pending_callbacks) override {}
- void Append(std::unique_ptr<std::string> data) override;
- const std::string& handle() override { return handle_; }
-
- private:
- ~TempFileStream() override;
-
- void ReadOnFileSequence(off_t pos, size_t max_size, ReadCallback callback);
- void AppendOnFileSequence(std::unique_ptr<std::string> data);
- bool InitOnFileSequenceIfNeeded();
-
- const std::string handle_;
- base::File file_;
- scoped_refptr<base::SequencedTaskRunner> task_runner_;
- bool had_errors_;
- off_t last_read_pos_;
- bool binary_;
-
- DISALLOW_COPY_AND_ASSIGN(TempFileStream);
-};
-
-TempFileStream::TempFileStream(bool binary)
- : DevToolsIOContext::RWStream(impl_task_runner()),
- handle_(base::UintToString(++s_last_stream_handle)),
- task_runner_(impl_task_runner()),
- had_errors_(false),
- last_read_pos_(0),
- binary_(binary) {}
-
-TempFileStream::~TempFileStream() {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
+void DevToolsIOContext::Stream::Register(DevToolsIOContext* context,
+ const std::string& handle) {
+ context->RegisterStream(this, handle);
}
-bool TempFileStream::InitOnFileSequenceIfNeeded() {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- base::AssertBlockingAllowed();
- if (had_errors_)
- return false;
- if (file_.IsValid())
- return true;
- base::FilePath temp_path;
- if (!base::CreateTemporaryFile(&temp_path)) {
- LOG(ERROR) << "Failed to create temporary file";
- had_errors_ = true;
- return false;
- }
- const unsigned flags = base::File::FLAG_OPEN_TRUNCATED |
- base::File::FLAG_WRITE | base::File::FLAG_READ |
- base::File::FLAG_DELETE_ON_CLOSE;
- file_.Initialize(temp_path, flags);
- if (!file_.IsValid()) {
- LOG(ERROR) << "Failed to open temporary file: " << temp_path.value()
- << ", " << base::File::ErrorToString(file_.error_details());
- had_errors_ = true;
- DeleteFile(temp_path, false);
- return false;
- }
+bool DevToolsIOContext::Stream::SupportsSeek() const {
return true;
}
-void TempFileStream::Read(off_t position,
- size_t max_size,
- ReadCallback callback) {
- task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&TempFileStream::ReadOnFileSequence, this,
- position, max_size, std::move(callback)));
-}
+DevToolsIOContext::Stream::~Stream() = default;
-void TempFileStream::Append(std::unique_ptr<std::string> data) {
- task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&TempFileStream::AppendOnFileSequence, this,
- std::move(data)));
-}
+DevToolsIOContext::DevToolsIOContext() = default;
-void TempFileStream::ReadOnFileSequence(off_t position,
- size_t max_size,
- ReadCallback callback) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- Status status = StatusFailure;
- std::unique_ptr<std::string> data;
- bool base64_encoded = false;
+DevToolsIOContext::~DevToolsIOContext() = default;
- if (file_.IsValid()) {
- std::string buffer;
- buffer.resize(max_size);
- if (position < 0)
- position = last_read_pos_;
- int size_got = file_.ReadNoBestEffort(position, &*buffer.begin(), max_size);
- if (size_got < 0) {
- LOG(ERROR) << "Failed to read temporary file";
- had_errors_ = true;
- file_.Close();
- } else {
- // Provided client has requested sufficient large block, make their
- // life easier by not truncating in the middle of a UTF-8 character.
- if (size_got > 6 && !CBU8_IS_SINGLE(buffer[size_got - 1])) {
- base::TruncateUTF8ToByteSize(buffer, size_got, &buffer);
- size_got = buffer.size();
- } else {
- buffer.resize(size_got);
- }
- data.reset(new std::string(std::move(buffer)));
- status = size_got ? StatusSuccess : StatusEOF;
- last_read_pos_ = position + size_got;
- }
- }
- if (binary_) {
- std::string raw_data(std::move(*data));
- base::Base64Encode(raw_data, data.get());
- base64_encoded = true;
- }
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::BindOnce(std::move(callback), std::move(data),
- base64_encoded, status));
+void DevToolsIOContext::RegisterStream(scoped_refptr<Stream> stream,
+ const std::string& id) {
+ bool inserted = streams_.emplace(id, std::move(stream)).second;
+ DCHECK(inserted);
}
-void TempFileStream::AppendOnFileSequence(std::unique_ptr<std::string> data) {
- if (!InitOnFileSequenceIfNeeded())
- return;
- int size_written = file_.WriteAtCurrentPos(&*data->begin(), data->length());
- if (size_written != static_cast<int>(data->length())) {
- LOG(ERROR) << "Failed to write temporary file";
- had_errors_ = true;
- file_.Close();
- }
+scoped_refptr<DevToolsIOContext::Stream> DevToolsIOContext::GetByHandle(
+ const std::string& handle) {
+ auto it = streams_.find(handle);
+ return it == streams_.end() ? nullptr : it->second;
}
-class BlobStream : public DevToolsIOContext::ROStream {
- public:
- using OpenCallback = base::OnceCallback<void(bool)>;
-
- BlobStream()
- : DevToolsIOContext::ROStream(
- BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)),
- last_read_pos_(0),
- failed_(false),
- is_binary_(false) {}
-
- void Open(scoped_refptr<ChromeBlobStorageContext> context,
- StoragePartition* partition,
- const std::string& handle,
- OpenCallback callback);
-
- void Read(off_t position, size_t max_size, ReadCallback callback) override;
- void Close(bool invoke_pending_callbacks) override;
-
- private:
- struct ReadRequest {
- off_t position;
- size_t max_size;
- ReadCallback callback;
-
- void Fail();
-
- ReadRequest(off_t position, size_t max_size, ReadCallback callback)
- : position(position),
- max_size(max_size),
- callback(std::move(callback)) {}
- };
-
- ~BlobStream() override = default;
-
- void OpenOnIO(scoped_refptr<ChromeBlobStorageContext> blob_context,
- const std::string& uuid,
- OpenCallback callback);
- void ReadOnIO(std::unique_ptr<ReadRequest> request);
- void CloseOnIO(bool invoke_pending_callbacks);
-
- void FailOnIO();
- void FailOnIO(OpenCallback callback) {
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::BindOnce(std::move(callback), false));
- FailOnIO();
- }
-
- void StartReadRequest();
- void CreateReader();
- void BeginRead();
-
- void OnReadComplete(int bytes_read);
- void OnBlobConstructionComplete(storage::BlobStatus status);
- void OnCalculateSizeComplete(int net_error);
-
- static bool IsTextMimeType(const std::string& mime_type);
-
- std::unique_ptr<storage::BlobDataHandle> blob_handle_;
- OpenCallback open_callback_;
- std::unique_ptr<BlobReader> blob_reader_;
- base::queue<std::unique_ptr<ReadRequest>> pending_reads_;
- scoped_refptr<net::IOBufferWithSize> io_buf_;
- off_t last_read_pos_;
- bool failed_;
- bool is_binary_;
-
- DISALLOW_COPY_AND_ASSIGN(BlobStream);
-};
+bool DevToolsIOContext::Close(const std::string& handle) {
+ size_t erased_count = streams_.erase(handle);
+ return !!erased_count;
+}
-void BlobStream::ReadRequest::Fail() {
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::BindOnce(std::move(callback), nullptr, false,
- ROStream::StatusFailure));
+void DevToolsIOContext::DiscardAllStreams() {
+ streams_.clear();
}
// static
-bool BlobStream::IsTextMimeType(const std::string& mime_type) {
+bool DevToolsIOContext::IsTextMimeType(const std::string& mime_type) {
static const char* kTextMIMETypePrefixes[] = {
"text/", "application/x-javascript", "application/json",
"application/xml"};
@@ -266,267 +74,4 @@ bool BlobStream::IsTextMimeType(const std::string& mime_type) {
return false;
}
-void BlobStream::Open(scoped_refptr<ChromeBlobStorageContext> context,
- StoragePartition* partition,
- const std::string& handle,
- OpenCallback callback) {
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
- base::BindOnce(&BlobStream::OpenOnIO, this, context,
- handle, std::move(callback)));
-}
-
-void BlobStream::Read(off_t position, size_t max_size, ReadCallback callback) {
- std::unique_ptr<ReadRequest> request(
- new ReadRequest(position, max_size, std::move(callback)));
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::BindOnce(&BlobStream::ReadOnIO, this, std::move(request)));
-}
-
-void BlobStream::Close(bool invoke_pending_callbacks) {
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::BindOnce(&BlobStream::CloseOnIO, this, invoke_pending_callbacks));
-}
-
-void BlobStream::OpenOnIO(scoped_refptr<ChromeBlobStorageContext> blob_context,
- const std::string& uuid,
- OpenCallback callback) {
- DCHECK(!blob_handle_);
-
- storage::BlobStorageContext* bsc = blob_context->context();
- blob_handle_ = bsc->GetBlobDataFromUUID(uuid);
- if (!blob_handle_) {
- LOG(ERROR) << "No blob with uuid: " << uuid;
- FailOnIO(std::move(callback));
- return;
- }
- is_binary_ = !IsTextMimeType(blob_handle_->content_type());
- open_callback_ = std::move(callback);
- blob_handle_->RunOnConstructionComplete(
- base::BindOnce(&BlobStream::OnBlobConstructionComplete, this));
-}
-
-void BlobStream::OnBlobConstructionComplete(storage::BlobStatus status) {
- DCHECK(!BlobStatusIsPending(status));
- if (BlobStatusIsError(status)) {
- LOG(ERROR) << "Blob building failed: " << static_cast<int>(status);
- FailOnIO(std::move(open_callback_));
- return;
- }
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::BindOnce(std::move(open_callback_), true));
- if (!pending_reads_.empty())
- StartReadRequest();
-}
-
-void BlobStream::ReadOnIO(std::unique_ptr<ReadRequest> request) {
- if (failed_) {
- request->Fail();
- return;
- }
- pending_reads_.push(std::move(request));
- if (pending_reads_.size() > 1 || open_callback_)
- return;
- StartReadRequest();
-}
-
-void BlobStream::FailOnIO() {
- failed_ = true;
- while (!pending_reads_.empty()) {
- pending_reads_.front()->Fail();
- pending_reads_.pop();
- }
-}
-
-void BlobStream::CloseOnIO(bool invoke_pending_callbacks) {
- if (blob_reader_) {
- blob_reader_->Kill();
- blob_reader_.reset();
- }
- if (blob_handle_)
- blob_handle_.reset();
- if (invoke_pending_callbacks) {
- FailOnIO();
- return;
- }
- failed_ = true;
- pending_reads_ = base::queue<std::unique_ptr<ReadRequest>>();
- open_callback_ = OpenCallback();
-}
-
-void BlobStream::StartReadRequest() {
- DCHECK_GE(pending_reads_.size(), 1UL);
- DCHECK(blob_handle_);
- DCHECK(!failed_);
-
- ReadRequest& request = *pending_reads_.front();
- if (request.position < 0)
- request.position = last_read_pos_;
- if (request.position != last_read_pos_)
- blob_reader_.reset();
- if (!blob_reader_)
- CreateReader();
- else
- BeginRead();
-}
-
-void BlobStream::BeginRead() {
- DCHECK_GE(pending_reads_.size(), 1UL);
- ReadRequest& request = *pending_reads_.front();
- if (!io_buf_ || static_cast<size_t>(io_buf_->size()) < request.max_size)
- io_buf_ = new net::IOBufferWithSize(request.max_size);
- int bytes_read;
- BlobReader::Status status =
- blob_reader_->Read(io_buf_.get(), request.max_size, &bytes_read,
- base::BindOnce(&BlobStream::OnReadComplete, this));
- if (status == BlobReader::Status::IO_PENDING)
- return;
- // This is for uniformity with the asynchronous case.
- if (status == BlobReader::Status::NET_ERROR) {
- bytes_read = blob_reader_->net_error();
- DCHECK_LT(0, bytes_read);
- }
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::BindOnce(&BlobStream::OnReadComplete, this, bytes_read));
-}
-
-void BlobStream::OnReadComplete(int bytes_read) {
- std::unique_ptr<ReadRequest> request = std::move(pending_reads_.front());
- pending_reads_.pop();
-
- Status status;
- std::unique_ptr<std::string> data(new std::string());
- bool base64_encoded = false;
-
- if (bytes_read < 0) {
- status = StatusFailure;
- LOG(ERROR) << "Error reading blob: " << net::ErrorToString(bytes_read);
- } else if (!bytes_read) {
- status = StatusEOF;
- } else {
- last_read_pos_ += bytes_read;
- status = blob_reader_->remaining_bytes() ? StatusSuccess : StatusEOF;
- if (is_binary_) {
- base64_encoded = true;
- Base64Encode(base::StringPiece(io_buf_->data(), bytes_read), data.get());
- } else {
- // TODO(caseq): truncate at UTF8 boundary.
- *data = std::string(io_buf_->data(), bytes_read);
- }
- }
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::BindOnce(std::move(request->callback), std::move(data),
- base64_encoded, status));
- if (!pending_reads_.empty())
- StartReadRequest();
-}
-
-void BlobStream::CreateReader() {
- DCHECK(!blob_reader_);
- blob_reader_ = blob_handle_->CreateReader();
- BlobReader::Status status = blob_reader_->CalculateSize(
- base::BindOnce(&BlobStream::OnCalculateSizeComplete, this));
- if (status != BlobReader::Status::IO_PENDING) {
- OnCalculateSizeComplete(status == BlobReader::Status::NET_ERROR
- ? blob_reader_->net_error()
- : net::OK);
- }
-}
-
-void BlobStream::OnCalculateSizeComplete(int net_error) {
- if (net_error != net::OK) {
- FailOnIO();
- return;
- }
- off_t seek_to = pending_reads_.front()->position;
- if (seek_to != 0UL) {
- if (seek_to >= static_cast<off_t>(blob_reader_->total_size())) {
- OnReadComplete(0);
- return;
- }
- BlobReader::Status status = blob_reader_->SetReadRange(
- seek_to, blob_reader_->total_size() - seek_to);
- if (status != BlobReader::Status::DONE) {
- FailOnIO();
- return;
- }
- }
- BeginRead();
-}
-
-} // namespace
-
-DevToolsIOContext::ROStream::ROStream(
- scoped_refptr<base::SequencedTaskRunner> task_runner)
- : RefCountedDeleteOnSequence<DevToolsIOContext::ROStream>(
- std::move(task_runner)) {}
-
-DevToolsIOContext::ROStream::~ROStream() = default;
-
-DevToolsIOContext::RWStream::RWStream(
- scoped_refptr<base::SequencedTaskRunner> task_runner)
- : DevToolsIOContext::ROStream(std::move(task_runner)) {}
-
-DevToolsIOContext::RWStream::~RWStream() = default;
-
-DevToolsIOContext::DevToolsIOContext() : weak_factory_(this) {}
-
-DevToolsIOContext::~DevToolsIOContext() {
- DiscardAllStreams();
-}
-
-scoped_refptr<DevToolsIOContext::RWStream>
-DevToolsIOContext::CreateTempFileBackedStream(bool binary) {
- scoped_refptr<TempFileStream> result = new TempFileStream(binary);
- bool inserted =
- streams_.insert(std::make_pair(result->handle(), result)).second;
- DCHECK(inserted);
- return result;
-}
-
-scoped_refptr<DevToolsIOContext::ROStream> DevToolsIOContext::GetByHandle(
- const std::string& handle) {
- StreamsMap::const_iterator it = streams_.find(handle);
- return it == streams_.end() ? scoped_refptr<ROStream>() : it->second;
-}
-
-scoped_refptr<DevToolsIOContext::ROStream> DevToolsIOContext::OpenBlob(
- ChromeBlobStorageContext* context,
- StoragePartition* partition,
- const std::string& handle,
- const std::string& uuid) {
- scoped_refptr<BlobStream> result = new BlobStream();
- bool inserted = streams_.insert(std::make_pair(handle, result)).second;
-
- result->Open(context, partition, uuid,
- base::BindOnce(&DevToolsIOContext::OnBlobOpenComplete,
- weak_factory_.GetWeakPtr(), handle));
- DCHECK(inserted);
- return std::move(result);
-}
-
-void DevToolsIOContext::OnBlobOpenComplete(const std::string& handle,
- bool success) {
- if (!success)
- Close(handle);
-}
-
-bool DevToolsIOContext::Close(const std::string& handle) {
- StreamsMap::iterator it = streams_.find(handle);
- if (it == streams_.end())
- return false;
- it->second->Close(false);
- streams_.erase(it);
- return true;
-}
-
-void DevToolsIOContext::DiscardAllStreams() {
- for (auto& entry : streams_)
- entry.second->Close(true);
- return streams_.clear();
-}
-
} // namespace content