--- /dev/null
+// Copyright (c) 2012 The Chromium Embedded Framework 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 "include/wrapper/cef_stream_resource_handler.h"
+
+#include <algorithm>
+
+#include "include/base/cef_bind.h"
+#include "include/base/cef_logging.h"
+#include "include/base/cef_macros.h"
+#include "include/cef_callback.h"
+#include "include/cef_request.h"
+#include "include/cef_stream.h"
+#include "include/wrapper/cef_closure_task.h"
+#include "include/wrapper/cef_helpers.h"
+
+// Class that represents a readable/writable character buffer.
+class CefStreamResourceHandler::Buffer {
+ public:
+ Buffer()
+ : size_(0),
+ bytes_requested_(0),
+ bytes_written_(0),
+ bytes_read_(0) {
+ }
+
+ void Reset(int new_size) {
+ if (size_ < new_size) {
+ size_ = new_size;
+ buffer_.reset(new char[size_]);
+ DCHECK(buffer_);
+ }
+ bytes_requested_ = new_size;
+ bytes_written_ = 0;
+ bytes_read_ = 0;
+ }
+
+ bool IsEmpty() const {
+ return (bytes_written_ == 0);
+ }
+
+ bool CanRead() const {
+ return (bytes_read_ < bytes_written_);
+ }
+
+ int WriteTo(void* data_out, int bytes_to_read) {
+ const int write_size =
+ std::min(bytes_to_read, bytes_written_ - bytes_read_);
+ if (write_size > 0) {
+ memcpy(data_out, buffer_ .get() + bytes_read_, write_size);
+ bytes_read_ += write_size;
+ }
+ return write_size;
+ }
+
+ int ReadFrom(CefRefPtr<CefStreamReader> reader) {
+ // Read until the buffer is full or until Read() returns 0 to indicate no
+ // more data.
+ int bytes_read;
+ do {
+ bytes_read = static_cast<int>(
+ reader->Read(buffer_.get() + bytes_written_, 1,
+ bytes_requested_ - bytes_written_));
+ bytes_written_ += bytes_read;
+ } while (bytes_read != 0 && bytes_written_ < bytes_requested_);
+
+ return bytes_written_;
+ }
+
+ private:
+ scoped_ptr<char[]> buffer_;
+ int size_;
+ int bytes_requested_;
+ int bytes_written_;
+ int bytes_read_;
+
+ DISALLOW_COPY_AND_ASSIGN(Buffer);
+};
+
+CefStreamResourceHandler::CefStreamResourceHandler(
+ const CefString& mime_type,
+ CefRefPtr<CefStreamReader> stream)
+ : status_code_(200),
+ status_text_("OK"),
+ mime_type_(mime_type),
+ stream_(stream)
+#if DCHECK_IS_ON()
+ , buffer_owned_by_file_thread_(false)
+#endif
+{
+ DCHECK(!mime_type_.empty());
+ DCHECK(stream_.get());
+ read_on_file_thread_ = stream_->MayBlock();
+}
+
+CefStreamResourceHandler::CefStreamResourceHandler(
+ int status_code,
+ const CefString& status_text,
+ const CefString& mime_type,
+ CefResponse::HeaderMap header_map,
+ CefRefPtr<CefStreamReader> stream)
+ : status_code_(status_code),
+ status_text_(status_text),
+ mime_type_(mime_type),
+ header_map_(header_map),
+ stream_(stream)
+#if DCHECK_IS_ON()
+ , buffer_owned_by_file_thread_(false)
+#endif
+{
+ DCHECK(!mime_type_.empty());
+ DCHECK(stream_.get());
+ read_on_file_thread_ = stream_->MayBlock();
+}
+
+CefStreamResourceHandler::~CefStreamResourceHandler() {
+}
+
+bool CefStreamResourceHandler::ProcessRequest(CefRefPtr<CefRequest> request,
+ CefRefPtr<CefCallback> callback) {
+ callback->Continue();
+ return true;
+}
+
+void CefStreamResourceHandler::GetResponseHeaders(
+ CefRefPtr<CefResponse> response,
+ int64& response_length,
+ CefString& redirectUrl) {
+ response->SetStatus(status_code_);
+ response->SetStatusText(status_text_);
+ response->SetMimeType(mime_type_);
+
+ if (!header_map_.empty())
+ response->SetHeaderMap(header_map_);
+
+ response_length = -1;
+}
+
+bool CefStreamResourceHandler::ReadResponse(void* data_out,
+ int bytes_to_read,
+ int& bytes_read,
+ CefRefPtr<CefCallback> callback) {
+ DCHECK_GT(bytes_to_read, 0);
+
+ if (read_on_file_thread_) {
+#if DCHECK_IS_ON()
+ DCHECK(!buffer_owned_by_file_thread_);
+#endif
+ if (buffer_ && (buffer_->CanRead() || buffer_->IsEmpty())) {
+ if (buffer_->CanRead()) {
+ // Provide data from the buffer.
+ bytes_read = buffer_->WriteTo(data_out, bytes_to_read);
+ return (bytes_read > 0);
+ } else {
+ // End of the steam.
+ bytes_read = 0;
+ return false;
+ }
+ } else {
+ // Perform another read on the file thread.
+ bytes_read = 0;
+#if DCHECK_IS_ON()
+ buffer_owned_by_file_thread_ = true;
+#endif
+ CefPostTask(TID_FILE,
+ base::Bind(&CefStreamResourceHandler::ReadOnFileThread, this,
+ bytes_to_read, callback));
+ return true;
+ }
+ } else {
+ // Read until the buffer is full or until Read() returns 0 to indicate no
+ // more data.
+ bytes_read = 0;
+ int read = 0;
+ do {
+ read = static_cast<int>(
+ stream_->Read(static_cast<char*>(data_out) + bytes_read, 1,
+ bytes_to_read - bytes_read));
+ bytes_read += read;
+ } while (read != 0 && bytes_read < bytes_to_read);
+
+ return (bytes_read > 0);
+ }
+}
+
+void CefStreamResourceHandler::Cancel() {
+}
+
+void CefStreamResourceHandler::ReadOnFileThread(
+ int bytes_to_read,
+ CefRefPtr<CefCallback> callback) {
+ CEF_REQUIRE_FILE_THREAD();
+#if DCHECK_IS_ON()
+ DCHECK(buffer_owned_by_file_thread_);
+#endif
+
+ if (!buffer_)
+ buffer_.reset(new Buffer());
+ buffer_->Reset(bytes_to_read);
+ buffer_->ReadFrom(stream_);
+
+#if DCHECK_IS_ON()
+ buffer_owned_by_file_thread_ = false;
+#endif
+ callback->Continue();
+}