// 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 #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 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( 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 buffer_; int size_; int bytes_requested_; int bytes_written_; int bytes_read_; DISALLOW_COPY_AND_ASSIGN(Buffer); }; CefStreamResourceHandler::CefStreamResourceHandler( const CefString& mime_type, CefRefPtr stream) : status_code_(200), status_text_("OK"), mime_type_(mime_type), stream_(stream) #ifndef NDEBUG , 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 stream) : status_code_(status_code), status_text_(status_text), mime_type_(mime_type), header_map_(header_map), stream_(stream) { DCHECK(!mime_type_.empty()); DCHECK(stream_.get()); read_on_file_thread_ = stream_->MayBlock(); } CefStreamResourceHandler::~CefStreamResourceHandler() { } bool CefStreamResourceHandler::ProcessRequest(CefRefPtr request, CefRefPtr callback) { callback->Continue(); return true; } void CefStreamResourceHandler::GetResponseHeaders( CefRefPtr 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 callback) { DCHECK_GT(bytes_to_read, 0); if (read_on_file_thread_) { #ifndef NDEBUG 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; #ifndef NDEBUG 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( stream_->Read(static_cast(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 callback) { CEF_REQUIRE_FILE_THREAD(); #ifndef NDEBUG DCHECK(buffer_owned_by_file_thread_); #endif if (!buffer_) buffer_.reset(new Buffer()); buffer_->Reset(bytes_to_read); buffer_->ReadFrom(stream_); #ifndef NDEBUG buffer_owned_by_file_thread_ = false; #endif callback->Continue(); }