diff --git a/include/cef.h b/include/cef.h index ab4b1ed9b..a7825da4a 100644 --- a/include/cef.h +++ b/include/cef.h @@ -808,11 +808,12 @@ public: // Called on the IO thread before a resource is loaded. To allow the resource // to load normally return RV_CONTINUE. To redirect the resource to a new url // populate the |redirectUrl| value and return RV_CONTINUE. To specify data - // for the resource return a CefStream object in |resourceStream|, set - // |mimeType| to the resource stream's mime type, and return RV_CONTINUE. To - // cancel loading of the resource return RV_HANDLED. Any modifications to - // |request| will be observed. If the URL in |request| is changed and - // |redirectUrl| is also set, the URL in |request| will be used. + // for the resource return a CefStream object in |resourceStream|, use the + // |response| object to set mime type, HTTP status code and optional header + // values, and return RV_CONTINUE. To cancel loading of the resource return + // RV_HANDLED. Any modifications to |request| will be observed. If the URL in + // |request| is changed and |redirectUrl| is also set, the URL in |request| + // will be used. /*--cef()--*/ virtual RetVal HandleBeforeResourceLoad(CefRefPtr browser, CefRefPtr request, @@ -1637,16 +1638,19 @@ class CefSchemeHandler : public CefBase { public: // Process the request. All response generation should take place in this - // method. If there is no response set |response_length| to zero and - // ReadResponse() will not be called. If the response length is not known then - // set |response_length| to -1 and ReadResponse() will be called until it - // returns false or until the value of |bytes_read| is set to 0. Otherwise, - // set |response_length| to a positive value and ReadResponse() will be called - // until it returns false, the value of |bytes_read| is set to 0 or the - // specified number of bytes have been read. If there is a response set - // |mime_type| to the mime type for the response. + // method. If there is no response set |response_length| to zero or return + // false and ReadResponse() will not be called. If the response length is not + // known set |response_length| to -1 and ReadResponse() will be called until + // it returns false or until the value of |bytes_read| is set to 0. If the + // response length is known set |response_length| to a positive value and + // ReadResponse() will be called until it returns false, the value of + // |bytes_read| is set to 0 or the specified number of bytes have been read. + // Use the |response| object to set the mime type, http status code and + // optional header values for the response and return true. To redirect the + // request to a new URL set |redirectUrl| to the new URL and return true. /*--cef()--*/ virtual bool ProcessRequest(CefRefPtr request, + CefString& redirectUrl, CefRefPtr response, int* response_length) =0; diff --git a/include/cef_capi.h b/include/cef_capi.h index 3af083b39..a8895f84d 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -644,11 +644,12 @@ typedef struct _cef_handler_t // Called on the IO thread before a resource is loaded. To allow the resource // to load normally return RV_CONTINUE. To redirect the resource to a new url // populate the |redirectUrl| value and return RV_CONTINUE. To specify data - // for the resource return a CefStream object in |resourceStream|, set - // |mimeType| to the resource stream's mime type, and return RV_CONTINUE. To - // cancel loading of the resource return RV_HANDLED. Any modifications to - // |request| will be observed. If the URL in |request| is changed and - // |redirectUrl| is also set, the URL in |request| will be used. + // for the resource return a CefStream object in |resourceStream|, use the + // |response| object to set mime type, HTTP status code and optional header + // values, and return RV_CONTINUE. To cancel loading of the resource return + // RV_HANDLED. Any modifications to |request| will be observed. If the URL in + // |request| is changed and |redirectUrl| is also set, the URL in |request| + // will be used. enum cef_retval_t (CEF_CALLBACK *handle_before_resource_load)( struct _cef_handler_t* self, struct _cef_browser_t* browser, struct _cef_request_t* request, cef_string_t* redirectUrl, @@ -1399,17 +1400,20 @@ typedef struct _cef_scheme_handler_t cef_base_t base; // Process the request. All response generation should take place in this - // function. If there is no response set |response_length| to zero and - // read_response() will not be called. If the response length is not known - // then set |response_length| to -1 and read_response() will be called until - // it returns false (0) or until the value of |bytes_read| is set to 0. - // Otherwise, set |response_length| to a positive value and read_response() - // will be called until it returns false (0), the value of |bytes_read| is set - // to 0 or the specified number of bytes have been read. If there is a - // response set |mime_type| to the mime type for the response. + // function. If there is no response set |response_length| to zero or return + // false (0) and read_response() will not be called. If the response length is + // not known set |response_length| to -1 and read_response() will be called + // until it returns false (0) or until the value of |bytes_read| is set to 0. + // If the response length is known set |response_length| to a positive value + // and read_response() will be called until it returns false (0), the value of + // |bytes_read| is set to 0 or the specified number of bytes have been read. + // Use the |response| object to set the mime type, http status code and + // optional header values for the response and return true (1). To redirect + // the request to a new URL set |redirectUrl| to the new URL and return true + // (1). int (CEF_CALLBACK *process_request)(struct _cef_scheme_handler_t* self, - struct _cef_request_t* request, struct _cef_response_t* response, - int* response_length); + struct _cef_request_t* request, cef_string_t* redirectUrl, + struct _cef_response_t* response, int* response_length); // Cancel processing of the request. void (CEF_CALLBACK *cancel)(struct _cef_scheme_handler_t* self); diff --git a/libcef/scheme_impl.cc b/libcef/scheme_impl.cc index aa0fbcb0b..40d676917 100644 --- a/libcef/scheme_impl.cc +++ b/libcef/scheme_impl.cc @@ -129,6 +129,12 @@ public: virtual bool IsRedirectResponse(GURL* location, int* http_status_code) { + if (redirect_url_.is_valid()) { + // Redirect to the new URL. + *http_status_code = 303; + location->Swap(&redirect_url_); + return true; + } return false; } @@ -150,6 +156,7 @@ public: protected: GURL url_; + GURL redirect_url_; private: void DidResolve(const GURL& url) @@ -189,12 +196,23 @@ private: static_cast(req.get())->Set(owner_->request()); owner_->handler_->Cancel(); + int response_length = 0; + CefString redirectUrl; + // handler should complete content generation in ProcessRequest - bool res = owner_->handler_->ProcessRequest(req, owner_->response_, - &response_length); - if (res) - owner_->response_length_ = response_length; + bool res = owner_->handler_->ProcessRequest(req, redirectUrl, + owner_->response_, &response_length); + if (res) { + if (!redirectUrl.empty()) { + // Treat the request as a redirect. + std::string redirectUrlStr = redirectUrl; + owner_->redirect_url_ = GURL(redirectUrlStr); + owner_->response_length_ = 0; + } else { + owner_->response_length_ = response_length; + } + } ////////////////////////////////////////////////////////////////////////// if (owner_loop_) { diff --git a/libcef_dll/cpptoc/scheme_handler_cpptoc.cc b/libcef_dll/cpptoc/scheme_handler_cpptoc.cc index 12d7f28de..6abdecffb 100644 --- a/libcef_dll/cpptoc/scheme_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/scheme_handler_cpptoc.cc @@ -19,18 +19,20 @@ int CEF_CALLBACK scheme_handler_process_request( struct _cef_scheme_handler_t* self, cef_request_t* request, - cef_response_t* response, int* response_length) + cef_string_t* redirectUrl, cef_response_t* response, int* response_length) { DCHECK(self); DCHECK(request); + DCHECK(redirectUrl); DCHECK(response); DCHECK(response_length); - if(!self || !request || !response || !response_length) + if(!self || !request || !redirectUrl || !response || !response_length) return 0; + CefString redirectUrlStr(redirectUrl); return CefSchemeHandlerCppToC::Get(self)->ProcessRequest( - CefRequestCToCpp::Wrap(request), CefResponseCToCpp::Wrap(response), - response_length); + CefRequestCToCpp::Wrap(request), redirectUrlStr, + CefResponseCToCpp::Wrap(response), response_length); } void CEF_CALLBACK scheme_handler_cancel(struct _cef_scheme_handler_t* self) diff --git a/libcef_dll/ctocpp/scheme_handler_ctocpp.cc b/libcef_dll/ctocpp/scheme_handler_ctocpp.cc index 8433cdce8..069c83d76 100644 --- a/libcef_dll/ctocpp/scheme_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/scheme_handler_ctocpp.cc @@ -18,13 +18,15 @@ // VIRTUAL METHODS - Body may be edited by hand. bool CefSchemeHandlerCToCpp::ProcessRequest(CefRefPtr request, - CefRefPtr response, int* response_length) + CefString& redirectUrl, CefRefPtr response, + int* response_length) { if(CEF_MEMBER_MISSING(struct_, process_request)) return false; return struct_->process_request(struct_, CefRequestCppToC::Wrap(request), - CefResponseCppToC::Wrap(response), response_length) ? true : false; + redirectUrl.GetWritableStruct(), CefResponseCppToC::Wrap(response), + response_length) ? true : false; } void CefSchemeHandlerCToCpp::Cancel() diff --git a/libcef_dll/ctocpp/scheme_handler_ctocpp.h b/libcef_dll/ctocpp/scheme_handler_ctocpp.h index d558cb0b5..3bd60ca2a 100644 --- a/libcef_dll/ctocpp/scheme_handler_ctocpp.h +++ b/libcef_dll/ctocpp/scheme_handler_ctocpp.h @@ -34,7 +34,8 @@ public: // CefSchemeHandler methods virtual bool ProcessRequest(CefRefPtr request, - CefRefPtr response, int* response_length); + CefString& redirectUrl, CefRefPtr response, + int* response_length); virtual void Cancel(); virtual bool ReadResponse(void* data_out, int bytes_to_read, int* bytes_read); }; diff --git a/tests/cefclient/scheme_test.cpp b/tests/cefclient/scheme_test.cpp index c8df06052..cbc65f49f 100644 --- a/tests/cefclient/scheme_test.cpp +++ b/tests/cefclient/scheme_test.cpp @@ -20,15 +20,18 @@ public: ClientSchemeHandler() : offset_(0) {} // Process the request. All response generation should take place in this - // method. If there is no response set |response_length| to zero and - // ReadResponse() will not be called. If the response length is not known then - // set |response_length| to -1 and ReadResponse() will be called until it - // returns false or until the value of |bytes_read| is set to 0. Otherwise, - // set |response_length| to a positive value and ReadResponse() will be called - // until it returns false, the value of |bytes_read| is set to 0 or the - // specified number of bytes have been read. If there is a response set - // |mime_type| to the mime type for the response. + // method. If there is no response set |response_length| to zero or return + // false and ReadResponse() will not be called. If the response length is not + // known set |response_length| to -1 and ReadResponse() will be called until + // it returns false or until the value of |bytes_read| is set to 0. If the + // response length is known set |response_length| to a positive value and + // ReadResponse() will be called until it returns false, the value of + // |bytes_read| is set to 0 or the specified number of bytes have been read. + // Use the |response| object to set the mime type, http status code and + // optional header values for the response and return true. To redirect the + // request to a new URL set |redirectUrl| to the new URL and return true. virtual bool ProcessRequest(CefRefPtr request, + CefString& redirectUrl, CefRefPtr response, int* response_length) { diff --git a/tests/unittests/scheme_handler_unittest.cc b/tests/unittests/scheme_handler_unittest.cc index 1a57975c1..3ff4a1a08 100644 --- a/tests/unittests/scheme_handler_unittest.cc +++ b/tests/unittests/scheme_handler_unittest.cc @@ -21,16 +21,19 @@ public: got_request.reset(); got_read.reset(); got_output.reset(); + got_redirect.reset(); } std::string url; std::string html; int status_code; + std::string redirect_url; TrackCallback got_request, got_read, - got_output; + got_output, + got_redirect; }; class TestSchemeHandler : public TestHandler @@ -46,6 +49,28 @@ public: CreateBrowser(test_results_->url); } + virtual RetVal HandleBeforeBrowse(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + NavType navType, bool isRedirect) + { + if (isRedirect) { + test_results_->got_redirect.yes(); + std::string newUrl = request->GetURL(); + EXPECT_EQ(newUrl, test_results_->redirect_url); + + // No read should have occurred for the redirect. + EXPECT_TRUE(test_results_->got_request); + EXPECT_FALSE(test_results_->got_read); + + // Now loading the redirect URL. + test_results_->url = test_results_->redirect_url; + test_results_->redirect_url.clear(); + } + + return RV_CONTINUE; + } + virtual RetVal HandleLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) @@ -74,6 +99,7 @@ public: : test_results_(tr), offset_(0) {} virtual bool ProcessRequest(CefRefPtr request, + CefString& redirectUrl, CefRefPtr response, int* response_length) { @@ -85,14 +111,17 @@ public: EXPECT_EQ(url, test_results_->url); response->SetStatus(test_results_->status_code); - - bool handled = !test_results_->html.empty(); - if(handled) { + + if (!test_results_->redirect_url.empty()) { + redirectUrl = test_results_->redirect_url; + return true; + } else if (!test_results_->html.empty()) { response->SetMimeType("text/html"); *response_length = test_results_->html.size(); + return true; } - return handled; + return false; } virtual void Cancel() @@ -251,6 +280,24 @@ TEST(SchemeHandlerTest, StandardSchemeNoResponse) EXPECT_FALSE(g_TestResults.got_output); } +// Test that a standard scheme can generate redirects. +TEST(SchemeHandlerTest, StandardSchemeRedirect) +{ + CreateStandardTestScheme(); + g_TestResults.url = "stdscheme://tests/run.html"; + g_TestResults.redirect_url = "stdscheme://tests/redirect.html"; + g_TestResults.html = + "

Redirected

"; + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_redirect); +} + // Test that a non-standard scheme can return normal results. TEST(SchemeHandlerTest, NonStandardSchemeNormalResponse) { @@ -313,3 +360,21 @@ TEST(SchemeHandlerTest, NonStandardSchemeNoResponse) EXPECT_FALSE(g_TestResults.got_read); EXPECT_FALSE(g_TestResults.got_output); } + +// Test that a non-standard scheme can generate redirects. +TEST(SchemeHandlerTest, NonStandardSchemeRedirect) +{ + CreateNonStandardTestScheme(); + g_TestResults.url = "nonstdscheme:some%20value"; + g_TestResults.redirect_url = "nonstdscheme:some%20other%20value"; + g_TestResults.html = + "

Redirected

"; + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_redirect); +}