Index: content/browser/devtools/devtools_io_context.cc |
diff --git a/content/browser/devtools/devtools_io_context.cc b/content/browser/devtools/devtools_io_context.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e3082ddb3cd7d5e2a95f6b2527e36bfa10a31ae7 |
--- /dev/null |
+++ b/content/browser/devtools/devtools_io_context.cc |
@@ -0,0 +1,145 @@ |
+// Copyright 2015 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 "content/browser/devtools/devtools_io_context.h" |
+ |
+#include "base/files/file.h" |
+#include "base/files/file_util.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_util.h" |
+#include "base/third_party/icu/icu_utf.h" |
+#include "content/public/browser/browser_thread.h" |
+ |
+namespace content { |
+namespace devtools { |
+ |
+namespace { |
+unsigned s_last_stream_handle = 0; |
+} |
+ |
+using Stream = DevToolsIOContext::Stream; |
+ |
+Stream::Stream() |
+ : base::RefCountedDeleteOnMessageLoop<Stream>( |
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)), |
+ handle_(base::IntToString(++s_last_stream_handle)), |
+ had_errors_(false), |
+ last_read_pos_(0) {} |
+ |
+Stream::~Stream() { |
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
+} |
+ |
+bool Stream::InitOnFileThreadIfNeeded() { |
+ 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; |
+ } |
+ return true; |
+} |
+ |
+void Stream::Read(off_t position, size_t max_size, ReadCallback callback) { |
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
+ base::Bind(&Stream::ReadOnFileThread, this, position, max_size, |
+ callback)); |
+} |
+ |
+void Stream::Append(const scoped_refptr<base::RefCountedString>& data) { |
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
+ base::Bind(&Stream::AppendOnFileThread, this, data)); |
+} |
+ |
+void Stream::ReadOnFileThread(off_t position, size_t max_size, |
+ ReadCallback callback) { |
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
+ Status status = StatusFailure; |
+ scoped_refptr<base::RefCountedString> data; |
+ |
+ 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 = base::RefCountedString::TakeString(&buffer); |
+ status = size_got ? StatusSuccess : StatusEOF; |
+ last_read_pos_ = position + size_got; |
+ } |
+ } |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(callback, data, status)); |
+} |
+ |
+void Stream::AppendOnFileThread( |
+ const scoped_refptr<base::RefCountedString>& data) { |
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
+ if (!InitOnFileThreadIfNeeded()) |
+ return; |
+ const std::string& buffer = data->data(); |
+ int size_written = file_.WriteAtCurrentPos(&*buffer.begin(), buffer.size()); |
+ if (size_written != static_cast<int>(buffer.size())) { |
+ LOG(ERROR) << "Failed to write temporary file"; |
+ had_errors_ = true; |
+ file_.Close(); |
+ } |
+} |
+ |
+DevToolsIOContext::DevToolsIOContext() {} |
+ |
+DevToolsIOContext::~DevToolsIOContext() {} |
+ |
+scoped_refptr<Stream> DevToolsIOContext::CreateTempFileBackedStream() { |
+ scoped_refptr<Stream> result = new Stream(); |
+ bool inserted = |
+ streams_.insert(std::make_pair(result->handle(), result)).second; |
+ DCHECK(inserted); |
+ return result; |
+} |
+ |
+scoped_refptr<Stream> |
+ DevToolsIOContext::GetByHandle(const std::string& handle) { |
+ StreamsMap::const_iterator it = streams_.find(handle); |
+ return it == streams_.end() ? scoped_refptr<Stream>() : it->second; |
+} |
+ |
+bool DevToolsIOContext::Close(const std::string& handle) { |
+ return streams_.erase(handle) == 1; |
+} |
+ |
+void DevToolsIOContext::DiscardAllStreams() { |
+ return streams_.clear(); |
+} |
+ |
+} // namespace devtools |
+} // namespace content |