diff options
Diffstat (limited to 'chromium/content/browser/devtools/devtools_io_context.cc')
-rw-r--r-- | chromium/content/browser/devtools/devtools_io_context.cc | 523 |
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 |