diff --git a/AUTHORS.txt b/AUTHORS.txt index aa45e3a95..1af953e02 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -28,3 +28,4 @@ Czarek Tomczak Felix Bruns YuTeh Shen Andrei Kurushin +Gonzo Berman diff --git a/include/capi/cef_request_capi.h b/include/capi/cef_request_capi.h index 0479e5279..a42c0fd7a 100644 --- a/include/capi/cef_request_capi.h +++ b/include/capi/cef_request_capi.h @@ -159,6 +159,13 @@ typedef struct _cef_request_t { /// cef_transition_type_t (CEF_CALLBACK *get_transition_type)( struct _cef_request_t* self); + + /// + // Returns the globally unique identifier for this request or 0 if not + // specified. Can be used by cef_request_tHandler implementations in the + // browser process to track a single request across multiple callbacks. + /// + uint64 (CEF_CALLBACK *get_identifier)(struct _cef_request_t* self); } cef_request_t; diff --git a/include/capi/cef_request_handler_capi.h b/include/capi/cef_request_handler_capi.h index 2b008a9e0..ac6328e5f 100644 --- a/include/capi/cef_request_handler_capi.h +++ b/include/capi/cef_request_handler_capi.h @@ -159,13 +159,24 @@ typedef struct _cef_request_handler_t { struct _cef_frame_t* frame, struct _cef_request_t* request); /// - // Called on the IO thread when a resource load is redirected. The |old_url| - // parameter will contain the old URL. The |new_url| parameter will contain - // the new URL and can be changed if desired. + // Called on the IO thread when a resource load is redirected. The |request| + // parameter will contain the old URL and other request-related information. + // The |new_url| parameter will contain the new URL and can be changed if + // desired. The |request| object cannot be modified in this callback. /// void (CEF_CALLBACK *on_resource_redirect)(struct _cef_request_handler_t* self, struct _cef_browser_t* browser, struct _cef_frame_t* frame, - const cef_string_t* old_url, cef_string_t* new_url); + struct _cef_request_t* request, cef_string_t* new_url); + + /// + // Called on the IO thread when a resource response is received. To allow the + // resource to load normally return false (0). To redirect or retry the + // resource modify |request| (url, headers or post body) and return true (1). + // The |response| object cannot be modified in this callback. + /// + int (CEF_CALLBACK *on_resource_response)(struct _cef_request_handler_t* self, + struct _cef_browser_t* browser, struct _cef_frame_t* frame, + struct _cef_request_t* request, struct _cef_response_t* response); /// // Called on the IO thread when the browser needs credentials from the user. diff --git a/include/cef_request.h b/include/cef_request.h index 8dd86791a..6d13da128 100644 --- a/include/cef_request.h +++ b/include/cef_request.h @@ -168,6 +168,14 @@ class CefRequest : public virtual CefBase { /// /*--cef(default_retval=TT_EXPLICIT)--*/ virtual TransitionType GetTransitionType() =0; + + /// + // Returns the globally unique identifier for this request or 0 if not + // specified. Can be used by CefRequestHandler implementations in the browser + // process to track a single request across multiple callbacks. + /// + /*--cef()--*/ + virtual uint64 GetIdentifier() =0; }; diff --git a/include/cef_request_handler.h b/include/cef_request_handler.h index dda9010c0..0d16b9f4e 100644 --- a/include/cef_request_handler.h +++ b/include/cef_request_handler.h @@ -165,16 +165,31 @@ class CefRequestHandler : public virtual CefBase { } /// - // Called on the IO thread when a resource load is redirected. The |old_url| - // parameter will contain the old URL. The |new_url| parameter will contain - // the new URL and can be changed if desired. + // Called on the IO thread when a resource load is redirected. The |request| + // parameter will contain the old URL and other request-related information. + // The |new_url| parameter will contain the new URL and can be changed if + // desired. The |request| object cannot be modified in this callback. /// /*--cef()--*/ virtual void OnResourceRedirect(CefRefPtr browser, CefRefPtr frame, - const CefString& old_url, + CefRefPtr request, CefString& new_url) {} + /// + // Called on the IO thread when a resource response is received. To allow the + // resource to load normally return false. To redirect or retry the resource + // modify |request| (url, headers or post body) and return true. The + // |response| object cannot be modified in this callback. + /// + /*--cef()--*/ + virtual bool OnResourceResponse(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) { + return false; + } + /// // Called on the IO thread when the browser needs credentials from the user. // |isProxy| indicates whether the host is a proxy server. |host| contains the diff --git a/libcef/browser/url_request_interceptor.cc b/libcef/browser/url_request_interceptor.cc index f36bf4f38..5258eec74 100644 --- a/libcef/browser/url_request_interceptor.cc +++ b/libcef/browser/url_request_interceptor.cc @@ -9,8 +9,13 @@ #include "libcef/browser/browser_host_impl.h" #include "libcef/browser/resource_request_job.h" #include "libcef/browser/thread_util.h" +#include "libcef/common/http_header_utils.h" #include "libcef/common/request_impl.h" +#include "libcef/common/response_impl.h" +#include "net/base/upload_data_stream.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request_job_manager.h" #include "net/url_request/url_request_redirect_job.h" CefRequestInterceptor::CefRequestInterceptor() { @@ -40,9 +45,10 @@ net::URLRequestJob* CefRequestInterceptor::MaybeInterceptRequest( // Give the client an opportunity to replace the request. CefRefPtr resourceHandler = handler->GetResourceHandler(browser.get(), frame, req); - if (resourceHandler.get()) + if (resourceHandler.get()) { return new CefResourceRequestJob(request, network_delegate, resourceHandler); + } } } } @@ -63,12 +69,16 @@ net::URLRequestJob* CefRequestInterceptor::MaybeInterceptRedirect( if (handler.get()) { CefRefPtr frame = browser->GetFrameForRequest(request); + CefRefPtr cefRequest = new CefRequestImpl(); + static_cast(cefRequest.get())->Set(request); + static_cast(cefRequest.get())->SetReadOnly(true); + // Give the client an opportunity to redirect the request. CefString newUrlStr = location.spec(); - handler->OnResourceRedirect(browser.get(), frame, request->url().spec(), - newUrlStr); + handler->OnResourceRedirect(browser.get(), frame, cefRequest, + newUrlStr); if (newUrlStr != location.spec()) { - GURL new_url = GURL(std::string(newUrlStr)); + const GURL new_url = GURL(newUrlStr.ToString()); if (!new_url.is_empty() && new_url.is_valid()) { return new net::URLRequestRedirectJob( request, network_delegate, new_url, @@ -86,5 +96,74 @@ net::URLRequestJob* CefRequestInterceptor::MaybeInterceptRedirect( net::URLRequestJob* CefRequestInterceptor::MaybeInterceptResponse( net::URLRequest* request, net::NetworkDelegate* network_delegate) const { - return NULL; + CefRefPtr browser = + CefBrowserHostImpl::GetBrowserForRequest(request); + if (!browser.get()) + return NULL; + + CefRefPtr client = browser->GetClient(); + if (!client.get()) + return NULL; + + CefRefPtr handler = client->GetRequestHandler(); + if (!handler.get()) + return NULL; + + CefRefPtr frame = browser->GetFrameForRequest(request); + + CefRefPtr cefRequest = new CefRequestImpl(); + static_cast(cefRequest.get())->Set(request); + + CefRefPtr cefResponse = new CefResponseImpl(); + static_cast(cefResponse.get())->Set(request); + static_cast(cefResponse.get())->SetReadOnly(true); + + // Give the client an opportunity to retry or redirect the request. + if (!handler->OnResourceResponse(browser.get(), frame, cefRequest, + cefResponse)) { + return NULL; + } + + // This flag will be reset by URLRequest::RestartWithJob() calling + // URLRequest::PrepareToRestart() after this method returns but we need it + // reset sooner so that we can modify the request headers without asserting. + request->set_is_pending(false); + + // Update the request headers to match the CefRequest. + CefRequest::HeaderMap cefHeaders; + cefRequest->GetHeaderMap(cefHeaders); + + CefString referrerStr; + referrerStr.FromASCII(net::HttpRequestHeaders::kReferer); + CefRequest::HeaderMap::iterator it = cefHeaders.find(referrerStr); + if (it != cefHeaders.end()) { + request->SetReferrer(it->second); + cefHeaders.erase(it); + } + + net::HttpRequestHeaders netHeaders; + netHeaders.AddHeadersFromString(HttpHeaderUtils::GenerateHeaders(cefHeaders)); + request->SetExtraRequestHeaders(netHeaders); + + // Update the request body to match the CefRequest. + CefRefPtr post_data = cefRequest->GetPostData(); + if (post_data.get()) { + request->set_upload( + make_scoped_ptr(static_cast(post_data.get())->Get())); + } else if (request->get_upload()) { + request->set_upload(scoped_ptr()); + } + + // If the URL was modified redirect the request. + const GURL url(cefRequest->GetURL().ToString()); + if (url != request->url()) { + return new net::URLRequestRedirectJob( + request, network_delegate, url, + net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, + "Resource Redirect"); + } + + // Otherwise queue a new job. + return net::URLRequestJobManager::GetInstance()->CreateJob( + request, network_delegate); } diff --git a/libcef/common/request_impl.cc b/libcef/common/request_impl.cc index 6a4f8be6e..3cdb8616b 100644 --- a/libcef/common/request_impl.cc +++ b/libcef/common/request_impl.cc @@ -104,6 +104,7 @@ CefRequestImpl::CefRequestImpl() : method_("GET"), resource_type_(RT_SUB_RESOURCE), transition_type_(TT_EXPLICIT), + identifier_(0U), flags_(UR_FLAG_NONE), read_only_(false) { } @@ -199,6 +200,11 @@ CefRequestImpl::TransitionType CefRequestImpl::GetTransitionType() { return transition_type_; } +uint64 CefRequestImpl::GetIdentifier() { + base::AutoLock lock_scope(lock_); + return identifier_; +} + void CefRequestImpl::Set(net::URLRequest* request) { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); @@ -206,6 +212,7 @@ void CefRequestImpl::Set(net::URLRequest* request) { url_ = request->url().spec(); method_ = request->method(); first_party_for_cookies_ = request->first_party_for_cookies().spec(); + identifier_ = request->identifier(); net::HttpRequestHeaders headers = request->extra_request_headers(); diff --git a/libcef/common/request_impl.h b/libcef/common/request_impl.h index 6e6ac9053..a814fc778 100644 --- a/libcef/common/request_impl.h +++ b/libcef/common/request_impl.h @@ -48,6 +48,7 @@ class CefRequestImpl : public CefRequest { void SetFirstPartyForCookies(const CefString& url) override; ResourceType GetResourceType() override; TransitionType GetTransitionType() override; + uint64 GetIdentifier() override; // Populate this object from the URLRequest object. void Set(net::URLRequest* request); @@ -77,6 +78,7 @@ class CefRequestImpl : public CefRequest { HeaderMap headermap_; ResourceType resource_type_; TransitionType transition_type_; + uint64 identifier_; // The below members are used by CefURLRequest. int flags_; diff --git a/libcef/common/response_impl.cc b/libcef/common/response_impl.cc index 6cd79ec14..e506f66e6 100644 --- a/libcef/common/response_impl.cc +++ b/libcef/common/response_impl.cc @@ -10,6 +10,7 @@ #include "base/strings/stringprintf.h" #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" +#include "net/url_request/url_request.h" #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" @@ -152,6 +153,7 @@ net::HttpResponseHeaders* CefResponseImpl::GetResponseHeaders() { void CefResponseImpl::SetResponseHeaders( const net::HttpResponseHeaders& headers) { base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); header_map_.empty(); @@ -166,6 +168,8 @@ void CefResponseImpl::SetResponseHeaders( std::string mime_type; if (headers.GetMimeType(&mime_type)) mime_type_ = mime_type; + else + mime_type_.clear(); } void CefResponseImpl::Set(const blink::WebURLResponse& response) { @@ -199,6 +203,14 @@ void CefResponseImpl::Set(const blink::WebURLResponse& response) { response.visitHTTPHeaderFields(&visitor); } +void CefResponseImpl::Set(const net::URLRequest* request) { + DCHECK(request); + + const net::HttpResponseHeaders* headers = request->response_headers(); + if (headers) + SetResponseHeaders(*headers); +} + void CefResponseImpl::SetReadOnly(bool read_only) { base::AutoLock lock_scope(lock_); read_only_ = read_only; diff --git a/libcef/common/response_impl.h b/libcef/common/response_impl.h index df78a0c11..e8bc0c1ff 100644 --- a/libcef/common/response_impl.h +++ b/libcef/common/response_impl.h @@ -12,6 +12,7 @@ namespace net { class HttpResponseHeaders; +class URLRequest; } namespace blink { @@ -39,6 +40,7 @@ class CefResponseImpl : public CefResponse { void SetResponseHeaders(const net::HttpResponseHeaders& headers); void Set(const blink::WebURLResponse& response); + void Set(const net::URLRequest* request); void SetReadOnly(bool read_only); diff --git a/libcef_dll/cpptoc/request_cpptoc.cc b/libcef_dll/cpptoc/request_cpptoc.cc index 1768e3950..ecab5fb7b 100644 --- a/libcef_dll/cpptoc/request_cpptoc.cc +++ b/libcef_dll/cpptoc/request_cpptoc.cc @@ -309,6 +309,20 @@ cef_transition_type_t CEF_CALLBACK request_get_transition_type( return _retval; } +uint64 CEF_CALLBACK request_get_identifier(struct _cef_request_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + uint64 _retval = CefRequestCppToC::Get(self)->GetIdentifier(); + + // Return type: simple + return _retval; +} + // CONSTRUCTOR - Do not edit by hand. @@ -332,6 +346,7 @@ CefRequestCppToC::CefRequestCppToC(CefRequest* cls) request_set_first_party_for_cookies; struct_.struct_.get_resource_type = request_get_resource_type; struct_.struct_.get_transition_type = request_get_transition_type; + struct_.struct_.get_identifier = request_get_identifier; } #ifndef NDEBUG diff --git a/libcef_dll/cpptoc/request_handler_cpptoc.cc b/libcef_dll/cpptoc/request_handler_cpptoc.cc index 3448e2404..b214a391d 100644 --- a/libcef_dll/cpptoc/request_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/request_handler_cpptoc.cc @@ -18,6 +18,7 @@ #include "libcef_dll/ctocpp/frame_ctocpp.h" #include "libcef_dll/ctocpp/quota_callback_ctocpp.h" #include "libcef_dll/ctocpp/request_ctocpp.h" +#include "libcef_dll/ctocpp/response_ctocpp.h" #include "libcef_dll/ctocpp/sslinfo_ctocpp.h" #include "libcef_dll/ctocpp/web_plugin_info_ctocpp.h" @@ -155,7 +156,7 @@ struct _cef_resource_handler_t* CEF_CALLBACK request_handler_get_resource_handle void CEF_CALLBACK request_handler_on_resource_redirect( struct _cef_request_handler_t* self, cef_browser_t* browser, - cef_frame_t* frame, const cef_string_t* old_url, cef_string_t* new_url) { + cef_frame_t* frame, cef_request_t* request, cef_string_t* new_url) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING DCHECK(self); @@ -169,9 +170,9 @@ void CEF_CALLBACK request_handler_on_resource_redirect( DCHECK(frame); if (!frame) return; - // Verify param: old_url; type: string_byref_const - DCHECK(old_url); - if (!old_url) + // Verify param: request; type: refptr_diff + DCHECK(request); + if (!request) return; // Verify param: new_url; type: string_byref DCHECK(new_url); @@ -185,10 +186,47 @@ void CEF_CALLBACK request_handler_on_resource_redirect( CefRequestHandlerCppToC::Get(self)->OnResourceRedirect( CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame), - CefString(old_url), + CefRequestCToCpp::Wrap(request), new_urlStr); } +int CEF_CALLBACK request_handler_on_resource_response( + struct _cef_request_handler_t* self, cef_browser_t* browser, + cef_frame_t* frame, cef_request_t* request, + struct _cef_response_t* response) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) + return 0; + // Verify param: frame; type: refptr_diff + DCHECK(frame); + if (!frame) + return 0; + // Verify param: request; type: refptr_diff + DCHECK(request); + if (!request) + return 0; + // Verify param: response; type: refptr_diff + DCHECK(response); + if (!response) + return 0; + + // Execute + bool _retval = CefRequestHandlerCppToC::Get(self)->OnResourceResponse( + CefBrowserCToCpp::Wrap(browser), + CefFrameCToCpp::Wrap(frame), + CefRequestCToCpp::Wrap(request), + CefResponseCToCpp::Wrap(response)); + + // Return type: bool + return _retval; +} + int CEF_CALLBACK request_handler_get_auth_credentials( struct _cef_request_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, int isProxy, const cef_string_t* host, int port, @@ -445,6 +483,7 @@ CefRequestHandlerCppToC::CefRequestHandlerCppToC(CefRequestHandler* cls) request_handler_on_before_resource_load; struct_.struct_.get_resource_handler = request_handler_get_resource_handler; struct_.struct_.on_resource_redirect = request_handler_on_resource_redirect; + struct_.struct_.on_resource_response = request_handler_on_resource_response; struct_.struct_.get_auth_credentials = request_handler_get_auth_credentials; struct_.struct_.on_quota_request = request_handler_on_quota_request; struct_.struct_.on_protocol_execution = request_handler_on_protocol_execution; diff --git a/libcef_dll/ctocpp/request_ctocpp.cc b/libcef_dll/ctocpp/request_ctocpp.cc index bac0dd35a..5371cbdea 100644 --- a/libcef_dll/ctocpp/request_ctocpp.cc +++ b/libcef_dll/ctocpp/request_ctocpp.cc @@ -295,6 +295,19 @@ CefRequest::TransitionType CefRequestCToCpp::GetTransitionType() { return _retval; } +uint64 CefRequestCToCpp::GetIdentifier() { + if (CEF_MEMBER_MISSING(struct_, get_identifier)) + return 0; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + uint64 _retval = struct_->get_identifier(struct_); + + // Return type: simple + return _retval; +} + #ifndef NDEBUG template<> base::AtomicRefCount CefCToCpp CefRequestHandlerCToCpp::GetResourceHandler( } void CefRequestHandlerCToCpp::OnResourceRedirect(CefRefPtr browser, - CefRefPtr frame, const CefString& old_url, CefString& new_url) { + CefRefPtr frame, CefRefPtr request, + CefString& new_url) { if (CEF_MEMBER_MISSING(struct_, on_resource_redirect)) return; @@ -166,19 +168,55 @@ void CefRequestHandlerCToCpp::OnResourceRedirect(CefRefPtr browser, DCHECK(frame.get()); if (!frame.get()) return; - // Verify param: old_url; type: string_byref_const - DCHECK(!old_url.empty()); - if (old_url.empty()) + // Verify param: request; type: refptr_diff + DCHECK(request.get()); + if (!request.get()) return; // Execute struct_->on_resource_redirect(struct_, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame), - old_url.GetStruct(), + CefRequestCppToC::Wrap(request), new_url.GetWritableStruct()); } +bool CefRequestHandlerCToCpp::OnResourceResponse(CefRefPtr browser, + CefRefPtr frame, CefRefPtr request, + CefRefPtr response) { + if (CEF_MEMBER_MISSING(struct_, on_resource_response)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) + return false; + // Verify param: frame; type: refptr_diff + DCHECK(frame.get()); + if (!frame.get()) + return false; + // Verify param: request; type: refptr_diff + DCHECK(request.get()); + if (!request.get()) + return false; + // Verify param: response; type: refptr_diff + DCHECK(response.get()); + if (!response.get()) + return false; + + // Execute + int _retval = struct_->on_resource_response(struct_, + CefBrowserCppToC::Wrap(browser), + CefFrameCppToC::Wrap(frame), + CefRequestCppToC::Wrap(request), + CefResponseCppToC::Wrap(response)); + + // Return type: bool + return _retval?true:false; +} + bool CefRequestHandlerCToCpp::GetAuthCredentials(CefRefPtr browser, CefRefPtr frame, bool isProxy, const CefString& host, int port, const CefString& realm, const CefString& scheme, diff --git a/libcef_dll/ctocpp/request_handler_ctocpp.h b/libcef_dll/ctocpp/request_handler_ctocpp.h index 4462bd876..5cb9ef68e 100644 --- a/libcef_dll/ctocpp/request_handler_ctocpp.h +++ b/libcef_dll/ctocpp/request_handler_ctocpp.h @@ -44,8 +44,11 @@ class CefRequestHandlerCToCpp CefRefPtr browser, CefRefPtr frame, CefRefPtr request) override; void OnResourceRedirect(CefRefPtr browser, - CefRefPtr frame, const CefString& old_url, + CefRefPtr frame, CefRefPtr request, CefString& new_url) override; + bool OnResourceResponse(CefRefPtr browser, + CefRefPtr frame, CefRefPtr request, + CefRefPtr response) override; bool GetAuthCredentials(CefRefPtr browser, CefRefPtr frame, bool isProxy, const CefString& host, int port, const CefString& realm, const CefString& scheme, diff --git a/patch/patch.cfg b/patch/patch.cfg index 16688af44..fb25d9f86 100644 --- a/patch/patch.cfg +++ b/patch/patch.cfg @@ -136,6 +136,13 @@ patches = [ 'name': 'net_proxy_462624', 'path': '../net/proxy/', }, + { + # Make URLRequest::set_is_pending() public so that it can be called from + # CefRequestInterceptor::MaybeInterceptResponse(). + # https://code.google.com/p/chromiumembedded/issues/detail?id=1327 + 'name': 'net_urlrequest_1327', + 'path': '../net/url_request/', + }, { # Disable scollbar bounce and overlay on OS X. # http://code.google.com/p/chromiumembedded/issues/detail?id=364 diff --git a/patch/patches/net_urlrequest_1327.patch b/patch/patches/net_urlrequest_1327.patch new file mode 100644 index 000000000..039967980 --- /dev/null +++ b/patch/patches/net_urlrequest_1327.patch @@ -0,0 +1,16 @@ +diff --git url_request.h url_request.h +index 1623368..16d292d 100644 +--- url_request.h ++++ url_request.h +@@ -609,10 +609,10 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), + return proxy_server_; + } + +- protected: + // Allow the URLRequestJob class to control the is_pending() flag. + void set_is_pending(bool value) { is_pending_ = value; } + ++ protected: + // Allow the URLRequestJob class to set our status too + void set_status(const URLRequestStatus& value) { status_ = value; } + diff --git a/tests/unittests/navigation_unittest.cc b/tests/unittests/navigation_unittest.cc index ea46345c9..827a6bbaa 100644 --- a/tests/unittests/navigation_unittest.cc +++ b/tests/unittests/navigation_unittest.cc @@ -762,10 +762,11 @@ class RedirectTestHandler : public TestHandler { void OnResourceRedirect(CefRefPtr browser, CefRefPtr frame, - const CefString& old_url, + CefRefPtr request, CefString& new_url) override { // Should be called for each redirected URL. + const std::string& old_url = request->GetURL(); if (old_url == kRNav1 && new_url == kRNav2) { // Called due to the nav1 redirect response. got_nav1_redirect_.yes(); @@ -1670,8 +1671,14 @@ class LoadNavTestHandler : public TestHandler { CefMouseEvent mouse_event; mouse_event.x = 20; mouse_event.y = 20; +#if defined(OS_MACOSX) + // Use cmd instead of ctrl on OS X. + mouse_event.modifiers = + (mode_ == CTRL_LEFT_CLICK ? EVENTFLAG_COMMAND_DOWN : 0); +#else mouse_event.modifiers = (mode_ == CTRL_LEFT_CLICK ? EVENTFLAG_CONTROL_DOWN : 0); +#endif cef_mouse_button_type_t button_type = (mode_ == MIDDLE_CLICK ? MBT_MIDDLE : MBT_LEFT); diff --git a/tests/unittests/request_handler_unittest.cc b/tests/unittests/request_handler_unittest.cc index 580179621..95a7c7681 100644 --- a/tests/unittests/request_handler_unittest.cc +++ b/tests/unittests/request_handler_unittest.cc @@ -10,10 +10,12 @@ #include "include/base/cef_bind.h" #include "include/cef_cookie.h" #include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_stream_resource_handler.h" #include "testing/gtest/include/gtest/gtest.h" #include "tests/cefclient/browser/client_app_browser.h" #include "tests/cefclient/renderer/client_app_renderer.h" #include "tests/unittests/test_handler.h" +#include "tests/unittests/test_util.h" using client::ClientAppBrowser; using client::ClientAppRenderer; @@ -509,6 +511,413 @@ TEST(RequestHandlerTest, NotificationsCrossOriginDelayedBrowser) { } +namespace { + +const char kResourceTestHtml[] = "http://test.com/resource.html"; + +class ResourceResponseTest : public TestHandler { + public: + enum TestMode { + URL, + HEADER, + POST, + }; + + explicit ResourceResponseTest(TestMode mode) + : browser_id_(0), + main_request_id_(0U), + sub_request_id_(0U) { + if (mode == URL) + resource_test_.reset(new UrlResourceTest); + else if (mode == HEADER) + resource_test_.reset(new HeaderResourceTest); + else + resource_test_.reset(new PostResourceTest); + } + + void RunTest() override { + AddResource(kResourceTestHtml, GetHtml(), "text/html"); + CreateBrowser(kResourceTestHtml); + SetTestTimeout(); + } + + bool OnBeforeBrowse(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + bool is_redirect) override { + EXPECT_UI_THREAD(); + EXPECT_EQ(0, browser_id_); + browser_id_ = browser->GetIdentifier(); + EXPECT_GT(browser_id_, 0); + + // This method is only called for the main resource. + EXPECT_STREQ(kResourceTestHtml, request->GetURL().ToString().c_str()); + + // All loads of the main resource should keep the same request id. + EXPECT_EQ(0U, main_request_id_); + main_request_id_ = request->GetIdentifier(); + EXPECT_GT(main_request_id_, 0U); + + return false; + } + + bool OnBeforeResourceLoad(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override { + EXPECT_IO_THREAD(); + EXPECT_EQ(browser_id_, browser->GetIdentifier()); + + if (request->GetURL() == kResourceTestHtml) { + EXPECT_EQ(main_request_id_, request->GetIdentifier()); + return false; + } + + // All redirects of the sub-resource should keep the same request id. + if (sub_request_id_ == 0U) { + sub_request_id_ = request->GetIdentifier(); + EXPECT_GT(sub_request_id_, 0U); + } else { + EXPECT_EQ(sub_request_id_, request->GetIdentifier()); + } + + return resource_test_->OnBeforeResourceLoad(browser, frame, request); + } + + CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) override { + EXPECT_IO_THREAD(); + EXPECT_EQ(browser_id_, browser->GetIdentifier()); + + if (request->GetURL() == kResourceTestHtml) { + EXPECT_EQ(main_request_id_, request->GetIdentifier()); + return TestHandler::GetResourceHandler(browser, frame, request); + } + + EXPECT_EQ(sub_request_id_, request->GetIdentifier()); + return resource_test_->GetResourceHandler(browser, frame, request); + } + + void OnResourceRedirect(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefString& new_url) override { + EXPECT_IO_THREAD(); + EXPECT_EQ(browser_id_, browser->GetIdentifier()); + EXPECT_EQ(sub_request_id_, request->GetIdentifier()); + + resource_test_->OnResourceRedirect(browser, frame, request, new_url); + } + + bool OnResourceResponse(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) override { + EXPECT_IO_THREAD(); + EXPECT_TRUE(browser.get()); + EXPECT_EQ(browser_id_, browser->GetIdentifier()); + + EXPECT_TRUE(frame.get()); + EXPECT_TRUE(frame->IsMain()); + + if (request->GetURL() == kResourceTestHtml) { + EXPECT_EQ(main_request_id_, request->GetIdentifier()); + return false; + } + + EXPECT_EQ(sub_request_id_, request->GetIdentifier()); + return resource_test_->OnResourceResponse(browser, frame, request, + response); + } + + void OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, + int httpStatusCode) override { + EXPECT_UI_THREAD(); + EXPECT_EQ(browser_id_, browser->GetIdentifier()); + + TestHandler::OnLoadEnd(browser, frame, httpStatusCode); + DestroyTest(); + } + + void DestroyTest() override { + resource_test_->CheckExpected(); + resource_test_.reset(NULL); + + TestHandler::DestroyTest(); + } + + private: + std::string GetHtml() const { + std::stringstream html; + html << ""; + + const std::string& url = resource_test_->start_url(); + html << ""; + + html << "

Main

"; + return html.str(); + } + + class ResourceTest { + public: + ResourceTest(const std::string& start_url, + size_t expected_resource_response_ct = 2U, + size_t expected_before_resource_load_ct = 1U, + size_t expected_resource_redirect_ct = 0U) + : start_url_(start_url), + resource_response_ct_(0U), + expected_resource_response_ct_(expected_resource_response_ct), + before_resource_load_ct_(0), + expected_before_resource_load_ct_(expected_before_resource_load_ct), + get_resource_handler_ct_(0U), + resource_redirect_ct_(0U), + expected_resource_redirect_ct_(expected_resource_redirect_ct) { + } + virtual ~ResourceTest() { + } + + const std::string& start_url() const { + return start_url_; + } + + virtual bool OnBeforeResourceLoad(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) { + before_resource_load_ct_++; + return false; + } + + virtual CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) { + get_resource_handler_ct_++; + + const std::string& js_content = ""; + + CefRefPtr stream = + CefStreamReader::CreateForData(const_cast(js_content.c_str()), + js_content.size()); + + return new CefStreamResourceHandler(200, "OK", "text/javascript", + CefResponse::HeaderMap(), stream); + } + + virtual void OnResourceRedirect(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefString& new_url) { + resource_redirect_ct_++; + } + + bool OnResourceResponse(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) { + EXPECT_TRUE(CheckUrl(request->GetURL())); + + // Verify the response returned by GetResourceHandler. + EXPECT_EQ(200, response->GetStatus()); + EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str()); + EXPECT_STREQ("text/javascript", + response->GetMimeType().ToString().c_str()); + + if (resource_response_ct_++ == 0U) { + // Always redirect at least one time. + OnResourceReceived(browser, frame, request, response); + return true; + } + + OnRetryReceived(browser, frame, request, response); + return (resource_response_ct_ < expected_resource_response_ct_); + } + + virtual bool CheckUrl(const std::string& url) const { + return (url == start_url_); + } + + virtual void CheckExpected() { + EXPECT_TRUE(got_resource_); + EXPECT_TRUE(got_resource_retry_); + + EXPECT_EQ(expected_resource_response_ct_, resource_response_ct_); + EXPECT_EQ(expected_resource_response_ct_, get_resource_handler_ct_); + EXPECT_EQ(expected_before_resource_load_ct_, before_resource_load_ct_); + EXPECT_EQ(expected_resource_redirect_ct_, resource_redirect_ct_); + } + + protected: + virtual void OnResourceReceived(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) { + got_resource_.yes(); + } + + virtual void OnRetryReceived(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) { + got_resource_retry_.yes(); + } + + private: + std::string start_url_; + + size_t resource_response_ct_; + size_t expected_resource_response_ct_; + size_t before_resource_load_ct_; + size_t expected_before_resource_load_ct_; + size_t get_resource_handler_ct_; + size_t resource_redirect_ct_; + size_t expected_resource_redirect_ct_; + + TrackCallback got_resource_; + TrackCallback got_resource_retry_; + }; + + class UrlResourceTest : public ResourceTest { + public: + UrlResourceTest() + : ResourceTest("http://test.com/start_url.js", 3U, 2U, 1U) { + redirect_url_ = "http://test.com/redirect_url.js"; + } + + bool CheckUrl(const std::string& url) const override { + if (url == redirect_url_) + return true; + + return ResourceTest::CheckUrl(url); + } + + void OnResourceRedirect(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefString& new_url) override { + ResourceTest::OnResourceRedirect(browser, frame, request, new_url); + const std::string& old_url = request->GetURL(); + EXPECT_STREQ(start_url().c_str(), old_url.c_str()); + EXPECT_STREQ(redirect_url_.c_str(), new_url.ToString().c_str()); + } + + private: + void OnResourceReceived(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) override { + ResourceTest::OnResourceReceived(browser, frame, request, response); + request->SetURL(redirect_url_); + } + + void OnRetryReceived(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) override { + ResourceTest::OnRetryReceived(browser, frame, request, response); + const std::string& new_url = request->GetURL(); + EXPECT_STREQ(redirect_url_.c_str(), new_url.c_str()); + } + + std::string redirect_url_; + }; + + class HeaderResourceTest : public ResourceTest { + public: + HeaderResourceTest() + : ResourceTest("http://test.com/start_header.js") { + expected_headers_.insert(std::make_pair("Test-Key1", "Value1")); + expected_headers_.insert(std::make_pair("Test-Key2", "Value2")); + } + + private: + void OnResourceReceived(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) override { + ResourceTest::OnResourceReceived(browser, frame, request, response); + request->SetHeaderMap(expected_headers_); + } + + void OnRetryReceived(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) override { + ResourceTest::OnRetryReceived(browser, frame, request, response); + CefRequest::HeaderMap actual_headers; + request->GetHeaderMap(actual_headers); + TestMapEqual(expected_headers_, actual_headers, true); + } + + CefRequest::HeaderMap expected_headers_; + }; + + class PostResourceTest : public ResourceTest { + public: + PostResourceTest() + : ResourceTest("http://test.com/start_post.js") { + CefRefPtr elem = CefPostDataElement::Create(); + const std::string data("Test Post Data"); + elem->SetToBytes(data.size(), data.c_str()); + + expected_post_ = CefPostData::Create(); + expected_post_->AddElement(elem); + } + + private: + void OnResourceReceived(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) override { + ResourceTest::OnResourceReceived(browser, frame, request, response); + request->SetPostData(expected_post_); + } + + void OnRetryReceived(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr response) override { + ResourceTest::OnRetryReceived(browser, frame, request, response); + CefRefPtr actual_post = request->GetPostData(); + TestPostDataEqual(expected_post_, actual_post); + } + + CefRefPtr expected_post_; + }; + + int browser_id_; + uint64 main_request_id_; + uint64 sub_request_id_; + scoped_ptr resource_test_; +}; + +} // namespace + +TEST(RequestHandlerTest, ResourceResponseURL) { + CefRefPtr handler = + new ResourceResponseTest(ResourceResponseTest::URL); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +TEST(RequestHandlerTest, ResourceResponseHeader) { + CefRefPtr handler = + new ResourceResponseTest(ResourceResponseTest::HEADER); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +TEST(RequestHandlerTest, ResourceResponsePost) { + CefRefPtr handler = + new ResourceResponseTest(ResourceResponseTest::POST); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + + // Entry point for creating request handler browser test objects. // Called from client_app_delegates.cc. void CreateRequestHandlerBrowserTests( diff --git a/tests/unittests/request_unittest.cc b/tests/unittests/request_unittest.cc index 270be8e6b..b44e57797 100644 --- a/tests/unittests/request_unittest.cc +++ b/tests/unittests/request_unittest.cc @@ -21,7 +21,8 @@ using client::ClientAppRenderer; TEST(RequestTest, SetGet) { // CefRequest CreateRequest CefRefPtr request(CefRequest::Create()); - ASSERT_TRUE(request.get() != NULL); + EXPECT_TRUE(request.get() != NULL); + EXPECT_EQ(0U, request->GetIdentifier()); CefString url = "http://tests/run.html"; CefString method = "POST"; @@ -31,45 +32,45 @@ TEST(RequestTest, SetGet) { // CefPostData CreatePostData CefRefPtr postData(CefPostData::Create()); - ASSERT_TRUE(postData.get() != NULL); + EXPECT_TRUE(postData.get() != NULL); // CefPostDataElement CreatePostDataElement CefRefPtr element1(CefPostDataElement::Create()); - ASSERT_TRUE(element1.get() != NULL); + EXPECT_TRUE(element1.get() != NULL); CefRefPtr element2(CefPostDataElement::Create()); - ASSERT_TRUE(element2.get() != NULL); + EXPECT_TRUE(element2.get() != NULL); // CefPostDataElement SetToFile CefString file = "c:\\path\\to\\file.ext"; element1->SetToFile(file); - ASSERT_EQ(PDE_TYPE_FILE, element1->GetType()); - ASSERT_EQ(file, element1->GetFile()); + EXPECT_EQ(PDE_TYPE_FILE, element1->GetType()); + EXPECT_EQ(file, element1->GetFile()); // CefPostDataElement SetToBytes char bytes[] = "Test Bytes"; element2->SetToBytes(sizeof(bytes), bytes); - ASSERT_EQ(PDE_TYPE_BYTES, element2->GetType()); - ASSERT_EQ(sizeof(bytes), element2->GetBytesCount()); + EXPECT_EQ(PDE_TYPE_BYTES, element2->GetType()); + EXPECT_EQ(sizeof(bytes), element2->GetBytesCount()); char bytesOut[sizeof(bytes)]; element2->GetBytes(sizeof(bytes), bytesOut); - ASSERT_TRUE(!memcmp(bytes, bytesOut, sizeof(bytes))); + EXPECT_TRUE(!memcmp(bytes, bytesOut, sizeof(bytes))); // CefPostData AddElement postData->AddElement(element1); postData->AddElement(element2); - ASSERT_EQ((size_t)2, postData->GetElementCount()); + EXPECT_EQ((size_t)2, postData->GetElementCount()); // CefPostData RemoveElement postData->RemoveElement(element1); - ASSERT_EQ((size_t)1, postData->GetElementCount()); + EXPECT_EQ((size_t)1, postData->GetElementCount()); // CefPostData RemoveElements postData->RemoveElements(); - ASSERT_EQ((size_t)0, postData->GetElementCount()); + EXPECT_EQ((size_t)0, postData->GetElementCount()); postData->AddElement(element1); postData->AddElement(element2); - ASSERT_EQ((size_t)2, postData->GetElementCount()); + EXPECT_EQ((size_t)2, postData->GetElementCount()); CefPostData::ElementVector elements; postData->GetElements(elements); CefPostData::ElementVector::const_iterator it = elements.begin(); @@ -82,11 +83,11 @@ TEST(RequestTest, SetGet) { // CefRequest SetURL request->SetURL(url); - ASSERT_EQ(url, request->GetURL()); + EXPECT_EQ(url, request->GetURL()); // CefRequest SetMethod request->SetMethod(method); - ASSERT_EQ(method, request->GetMethod()); + EXPECT_EQ(method, request->GetMethod()); // CefRequest SetHeaderMap request->SetHeaderMap(setHeaders); @@ -98,13 +99,17 @@ TEST(RequestTest, SetGet) { request->SetPostData(postData); TestPostDataEqual(postData, request->GetPostData()); + EXPECT_EQ(0U, request->GetIdentifier()); + request = CefRequest::Create(); - ASSERT_TRUE(request.get() != NULL); + EXPECT_TRUE(request.get() != NULL); + EXPECT_EQ(0U, request->GetIdentifier()); // CefRequest Set request->Set(url, method, postData, setHeaders); - ASSERT_EQ(url, request->GetURL()); - ASSERT_EQ(method, request->GetMethod()); + EXPECT_EQ(0U, request->GetIdentifier()); + EXPECT_EQ(url, request->GetURL()); + EXPECT_EQ(method, request->GetMethod()); request->GetHeaderMap(getHeaders); TestMapEqual(setHeaders, getHeaders, false); getHeaders.clear(); @@ -115,7 +120,7 @@ namespace { void CreateRequest(CefRefPtr& request) { request = CefRequest::Create(); - ASSERT_TRUE(request.get() != NULL); + EXPECT_TRUE(request.get() != NULL); request->SetURL("http://tests/run.html"); request->SetMethod("POST"); @@ -126,11 +131,11 @@ void CreateRequest(CefRefPtr& request) { request->SetHeaderMap(headers); CefRefPtr postData(CefPostData::Create()); - ASSERT_TRUE(postData.get() != NULL); + EXPECT_TRUE(postData.get() != NULL); CefRefPtr element1( CefPostDataElement::Create()); - ASSERT_TRUE(element1.get() != NULL); + EXPECT_TRUE(element1.get() != NULL); char bytes[] = "Test Bytes"; element1->SetToBytes(sizeof(bytes), bytes); postData->AddElement(element1); @@ -204,8 +209,8 @@ TEST(RequestTest, SendRecv) { new RequestSendRecvTestHandler(); handler->ExecuteTest(); - ASSERT_TRUE(handler->got_before_resource_load_); - ASSERT_TRUE(handler->got_resource_handler_); + EXPECT_TRUE(handler->got_before_resource_load_); + EXPECT_TRUE(handler->got_resource_handler_); ReleaseAndWaitForDestructor(handler); }