diff --git a/cef.gyp b/cef.gyp index 15a1994e6..d2855455a 100644 --- a/cef.gyp +++ b/cef.gyp @@ -192,6 +192,7 @@ 'libcef_dll_wrapper', ], 'sources': [ + 'tests/unittests/content_filter_unittest.cc', 'tests/unittests/cookie_unittest.cc', 'tests/unittests/dom_unittest.cc', 'tests/unittests/request_unittest.cc', @@ -354,6 +355,8 @@ 'libcef_dll/cpptoc/zip_reader_cpptoc.h', 'libcef_dll/ctocpp/client_ctocpp.cc', 'libcef_dll/ctocpp/client_ctocpp.h', + 'libcef_dll/ctocpp/content_filter_ctocpp.cc', + 'libcef_dll/ctocpp/content_filter_ctocpp.h', 'libcef_dll/ctocpp/cookie_visitor_ctocpp.cc', 'libcef_dll/ctocpp/cookie_visitor_ctocpp.h', 'libcef_dll/ctocpp/ctocpp.h', @@ -459,6 +462,8 @@ 'libcef_dll/cef_logging.h', 'libcef_dll/cpptoc/client_cpptoc.cc', 'libcef_dll/cpptoc/client_cpptoc.h', + 'libcef_dll/cpptoc/content_filter_cpptoc.cc', + 'libcef_dll/cpptoc/content_filter_cpptoc.h', 'libcef_dll/cpptoc/cookie_visitor_cpptoc.cc', 'libcef_dll/cpptoc/cookie_visitor_cpptoc.h', 'libcef_dll/cpptoc/cpptoc.h', diff --git a/include/cef.h b/include/cef.h index 89b5d2722..82f952392 100644 --- a/include/cef.h +++ b/include/cef.h @@ -56,6 +56,7 @@ class CefBrowser; class CefClient; +class CefContentFilter; class CefCookieVisitor; class CefDOMDocument; class CefDOMEvent; @@ -785,6 +786,15 @@ public: CefRefPtr response, int loadFlags) { return false; } + // Called on the UI thread after a response to the resource request is + // received. Set |filter| if response content needs to be monitored and/or + // modified as it arrives. + /*--cef()--*/ + virtual void OnResourceReponse(CefRefPtr browser, + const CefString& url, + CefRefPtr response, + CefRefPtr& filter) {} + // Called on the IO thread to handle requests for URLs with an unknown // protocol component. Return true to indicate that the request should // succeed because it was handled externally. Set |allowOSExecution| to true @@ -2365,4 +2375,24 @@ public: virtual void HandleEvent(CefRefPtr event) =0; }; + +// Interface to implement for filtering response content. The methods of this +// class will always be called on the UI thread. +/*--cef(source=client)--*/ +class CefContentFilter : public virtual CefBase +{ +public: + // Set |substitute_data| to the replacement for the data in |data| if data + // should be modified. + /*--cef()--*/ + virtual void ProcessData(const void* data, int data_size, + CefRefPtr& substitute_data) {} + + // Called when there is no more data to be processed. It is expected that + // whatever data was retained in the last ProcessData() call, it should be + // returned now by setting |remainder| if appropriate. + /*--cef()--*/ + virtual void Drain(CefRefPtr& remainder) {} +}; + #endif // _CEF_H diff --git a/include/cef_capi.h b/include/cef_capi.h index 36380913e..82e23a85a 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -655,6 +655,14 @@ typedef struct _cef_request_handler_t struct _cef_stream_reader_t** resourceStream, struct _cef_response_t* response, int loadFlags); + // Called on the UI thread after a response to the resource request is + // received. Set |filter| if response content needs to be monitored and/or + // modified as it arrives. + void (CEF_CALLBACK *on_resource_reponse)(struct _cef_request_handler_t* self, + struct _cef_browser_t* browser, const cef_string_t* url, + struct _cef_response_t* response, + struct _cef_content_filter_t** filter); + // Called on the IO thread to handle requests for URLs with an unknown // protocol component. Return true (1) to indicate that the request should // succeed because it was handled externally. Set |allowOSExecution| to true @@ -2157,6 +2165,28 @@ typedef struct _cef_domevent_listener_t } cef_domevent_listener_t; +// Structure to implement for filtering response content. The functions of this +// structure will always be called on the UI thread. +typedef struct _cef_content_filter_t +{ + // Base structure. + cef_base_t base; + + // Set |substitute_data| to the replacement for the data in |data| if data + // should be modified. + void (CEF_CALLBACK *process_data)(struct _cef_content_filter_t* self, + const void* data, int data_size, + struct _cef_stream_reader_t** substitute_data); + + // Called when there is no more data to be processed. It is expected that + // whatever data was retained in the last process_data() call, it should be + // returned now by setting |remainder| if appropriate. + void (CEF_CALLBACK *drain)(struct _cef_content_filter_t* self, + struct _cef_stream_reader_t** remainder); + +} cef_content_filter_t; + + #ifdef __cplusplus } #endif diff --git a/libcef/browser_resource_loader_bridge.cc b/libcef/browser_resource_loader_bridge.cc index 08a18f775..996c70d34 100644 --- a/libcef/browser_resource_loader_bridge.cc +++ b/libcef/browser_resource_loader_bridge.cc @@ -201,6 +201,25 @@ class RequestProxy : public net::URLRequest::Delegate, handler = client->GetRequestHandler(); if (handler.get()) { + CefRefPtr response = new CefResponseImpl(); + // Transfer response headers + if (info.headers) { + CefResponse::HeaderMap headerMap; + void* header_index = NULL; + std::string name, value; + while (info.headers->EnumerateHeaderLines(&header_index, &name, + &value)) { + if (!name.empty() && !value.empty()) + headerMap[name] = value; + } + response->SetHeaderMap(headerMap); + response->SetStatusText(info.headers->GetStatusText()); + response->SetStatus(info.headers->response_code()); + } + response->SetMimeType(info.mime_type); + handler->OnResourceReponse(browser_, url.spec(), response, + content_filter_); + std::string content_disposition; info.headers->GetNormalizedHeader("Content-Disposition", &content_disposition); @@ -241,6 +260,21 @@ class RequestProxy : public net::URLRequest::Delegate, CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( this, &RequestProxy::AsyncReadData)); + CefRefPtr resourceStream; + + if(content_filter_.get()) + content_filter_->ProcessData(buf_copy.get(), bytes_read, resourceStream); + + if (resourceStream.get()) { + // The filter made some changes to the data in the buffer. + resourceStream->Seek(0, SEEK_END); + bytes_read = resourceStream->Tell(); + resourceStream->Seek(0, SEEK_SET); + + buf_copy.reset(new char[bytes_read]); + resourceStream->Read(buf_copy.get(), 1, bytes_read); + } + if (download_handler_.get() && !download_handler_->ReceivedData(buf_copy.get(), bytes_read)) { // Cancel loading by proxying over to the io thread. @@ -265,6 +299,34 @@ class RequestProxy : public net::URLRequest::Delegate, void NotifyCompletedRequest(const net::URLRequestStatus& status, const std::string& security_info, const base::Time& complete_time) { + + // Drain the content filter of all remaining data + if (content_filter_.get()) { + CefRefPtr remainder; + content_filter_->Drain(remainder); + + if(remainder.get()) { + remainder->Seek(0, SEEK_END); + long size = remainder->Tell(); + if (size) { + remainder->Seek(0, SEEK_SET); + scoped_array buf(new char[size]); + remainder->Read(buf.get(), 1, size); + + if (download_handler_.get() && + !download_handler_->ReceivedData(buf.get(), size)) { + // Cancel loading by proxying over to the io thread. + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( + this, &RequestProxy::AsyncCancel)); + } + + if (peer_) + peer_->OnReceivedData(buf.get(), size, -1); + } + } + content_filter_ = NULL; + } + if (download_handler_.get()) { download_handler_->Complete(); download_handler_ = NULL; @@ -392,7 +454,7 @@ class RequestProxy : public net::URLRequest::Delegate, info.content_length = static_cast(offset); info.mime_type = response->GetMimeType(); info.headers = headers; - OnReceivedResponse(info); + OnReceivedResponse(info, params->url); AsyncReadData(); } else if (response->GetStatus() != 0) { // status set, but no resource stream @@ -408,7 +470,7 @@ class RequestProxy : public net::URLRequest::Delegate, info.content_length = 0; info.mime_type = response->GetMimeType(); info.headers = headers; - OnReceivedResponse(info); + OnReceivedResponse(info, params->url); AsyncReadData(); } @@ -541,7 +603,9 @@ class RequestProxy : public net::URLRequest::Delegate, } virtual void OnReceivedResponse( - const ResourceResponseInfo& info) { + const ResourceResponseInfo& info, + // only used when loading from a resource stream + const GURL& simulated_url) { GURL url; bool allow_download(false); if (request_.get()){ @@ -550,6 +614,8 @@ class RequestProxy : public net::URLRequest::Delegate, static_cast(request_->GetUserData(NULL)); if (info) allow_download = info->allow_download(); + } else if (!simulated_url.is_empty() && simulated_url.is_valid()) { + url = simulated_url; } owner_loop_->PostTask(FROM_HERE, NewRunnableMethod( @@ -596,7 +662,7 @@ class RequestProxy : public net::URLRequest::Delegate, if (request->status().is_success()) { ResourceResponseInfo info; PopulateResponseInfo(request, &info); - OnReceivedResponse(info); + OnReceivedResponse(info, GURL::EmptyGURL()); AsyncReadData(); // start reading } else { Done(); @@ -768,6 +834,7 @@ class RequestProxy : public net::URLRequest::Delegate, base::TimeTicks last_upload_ticks_; CefRefPtr download_handler_; + CefRefPtr content_filter_; }; //----------------------------------------------------------------------------- @@ -802,7 +869,8 @@ class SyncRequestProxy : public RequestProxy { result_->url = new_url; } - virtual void OnReceivedResponse(const ResourceResponseInfo& info) { + virtual void OnReceivedResponse(const ResourceResponseInfo& info, + const GURL&) { *static_cast(result_) = info; } diff --git a/libcef_dll/cpptoc/content_filter_cpptoc.cc b/libcef_dll/cpptoc/content_filter_cpptoc.cc new file mode 100644 index 000000000..912cd7b23 --- /dev/null +++ b/libcef_dll/cpptoc/content_filter_cpptoc.cc @@ -0,0 +1,69 @@ +// Copyright (c) 2011 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. +// +// --------------------------------------------------------------------------- +// +// A portion of this file was generated by the CEF translator tool. When +// making changes by hand only do so within the body of existing function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// + +#include "libcef_dll/cpptoc/content_filter_cpptoc.h" +#include "libcef_dll/ctocpp/stream_reader_ctocpp.h" + + +// MEMBER FUNCTIONS - Body may be edited by hand. + +void CEF_CALLBACK content_filter_process_data( + struct _cef_content_filter_t* self, const void* data, int data_size, + cef_stream_reader_t** substitute_data) +{ + DCHECK(self); + DCHECK(data); + DCHECK(substitute_data); + if (!self || !data || !substitute_data) + return; + + CefRefPtr substituteDataPtr; + + CefContentFilterCppToC::Get(self)->ProcessData(data, data_size, + substituteDataPtr); + + if(substituteDataPtr.get()) + *substitute_data = CefStreamReaderCToCpp::Unwrap(substituteDataPtr); +} + +void CEF_CALLBACK content_filter_drain(struct _cef_content_filter_t* self, + cef_stream_reader_t** remainder) +{ + DCHECK(self); + DCHECK(remainder); + if (!self || !remainder) + return; + + CefRefPtr remainderPtr; + + CefContentFilterCppToC::Get(self)->Drain(remainderPtr); + + if(remainderPtr.get()) + *remainder = CefStreamReaderCToCpp::Unwrap(remainderPtr); +} + + +// CONSTRUCTOR - Do not edit by hand. + +CefContentFilterCppToC::CefContentFilterCppToC(CefContentFilter* cls) + : CefCppToC( + cls) +{ + struct_.struct_.process_data = content_filter_process_data; + struct_.struct_.drain = content_filter_drain; +} + +#ifndef NDEBUG +template<> long CefCppToC::DebugObjCt = 0; +#endif + diff --git a/libcef_dll/cpptoc/content_filter_cpptoc.h b/libcef_dll/cpptoc/content_filter_cpptoc.h new file mode 100644 index 000000000..f8c84d41f --- /dev/null +++ b/libcef_dll/cpptoc/content_filter_cpptoc.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// +#ifndef _CONTENTFILTER_CPPTOC_H +#define _CONTENTFILTER_CPPTOC_H + +#ifndef USING_CEF_SHARED +#pragma message("Warning: "__FILE__" may be accessed wrapper-side only") +#else // USING_CEF_SHARED + +#include "include/cef.h" +#include "include/cef_capi.h" +#include "libcef_dll/cpptoc/cpptoc.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed wrapper-side only. +class CefContentFilterCppToC + : public CefCppToC +{ +public: + CefContentFilterCppToC(CefContentFilter* cls); + virtual ~CefContentFilterCppToC() {} +}; + +#endif // USING_CEF_SHARED +#endif // _CONTENTFILTER_CPPTOC_H + diff --git a/libcef_dll/cpptoc/request_handler_cpptoc.cc b/libcef_dll/cpptoc/request_handler_cpptoc.cc index 11e847a4b..1313e2423 100644 --- a/libcef_dll/cpptoc/request_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/request_handler_cpptoc.cc @@ -10,6 +10,7 @@ // for more information. // +#include "libcef_dll/cpptoc/content_filter_cpptoc.h" #include "libcef_dll/cpptoc/download_handler_cpptoc.h" #include "libcef_dll/cpptoc/request_handler_cpptoc.h" #include "libcef_dll/ctocpp/browser_ctocpp.h" @@ -67,6 +68,29 @@ int CEF_CALLBACK request_handler_on_before_resource_load( return rv; } +void CEF_CALLBACK request_handler_on_resource_reponse( + struct _cef_request_handler_t* self, cef_browser_t* browser, + const cef_string_t* url, struct _cef_response_t* response, + struct _cef_content_filter_t** filter) +{ + DCHECK(self); + DCHECK(browser); + DCHECK(url); + DCHECK(response); + DCHECK(filter); + if (!self || !browser || !url || !response || !filter) + return; + + CefRefPtr filterPtr; + + CefRequestHandlerCppToC::Get(self)->OnResourceReponse( + CefBrowserCToCpp::Wrap(browser), url, CefResponseCToCpp::Wrap(response), + filterPtr); + + if(filterPtr.get()) + *filter = CefContentFilterCppToC::Wrap(filterPtr); +} + int CEF_CALLBACK request_handler_on_protocol_execution( struct _cef_request_handler_t* self, cef_browser_t* browser, const cef_string_t* url, int* allowOSExecution) @@ -145,6 +169,7 @@ CefRequestHandlerCppToC::CefRequestHandlerCppToC(CefRequestHandler* cls) struct_.struct_.on_before_browse = request_handler_on_before_browse; struct_.struct_.on_before_resource_load = request_handler_on_before_resource_load; + struct_.struct_.on_resource_reponse = request_handler_on_resource_reponse; struct_.struct_.on_protocol_execution = request_handler_on_protocol_execution; struct_.struct_.get_download_handler = request_handler_get_download_handler; struct_.struct_.get_auth_credentials = request_handler_get_auth_credentials; diff --git a/libcef_dll/ctocpp/content_filter_ctocpp.cc b/libcef_dll/ctocpp/content_filter_ctocpp.cc new file mode 100644 index 000000000..6a123dce9 --- /dev/null +++ b/libcef_dll/ctocpp/content_filter_ctocpp.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2011 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. +// +// --------------------------------------------------------------------------- +// +// A portion of this file was generated by the CEF translator tool. When +// making changes by hand only do so within the body of existing static and +// virtual method implementations. See the translator.README.txt file in the +// tools directory for more information. +// + +#include "libcef_dll/cpptoc/stream_reader_cpptoc.h" +#include "libcef_dll/ctocpp/content_filter_ctocpp.h" + + +// VIRTUAL METHODS - Body may be edited by hand. + +void CefContentFilterCToCpp::ProcessData(const void* data, int data_size, + CefRefPtr& substitute_data) +{ + if (CEF_MEMBER_MISSING(struct_, process_data)) + return; + + cef_stream_reader_t* streamRet = NULL; + struct_->process_data(struct_, data, data_size, &streamRet); + + if(streamRet) + substitute_data = CefStreamReaderCppToC::Unwrap(streamRet); +} + +void CefContentFilterCToCpp::Drain(CefRefPtr& remainder) +{ + if (CEF_MEMBER_MISSING(struct_, drain)) + return; + + cef_stream_reader_t* streamRet = NULL; + struct_->drain(struct_, &streamRet); + + if(streamRet) + remainder = CefStreamReaderCppToC::Unwrap(streamRet); +} + + +#ifndef NDEBUG +template<> long CefCToCpp::DebugObjCt = 0; +#endif + diff --git a/libcef_dll/ctocpp/content_filter_ctocpp.h b/libcef_dll/ctocpp/content_filter_ctocpp.h new file mode 100644 index 000000000..7652e96d6 --- /dev/null +++ b/libcef_dll/ctocpp/content_filter_ctocpp.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011 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. +// +// ------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// + +#ifndef _CONTENTFILTER_CTOCPP_H +#define _CONTENTFILTER_CTOCPP_H + +#ifndef BUILDING_CEF_SHARED +#pragma message("Warning: "__FILE__" may be accessed DLL-side only") +#else // BUILDING_CEF_SHARED + +#include "include/cef.h" +#include "include/cef_capi.h" +#include "libcef_dll/ctocpp/ctocpp.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed DLL-side only. +class CefContentFilterCToCpp + : public CefCToCpp +{ +public: + CefContentFilterCToCpp(cef_content_filter_t* str) + : CefCToCpp(str) {} + virtual ~CefContentFilterCToCpp() {} + + // CefContentFilter methods + virtual void ProcessData(const void* data, int data_size, + CefRefPtr& substitute_data) OVERRIDE; + virtual void Drain(CefRefPtr& remainder) OVERRIDE; +}; + +#endif // BUILDING_CEF_SHARED +#endif // _CONTENTFILTER_CTOCPP_H + diff --git a/libcef_dll/ctocpp/request_handler_ctocpp.cc b/libcef_dll/ctocpp/request_handler_ctocpp.cc index 6fdb0e65f..3abe43d3a 100644 --- a/libcef_dll/ctocpp/request_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/request_handler_ctocpp.cc @@ -15,6 +15,7 @@ #include "libcef_dll/cpptoc/request_cpptoc.h" #include "libcef_dll/cpptoc/response_cpptoc.h" #include "libcef_dll/cpptoc/stream_reader_cpptoc.h" +#include "libcef_dll/ctocpp/content_filter_ctocpp.h" #include "libcef_dll/ctocpp/download_handler_ctocpp.h" #include "libcef_dll/ctocpp/request_handler_ctocpp.h" @@ -54,6 +55,23 @@ bool CefRequestHandlerCToCpp::OnBeforeResourceLoad( return (rv ? true : false); } +void CefRequestHandlerCToCpp::OnResourceReponse(CefRefPtr browser, + const CefString& url, CefRefPtr response, + CefRefPtr& filter) +{ + if (CEF_MEMBER_MISSING(struct_, on_resource_reponse)) + return; + + cef_content_filter_t* filterRet = NULL; + + struct_->on_resource_reponse(struct_, + CefBrowserCppToC::Wrap(browser), url.GetStruct(), + CefResponseCppToC::Wrap(response), &filterRet); + + if(filterRet) + filter = CefContentFilterCToCpp::Wrap(filterRet); +} + bool CefRequestHandlerCToCpp::OnProtocolExecution(CefRefPtr browser, const CefString& url, bool& allowOSExecution) { diff --git a/libcef_dll/ctocpp/request_handler_ctocpp.h b/libcef_dll/ctocpp/request_handler_ctocpp.h index 188f84574..0678e3f05 100644 --- a/libcef_dll/ctocpp/request_handler_ctocpp.h +++ b/libcef_dll/ctocpp/request_handler_ctocpp.h @@ -40,6 +40,9 @@ public: CefRefPtr request, CefString& redirectUrl, CefRefPtr& resourceStream, CefRefPtr response, int loadFlags) OVERRIDE; + virtual void OnResourceReponse(CefRefPtr browser, + const CefString& url, CefRefPtr response, + CefRefPtr& filter) OVERRIDE; virtual bool OnProtocolExecution(CefRefPtr browser, const CefString& url, bool& allowOSExecution) OVERRIDE; virtual bool GetDownloadHandler(CefRefPtr browser, diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index 6c7c33501..fbdda443b 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -21,6 +21,7 @@ #include "cpptoc/web_urlrequest_cpptoc.h" #include "cpptoc/xml_reader_cpptoc.h" #include "cpptoc/zip_reader_cpptoc.h" +#include "ctocpp/content_filter_ctocpp.h" #include "ctocpp/cookie_visitor_ctocpp.h" #include "ctocpp/domevent_listener_ctocpp.h" #include "ctocpp/domvisitor_ctocpp.h" @@ -69,6 +70,7 @@ CEF_EXPORT void cef_shutdown() DCHECK(CefWebURLRequestCppToC::DebugObjCt == 0); DCHECK(CefXmlReaderCppToC::DebugObjCt == 0); DCHECK(CefZipReaderCppToC::DebugObjCt == 0); + DCHECK(CefContentFilterCToCpp::DebugObjCt == 0); DCHECK(CefCookieVisitorCToCpp::DebugObjCt == 0); DCHECK(CefDOMEventListenerCToCpp::DebugObjCt == 0); DCHECK(CefDOMVisitorCToCpp::DebugObjCt == 0); diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index 56837be62..4168af01d 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -6,6 +6,7 @@ #include "include/cef_capi.h" #include "include/cef_nplugin.h" #include "include/cef_nplugin_capi.h" +#include "libcef_dll/cpptoc/content_filter_cpptoc.h" #include "libcef_dll/cpptoc/cookie_visitor_cpptoc.h" #include "libcef_dll/cpptoc/domevent_listener_cpptoc.h" #include "libcef_dll/cpptoc/domvisitor_cpptoc.h" @@ -45,6 +46,7 @@ void CefShutdown() #ifndef NDEBUG // Check that all wrapper objects have been destroyed + DCHECK(CefContentFilterCppToC::DebugObjCt == 0); DCHECK(CefCookieVisitorCppToC::DebugObjCt == 0); DCHECK(CefDOMEventListenerCppToC::DebugObjCt == 0); DCHECK(CefDOMVisitorCppToC::DebugObjCt == 0); diff --git a/tests/unittests/content_filter_unittest.cc b/tests/unittests/content_filter_unittest.cc new file mode 100644 index 000000000..704daec30 --- /dev/null +++ b/tests/unittests/content_filter_unittest.cc @@ -0,0 +1,219 @@ +// Copyright (c) 2011 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/cef.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "test_handler.h" + +namespace { + +bool g_ContentFilterTestHandlerHandleResourceResponseCalled; +bool g_ContentFilterProcessDataCalled; +bool g_ContentFilterDrainCalled; + +class TestContentFilter : public CefContentFilter +{ +public: + TestContentFilter() + { + look_for_ = "FAILURE!"; + replace_with_ = "BIG SUCCESS!"; + } + + virtual void ProcessData(const void* data, int data_size, + CefRefPtr& substitute_data) + OVERRIDE + { + g_ContentFilterProcessDataCalled = true; + + std::string in_out((char*)data, data_size); + std::string::const_iterator look_for_it = look_for_.begin(); + + bool is_modified = false; + if (!remainder_.empty()) { + in_out.insert(in_out.begin(), remainder_.begin(), remainder_.end()); + remainder_.clear(); + } + + std::string::size_type off = 0; + do { + if ((look_for_it == look_for_.end()) || + (look_for_it == look_for_.begin())) { + // start over + off = in_out.find(look_for_[0], off); + if (off == in_out.npos) + break; + look_for_it = look_for_.begin(); + } + + while (look_for_it != look_for_.end()) { + if (*look_for_it != in_out[off]) { + look_for_it = look_for_.begin(); + break; + } + + if (++off == in_out.length()) + off = in_out.npos; + + if (off == in_out.npos) + break; + + look_for_it++; + } + + if (look_for_it == look_for_.end()) { + // found it + in_out.replace(in_out.begin() + off - look_for_.length(), + in_out.begin() + off, + replace_with_); + off += replace_with_.length() - look_for_.length(); + if (off >= in_out.length()) + off = in_out.npos; + + look_for_it = look_for_.begin(); + is_modified = true; + } + } while (off != in_out.npos); + + if (look_for_it != look_for_.begin()) { + // partial match at the end of the buffer + // save for next packet + size_t slice_off = + in_out.length() - (look_for_it - look_for_.begin()) - 1; + + remainder_ = in_out.substr(slice_off); + in_out.erase(slice_off); + } + + if (is_modified) { + substitute_data = + CefStreamReader::CreateForData((void*)in_out.data(), in_out.size()); + } + } + + virtual void Drain(CefRefPtr& remainder) OVERRIDE + { + g_ContentFilterDrainCalled = true; + + if (remainder_.empty()) + return; + + remainder = CefStreamReader::CreateForData((void*)remainder_.data(), + remainder_.size()); + } + +protected: + IMPLEMENT_REFCOUNTING(TestContentFilter); + +private: + std::string look_for_; + std::string replace_with_; + std::string remainder_; +}; + +class ContentFilterTestHandler : public TestHandler +{ +public: + class Visitor : public CefDOMVisitor + { + public: + Visitor(ContentFilterTestHandler* handler) : handler_(handler) {} + + // Test if the filter succeeded in modifying the content + void TestContentReplaced(CefRefPtr document) + { + // Navigate the complete document structure. + CefRefPtr resultNode = + document->GetElementById("test_result"); + + EXPECT_TRUE(resultNode.get()); + EXPECT_EQ("BIG SUCCESS!", resultNode->GetElementInnerText().ToString()); + } + + virtual void Visit(CefRefPtr document) OVERRIDE + { + handler_->got_visitor_called_.yes(); + + TestContentReplaced(document); + + handler_->DestroyTest(); + } + + protected: + ContentFilterTestHandler* handler_; + IMPLEMENT_REFCOUNTING(Visitor); + }; + + ContentFilterTestHandler() + { + visitor_ = new Visitor(this); + } + + virtual void RunTest() OVERRIDE + { + std::string mainHtml = + "

