From 2534b673657a9a0c618e9990b30ec99af42ba15b Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Thu, 30 Jan 2014 23:15:55 +0000 Subject: [PATCH] - Add MayBlock() method to stream classes which is used as a hint when determining the thread to access the stream from (issue #1187). - In cases where MayBlock() returns true have CefStreamResourceReader perform the reads on the FILE thread (issue #1187). git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1582 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- cef.gyp | 4 +- cef_paths.gypi | 2 + include/capi/cef_stream_capi.h | 28 +++ include/cef_stream.h | 32 ++++ include/wrapper/cef_byte_read_handler.h | 1 + include/wrapper/cef_stream_resource_handler.h | 26 ++- libcef/browser/stream_impl.h | 10 ++ libcef_dll/cef_logging.h | 2 + libcef_dll/cpptoc/read_handler_cpptoc.cc | 15 ++ libcef_dll/cpptoc/stream_reader_cpptoc.cc | 15 ++ libcef_dll/cpptoc/stream_writer_cpptoc.cc | 15 ++ libcef_dll/cpptoc/write_handler_cpptoc.cc | 15 ++ libcef_dll/ctocpp/read_handler_ctocpp.cc | 13 ++ libcef_dll/ctocpp/read_handler_ctocpp.h | 1 + libcef_dll/ctocpp/stream_reader_ctocpp.cc | 13 ++ libcef_dll/ctocpp/stream_reader_ctocpp.h | 1 + libcef_dll/ctocpp/stream_writer_ctocpp.cc | 13 ++ libcef_dll/ctocpp/stream_writer_ctocpp.h | 1 + libcef_dll/ctocpp/write_handler_ctocpp.cc | 13 ++ libcef_dll/ctocpp/write_handler_ctocpp.h | 1 + .../wrapper/cef_stream_resource_handler.cc | 150 ++++++++++++++++- .../stream_resource_handler_unittest.cc | 159 ++++++++++++++++++ tests/unittests/stream_unittest.cc | 29 +++- 23 files changed, 541 insertions(+), 18 deletions(-) create mode 100644 tests/unittests/stream_resource_handler_unittest.cc diff --git a/cef.gyp b/cef.gyp index e0c5a360a..2ee7d7ba7 100644 --- a/cef.gyp +++ b/cef.gyp @@ -399,6 +399,7 @@ 'tests/cefclient/client_app.h', 'tests/cefclient/client_switches.cpp', 'tests/cefclient/client_switches.h', + 'tests/cefclient/resource_util.h', 'tests/cefclient/res/osr_test.html', 'tests/unittests/browser_info_map_unittest.cc', 'tests/unittests/command_line_unittest.cc', @@ -417,12 +418,12 @@ 'tests/unittests/request_context_unittest.cc', 'tests/unittests/request_handler_unittest.cc', 'tests/unittests/request_unittest.cc', - 'tests/cefclient/resource_util.h', 'tests/unittests/routing_test_handler.cc', 'tests/unittests/routing_test_handler.h', 'tests/unittests/run_all_unittests.cc', 'tests/unittests/scheme_handler_unittest.cc', 'tests/unittests/stream_unittest.cc', + 'tests/unittests/stream_resource_handler_unittest.cc', 'tests/unittests/string_unittest.cc', 'tests/unittests/client_app_delegates.cc', 'tests/unittests/task_unittest.cc', @@ -920,6 +921,7 @@ 'libcef/browser/download_item_impl.h', 'libcef/browser/download_manager_delegate.cc', 'libcef/browser/download_manager_delegate.h', + 'libcef/browser/file_impl.cc', 'libcef/browser/frame_host_impl.cc', 'libcef/browser/frame_host_impl.h', 'libcef/browser/geolocation_impl.cc', diff --git a/cef_paths.gypi b/cef_paths.gypi index 6ec2b0a51..54cc36217 100644 --- a/cef_paths.gypi +++ b/cef_paths.gypi @@ -28,6 +28,7 @@ 'include/cef_download_item.h', 'include/cef_drag_data.h', 'include/cef_drag_handler.h', + 'include/cef_file.h', 'include/cef_focus_handler.h', 'include/cef_frame.h', 'include/cef_geolocation.h', @@ -80,6 +81,7 @@ 'include/capi/cef_download_item_capi.h', 'include/capi/cef_drag_data_capi.h', 'include/capi/cef_drag_handler_capi.h', + 'include/capi/cef_file_capi.h', 'include/capi/cef_focus_handler_capi.h', 'include/capi/cef_frame_capi.h', 'include/capi/cef_geolocation_capi.h', diff --git a/include/capi/cef_stream_capi.h b/include/capi/cef_stream_capi.h index 1265f830c..84830c84e 100644 --- a/include/capi/cef_stream_capi.h +++ b/include/capi/cef_stream_capi.h @@ -77,6 +77,13 @@ typedef struct _cef_read_handler_t { // Return non-zero if at end of file. /// int (CEF_CALLBACK *eof)(struct _cef_read_handler_t* self); + + /// + // Return true (1) if this handler performs work like accessing the file + // system which may block. Used as a hint for determining the thread to access + // the handler from. + /// + int (CEF_CALLBACK *may_block)(struct _cef_read_handler_t* self); } cef_read_handler_t; @@ -112,6 +119,13 @@ typedef struct _cef_stream_reader_t { // Return non-zero if at end of file. /// int (CEF_CALLBACK *eof)(struct _cef_stream_reader_t* self); + + /// + // Returns true (1) if this reader performs work like accessing the file + // system which may block. Used as a hint for determining the thread to access + // the reader from. + /// + int (CEF_CALLBACK *may_block)(struct _cef_stream_reader_t* self); } cef_stream_reader_t; @@ -166,6 +180,13 @@ typedef struct _cef_write_handler_t { // Flush the stream. /// int (CEF_CALLBACK *flush)(struct _cef_write_handler_t* self); + + /// + // Return true (1) if this handler performs work like accessing the file + // system which may block. Used as a hint for determining the thread to access + // the handler from. + /// + int (CEF_CALLBACK *may_block)(struct _cef_write_handler_t* self); } cef_write_handler_t; @@ -201,6 +222,13 @@ typedef struct _cef_stream_writer_t { // Flush the stream. /// int (CEF_CALLBACK *flush)(struct _cef_stream_writer_t* self); + + /// + // Returns true (1) if this writer performs work like accessing the file + // system which may block. Used as a hint for determining the thread to access + // the writer from. + /// + int (CEF_CALLBACK *may_block)(struct _cef_stream_writer_t* self); } cef_stream_writer_t; diff --git a/include/cef_stream.h b/include/cef_stream.h index 6ccec8dec..3d0633c25 100644 --- a/include/cef_stream.h +++ b/include/cef_stream.h @@ -71,6 +71,14 @@ class CefReadHandler : public virtual CefBase { /// /*--cef()--*/ virtual int Eof() =0; + + /// + // Return true if this handler performs work like accessing the file system + // which may block. Used as a hint for determining the thread to access the + // handler from. + /// + /*--cef()--*/ + virtual bool MayBlock() =0; }; @@ -123,6 +131,14 @@ class CefStreamReader : public virtual CefBase { /// /*--cef()--*/ virtual int Eof() =0; + + /// + // Returns true if this reader performs work like accessing the file system + // which may block. Used as a hint for determining the thread to access the + // reader from. + /// + /*--cef()--*/ + virtual bool MayBlock() =0; }; @@ -158,6 +174,14 @@ class CefWriteHandler : public virtual CefBase { /// /*--cef()--*/ virtual int Flush() =0; + + /// + // Return true if this handler performs work like accessing the file system + // which may block. Used as a hint for determining the thread to access the + // handler from. + /// + /*--cef()--*/ + virtual bool MayBlock() =0; }; @@ -205,6 +229,14 @@ class CefStreamWriter : public virtual CefBase { /// /*--cef()--*/ virtual int Flush() =0; + + /// + // Returns true if this writer performs work like accessing the file system + // which may block. Used as a hint for determining the thread to access the + // writer from. + /// + /*--cef()--*/ + virtual bool MayBlock() =0; }; #endif // CEF_INCLUDE_CEF_STREAM_H_ diff --git a/include/wrapper/cef_byte_read_handler.h b/include/wrapper/cef_byte_read_handler.h index 1752588de..2954264d8 100644 --- a/include/wrapper/cef_byte_read_handler.h +++ b/include/wrapper/cef_byte_read_handler.h @@ -60,6 +60,7 @@ class CefByteReadHandler : public CefReadHandler { virtual int Seek(int64 offset, int whence) OVERRIDE; virtual int64 Tell() OVERRIDE; virtual int Eof() OVERRIDE; + virtual bool MayBlock() OVERRIDE { return false; } private: const unsigned char* bytes_; diff --git a/include/wrapper/cef_stream_resource_handler.h b/include/wrapper/cef_stream_resource_handler.h index 181bbbfe8..e6d09c074 100644 --- a/include/wrapper/cef_stream_resource_handler.h +++ b/include/wrapper/cef_stream_resource_handler.h @@ -57,11 +57,14 @@ class CefStreamResourceHandler : public CefResourceHandler { // Create a new object with explicit response values. /// CefStreamResourceHandler(int status_code, + const CefString& status_text, const CefString& mime_type, CefResponse::HeaderMap header_map, CefRefPtr stream); - // CefStreamResourceHandler methods. + virtual ~CefStreamResourceHandler(); + + // CefResourceHandler methods. virtual bool ProcessRequest(CefRefPtr request, CefRefPtr callback) OVERRIDE; virtual void GetResponseHeaders(CefRefPtr response, @@ -74,10 +77,23 @@ class CefStreamResourceHandler : public CefResourceHandler { virtual void Cancel() OVERRIDE; private: - int status_code_; - CefString mime_type_; - CefResponse::HeaderMap header_map_; - CefRefPtr stream_; + void ReadOnFileThread(int bytes_to_read, + CefRefPtr callback); + + const int status_code_; + const CefString status_text_; + const CefString mime_type_; + const CefResponse::HeaderMap header_map_; + const CefRefPtr stream_; + bool read_on_file_thread_; + + class Buffer; + Buffer* buffer_; +#ifndef NDEBUG + // Used in debug builds to verify that |buffer_| isn't being accessed on + // multiple threads at the same time. + bool buffer_owned_by_file_thread_; +#endif IMPLEMENT_REFCOUNTING(CefStreamResourceHandler); }; diff --git a/libcef/browser/stream_impl.h b/libcef/browser/stream_impl.h index 0039b9b2f..d1aed4a8c 100644 --- a/libcef/browser/stream_impl.h +++ b/libcef/browser/stream_impl.h @@ -20,6 +20,7 @@ class CefFileReader : public CefStreamReader { virtual int Seek(int64 offset, int whence) OVERRIDE; virtual int64 Tell() OVERRIDE; virtual int Eof() OVERRIDE; + virtual bool MayBlock() OVERRIDE { return true; } protected: bool close_; @@ -39,6 +40,7 @@ class CefFileWriter : public CefStreamWriter { virtual int Seek(int64 offset, int whence) OVERRIDE; virtual int64 Tell() OVERRIDE; virtual int Flush() OVERRIDE; + virtual bool MayBlock() OVERRIDE { return true; } protected: FILE* file_; @@ -58,6 +60,7 @@ class CefBytesReader : public CefStreamReader { virtual int Seek(int64 offset, int whence) OVERRIDE; virtual int64 Tell() OVERRIDE; virtual int Eof() OVERRIDE; + virtual bool MayBlock() OVERRIDE { return false; } void SetData(void* data, int64 datasize, bool copy); @@ -84,6 +87,7 @@ class CefBytesWriter : public CefStreamWriter { virtual int Seek(int64 offset, int whence) OVERRIDE; virtual int64 Tell() OVERRIDE; virtual int Flush() OVERRIDE; + virtual bool MayBlock() OVERRIDE { return false; } void* GetData() { return data_; } int64 GetDataSize() { return offset_; } @@ -119,6 +123,9 @@ class CefHandlerReader : public CefStreamReader { virtual int Eof() OVERRIDE { return handler_->Eof(); } + virtual bool MayBlock() OVERRIDE { + return handler_->MayBlock(); + } protected: CefRefPtr handler_; @@ -144,6 +151,9 @@ class CefHandlerWriter : public CefStreamWriter { virtual int Flush() OVERRIDE { return handler_->Flush(); } + virtual bool MayBlock() OVERRIDE { + return handler_->MayBlock(); + } protected: CefRefPtr handler_; diff --git a/libcef_dll/cef_logging.h b/libcef_dll/cef_logging.h index 3af50687f..55a0fa6e9 100644 --- a/libcef_dll/cef_logging.h +++ b/libcef_dll/cef_logging.h @@ -28,6 +28,8 @@ #endif // !BUILDING_CEF_SHARED #define CEF_REQUIRE_UI_THREAD() DCHECK(CefCurrentlyOn(TID_UI)); +#define CEF_REQUIRE_IO_THREAD() DCHECK(CefCurrentlyOn(TID_IO)); +#define CEF_REQUIRE_FILE_THREAD() DCHECK(CefCurrentlyOn(TID_FILE)); #define CEF_REQUIRE_RENDERER_THREAD() DCHECK(CefCurrentlyOn(TID_RENDERER)); #endif // CEF_LIBCEF_DLL_CEF_LOGGING_H_ diff --git a/libcef_dll/cpptoc/read_handler_cpptoc.cc b/libcef_dll/cpptoc/read_handler_cpptoc.cc index 9869480c6..119163e63 100644 --- a/libcef_dll/cpptoc/read_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/read_handler_cpptoc.cc @@ -82,6 +82,20 @@ int CEF_CALLBACK read_handler_eof(struct _cef_read_handler_t* self) { return _retval; } +int CEF_CALLBACK read_handler_may_block(struct _cef_read_handler_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefReadHandlerCppToC::Get(self)->MayBlock(); + + // Return type: bool + return _retval; +} + // CONSTRUCTOR - Do not edit by hand. @@ -91,6 +105,7 @@ CefReadHandlerCppToC::CefReadHandlerCppToC(CefReadHandler* cls) struct_.struct_.seek = read_handler_seek; struct_.struct_.tell = read_handler_tell; struct_.struct_.eof = read_handler_eof; + struct_.struct_.may_block = read_handler_may_block; } #ifndef NDEBUG diff --git a/libcef_dll/cpptoc/stream_reader_cpptoc.cc b/libcef_dll/cpptoc/stream_reader_cpptoc.cc index f2d79ce10..04a3b6388 100644 --- a/libcef_dll/cpptoc/stream_reader_cpptoc.cc +++ b/libcef_dll/cpptoc/stream_reader_cpptoc.cc @@ -138,6 +138,20 @@ int CEF_CALLBACK stream_reader_eof(struct _cef_stream_reader_t* self) { return _retval; } +int CEF_CALLBACK stream_reader_may_block(struct _cef_stream_reader_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefStreamReaderCppToC::Get(self)->MayBlock(); + + // Return type: bool + return _retval; +} + // CONSTRUCTOR - Do not edit by hand. @@ -148,6 +162,7 @@ CefStreamReaderCppToC::CefStreamReaderCppToC(CefStreamReader* cls) struct_.struct_.seek = stream_reader_seek; struct_.struct_.tell = stream_reader_tell; struct_.struct_.eof = stream_reader_eof; + struct_.struct_.may_block = stream_reader_may_block; } #ifndef NDEBUG diff --git a/libcef_dll/cpptoc/stream_writer_cpptoc.cc b/libcef_dll/cpptoc/stream_writer_cpptoc.cc index 1dfcf3d63..ee236b99a 100644 --- a/libcef_dll/cpptoc/stream_writer_cpptoc.cc +++ b/libcef_dll/cpptoc/stream_writer_cpptoc.cc @@ -120,6 +120,20 @@ int CEF_CALLBACK stream_writer_flush(struct _cef_stream_writer_t* self) { return _retval; } +int CEF_CALLBACK stream_writer_may_block(struct _cef_stream_writer_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefStreamWriterCppToC::Get(self)->MayBlock(); + + // Return type: bool + return _retval; +} + // CONSTRUCTOR - Do not edit by hand. @@ -130,6 +144,7 @@ CefStreamWriterCppToC::CefStreamWriterCppToC(CefStreamWriter* cls) struct_.struct_.seek = stream_writer_seek; struct_.struct_.tell = stream_writer_tell; struct_.struct_.flush = stream_writer_flush; + struct_.struct_.may_block = stream_writer_may_block; } #ifndef NDEBUG diff --git a/libcef_dll/cpptoc/write_handler_cpptoc.cc b/libcef_dll/cpptoc/write_handler_cpptoc.cc index cdeba0145..67f1eb427 100644 --- a/libcef_dll/cpptoc/write_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/write_handler_cpptoc.cc @@ -82,6 +82,20 @@ int CEF_CALLBACK write_handler_flush(struct _cef_write_handler_t* self) { return _retval; } +int CEF_CALLBACK write_handler_may_block(struct _cef_write_handler_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefWriteHandlerCppToC::Get(self)->MayBlock(); + + // Return type: bool + return _retval; +} + // CONSTRUCTOR - Do not edit by hand. @@ -92,6 +106,7 @@ CefWriteHandlerCppToC::CefWriteHandlerCppToC(CefWriteHandler* cls) struct_.struct_.seek = write_handler_seek; struct_.struct_.tell = write_handler_tell; struct_.struct_.flush = write_handler_flush; + struct_.struct_.may_block = write_handler_may_block; } #ifndef NDEBUG diff --git a/libcef_dll/ctocpp/read_handler_ctocpp.cc b/libcef_dll/ctocpp/read_handler_ctocpp.cc index ef8909457..c99c2df76 100644 --- a/libcef_dll/ctocpp/read_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/read_handler_ctocpp.cc @@ -77,6 +77,19 @@ int CefReadHandlerCToCpp::Eof() { return _retval; } +bool CefReadHandlerCToCpp::MayBlock() { + if (CEF_MEMBER_MISSING(struct_, may_block)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = struct_->may_block(struct_); + + // Return type: bool + return _retval?true:false; +} + #ifndef NDEBUG template<> long CefCToCppmay_block(struct_); + + // Return type: bool + return _retval?true:false; +} + #ifndef NDEBUG template<> long CefCToCppmay_block(struct_); + + // Return type: bool + return _retval?true:false; +} + #ifndef NDEBUG template<> long CefCToCppmay_block(struct_); + + // Return type: bool + return _retval?true:false; +} + #ifndef NDEBUG template<> long CefCToCpp #include "include/cef_callback.h" #include "include/cef_request.h" +#include "include/cef_runnable.h" #include "include/cef_stream.h" #include "libcef_dll/cef_logging.h" +// Class that represents a readable/writable character buffer. +class CefStreamResourceHandler::Buffer { + public: + Buffer() + : buffer_(NULL), + size_(0), + bytes_requested_(0), + bytes_written_(0), + bytes_read_(0) { + } + + ~Buffer() { + if (buffer_) + delete [] buffer_; + } + + void Reset(int new_size) { + if (size_ < new_size) { + if (buffer_) + delete [] buffer_; + size_ = new_size; + buffer_ = 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_ + 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_ + bytes_written_, 1, + bytes_requested_ - bytes_written_)); + bytes_written_ += bytes_read; + } while (bytes_read != 0 && bytes_written_ < bytes_requested_); + + return bytes_written_; + } + + private: + char* buffer_; + int size_; + int bytes_requested_; + int bytes_written_; + int bytes_read_; +}; + CefStreamResourceHandler::CefStreamResourceHandler( const CefString& mime_type, CefRefPtr stream) : status_code_(200), + status_text_("OK"), mime_type_(mime_type), - stream_(stream) { + stream_(stream), + buffer_(NULL) +#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) { + stream_(stream), + buffer_(NULL) { DCHECK(!mime_type_.empty()); DCHECK(stream_.get()); + read_on_file_thread_ = stream_->MayBlock(); +} + +CefStreamResourceHandler::~CefStreamResourceHandler() { + if (buffer_) + delete buffer_; } bool CefStreamResourceHandler::ProcessRequest(CefRefPtr request, @@ -42,6 +129,7 @@ void CefStreamResourceHandler::GetResponseHeaders( int64& response_length, CefString& redirectUrl) { response->SetStatus(status_code_); + response->SetStatusText(status_text_); response->SetMimeType(mime_type_); if (!header_map_.empty()) @@ -54,9 +142,63 @@ bool CefStreamResourceHandler::ReadResponse(void* data_out, int bytes_to_read, int& bytes_read, CefRefPtr callback) { - bytes_read = static_cast(stream_->Read(data_out, 1, bytes_to_read)); - return (bytes_read > 0); + DCHECK_GT(bytes_to_read, 0); + + if (read_on_file_thread_) { + DCHECK(!buffer_owned_by_file_thread_); + 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, + NewCefRunnableMethod(this, &CefStreamResourceHandler::ReadOnFileThread, + 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(); + DCHECK(buffer_owned_by_file_thread_); + + if (!buffer_) + buffer_ = new Buffer(); + buffer_->Reset(bytes_to_read); + buffer_->ReadFrom(stream_); + +#ifndef NDEBUG + buffer_owned_by_file_thread_ = false; +#endif + callback->Continue(); +} diff --git a/tests/unittests/stream_resource_handler_unittest.cc b/tests/unittests/stream_resource_handler_unittest.cc new file mode 100644 index 000000000..9a76e11db --- /dev/null +++ b/tests/unittests/stream_resource_handler_unittest.cc @@ -0,0 +1,159 @@ +// Copyright (c) 2014 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 +#include +#include "include/cef_stream.h" +#include "include/wrapper/cef_stream_resource_handler.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "tests/unittests/routing_test_handler.h" + +namespace { + +const char kTestUrl[] = "http://tests-srh/test.html"; +const size_t kReadBlockSize = 1024U; // 1k. + +// The usual network buffer size is about 32k. Choose a value that's larger. +const size_t kReadDesiredSize = 100U * 1024U; // 100k + +class ReadHandler : public CefReadHandler { + public: + explicit ReadHandler(bool may_block) + : may_block_(may_block), + offset_(0), + expected_result_(0) { + } + + void CreateContent() { + // To verify that the data transers successfully we're going to make a big + // math problem. + content_.reserve(kReadDesiredSize + 50U); + content_ = ""; + } + + int GetExpectedResult() const { + return expected_result_; + } + + virtual size_t Read(void* ptr, size_t size, size_t n) OVERRIDE { + EXPECT_EQ(1, size); + + // Read the minimum of requested size, remaining size or kReadBlockSize. + const size_t read_bytes = + std::min(std::min(size * n, content_.size() - offset_), kReadBlockSize); + if (read_bytes > 0) { + memcpy(ptr, content_.c_str() + offset_, read_bytes); + offset_ += read_bytes; + } + + return read_bytes; + } + + virtual int Seek(int64 offset, int whence) OVERRIDE { + EXPECT_TRUE(false); // Not reached. + return 0; + } + + virtual int64 Tell() OVERRIDE { + EXPECT_TRUE(false); // Not reached. + return 0; + } + + virtual int Eof() OVERRIDE { + EXPECT_TRUE(false); // Not reached. + return 0; + } + + virtual bool MayBlock() OVERRIDE { + return may_block_; + } + + private: + const bool may_block_; + std::string content_; + size_t offset_; + int expected_result_; + + IMPLEMENT_REFCOUNTING(StreamReader); +}; + +class ReadTestHandler : public RoutingTestHandler { + public: + explicit ReadTestHandler(bool may_block) + : may_block_(may_block), + expected_result_(0) {} + + virtual void RunTest() OVERRIDE { + // Create the browser. + CreateBrowser(kTestUrl); + } + + virtual CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) OVERRIDE { + got_resource_handler_.yes(); + + const std::string& url = request->GetURL(); + EXPECT_STREQ(kTestUrl, url.c_str()); + + CefRefPtr handler = new ReadHandler(may_block_); + handler->CreateContent(); + expected_result_ = handler->GetExpectedResult(); + + CefRefPtr stream = + CefStreamReader::CreateForHandler(handler.get()); + return new CefStreamResourceHandler("text/html", stream); + } + + virtual bool OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64 query_id, + const CefString& request, + bool persistent, + CefRefPtr callback) OVERRIDE { + got_on_query_.yes(); + + const int actual_result = atoi(request.ToString().c_str()); + EXPECT_EQ(expected_result_, actual_result); + + DestroyTest(); + + return true; + } + + private: + virtual void DestroyTest() OVERRIDE { + EXPECT_TRUE(got_resource_handler_); + EXPECT_TRUE(got_on_query_); + RoutingTestHandler::DestroyTest(); + } + + const bool may_block_; + + int expected_result_; + TrackCallback got_resource_handler_; + TrackCallback got_on_query_; +}; + +} // namespace + +TEST(StreamResourceHandlerTest, ReadWillBlock) { + CefRefPtr handler = new ReadTestHandler(true); + handler->ExecuteTest(); +} + +TEST(StreamResourceHandlerTest, ReadWontBlock) { + CefRefPtr handler = new ReadTestHandler(false); + handler->ExecuteTest(); +} diff --git a/tests/unittests/stream_unittest.cc b/tests/unittests/stream_unittest.cc index 4ef9a0fb3..d65409f87 100644 --- a/tests/unittests/stream_unittest.cc +++ b/tests/unittests/stream_unittest.cc @@ -87,6 +87,7 @@ TEST(StreamTest, ReadFile) { CefRefPtr stream( CefStreamReader::CreateForFile(fileNameStr)); ASSERT_TRUE(stream.get() != NULL); + ASSERT_TRUE(stream->MayBlock()); VerifyStreamReadBehavior(stream, contents); // Release the file pointer @@ -109,6 +110,7 @@ TEST(StreamTest, ReadData) { static_cast(const_cast(contents.c_str())), contents.size())); ASSERT_TRUE(stream.get() != NULL); + ASSERT_FALSE(stream->MayBlock()); VerifyStreamReadBehavior(stream, contents); } @@ -121,6 +123,7 @@ TEST(StreamTest, WriteFile) { CefRefPtr stream( CefStreamWriter::CreateForFile(fileNameStr)); ASSERT_TRUE(stream.get() != NULL); + ASSERT_TRUE(stream->MayBlock()); VerifyStreamWriteBehavior(stream, contents); // Release the file pointer @@ -173,7 +176,7 @@ class ReadHandlerTester : public CefReadHandler { g_ReadHandlerTesterDeleted = true; } - virtual size_t Read(void* ptr, size_t size, size_t n) { + virtual size_t Read(void* ptr, size_t size, size_t n) OVERRIDE { read_called_ = true; read_ptr_ = ptr; read_size_ = size; @@ -181,23 +184,27 @@ class ReadHandlerTester : public CefReadHandler { return 10; } - virtual int Seek(int64 offset, int whence) { + virtual int Seek(int64 offset, int whence) OVERRIDE { seek_called_ = true; seek_offset_ = offset; seek_whence_ = whence; return 10; } - virtual int64 Tell() { + virtual int64 Tell() OVERRIDE { tell_called_ = true; return 10; } - virtual int Eof() { + virtual int Eof() OVERRIDE { eof_called_ = true; return 10; } + virtual bool MayBlock() OVERRIDE { + return false; + } + bool read_called_; const void* read_ptr_; size_t read_size_; @@ -220,6 +227,7 @@ TEST(StreamTest, ReadHandler) { CefRefPtr stream(CefStreamReader::CreateForHandler(handler)); ASSERT_TRUE(stream.get() != NULL); + ASSERT_FALSE(stream->MayBlock()); // CefReadHandler Read const char* read_ptr = "My data"; @@ -278,7 +286,7 @@ class WriteHandlerTester : public CefWriteHandler { g_WriteHandlerTesterDeleted = true; } - virtual size_t Write(const void* ptr, size_t size, size_t n) { + virtual size_t Write(const void* ptr, size_t size, size_t n) OVERRIDE { write_called_ = true; write_ptr_ = ptr; write_size_ = size; @@ -286,23 +294,27 @@ class WriteHandlerTester : public CefWriteHandler { return 10; } - virtual int Seek(int64 offset, int whence) { + virtual int Seek(int64 offset, int whence) OVERRIDE { seek_called_ = true; seek_offset_ = offset; seek_whence_ = whence; return 10; } - virtual int64 Tell() { + virtual int64 Tell() OVERRIDE { tell_called_ = true; return 10; } - virtual int Flush() { + virtual int Flush() OVERRIDE { flush_called_ = true; return 10; } + virtual bool MayBlock() OVERRIDE { + return false; + } + bool write_called_; const void* write_ptr_; size_t write_size_; @@ -325,6 +337,7 @@ TEST(StreamTest, WriteHandler) { CefRefPtr stream(CefStreamWriter::CreateForHandler(handler)); ASSERT_TRUE(stream.get() != NULL); + ASSERT_FALSE(stream->MayBlock()); // CefWriteHandler Write const char* write_ptr = "My data";