diff --git a/cef.gyp b/cef.gyp index 050e48fb3..6ea85aeed 100644 --- a/cef.gyp +++ b/cef.gyp @@ -1264,6 +1264,7 @@ 'tests/unittests/navigation_unittest.cc', 'tests/unittests/process_message_unittest.cc', 'tests/unittests/request_handler_unittest.cc', + 'tests/unittests/request_unittest.cc', 'tests/unittests/scheme_handler_unittest.cc', 'tests/unittests/urlrequest_unittest.cc', 'tests/unittests/test_handler.cc', diff --git a/include/capi/cef_request_capi.h b/include/capi/cef_request_capi.h index da895f362..5b3983798 100644 --- a/include/capi/cef_request_capi.h +++ b/include/capi/cef_request_capi.h @@ -142,6 +142,21 @@ typedef struct _cef_request_t { /// void (CEF_CALLBACK *set_first_party_for_cookies)(struct _cef_request_t* self, const cef_string_t* url); + + /// + // Get the resource type for this request. Accurate resource type information + // may only be available in the browser process. + /// + enum cef_resource_type_t (CEF_CALLBACK *get_resource_type)( + struct _cef_request_t* self); + + /// + // Get the transition type for this request. Only available in the browser + // process and only applies to requests that represent a main frame or sub- + // frame navigation. + /// + enum cef_transition_type_t (CEF_CALLBACK *get_transition_type)( + struct _cef_request_t* self); } cef_request_t; diff --git a/include/cef_request.h b/include/cef_request.h index 7ac4cb027..da9ec4304 100644 --- a/include/cef_request.h +++ b/include/cef_request.h @@ -53,6 +53,8 @@ class CefPostDataElement; class CefRequest : public virtual CefBase { public: typedef std::multimap HeaderMap; + typedef cef_resource_type_t ResourceType; + typedef cef_transition_type_t TransitionType; /// // Create a new CefRequest object. @@ -151,6 +153,21 @@ class CefRequest : public virtual CefBase { /// /*--cef()--*/ virtual void SetFirstPartyForCookies(const CefString& url) =0; + + /// + // Get the resource type for this request. Accurate resource type information + // may only be available in the browser process. + /// + /*--cef(default_retval=RT_SUB_RESOURCE)--*/ + virtual ResourceType GetResourceType() =0; + + /// + // Get the transition type for this request. Only available in the browser + // process and only applies to requests that represent a main frame or + // sub-frame navigation. + /// + /*--cef(default_retval=TT_EXPLICIT)--*/ + virtual TransitionType GetTransitionType() =0; }; diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 97a7db2c0..89a347d10 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -793,6 +793,183 @@ enum cef_postdataelement_type_t { PDE_TYPE_FILE, }; +/// +// Resource type for a request. +/// +enum cef_resource_type_t { + /// + // Top level page. + /// + RT_MAIN_FRAME = 0, + + /// + // Frame or iframe. + /// + RT_SUB_FRAME, + + /// + // CSS stylesheet. + /// + RT_STYLESHEET, + + /// + // External script. + /// + RT_SCRIPT, + + /// + // Image (jpg/gif/png/etc). + /// + RT_IMAGE, + + /// + // Font. + /// + RT_FONT_RESOURCE, + + /// + // Some other subresource. This is the default type if the actual type is + // unknown. + /// + RT_SUB_RESOURCE, + + /// + // Object (or embed) tag for a plugin, or a resource that a plugin requested. + /// + RT_OBJECT, + + /// + // Media resource. + /// + RT_MEDIA, + + /// + // Main resource of a dedicated worker. + /// + RT_WORKER, + + /// + // Main resource of a shared worker. + /// + RT_SHARED_WORKER, + + /// + // Explicitly requested prefetch. + /// + RT_PREFETCH, + + /// + // Favicon. + /// + RT_FAVICON, + + /// + // XMLHttpRequest. + /// + RT_XHR, +}; + +/// +// Transition type for a request. Made up of one source value and 0 or more +// qualifiers. +/// +enum cef_transition_type_t { + /// + // Source is a link click or the JavaScript window.open function. This is + // also the default value for requests like sub-resource loads that are not + // navigations. + /// + TT_LINK = 0, + + /// + // Source is some other "explicit" navigation action such as creating a new + // browser or using the LoadURL function. This is also the default value + // for navigations where the actual type is unknown. + /// + TT_EXPLICIT = 1, + + /// + // Source is a subframe navigation. This is any content that is automatically + // loaded in a non-toplevel frame. For example, if a page consists of several + // frames containing ads, those ad URLs will have this transition type. + // The user may not even realize the content in these pages is a separate + // frame, so may not care about the URL. + /// + TT_AUTO_SUBFRAME = 3, + + /// + // Source is a subframe navigation explicitly requested by the user that will + // generate new navigation entries in the back/forward list. These are + // probably more important than frames that were automatically loaded in + // the background because the user probably cares about the fact that this + // link was loaded. + /// + TT_MANUAL_SUBFRAME = 4, + + /// + // Source is a form submission by the user. NOTE: In some situations + // submitting a form does not result in this transition type. This can happen + // if the form uses a script to submit the contents. + /// + TT_FORM_SUBMIT = 7, + + /// + // Source is a "reload" of the page via the Reload function or by re-visiting + // the same URL. NOTE: This is distinct from the concept of whether a + // particular load uses "reload semantics" (i.e. bypasses cached data). + /// + TT_RELOAD = 8, + + /// + // General mask defining the bits used for the source values. + /// + TT_SOURCE_MASK = 0xFF, + + // Qualifiers. + // Any of the core values above can be augmented by one or more qualifiers. + // These qualifiers further define the transition. + + /// + // Attempted to visit a URL but was blocked. + /// + TT_BLOCKED_FLAG = 0x00800000, + + /// + // Used the Forward or Back function to navigate among browsing history. + /// + TT_FORWARD_BACK_FLAG = 0x01000000, + + /// + // The beginning of a navigation chain. + /// + TT_CHAIN_START_FLAG = 0x10000000, + + /// + // The last transition in a redirect chain. + /// + TT_CHAIN_END_FLAG = 0x20000000, + + /// + // Redirects caused by JavaScript or a meta refresh tag on the page. + /// + TT_CLIENT_REDIRECT_FLAG = 0x40000000, + + /// + // Redirects sent from the server by HTTP headers. + /// + TT_SERVER_REDIRECT_FLAG = 0x80000000, + + /// + // Used to test whether a transition involves a redirect. + /// + TT_IS_REDIRECT_MASK = 0xC0000000, + + /// + // General mask defining the bits used for the qualifiers. + /// + TT_QUALIFIER_MASK = 0xFFFFFF00, +}; + /// // Flags used to customize the behavior of CefURLRequest. /// diff --git a/libcef/common/request_impl.cc b/libcef/common/request_impl.cc index 3e2de3698..57ab7014d 100644 --- a/libcef/common/request_impl.cc +++ b/libcef/common/request_impl.cc @@ -10,6 +10,7 @@ #include "libcef/common/task_runner_impl.h" #include "base/logging.h" +#include "content/public/browser/resource_request_info.h" #include "net/base/upload_data.h" #include "net/base/upload_data_stream.h" #include "net/base/upload_element_reader.h" @@ -22,6 +23,7 @@ #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebURLError.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" +#include "webkit/common/resource_type.h" namespace { @@ -103,6 +105,8 @@ CefRefPtr CefRequest::Create() { CefRequestImpl::CefRequestImpl() : method_("GET"), + resource_type_(RT_SUB_RESOURCE), + transition_type_(TT_EXPLICIT), flags_(UR_FLAG_NONE), read_only_(false) { } @@ -188,6 +192,16 @@ void CefRequestImpl::SetFirstPartyForCookies(const CefString& url) { first_party_for_cookies_ = url; } +CefRequestImpl::ResourceType CefRequestImpl::GetResourceType() { + AutoLock lock_scope(this); + return resource_type_; +} + +CefRequestImpl::TransitionType CefRequestImpl::GetTransitionType() { + AutoLock lock_scope(this); + return transition_type_; +} + void CefRequestImpl::Set(net::URLRequest* request) { AutoLock lock_scope(this); CHECK_READONLY_RETURN_VOID(); @@ -223,6 +237,18 @@ void CefRequestImpl::Set(net::URLRequest* request) { } else if (postdata_.get()) { postdata_ = NULL; } + + const content::ResourceRequestInfo* info = + content::ResourceRequestInfo::ForRequest(request); + if (info) { + resource_type_ = + static_cast(info->GetResourceType()); + transition_type_ = + static_cast(info->GetPageTransition()); + } else { + resource_type_ = RT_SUB_RESOURCE; + transition_type_ = TT_EXPLICIT; + } } void CefRequestImpl::Get(net::URLRequest* request) { @@ -291,6 +317,9 @@ void CefRequestImpl::Set(const WebKit::WebURLRequest& request) { flags_ |= UR_FLAG_REPORT_RAW_HEADERS; first_party_for_cookies_ = request.firstPartyForCookies().spec().utf16(); + + resource_type_ = static_cast( + ::ResourceType::FromTargetType(request.targetType())); } void CefRequestImpl::Get(WebKit::WebURLRequest& request) { diff --git a/libcef/common/request_impl.h b/libcef/common/request_impl.h index cba6461db..5830f69ee 100644 --- a/libcef/common/request_impl.h +++ b/libcef/common/request_impl.h @@ -45,6 +45,8 @@ class CefRequestImpl : public CefRequest { virtual void SetFlags(int flags) OVERRIDE; virtual CefString GetFirstPartyForCookies() OVERRIDE; virtual void SetFirstPartyForCookies(const CefString& url) OVERRIDE; + virtual ResourceType GetResourceType() OVERRIDE; + virtual TransitionType GetTransitionType() OVERRIDE; // Populate this object from the URLRequest object. void Set(net::URLRequest* request); @@ -72,6 +74,8 @@ class CefRequestImpl : public CefRequest { CefString method_; CefRefPtr postdata_; HeaderMap headermap_; + ResourceType resource_type_; + TransitionType transition_type_; // The below members are used by CefURLRequest. int flags_; diff --git a/libcef_dll/cpptoc/request_cpptoc.cc b/libcef_dll/cpptoc/request_cpptoc.cc index 5201bf57e..32fb7afe8 100644 --- a/libcef_dll/cpptoc/request_cpptoc.cc +++ b/libcef_dll/cpptoc/request_cpptoc.cc @@ -278,6 +278,37 @@ void CEF_CALLBACK request_set_first_party_for_cookies( CefString(url)); } +enum cef_resource_type_t CEF_CALLBACK request_get_resource_type( + struct _cef_request_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return RT_SUB_RESOURCE; + + // Execute + cef_resource_type_t _retval = CefRequestCppToC::Get(self)->GetResourceType(); + + // Return type: simple + return _retval; +} + +enum cef_transition_type_t CEF_CALLBACK request_get_transition_type( + struct _cef_request_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return TT_EXPLICIT; + + // Execute + cef_transition_type_t _retval = CefRequestCppToC::Get( + self)->GetTransitionType(); + + // Return type: simple + return _retval; +} + // CONSTRUCTOR - Do not edit by hand. @@ -299,6 +330,8 @@ CefRequestCppToC::CefRequestCppToC(CefRequest* cls) request_get_first_party_for_cookies; struct_.struct_.set_first_party_for_cookies = request_set_first_party_for_cookies; + struct_.struct_.get_resource_type = request_get_resource_type; + struct_.struct_.get_transition_type = request_get_transition_type; } #ifndef NDEBUG diff --git a/libcef_dll/ctocpp/request_ctocpp.cc b/libcef_dll/ctocpp/request_ctocpp.cc index 4921e3f2d..6af26bd60 100644 --- a/libcef_dll/ctocpp/request_ctocpp.cc +++ b/libcef_dll/ctocpp/request_ctocpp.cc @@ -269,6 +269,32 @@ void CefRequestCToCpp::SetFirstPartyForCookies(const CefString& url) { url.GetStruct()); } +CefRequest::ResourceType CefRequestCToCpp::GetResourceType() { + if (CEF_MEMBER_MISSING(struct_, get_resource_type)) + return RT_SUB_RESOURCE; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + cef_resource_type_t _retval = struct_->get_resource_type(struct_); + + // Return type: simple + return _retval; +} + +CefRequest::TransitionType CefRequestCToCpp::GetTransitionType() { + if (CEF_MEMBER_MISSING(struct_, get_transition_type)) + return TT_EXPLICIT; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + cef_transition_type_t _retval = struct_->get_transition_type(struct_); + + // Return type: simple + return _retval; +} + #ifndef NDEBUG template<> long CefCToCppGetURL(); EXPECT_STREQ(item.target, url.c_str()); + EXPECT_EQ(RT_SUB_RESOURCE, request->GetResourceType()); + EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); + if (item.action == NA_LOAD) EXPECT_EQ(NAVIGATION_OTHER, navigation_type); else if (item.action == NA_BACK || item.action == NA_FORWARD) @@ -216,6 +219,12 @@ class HistoryNavTestHandler : public TestHandler { CefRefPtr request) OVERRIDE { const NavListItem& item = kHNavList[nav_]; + EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); + if (item.action == NA_LOAD) + EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); + else if (item.action == NA_BACK || item.action == NA_FORWARD) + EXPECT_EQ(TT_EXPLICIT | TT_FORWARD_BACK_FLAG, request->GetTransitionType()); + got_before_resource_load_[nav_].yes(); std::string url = request->GetURL(); @@ -389,6 +398,9 @@ class FrameNameIdentNavTestHandler : public TestHandler { std::string url = request->GetURL(); if (url == kFNav1) { + EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); + EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); + frame1_ident_ = frame->GetIdentifier(); if (name == "") { frame1_name_ = name; @@ -397,6 +409,9 @@ class FrameNameIdentNavTestHandler : public TestHandler { if (!parent.get()) got_frame1_ident_parent_before_.yes(); } else if (url == kFNav2) { + EXPECT_EQ(RT_SUB_FRAME, request->GetResourceType()); + EXPECT_EQ(TT_LINK, request->GetTransitionType()); + frame2_ident_ = frame->GetIdentifier(); if (name == "nav2") { frame2_name_ = name; @@ -405,6 +420,9 @@ class FrameNameIdentNavTestHandler : public TestHandler { if (parent.get() && frame1_ident_ == parent->GetIdentifier()) got_frame2_ident_parent_before_.yes(); } else if (url == kFNav3) { + EXPECT_EQ(RT_SUB_FRAME, request->GetResourceType()); + EXPECT_EQ(TT_LINK, request->GetTransitionType()); + frame3_ident_ = frame->GetIdentifier(); if (name == "-->") { frame3_name_ = name; @@ -648,6 +666,9 @@ class RedirectTestHandler : public TestHandler { // Should be called for all but the second URL. std::string url = request->GetURL(); + EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); + EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); + if (url == kRNav1) { got_nav1_before_resource_load_.yes(); } else if (url == kRNav3) { @@ -921,6 +942,9 @@ class OrderNavRendererTest : public ClientApp::RenderDelegate { EXPECT_TRUE(got_render_thread_created_); EXPECT_TRUE(got_webkit_initialized_); + EXPECT_EQ(RT_SUB_RESOURCE, request->GetResourceType()); + EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); + if (browser->IsPopup()) { EXPECT_TRUE(got_browser_created_popup_); EXPECT_FALSE(got_before_navigation_popup_); @@ -1038,10 +1062,14 @@ class OrderNavTestHandler : public TestHandler { virtual bool OnBeforeResourceLoad(CefRefPtr browser, CefRefPtr frame, CefRefPtr request) OVERRIDE { + EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); + if (browser->IsPopup()) { + EXPECT_EQ(TT_LINK, request->GetTransitionType()); EXPECT_GT(browser->GetIdentifier(), 0); EXPECT_EQ(browser_id_popup_, browser->GetIdentifier()); } else { + EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); EXPECT_GT(browser->GetIdentifier(), 0); EXPECT_EQ(browser_id_main_, browser->GetIdentifier()); } @@ -1246,6 +1274,9 @@ class CrossOriginNavRendererTest : public ClientApp::RenderDelegate { EXPECT_TRUE(got_render_thread_created_); EXPECT_TRUE(got_webkit_initialized_); + EXPECT_EQ(RT_SUB_RESOURCE, request->GetResourceType()); + EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); + Status* status = GetStatus(browser); EXPECT_TRUE(status); @@ -1373,6 +1404,9 @@ class CrossOriginNavTestHandler : public TestHandler { virtual bool OnBeforeResourceLoad(CefRefPtr browser, CefRefPtr frame, CefRefPtr request) OVERRIDE { + EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); + EXPECT_EQ(TT_EXPLICIT, request->GetTransitionType()); + EXPECT_GT(browser_id_current_, 0); EXPECT_EQ(browser_id_current_, browser->GetIdentifier()); diff --git a/tests/unittests/request_unittest.cc b/tests/unittests/request_unittest.cc index b651aedc0..2faaef222 100644 --- a/tests/unittests/request_unittest.cc +++ b/tests/unittests/request_unittest.cc @@ -2,7 +2,11 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. +#include + #include "include/cef_request.h" +#include "include/cef_runnable.h" +#include "tests/cefclient/client_app.h" #include "tests/unittests/test_handler.h" #include "tests/unittests/test_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -152,6 +156,8 @@ class RequestSendRecvTestHandler : public TestHandler { CefRefPtr request) OVERRIDE { // Verify that the request is the same TestRequestEqual(request_, request, true); + EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); + EXPECT_EQ(TT_LINK, request->GetTransitionType()); got_before_resource_load_.yes(); @@ -164,6 +170,8 @@ class RequestSendRecvTestHandler : public TestHandler { CefRefPtr request) OVERRIDE { // Verify that the request is the same TestRequestEqual(request_, request, true); + EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType()); + EXPECT_EQ(TT_LINK, request->GetTransitionType()); got_resource_handler_.yes(); @@ -190,3 +198,316 @@ TEST(RequestTest, SendRecv) { ASSERT_TRUE(handler->got_before_resource_load_); ASSERT_TRUE(handler->got_resource_handler_); } + +namespace { + +const char kTypeTestCompleteMsg[] = "RequestTest.Type"; +const char kTypeTestOrigin[] = "http://tests-requesttt.com/"; + +static struct TypeExpected { + const char* file; + bool browser_side; // True if this expectation applies to the browser side. + cef_transition_type_t transition_type; + cef_resource_type_t resource_type; + int expected_count; +} g_type_expected[] = { + // Initial main frame load due to browser creation. + {"main.html", true, TT_EXPLICIT, RT_MAIN_FRAME, 1}, + {"main.html", false, TT_EXPLICIT, RT_SUB_RESOURCE, 1}, + + // Sub frame load. + {"sub.html", true, TT_LINK, RT_SUB_FRAME, 1}, + {"sub.html", false, TT_EXPLICIT, RT_SUB_RESOURCE, 1}, + + // Stylesheet load. + {"style.css", true, TT_LINK, RT_STYLESHEET, 1}, + + // Script load. + {"script.js", true, TT_LINK, RT_SCRIPT, 1}, + + // Image load. + {"image.png", true, TT_LINK, RT_IMAGE, 1}, + + // Font load. + {"font.ttf", true, TT_LINK, RT_FONT_RESOURCE, 1}, + + // XHR load. + {"xhr.html", true, TT_LINK, RT_XHR, 1}, +}; + +class TypeExpectations { + public: + TypeExpectations(bool browser_side) + : browser_side_(browser_side) { + // Build the map of relevant requests. + for (int i = 0; i < sizeof(g_type_expected) / sizeof(TypeExpected); ++i) { + if (g_type_expected[i].browser_side != browser_side_) + continue; + + request_count_.insert(std::make_pair(i, 0)); + } + } + + // Notify that a request has been received. Returns true if the request is + // something we care about. + bool GotRequest(CefRefPtr request) { + const std::string& url = request->GetURL(); + if (url.find(kTypeTestOrigin) != 0) + return false; + + const std::string& file = url.substr(sizeof(kTypeTestOrigin)-1); + cef_transition_type_t transition_type = request->GetTransitionType(); + cef_resource_type_t resource_type = request->GetResourceType(); + + const int index = GetExpectedIndex(file, transition_type, resource_type); + EXPECT_GE(index, 0) + << "File: " << file.c_str() + << "; Browser Side: " << browser_side_ + << "; Transition Type: " << transition_type + << "; Resource Type: " << resource_type; + + RequestCount::iterator it = request_count_.find(index); + EXPECT_TRUE(it != request_count_.end()); + + const int actual_count = ++it->second; + const int expected_count = g_type_expected[index].expected_count; + EXPECT_LE(actual_count, expected_count) + << "File: " << file.c_str() + << "; Browser Side: " << browser_side_ + << "; Transition Type: " << transition_type + << "; Resource Type: " << resource_type; + + return true; + } + + // Test if all expectations have been met. + bool IsDone(bool assert) { + for (int i = 0; i < sizeof(g_type_expected) / sizeof(TypeExpected); ++i) { + if (g_type_expected[i].browser_side != browser_side_) + continue; + + RequestCount::const_iterator it = request_count_.find(i); + EXPECT_TRUE(it != request_count_.end()); + if (it->second != g_type_expected[i].expected_count) { + if (assert) { + EXPECT_EQ(g_type_expected[i].expected_count, it->second) + << "File: " << g_type_expected[i].file + << "; Browser Side: " << browser_side_ + << "; Transition Type: " << g_type_expected[i].transition_type + << "; Resource Type: " << g_type_expected[i].resource_type; + } + return false; + } + } + return true; + } + + private: + // Returns the index for the specified navigation. + int GetExpectedIndex(const std::string& file, + cef_transition_type_t transition_type, + cef_resource_type_t resource_type) { + for (int i = 0; i < sizeof(g_type_expected) / sizeof(TypeExpected); ++i) { + if (g_type_expected[i].file == file && + g_type_expected[i].browser_side == browser_side_ && + g_type_expected[i].transition_type == transition_type && + g_type_expected[i].resource_type == resource_type) { + return i; + } + } + return -1; + } + + bool browser_side_; + + // Map of TypeExpected index to actual request count. + typedef std::map RequestCount; + RequestCount request_count_; +}; + +// Renderer side. +class TypeRendererTest : public ClientApp::RenderDelegate { + public: + TypeRendererTest() : + expectations_(false) {} + + virtual bool OnBeforeNavigation(CefRefPtr app, + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + cef_navigation_type_t navigation_type, + bool is_redirect) OVERRIDE { + if (expectations_.GotRequest(request) && expectations_.IsDone(false)) + SendTestResults(browser); + return false; + } + + private: + // Send the test results. + void SendTestResults(CefRefPtr browser) { + // Check if the test has failed. + bool result = !TestFailed(); + + // Return the result to the browser process. + CefRefPtr return_msg = + CefProcessMessage::Create(kTypeTestCompleteMsg); + CefRefPtr args = return_msg->GetArgumentList(); + EXPECT_TRUE(args.get()); + EXPECT_TRUE(args->SetBool(0, result)); + EXPECT_TRUE(browser->SendProcessMessage(PID_BROWSER, return_msg)); + } + + TypeExpectations expectations_; + + IMPLEMENT_REFCOUNTING(TypeRendererTest); +}; + +// Browser side. +class TypeTestHandler : public TestHandler { + public: + TypeTestHandler() : + before_expectations_(true), + get_expectations_(true), + completed_browser_side_(false), + completed_render_side_(false), + timed_out_(false) {} + + virtual void RunTest() OVERRIDE { + AddResource(std::string(kTypeTestOrigin) + "main.html", + "" + "" + "" + "" + "" + "

Main

" + "" + "" + "" + "", + "text/html"); + AddResource(std::string(kTypeTestOrigin) + "sub.html", + "Sub", + "text/html"); + AddResource(std::string(kTypeTestOrigin) + "style.css", + "@font-face {" + " font-family: custom_font;" + " src: url('font.ttf');" + "}" + "p {" + " font-family: custom_font;" + "}", + "text/css"); + AddResource(std::string(kTypeTestOrigin) + "script.js", + "", + "text/javascript"); + AddResource(std::string(kTypeTestOrigin) + "image.png", + "", + "image/png"); + AddResource(std::string(kTypeTestOrigin) + "font.ttf", + "", + "font/ttf"); + AddResource(std::string(kTypeTestOrigin) + "xhr.html", + "XHR", + "text/html"); + + CreateBrowser(std::string(kTypeTestOrigin) + "main.html"); + + // Time out the test after a reasonable period of time. + CefPostDelayedTask(TID_UI, + NewCefRunnableMethod(this, &TypeTestHandler::DestroyTestInTimeout), + 2000); + } + + virtual bool OnBeforeResourceLoad(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) OVERRIDE { + before_expectations_.GotRequest(request); + + return false; + } + + virtual CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) OVERRIDE { + if (get_expectations_.GotRequest(request) && + get_expectations_.IsDone(false)) { + completed_browser_side_ = true; + // Destroy the test on the UI thread. + CefPostTask(TID_UI, + NewCefRunnableMethod(this, &TypeTestHandler::DestroyTestIfComplete)); + } + + return TestHandler::GetResourceHandler(browser, frame, request); + } + + virtual bool OnProcessMessageReceived( + CefRefPtr browser, + CefProcessId source_process, + CefRefPtr message) OVERRIDE { + const std::string& msg_name = message->GetName(); + if (msg_name == kTypeTestCompleteMsg) { + // Test that the renderer side succeeded. + CefRefPtr args = message->GetArgumentList(); + EXPECT_TRUE(args.get()); + EXPECT_TRUE(args->GetBool(0)); + + completed_render_side_ = true; + DestroyTestIfComplete(); + return true; + } + + // Message not handled. + return false; + } + + private: + void DestroyTestIfComplete() { + if (timed_out_) + return; + if (completed_browser_side_ && completed_render_side_) + DestroyTest(); + } + + void DestroyTestInTimeout() { + if (completed_browser_side_ && completed_render_side_) + return; + timed_out_ = true; + DestroyTest(); + } + + virtual void DestroyTest() OVERRIDE { + // Verify test expectations. + EXPECT_TRUE(completed_browser_side_); + EXPECT_TRUE(completed_render_side_); + EXPECT_TRUE(before_expectations_.IsDone(true)); + EXPECT_TRUE(get_expectations_.IsDone(true)); + + TestHandler::DestroyTest(); + } + + TypeExpectations before_expectations_; + TypeExpectations get_expectations_; + + bool completed_browser_side_; + bool completed_render_side_; + bool timed_out_; +}; + +} // namespace + +// Verify the order of navigation-related callbacks. +TEST(RequestTest, ResourceAndTransitionType) { + CefRefPtr handler = + new TypeTestHandler(); + handler->ExecuteTest(); +} + + +// Entry point for creating request renderer test objects. +// Called from client_app_delegates.cc. +void CreateRequestRendererTests(ClientApp::RenderDelegateSet& delegates) { + delegates.insert(new TypeRendererTest); +}