Allow custom schemes to cause redirects (issue #98).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@223 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt
2011-04-21 18:17:46 +00:00
parent f18083e5df
commit 06718b4049
8 changed files with 151 additions and 52 deletions

View File

@ -808,11 +808,12 @@ public:
// Called on the IO thread before a resource is loaded. To allow the resource // 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 // 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 // populate the |redirectUrl| value and return RV_CONTINUE. To specify data
// for the resource return a CefStream object in |resourceStream|, set // for the resource return a CefStream object in |resourceStream|, use the
// |mimeType| to the resource stream's mime type, and return RV_CONTINUE. To // |response| object to set mime type, HTTP status code and optional header
// cancel loading of the resource return RV_HANDLED. Any modifications to // values, and return RV_CONTINUE. To cancel loading of the resource return
// |request| will be observed. If the URL in |request| is changed and // RV_HANDLED. Any modifications to |request| will be observed. If the URL in
// |redirectUrl| is also set, the URL in |request| will be used. // |request| is changed and |redirectUrl| is also set, the URL in |request|
// will be used.
/*--cef()--*/ /*--cef()--*/
virtual RetVal HandleBeforeResourceLoad(CefRefPtr<CefBrowser> browser, virtual RetVal HandleBeforeResourceLoad(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefRequest> request, CefRefPtr<CefRequest> request,
@ -1637,16 +1638,19 @@ class CefSchemeHandler : public CefBase
{ {
public: public:
// Process the request. All response generation should take place in this // Process the request. All response generation should take place in this
// method. If there is no response set |response_length| to zero and // method. If there is no response set |response_length| to zero or return
// ReadResponse() will not be called. If the response length is not known then // false and ReadResponse() will not be called. If the response length is not
// set |response_length| to -1 and ReadResponse() will be called until it // known set |response_length| to -1 and ReadResponse() will be called until
// returns false or until the value of |bytes_read| is set to 0. Otherwise, // it returns false or until the value of |bytes_read| is set to 0. If the
// set |response_length| to a positive value and ReadResponse() will be called // response length is known set |response_length| to a positive value and
// until it returns false, the value of |bytes_read| is set to 0 or the // ReadResponse() will be called until it returns false, the value of
// specified number of bytes have been read. If there is a response set // |bytes_read| is set to 0 or the specified number of bytes have been read.
// |mime_type| to the mime type for the response. // 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()--*/ /*--cef()--*/
virtual bool ProcessRequest(CefRefPtr<CefRequest> request, virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
CefString& redirectUrl,
CefRefPtr<CefResponse> response, CefRefPtr<CefResponse> response,
int* response_length) =0; int* response_length) =0;

View File

@ -644,11 +644,12 @@ typedef struct _cef_handler_t
// Called on the IO thread before a resource is loaded. To allow the resource // 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 // 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 // populate the |redirectUrl| value and return RV_CONTINUE. To specify data
// for the resource return a CefStream object in |resourceStream|, set // for the resource return a CefStream object in |resourceStream|, use the
// |mimeType| to the resource stream's mime type, and return RV_CONTINUE. To // |response| object to set mime type, HTTP status code and optional header
// cancel loading of the resource return RV_HANDLED. Any modifications to // values, and return RV_CONTINUE. To cancel loading of the resource return
// |request| will be observed. If the URL in |request| is changed and // RV_HANDLED. Any modifications to |request| will be observed. If the URL in
// |redirectUrl| is also set, the URL in |request| will be used. // |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)( enum cef_retval_t (CEF_CALLBACK *handle_before_resource_load)(
struct _cef_handler_t* self, struct _cef_browser_t* browser, struct _cef_handler_t* self, struct _cef_browser_t* browser,
struct _cef_request_t* request, cef_string_t* redirectUrl, struct _cef_request_t* request, cef_string_t* redirectUrl,
@ -1399,17 +1400,20 @@ typedef struct _cef_scheme_handler_t
cef_base_t base; cef_base_t base;
// Process the request. All response generation should take place in this // Process the request. All response generation should take place in this
// function. If there is no response set |response_length| to zero and // function. If there is no response set |response_length| to zero or return
// read_response() will not be called. If the response length is not known // false (0) and read_response() will not be called. If the response length is
// then set |response_length| to -1 and read_response() will be called until // not known set |response_length| to -1 and read_response() will be called
// it returns false (0) or until the value of |bytes_read| is set to 0. // 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() // If the response length is known set |response_length| to a positive value
// will be called until it returns false (0), the value of |bytes_read| is set // and read_response() will be called until it returns false (0), the value of
// to 0 or the specified number of bytes have been read. If there is a // |bytes_read| is set to 0 or the specified number of bytes have been read.
// response set |mime_type| to the mime type for the response. // 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, int (CEF_CALLBACK *process_request)(struct _cef_scheme_handler_t* self,
struct _cef_request_t* request, struct _cef_response_t* response, struct _cef_request_t* request, cef_string_t* redirectUrl,
int* response_length); struct _cef_response_t* response, int* response_length);
// Cancel processing of the request. // Cancel processing of the request.
void (CEF_CALLBACK *cancel)(struct _cef_scheme_handler_t* self); void (CEF_CALLBACK *cancel)(struct _cef_scheme_handler_t* self);

View File

@ -129,6 +129,12 @@ public:
virtual bool IsRedirectResponse(GURL* location, int* http_status_code) 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; return false;
} }
@ -150,6 +156,7 @@ public:
protected: protected:
GURL url_; GURL url_;
GURL redirect_url_;
private: private:
void DidResolve(const GURL& url) void DidResolve(const GURL& url)
@ -189,12 +196,23 @@ private:
static_cast<CefRequestImpl*>(req.get())->Set(owner_->request()); static_cast<CefRequestImpl*>(req.get())->Set(owner_->request());
owner_->handler_->Cancel(); owner_->handler_->Cancel();
int response_length = 0; int response_length = 0;
CefString redirectUrl;
// handler should complete content generation in ProcessRequest // handler should complete content generation in ProcessRequest
bool res = owner_->handler_->ProcessRequest(req, owner_->response_, bool res = owner_->handler_->ProcessRequest(req, redirectUrl,
&response_length); owner_->response_, &response_length);
if (res) 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; owner_->response_length_ = response_length;
}
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
if (owner_loop_) { if (owner_loop_) {

View File

@ -19,18 +19,20 @@
int CEF_CALLBACK scheme_handler_process_request( int CEF_CALLBACK scheme_handler_process_request(
struct _cef_scheme_handler_t* self, cef_request_t* 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(self);
DCHECK(request); DCHECK(request);
DCHECK(redirectUrl);
DCHECK(response); DCHECK(response);
DCHECK(response_length); DCHECK(response_length);
if(!self || !request || !response || !response_length) if(!self || !request || !redirectUrl || !response || !response_length)
return 0; return 0;
CefString redirectUrlStr(redirectUrl);
return CefSchemeHandlerCppToC::Get(self)->ProcessRequest( return CefSchemeHandlerCppToC::Get(self)->ProcessRequest(
CefRequestCToCpp::Wrap(request), CefResponseCToCpp::Wrap(response), CefRequestCToCpp::Wrap(request), redirectUrlStr,
response_length); CefResponseCToCpp::Wrap(response), response_length);
} }
void CEF_CALLBACK scheme_handler_cancel(struct _cef_scheme_handler_t* self) void CEF_CALLBACK scheme_handler_cancel(struct _cef_scheme_handler_t* self)

View File

@ -18,13 +18,15 @@
// VIRTUAL METHODS - Body may be edited by hand. // VIRTUAL METHODS - Body may be edited by hand.
bool CefSchemeHandlerCToCpp::ProcessRequest(CefRefPtr<CefRequest> request, bool CefSchemeHandlerCToCpp::ProcessRequest(CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response, int* response_length) CefString& redirectUrl, CefRefPtr<CefResponse> response,
int* response_length)
{ {
if(CEF_MEMBER_MISSING(struct_, process_request)) if(CEF_MEMBER_MISSING(struct_, process_request))
return false; return false;
return struct_->process_request(struct_, CefRequestCppToC::Wrap(request), 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() void CefSchemeHandlerCToCpp::Cancel()

View File

@ -34,7 +34,8 @@ public:
// CefSchemeHandler methods // CefSchemeHandler methods
virtual bool ProcessRequest(CefRefPtr<CefRequest> request, virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response, int* response_length); CefString& redirectUrl, CefRefPtr<CefResponse> response,
int* response_length);
virtual void Cancel(); virtual void Cancel();
virtual bool ReadResponse(void* data_out, int bytes_to_read, int* bytes_read); virtual bool ReadResponse(void* data_out, int bytes_to_read, int* bytes_read);
}; };

View File

@ -20,15 +20,18 @@ public:
ClientSchemeHandler() : offset_(0) {} ClientSchemeHandler() : offset_(0) {}
// Process the request. All response generation should take place in this // Process the request. All response generation should take place in this
// method. If there is no response set |response_length| to zero and // method. If there is no response set |response_length| to zero or return
// ReadResponse() will not be called. If the response length is not known then // false and ReadResponse() will not be called. If the response length is not
// set |response_length| to -1 and ReadResponse() will be called until it // known set |response_length| to -1 and ReadResponse() will be called until
// returns false or until the value of |bytes_read| is set to 0. Otherwise, // it returns false or until the value of |bytes_read| is set to 0. If the
// set |response_length| to a positive value and ReadResponse() will be called // response length is known set |response_length| to a positive value and
// until it returns false, the value of |bytes_read| is set to 0 or the // ReadResponse() will be called until it returns false, the value of
// specified number of bytes have been read. If there is a response set // |bytes_read| is set to 0 or the specified number of bytes have been read.
// |mime_type| to the mime type for the response. // 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<CefRequest> request, virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
CefString& redirectUrl,
CefRefPtr<CefResponse> response, CefRefPtr<CefResponse> response,
int* response_length) int* response_length)
{ {

View File

@ -21,16 +21,19 @@ public:
got_request.reset(); got_request.reset();
got_read.reset(); got_read.reset();
got_output.reset(); got_output.reset();
got_redirect.reset();
} }
std::string url; std::string url;
std::string html; std::string html;
int status_code; int status_code;
std::string redirect_url;
TrackCallback TrackCallback
got_request, got_request,
got_read, got_read,
got_output; got_output,
got_redirect;
}; };
class TestSchemeHandler : public TestHandler class TestSchemeHandler : public TestHandler
@ -46,6 +49,28 @@ public:
CreateBrowser(test_results_->url); CreateBrowser(test_results_->url);
} }
virtual RetVal HandleBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> 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<CefBrowser> browser, virtual RetVal HandleLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefFrame> frame,
int httpStatusCode) int httpStatusCode)
@ -74,6 +99,7 @@ public:
: test_results_(tr), offset_(0) {} : test_results_(tr), offset_(0) {}
virtual bool ProcessRequest(CefRefPtr<CefRequest> request, virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
CefString& redirectUrl,
CefRefPtr<CefResponse> response, CefRefPtr<CefResponse> response,
int* response_length) int* response_length)
{ {
@ -86,13 +112,16 @@ public:
response->SetStatus(test_results_->status_code); response->SetStatus(test_results_->status_code);
bool handled = !test_results_->html.empty(); if (!test_results_->redirect_url.empty()) {
if(handled) { redirectUrl = test_results_->redirect_url;
return true;
} else if (!test_results_->html.empty()) {
response->SetMimeType("text/html"); response->SetMimeType("text/html");
*response_length = test_results_->html.size(); *response_length = test_results_->html.size();
return true;
} }
return handled; return false;
} }
virtual void Cancel() virtual void Cancel()
@ -251,6 +280,24 @@ TEST(SchemeHandlerTest, StandardSchemeNoResponse)
EXPECT_FALSE(g_TestResults.got_output); 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 =
"<html><head></head><body><h1>Redirected</h1></body></html>";
CefRefPtr<TestSchemeHandler> 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 that a non-standard scheme can return normal results.
TEST(SchemeHandlerTest, NonStandardSchemeNormalResponse) TEST(SchemeHandlerTest, NonStandardSchemeNormalResponse)
{ {
@ -313,3 +360,21 @@ TEST(SchemeHandlerTest, NonStandardSchemeNoResponse)
EXPECT_FALSE(g_TestResults.got_read); EXPECT_FALSE(g_TestResults.got_read);
EXPECT_FALSE(g_TestResults.got_output); 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 =
"<html><head></head><body><h1>Redirected</h1></body></html>";
CefRefPtr<TestSchemeHandler> 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);
}