If filtering works you should see BIG SUCCESS! below:

" + "
FAILURE!
"; + + AddResource("http://tests/test_filter.html", mainHtml, "text/html"); + CreateBrowser("http://tests/test_filter.html"); + } + + virtual void OnResourceReponse(CefRefPtr browser, + const CefString& url, + CefRefPtr response, + CefRefPtr& filter) OVERRIDE + { + g_ContentFilterTestHandlerHandleResourceResponseCalled = true; + + ASSERT_EQ(url, "http://tests/test_filter.html"); + + CefResponse::HeaderMap headers; + response->GetHeaderMap(headers); + std::string mime_type = response->GetMimeType(); + int status_code = response->GetStatus(); + std::string status_text = response->GetStatusText(); + + ASSERT_TRUE(headers.empty()); + ASSERT_EQ(mime_type, "text/html"); + ASSERT_EQ(status_code, 200); + ASSERT_EQ(status_text, "OK"); + + filter = new TestContentFilter(); + } + + virtual void OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, + int httpStatusCode) OVERRIDE + { + if(frame->IsMain()) { + // The page is done loading so visit the DOM. + browser->GetMainFrame()->VisitDOM(visitor_.get()); + } + } + + TrackCallback got_visitor_called_; + +private: + CefRefPtr visitor_; +}; + +} // anonymous + +// Verify send and recieve +TEST(ContentFilterTest, ContentFilter) +{ + g_ContentFilterTestHandlerHandleResourceResponseCalled = false; + g_ContentFilterProcessDataCalled = false; + g_ContentFilterDrainCalled = false; + + CefRefPtr handler = + new ContentFilterTestHandler(); + handler->ExecuteTest(); + + ASSERT_TRUE(handler->got_visitor_called_); + ASSERT_TRUE(g_ContentFilterTestHandlerHandleResourceResponseCalled); + ASSERT_TRUE(g_ContentFilterProcessDataCalled); + ASSERT_TRUE(g_ContentFilterDrainCalled); +}