blob: 300692fd8dc32971959f6ea01a08a162c241b9a2 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include <algorithm>
#include <cmath>
#include <sstream>
#include <string>
#include "include/base/cef_bind.h"
#include "include/base/cef_scoped_ptr.h"
#include "include/cef_request_context_handler.h"
#include "include/cef_scheme.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"
namespace {
// Normal stream resource handler implementation that additionally verifies
// calls to Cancel.
// This also tests the CefStreamResourceHandler implementation.
class NormalResourceHandler : public CefStreamResourceHandler {
public:
NormalResourceHandler(int status_code,
const CefString& status_text,
const CefString& mime_type,
CefResponse::HeaderMap header_map,
CefRefPtr<CefStreamReader> stream,
const base::Closure& destroy_callback)
: CefStreamResourceHandler(status_code,
status_text,
mime_type,
header_map,
stream),
destroy_callback_(destroy_callback) {}
~NormalResourceHandler() override {
EXPECT_EQ(1, cancel_ct_);
destroy_callback_.Run();
}
void Cancel() override {
EXPECT_IO_THREAD();
cancel_ct_++;
}
private:
const base::Closure destroy_callback_;
int cancel_ct_ = 0;
};
// Normal stream resource handler implementation that additionally continues
// using the callback object and verifies calls to Cancel.
class CallbackResourceHandler : public CefResourceHandler {
public:
enum Mode {
DELAYED_OPEN,
DELAYED_READ,
IMMEDIATE_OPEN,
IMMEDIATE_READ,
DELAYED_ALL,
IMMEDIATE_ALL,
};
bool IsDelayedOpen() const {
return mode_ == DELAYED_OPEN || mode_ == DELAYED_ALL;
}
bool IsDelayedRead() const {
return mode_ == DELAYED_READ || mode_ == DELAYED_ALL;
}
bool IsImmediateOpen() const {
return mode_ == IMMEDIATE_OPEN || mode_ == IMMEDIATE_ALL;
}
bool IsImmediateRead() const {
return mode_ == IMMEDIATE_READ || mode_ == IMMEDIATE_ALL;
}
CallbackResourceHandler(Mode mode,
int status_code,
const CefString& status_text,
const CefString& mime_type,
CefResponse::HeaderMap header_map,
CefRefPtr<CefStreamReader> stream,
const base::Closure& destroy_callback)
: mode_(mode),
status_code_(status_code),
status_text_(status_text),
mime_type_(mime_type),
header_map_(header_map),
stream_(stream),
destroy_callback_(destroy_callback) {
DCHECK(!mime_type_.empty());
DCHECK(stream_.get());
}
~CallbackResourceHandler() override {
EXPECT_EQ(1, cancel_ct_);
destroy_callback_.Run();
}
bool Open(CefRefPtr<CefRequest> request,
bool& handle_request,
CefRefPtr<CefCallback> callback) override {
EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
if (IsDelayedOpen()) {
// Continue the request asynchronously by executing the callback.
CefPostTask(TID_FILE_USER_VISIBLE,
base::Bind(&CefCallback::Continue, callback));
handle_request = false;
return true;
} else if (IsImmediateOpen()) {
// Continue the request immediately be executing the callback.
callback->Continue();
handle_request = false;
return true;
}
// Continue the request immediately in the default manner.
handle_request = true;
return true;
}
void GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length,
CefString& redirectUrl) override {
response->SetStatus(status_code_);
response->SetStatusText(status_text_);
response->SetMimeType(mime_type_);
if (!header_map_.empty())
response->SetHeaderMap(header_map_);
response_length = -1;
}
bool Read(void* data_out,
int bytes_to_read,
int& bytes_read,
CefRefPtr<CefResourceReadCallback> callback) override {
EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
EXPECT_GT(bytes_to_read, 0);
bytes_read = 0;
if (IsDelayedRead()) {
// Continue the request asynchronously by executing the callback.
CefPostTask(TID_FILE_USER_VISIBLE,
base::Bind(&CallbackResourceHandler::ContinueRead, this,
data_out, bytes_to_read, callback));
return true;
} else if (IsImmediateRead()) {
// Continue the request immediately be executing the callback.
ContinueRead(data_out, bytes_to_read, callback);
return true;
}
// Continue the request immediately in the default manner.
return DoRead(data_out, bytes_to_read, bytes_read);
}
void Cancel() override {
EXPECT_IO_THREAD();
cancel_ct_++;
}
private:
void ContinueRead(void* data_out,
int bytes_to_read,
CefRefPtr<CefResourceReadCallback> callback) {
EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
int bytes_read = 0;
DoRead(data_out, bytes_to_read, bytes_read);
callback->Continue(bytes_read);
}
bool DoRead(void* data_out, int bytes_to_read, int& bytes_read) {
DCHECK_GT(bytes_to_read, 0);
// Read until the buffer is full or until Read() returns 0 to indicate no
// more data.
bytes_read = 0;
int read = 0;
do {
read = static_cast<int>(
stream_->Read(static_cast<char*>(data_out) + bytes_read, 1,
bytes_to_read - bytes_read));
bytes_read += read;
} while (read != 0 && bytes_read < bytes_to_read);
return (bytes_read > 0);
}
const Mode mode_;
const int status_code_;
const CefString status_text_;
const CefString mime_type_;
const CefResponse::HeaderMap header_map_;
const CefRefPtr<CefStreamReader> stream_;
const base::Closure destroy_callback_;
int cancel_ct_ = 0;
IMPLEMENT_REFCOUNTING(CallbackResourceHandler);
DISALLOW_COPY_AND_ASSIGN(CallbackResourceHandler);
};
// Resource handler implementation that never completes. Used to test
// destruction handling behavior for in-progress requests.
class IncompleteResourceHandlerOld : public CefResourceHandler {
public:
enum TestMode {
BLOCK_PROCESS_REQUEST,
BLOCK_READ_RESPONSE,
};
IncompleteResourceHandlerOld(TestMode test_mode,
const std::string& mime_type,
const base::Closure& destroy_callback)
: test_mode_(test_mode),
mime_type_(mime_type),
destroy_callback_(destroy_callback) {}
~IncompleteResourceHandlerOld() override {
EXPECT_EQ(1, process_request_ct_);
EXPECT_EQ(1, cancel_ct_);
if (test_mode_ == BLOCK_READ_RESPONSE) {
EXPECT_EQ(1, get_response_headers_ct_);
EXPECT_EQ(1, read_response_ct_);
} else {
EXPECT_EQ(0, get_response_headers_ct_);
EXPECT_EQ(0, read_response_ct_);
}
destroy_callback_.Run();
}
bool ProcessRequest(CefRefPtr<CefRequest> request,
CefRefPtr<CefCallback> callback) override {
EXPECT_IO_THREAD();
process_request_ct_++;
if (test_mode_ == BLOCK_PROCESS_REQUEST) {
// Never release or execute this callback.
incomplete_callback_ = callback;
} else {
callback->Continue();
}
return true;
}
void GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length,
CefString& redirectUrl) override {
EXPECT_IO_THREAD();
EXPECT_EQ(test_mode_, BLOCK_READ_RESPONSE);
get_response_headers_ct_++;
response->SetStatus(200);
response->SetStatusText("OK");
response->SetMimeType(mime_type_);
response_length = 100;
}
bool ReadResponse(void* data_out,
int bytes_to_read,
int& bytes_read,
CefRefPtr<CefCallback> callback) override {
EXPECT_IO_THREAD();
EXPECT_EQ(test_mode_, BLOCK_READ_RESPONSE);
read_response_ct_++;
// Never release or execute this callback.
incomplete_callback_ = callback;
bytes_read = 0;
return true;
}
void Cancel() override {
EXPECT_IO_THREAD();
cancel_ct_++;
}
private:
const TestMode test_mode_;
const std::string mime_type_;
const base::Closure destroy_callback_;
int process_request_ct_ = 0;
int get_response_headers_ct_ = 0;
int read_response_ct_ = 0;
int cancel_ct_ = 0;
CefRefPtr<CefCallback> incomplete_callback_;
IMPLEMENT_REFCOUNTING(IncompleteResourceHandlerOld);
DISALLOW_COPY_AND_ASSIGN(IncompleteResourceHandlerOld);
};
class IncompleteResourceHandler : public CefResourceHandler {
public:
enum TestMode {
BLOCK_OPEN,
BLOCK_READ,
};
IncompleteResourceHandler(TestMode test_mode,
const std::string& mime_type,
const base::Closure& destroy_callback)
: test_mode_(test_mode),
mime_type_(mime_type),
destroy_callback_(destroy_callback) {}
~IncompleteResourceHandler() override {
EXPECT_EQ(1, open_ct_);
EXPECT_EQ(1, cancel_ct_);
if (test_mode_ == BLOCK_READ) {
EXPECT_EQ(1, get_response_headers_ct_);
EXPECT_EQ(1, read_ct_);
} else {
EXPECT_EQ(0, get_response_headers_ct_);
EXPECT_EQ(0, read_ct_);
}
destroy_callback_.Run();
}
bool Open(CefRefPtr<CefRequest> request,
bool& handle_request,
CefRefPtr<CefCallback> callback) override {
EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
open_ct_++;
if (test_mode_ == BLOCK_OPEN) {
// Never release or execute this callback.
incomplete_open_callback_ = callback;
} else {
// Continue immediately.
handle_request = true;
}
return true;
}
bool ProcessRequest(CefRefPtr<CefRequest> request,
CefRefPtr<CefCallback> callback) override {
EXPECT_TRUE(false); // Not reached.
return false;
}
void GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length,
CefString& redirectUrl) override {
EXPECT_IO_THREAD();
EXPECT_EQ(test_mode_, BLOCK_READ);
get_response_headers_ct_++;
response->SetStatus(200);
response->SetStatusText("OK");
response->SetMimeType(mime_type_);
response_length = 100;
}
bool Read(void* data_out,
int bytes_to_read,
int& bytes_read,
CefRefPtr<CefResourceReadCallback> callback) override {
EXPECT_FALSE(CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO));
EXPECT_EQ(test_mode_, BLOCK_READ);
read_ct_++;
// Never release or execute this callback.
incomplete_read_callback_ = callback;
bytes_read = 0;
return true;
}
bool ReadResponse(void* data_out,
int bytes_to_read,
int& bytes_read,
CefRefPtr<CefCallback> callback) override {
EXPECT_TRUE(false); // Not reached.
bytes_read = -2;
return false;
}
void Cancel() override {
EXPECT_IO_THREAD();
cancel_ct_++;
}
private:
const TestMode test_mode_;
const std::string mime_type_;
const base::Closure destroy_callback_;
int open_ct_ = 0;
int get_response_headers_ct_ = 0;
int read_ct_ = 0;
int cancel_ct_ = 0;
CefRefPtr<CefCallback> incomplete_open_callback_;
CefRefPtr<CefResourceReadCallback> incomplete_read_callback_;
IMPLEMENT_REFCOUNTING(IncompleteResourceHandler);
DISALLOW_COPY_AND_ASSIGN(IncompleteResourceHandler);
};
class BasicResponseTest : public TestHandler {
public:
enum TestMode {
// Normal load, nothing fancy.
LOAD,
// Close the browser in OnAfterCreated to verify destruction handling of
// uninitialized requests.
ABORT_AFTER_CREATED,
// Close the browser in OnBeforeBrowse to verify destruction handling of
// uninitialized requests.
ABORT_BEFORE_BROWSE,
// Don't continue from OnBeforeResourceLoad, then close the browser to
// verify destruction handling of in-progress requests.
INCOMPLETE_BEFORE_RESOURCE_LOAD,
// Modify the request (add headers) in OnBeforeResourceLoad.
MODIFY_BEFORE_RESOURCE_LOAD,
// Redirect the request (change the URL) in OnBeforeResourceLoad.
REDIRECT_BEFORE_RESOURCE_LOAD,
// Return a CefResourceHandler from GetResourceHandler that continues
// immediately by using the callback object instead of the return value.
IMMEDIATE_REQUEST_HANDLER_OPEN,
IMMEDIATE_REQUEST_HANDLER_READ,
IMMEDIATE_REQUEST_HANDLER_ALL,
// Return a CefResourceHandler from GetResourceHandler that continues with
// a delay by using the callback object.
DELAYED_REQUEST_HANDLER_OPEN,
DELAYED_REQUEST_HANDLER_READ,
DELAYED_REQUEST_HANDLER_ALL,
// Return a CefResourceHandler from GetResourceHandler that never completes,
// then close the browser to verify destruction handling of in-progress
// requests.
INCOMPLETE_REQUEST_HANDLER_OPEN,
INCOMPLETE_REQUEST_HANDLER_READ,
// Redirect the request using a CefResourceHandler returned from
// GetResourceHandler.
REDIRECT_REQUEST_HANDLER,
// Redirect the request (change the URL) an additional time in
// OnResourceRedirect after using a CefResourceHandler returned from
// GetResourceHandler for the first redirect.
REDIRECT_RESOURCE_REDIRECT,
// Redirect the request (change the URL) in OnResourceResponse.
REDIRECT_RESOURCE_RESPONSE,
// Restart the request (add headers) in OnResourceResponse.
RESTART_RESOURCE_RESPONSE,
};
// If |custom_scheme| is true all requests will use a custom scheme.
// If |unhandled| is true the final request (after any redirects) will be
// unhandled, meaning that default handling is disabled and GetResourceHandler
// returns null.
BasicResponseTest(TestMode mode, bool custom_scheme, bool unhandled)
: mode_(mode), custom_scheme_(custom_scheme), unhandled_(unhandled) {}
void RunTest() override {
CreateBrowser(GetStartupURL());
SetTestTimeout();
}
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
EXPECT_UI_THREAD();
TestHandler::OnAfterCreated(browser);
if (mode_ == ABORT_AFTER_CREATED) {
SetSignalCompletionWhenAllBrowsersClose(false);
CloseBrowser(browser, false);
}
}
void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
EXPECT_UI_THREAD();
TestHandler::OnBeforeClose(browser);
if (IsAborted()) {
DestroyTest();
}
}
bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
bool is_redirect) override {
EXPECT_UI_THREAD();
if (browser_id_ == 0) {
// This is the first callback that provides a browser ID.
browser_id_ = browser->GetIdentifier();
EXPECT_GT(browser_id_, 0);
} else {
EXPECT_EQ(browser_id_, browser->GetIdentifier());
}
EXPECT_TRUE(frame->IsMain());
EXPECT_FALSE(user_gesture);
if (on_before_browse_ct_ == 0 || mode_ == RESTART_RESOURCE_RESPONSE) {
EXPECT_FALSE(is_redirect) << on_before_browse_ct_;
} else {
EXPECT_TRUE(is_redirect) << on_before_browse_ct_;
}
on_before_browse_ct_++;
VerifyState(kOnBeforeBrowse, request, nullptr);
if (mode_ == ABORT_BEFORE_BROWSE) {
SetSignalCompletionWhenAllBrowsersClose(false);
CloseBrowser(browser, false);
}
return false;
}
CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool is_navigation,
bool is_download,
const CefString& request_initiator,
bool& disable_default_handling) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
if (request_id_ == 0U) {
// This is the first callback that provides a request ID.
request_id_ = request->GetIdentifier();
EXPECT_GT(request_id_, 0U);
}
VerifyState(kGetResourceRequestHandler, request, nullptr);
EXPECT_TRUE(is_navigation);
EXPECT_FALSE(is_download);
EXPECT_STREQ("null", request_initiator.ToString().c_str());
// Check expected default value.
if (custom_scheme_) {
// There is no default handling for custom schemes.
EXPECT_TRUE(disable_default_handling);
} else {
EXPECT_FALSE(disable_default_handling);
// If |unhandled_| is true then we don't want default handling of requests
// (e.g. attempts to resolve over the network).
disable_default_handling = unhandled_;
}
get_resource_request_handler_ct_++;
return this;
}
CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
VerifyState(kGetCookieAccessFilter, request, nullptr);
get_cookie_access_filter_ct_++;
return nullptr;
}
cef_return_value_t OnBeforeResourceLoad(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefRequestCallback> callback) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
VerifyState(kOnBeforeResourceLoad, request, nullptr);
on_before_resource_load_ct_++;
if (mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD) {
incomplete_callback_ = callback;
// Close the browser asynchronously to complete the test.
CloseBrowserAsync();
return RV_CONTINUE_ASYNC;
}
if (mode_ == MODIFY_BEFORE_RESOURCE_LOAD) {
// Expect this data in the request for future callbacks.
SetCustomHeader(request);
} else if (mode_ == REDIRECT_BEFORE_RESOURCE_LOAD) {
// Redirect to this URL.
request->SetURL(GetURL(RESULT_HTML));
}
// Other continuation modes are tested by
// ResourceRequestHandlerTest.BeforeResourceLoad*.
return RV_CONTINUE;
}
CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
VerifyState(kGetResourceHandler, request, nullptr);
get_resource_handler_ct_++;
if (IsIncompleteRequestHandler()) {
// Close the browser asynchronously to complete the test.
CloseBrowserAsync();
return GetIncompleteResource();
}
const std::string& url = request->GetURL();
if (url == GetURL(RESULT_HTML) && mode_ == RESTART_RESOURCE_RESPONSE) {
if (get_resource_handler_ct_ == 1) {
// First request that will be restarted after response.
return GetOKResource();
} else {
// Restarted request.
if (unhandled_)
return nullptr;
return GetOKResource();
}
} else if (url == GetURL(RESULT_HTML)) {
if (unhandled_)
return nullptr;
return GetOKResource();
} else if (url == GetURL(REDIRECT_HTML) &&
mode_ == REDIRECT_RESOURCE_RESPONSE) {
if (get_resource_handler_ct_ == 1) {
// First request that will be redirected after response.
return GetOKResource();
} else {
// Redirected request.
if (unhandled_)
return nullptr;
return GetOKResource();
}
} else if (url == GetURL(REDIRECT_HTML) || url == GetURL(REDIRECT2_HTML)) {
std::string redirect_url;
if (mode_ == REDIRECT_REQUEST_HANDLER ||
mode_ == REDIRECT_RESOURCE_RESPONSE) {
EXPECT_STREQ(GetURL(REDIRECT_HTML), url.c_str());
redirect_url = GetURL(RESULT_HTML);
} else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
EXPECT_STREQ(GetURL(REDIRECT2_HTML), url.c_str());
redirect_url = GetURL(REDIRECT_HTML);
} else {
NOTREACHED();
}
return GetRedirectResource(redirect_url);
} else {
NOTREACHED();
return nullptr;
}
}
void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
CefString& new_url) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
VerifyState(kOnResourceRedirect, request, response);
if (mode_ == REDIRECT_REQUEST_HANDLER ||
mode_ == REDIRECT_RESOURCE_RESPONSE) {
// The URL redirected to from GetResourceHandler or OnResourceResponse.
EXPECT_STREQ(GetURL(RESULT_HTML), new_url.ToString().c_str());
} else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
if (on_resource_redirect_ct_ == 0) {
// The URL redirected to from GetResourceHandler.
EXPECT_STREQ(GetURL(REDIRECT_HTML), new_url.ToString().c_str());
// Redirect again.
new_url = GetURL(RESULT_HTML);
} else {
NOTREACHED();
}
}
on_resource_redirect_ct_++;
}
bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
VerifyState(kOnResourceResponse, request, response);
on_resource_response_ct_++;
if (on_resource_response_ct_ == 1) {
if (mode_ == REDIRECT_RESOURCE_RESPONSE) {
// Redirect the request to this URL.
request->SetURL(GetURL(RESULT_HTML));
return true;
} else if (mode_ == RESTART_RESOURCE_RESPONSE) {
// Restart the request loading this data.
SetCustomHeader(request);
return true;
}
}
return false;
}
CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
VerifyState(kGetResourceResponseFilter, request, response);
get_resource_response_filter_ct_++;
return nullptr;
}
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
VerifyState(kOnResourceLoadComplete, request, response);
if (unhandled_ || IsIncomplete() || IsAborted()) {
EXPECT_EQ(UR_FAILED, status);
EXPECT_EQ(0, received_content_length);
} else {
EXPECT_EQ(UR_SUCCESS, status);
EXPECT_EQ(static_cast<int64>(GetResponseBody().length()),
received_content_length);
}
on_resource_load_complete_ct_++;
if (IsIncomplete()) {
MaybeDestroyTest(false);
}
}
void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool& allow_os_execution) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
EXPECT_TRUE(custom_scheme_);
EXPECT_TRUE(unhandled_);
// Check expected default value.
EXPECT_FALSE(allow_os_execution);
VerifyState(kOnProtocolExecution, request, nullptr);
on_protocol_execution_ct_++;
}
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) override {
EXPECT_UI_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame->IsMain());
if (unhandled_)
EXPECT_EQ(httpStatusCode, 0);
else
EXPECT_EQ(httpStatusCode, 200);
on_load_end_ct_++;
TestHandler::OnLoadEnd(browser, frame, httpStatusCode);
DestroyTest();
}
void DestroyTest() override {
if (mode_ == RESTART_RESOURCE_RESPONSE) {
EXPECT_EQ(1, on_before_browse_ct_);
EXPECT_EQ(2, get_resource_request_handler_ct_);
EXPECT_EQ(2, get_cookie_access_filter_ct_);
EXPECT_EQ(2, on_before_resource_load_ct_);
EXPECT_EQ(2, get_resource_handler_ct_);
EXPECT_EQ(0, on_resource_redirect_ct_);
// Unhandled requests won't see a call to GetResourceResponseFilter or
// OnResourceResponse. In this case we're restarting from inside
// OnResourceResponse.
if (unhandled_) {
EXPECT_EQ(0, get_resource_response_filter_ct_);
EXPECT_EQ(1, on_resource_response_ct_);
} else {
EXPECT_EQ(1, get_resource_response_filter_ct_);
EXPECT_EQ(2, on_resource_response_ct_);
}
} else if (IsLoad()) {
EXPECT_EQ(1, on_before_browse_ct_);
EXPECT_EQ(1, get_resource_request_handler_ct_);
EXPECT_EQ(1, get_cookie_access_filter_ct_);
EXPECT_EQ(1, on_before_resource_load_ct_);
EXPECT_EQ(1, get_resource_handler_ct_);
EXPECT_EQ(0, on_resource_redirect_ct_);
// Unhandled requests won't see a call to GetResourceResponseFilter
// or OnResourceResponse.
if (unhandled_) {
EXPECT_EQ(0, get_resource_response_filter_ct_);
EXPECT_EQ(0, on_resource_response_ct_);
} else {
EXPECT_EQ(1, get_resource_response_filter_ct_);
EXPECT_EQ(1, on_resource_response_ct_);
}
} else if (IsRedirect()) {
EXPECT_EQ(2, on_before_browse_ct_);
EXPECT_EQ(2, get_resource_request_handler_ct_);
EXPECT_EQ(2, get_cookie_access_filter_ct_);
EXPECT_EQ(2, on_before_resource_load_ct_);
if (mode_ == REDIRECT_BEFORE_RESOURCE_LOAD) {
EXPECT_EQ(1, get_resource_handler_ct_);
} else {
EXPECT_EQ(2, get_resource_handler_ct_);
}
EXPECT_EQ(1, on_resource_redirect_ct_);
// Unhandled requests won't see a call to GetResourceResponseFilter.
if (unhandled_) {
EXPECT_EQ(0, get_resource_response_filter_ct_);
} else {
EXPECT_EQ(1, get_resource_response_filter_ct_);
}
// Unhandled requests won't see a call to OnResourceResponse.
if (mode_ == REDIRECT_RESOURCE_RESPONSE) {
// In this case we're redirecting from inside OnResourceResponse.
if (unhandled_) {
EXPECT_EQ(1, on_resource_response_ct_);
} else {
EXPECT_EQ(2, on_resource_response_ct_);
}
} else {
if (unhandled_) {
EXPECT_EQ(0, on_resource_response_ct_);
} else {
EXPECT_EQ(1, on_resource_response_ct_);
}
}
} else if (IsIncomplete()) {
EXPECT_EQ(1, on_before_browse_ct_);
EXPECT_EQ(1, get_resource_request_handler_ct_);
EXPECT_EQ(1, get_cookie_access_filter_ct_);
EXPECT_EQ(1, on_before_resource_load_ct_);
if (IsIncompleteRequestHandler()) {
EXPECT_EQ(1, get_resource_handler_ct_);
} else {
EXPECT_EQ(0, get_resource_handler_ct_);
}
EXPECT_EQ(0, on_resource_redirect_ct_);
if (mode_ == INCOMPLETE_REQUEST_HANDLER_READ) {
EXPECT_EQ(1, get_resource_response_filter_ct_);
EXPECT_EQ(1, on_resource_response_ct_);
} else {
EXPECT_EQ(0, get_resource_response_filter_ct_);
EXPECT_EQ(0, on_resource_response_ct_);
}
} else if (IsAborted()) {
EXPECT_EQ(1, on_before_browse_ct_);
if (custom_scheme_) {
EXPECT_EQ(0, get_resource_request_handler_ct_);
EXPECT_EQ(0, get_cookie_access_filter_ct_);
} else {
// The callbacks executed for standard schemes may vary based on timing.
}
EXPECT_EQ(0, on_before_resource_load_ct_);
EXPECT_EQ(0, get_resource_handler_ct_);
EXPECT_EQ(0, on_resource_redirect_ct_);
EXPECT_EQ(0, get_resource_response_filter_ct_);
EXPECT_EQ(0, on_resource_response_ct_);
} else {
NOTREACHED();
}
EXPECT_EQ(resource_handler_created_ct_, resource_handler_destroyed_ct_);
if (IsAborted()) {
EXPECT_EQ(0, on_resource_load_complete_ct_);
} else {
EXPECT_EQ(1, on_resource_load_complete_ct_);
}
if (IsIncomplete() || IsAborted()) {
EXPECT_EQ(0, on_load_end_ct_);
} else {
EXPECT_EQ(1, on_load_end_ct_);
}
if (custom_scheme_ && unhandled_ && !(IsIncomplete() || IsAborted())) {
EXPECT_EQ(1, on_protocol_execution_ct_);
} else {
EXPECT_EQ(0, on_protocol_execution_ct_);
}
TestHandler::DestroyTest();
if (!SignalCompletionWhenAllBrowsersClose()) {
// Complete asynchronously so the call stack has a chance to unwind.
CefPostTask(TID_UI, base::Bind(&BasicResponseTest::TestComplete, this));
}
}
private:
enum TestUrl {
RESULT_HTML,
REDIRECT_HTML,
REDIRECT2_HTML,
};
const char* GetURL(TestUrl url) const {
if (custom_scheme_) {
if (url == RESULT_HTML)
return "rrhcustom://test.com/result.html";
if (url == REDIRECT_HTML)
return "rrhcustom://test.com/redirect.html";
if (url == REDIRECT2_HTML)
return "rrhcustom://test.com/redirect2.html";
} else {
if (url == RESULT_HTML)
return "http://test.com/result.html";
if (url == REDIRECT_HTML)
return "http://test.com/redirect.html";
if (url == REDIRECT2_HTML)
return "http://test.com/redirect2.html";
}
NOTREACHED();
return "";
}
const char* GetStartupURL() const {
if (IsLoad() || IsIncomplete() || IsAborted()) {
return GetURL(RESULT_HTML);
} else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
return GetURL(REDIRECT2_HTML);
} else if (IsRedirect()) {
return GetURL(REDIRECT_HTML);
}
NOTREACHED();
return "";
}
std::string GetResponseBody() const {
return "<html><body>Response</body></html>";
}
std::string GetRedirectBody() const {
return "<html><body>Redirect</body></html>";
}
base::Closure GetResourceDestroyCallback() {
resource_handler_created_ct_++;
return base::Bind(&BasicResponseTest::MaybeDestroyTest, this, true);
}
bool GetCallbackResourceHandlerMode(CallbackResourceHandler::Mode& mode) {
switch (mode_) {
case IMMEDIATE_REQUEST_HANDLER_OPEN:
mode = CallbackResourceHandler::IMMEDIATE_OPEN;
return true;
case IMMEDIATE_REQUEST_HANDLER_READ:
mode = CallbackResourceHandler::IMMEDIATE_READ;
return true;
case IMMEDIATE_REQUEST_HANDLER_ALL:
mode = CallbackResourceHandler::IMMEDIATE_ALL;
return true;
case DELAYED_REQUEST_HANDLER_OPEN:
mode = CallbackResourceHandler::DELAYED_OPEN;
return true;
case DELAYED_REQUEST_HANDLER_READ:
mode = CallbackResourceHandler::DELAYED_READ;
return true;
case DELAYED_REQUEST_HANDLER_ALL:
mode = CallbackResourceHandler::DELAYED_ALL;
return true;
default:
break;
}
return false;
}
CefRefPtr<CefResourceHandler> GetResource(int status_code,
const CefString& status_text,
const CefString& mime_type,
CefResponse::HeaderMap header_map,
const std::string& body) {
CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
const_cast<char*>(body.c_str()), body.size());
CallbackResourceHandler::Mode handler_mode;
if (GetCallbackResourceHandlerMode(handler_mode)) {
return new CallbackResourceHandler(handler_mode, status_code, status_text,
mime_type, header_map, stream,
GetResourceDestroyCallback());
}
return new NormalResourceHandler(status_code, status_text, mime_type,
header_map, stream,
GetResourceDestroyCallback());
}
CefRefPtr<CefResourceHandler> GetOKResource() {
return GetResource(200, "OK", "text/html", CefResponse::HeaderMap(),
GetResponseBody());
}
CefRefPtr<CefResourceHandler> GetRedirectResource(
const std::string& redirect_url) {
CefResponse::HeaderMap headerMap;
headerMap.insert(std::make_pair("Location", redirect_url));
return GetResource(307, "Temporary Redirect", "text/html", headerMap,
GetRedirectBody());
}
CefRefPtr<CefResourceHandler> GetIncompleteResource() {
if (TestOldResourceAPI()) {
return new IncompleteResourceHandlerOld(
mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN
? IncompleteResourceHandlerOld::BLOCK_PROCESS_REQUEST
: IncompleteResourceHandlerOld::BLOCK_READ_RESPONSE,
"text/html", GetResourceDestroyCallback());
}
return new IncompleteResourceHandler(
mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN
? IncompleteResourceHandler::BLOCK_OPEN
: IncompleteResourceHandler::BLOCK_READ,
"text/html", GetResourceDestroyCallback());
}
bool IsLoad() const {
return mode_ == LOAD || mode_ == MODIFY_BEFORE_RESOURCE_LOAD ||
mode_ == RESTART_RESOURCE_RESPONSE ||
mode_ == IMMEDIATE_REQUEST_HANDLER_OPEN ||
mode_ == IMMEDIATE_REQUEST_HANDLER_READ ||
mode_ == IMMEDIATE_REQUEST_HANDLER_ALL ||
mode_ == DELAYED_REQUEST_HANDLER_OPEN ||
mode_ == DELAYED_REQUEST_HANDLER_READ ||
mode_ == DELAYED_REQUEST_HANDLER_ALL;
}
bool IsIncompleteRequestHandler() const {
return mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN ||
mode_ == INCOMPLETE_REQUEST_HANDLER_READ;
}
bool IsIncomplete() const {
return mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD ||
IsIncompleteRequestHandler();
}
bool IsAborted() const {
return mode_ == ABORT_AFTER_CREATED || mode_ == ABORT_BEFORE_BROWSE;
}
bool IsRedirect() const {
return mode_ == REDIRECT_BEFORE_RESOURCE_LOAD ||
mode_ == REDIRECT_REQUEST_HANDLER ||
mode_ == REDIRECT_RESOURCE_REDIRECT ||
mode_ == REDIRECT_RESOURCE_RESPONSE;
}
static void SetCustomHeader(CefRefPtr<CefRequest> request) {
EXPECT_FALSE(request->IsReadOnly());
request->SetHeaderByName("X-Custom-Header", "value", false);
}
static std::string GetCustomHeader(CefRefPtr<CefRequest> request) {
return request->GetHeaderByName("X-Custom-Header");
}
// Resource-related callbacks.
enum Callback {
kOnBeforeBrowse,
kGetResourceRequestHandler,
kGetCookieAccessFilter,
kOnBeforeResourceLoad,
kGetResourceHandler,
kOnResourceRedirect,
kOnResourceResponse,
kGetResourceResponseFilter,
kOnResourceLoadComplete,
kOnProtocolExecution,
};
bool ShouldHaveResponse(Callback callback) const {
return callback >= kOnResourceRedirect &&
callback <= kOnResourceLoadComplete;
}
bool ShouldHaveWritableRequest(Callback callback) const {
return callback == kOnBeforeResourceLoad || callback == kOnResourceResponse;
}
void VerifyState(Callback callback,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) const {
EXPECT_TRUE(request) << callback;
if (ShouldHaveResponse(callback)) {
EXPECT_TRUE(response) << callback;
EXPECT_TRUE(response->IsReadOnly()) << callback;
} else {
EXPECT_FALSE(response) << callback;
}
if (ShouldHaveWritableRequest(callback)) {
EXPECT_FALSE(request->IsReadOnly()) << callback;
} else {
EXPECT_TRUE(request->IsReadOnly()) << callback;
}
if (callback == kOnBeforeBrowse) {
// Browser-side navigation no longer exposes the actual request
// information.
EXPECT_EQ(0U, request->GetIdentifier()) << callback;
} else {
// All resource-related callbacks share the same request ID.
EXPECT_EQ(request_id_, request->GetIdentifier()) << callback;
}
if (IsLoad() || IsIncomplete() || IsAborted()) {
EXPECT_STREQ("GET", request->GetMethod().ToString().c_str()) << callback;
EXPECT_STREQ(GetURL(RESULT_HTML), request->GetURL().ToString().c_str())
<< callback;
// Expect the header for all callbacks following the callback that
// initially sets it.
const std::string& custom_header = GetCustomHeader(request);
if ((mode_ == RESTART_RESOURCE_RESPONSE &&
on_resource_response_ct_ > 0) ||
(mode_ == MODIFY_BEFORE_RESOURCE_LOAD &&
on_before_resource_load_ct_ > 0)) {
EXPECT_STREQ("value", custom_header.c_str()) << callback;
} else {
EXPECT_STREQ("", custom_header.c_str()) << callback;
}
if (response)
VerifyOKResponse(callback, response);
} else if (IsRedirect()) {
EXPECT_STREQ("GET", request->GetMethod().ToString().c_str()) << callback;
if (on_before_browse_ct_ == 1) {
// Before the redirect.
EXPECT_STREQ(GetStartupURL(), request->GetURL().ToString().c_str())
<< callback;
} else if (on_before_browse_ct_ == 2) {
// After the redirect.
EXPECT_STREQ(GetURL(RESULT_HTML), request->GetURL().ToString().c_str())
<< callback;
} else {
NOTREACHED() << callback;
}
if (response) {
if (callback == kOnResourceRedirect) {
// Before the redirect.
VerifyRedirectResponse(callback, response);
} else {
// After the redirect.
VerifyOKResponse(callback, response);
}
}
} else {
NOTREACHED() << callback;
}
}
void VerifyOKResponse(Callback callback,
CefRefPtr<CefResponse> response) const {
// True for the first response in cases where we're redirecting/restarting
// from inside OnResourceResponse (e.g. the first response always succeeds).
const bool override_unhandled = unhandled_ &&
(mode_ == REDIRECT_RESOURCE_RESPONSE ||
mode_ == RESTART_RESOURCE_RESPONSE) &&
get_resource_handler_ct_ == 1;
// True for tests where the request will be incomplete and never receive a
// response.
const bool incomplete_unhandled =
(mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD ||
mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN ||
(IsAborted() && !custom_scheme_));
if ((unhandled_ && !override_unhandled) || incomplete_unhandled) {
if (incomplete_unhandled) {
EXPECT_EQ(ERR_ABORTED, response->GetError()) << callback;
} else {
EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, response->GetError()) << callback;
}
EXPECT_EQ(0, response->GetStatus()) << callback;
EXPECT_STREQ("", response->GetStatusText().ToString().c_str())
<< callback;
EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
EXPECT_STREQ("", response->GetMimeType().ToString().c_str()) << callback;
EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
} else {
if ((mode_ == INCOMPLETE_REQUEST_HANDLER_READ || IsAborted()) &&
callback == kOnResourceLoadComplete) {
// We got a response, but we also got aborted.
EXPECT_EQ(ERR_ABORTED, response->GetError()) << callback;
} else {
EXPECT_EQ(ERR_NONE, response->GetError()) << callback;
}
EXPECT_EQ(200, response->GetStatus()) << callback;
EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str())
<< callback;
EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
EXPECT_STREQ("text/html", response->GetMimeType().ToString().c_str())
<< callback;
EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
}
}
void VerifyRedirectResponse(Callback callback,
CefRefPtr<CefResponse> response) const {
EXPECT_EQ(ERR_NONE, response->GetError()) << callback;
EXPECT_EQ(307, response->GetStatus()) << callback;
const std::string& status_text = response->GetStatusText();
EXPECT_TRUE(status_text == "Internal Redirect" ||
status_text == "Temporary Redirect")
<< status_text << callback;
EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
EXPECT_STREQ("", response->GetMimeType().ToString().c_str()) << callback;
EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
}
void CloseBrowserAsync() {
EXPECT_TRUE(IsIncomplete());
SetSignalCompletionWhenAllBrowsersClose(false);
CefPostDelayedTask(
TID_UI, base::Bind(&TestHandler::CloseBrowser, GetBrowser(), false),
100);
}
void MaybeDestroyTest(bool from_handler) {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::Bind(&BasicResponseTest::MaybeDestroyTest, this,
from_handler));
return;
}
if (from_handler) {
resource_handler_destroyed_ct_++;
}
bool destroy_test = false;
if (IsIncomplete()) {
// Destroy the test if we got OnResourceLoadComplete and either the
// resource handler will never complete or it was destroyed.
destroy_test =
on_resource_load_complete_ct_ > 0 &&
(!IsIncompleteRequestHandler() ||
resource_handler_destroyed_ct_ == resource_handler_created_ct_);
} else {
// Destroy the test if we got OnLoadEnd and the expected number of
// resource handlers were destroyed.
destroy_test = on_load_end_ct_ > 0 && resource_handler_destroyed_ct_ ==
resource_handler_created_ct_;
}
if (destroy_test) {
DestroyTest();
}
}
const TestMode mode_;
const bool custom_scheme_;
const bool unhandled_;
int browser_id_ = 0;
uint64 request_id_ = 0U;
int resource_handler_created_ct_ = 0;
int on_before_browse_ct_ = 0;
int on_load_end_ct_ = 0;
int get_resource_request_handler_ct_ = 0;
int on_before_resource_load_ct_ = 0;
int get_cookie_access_filter_ct_ = 0;
int get_resource_handler_ct_ = 0;
int on_resource_redirect_ct_ = 0;
int on_resource_response_ct_ = 0;
int get_resource_response_filter_ct_ = 0;
int on_resource_load_complete_ct_ = 0;
int on_protocol_execution_ct_ = 0;
int resource_handler_destroyed_ct_ = 0;
// Used with INCOMPLETE_BEFORE_RESOURCE_LOAD.
CefRefPtr<CefRequestCallback> incomplete_callback_;
DISALLOW_COPY_AND_ASSIGN(BasicResponseTest);
IMPLEMENT_REFCOUNTING(BasicResponseTest);
};
} // namespace
#define BASIC_TEST(name, test_mode, custom, unhandled) \
TEST(ResourceRequestHandlerTest, Basic##name) { \
CefRefPtr<BasicResponseTest> handler = new BasicResponseTest( \
BasicResponseTest::test_mode, custom, unhandled); \
handler->ExecuteTest(); \
ReleaseAndWaitForDestructor(handler); \
}
#define BASIC_TEST_ALL_MODES(name, custom, unhandled) \
BASIC_TEST(name##Load, LOAD, custom, unhandled) \
BASIC_TEST(name##AbortAfterCreated, ABORT_AFTER_CREATED, custom, unhandled) \
BASIC_TEST(name##AbortBeforeBrowse, ABORT_BEFORE_BROWSE, custom, unhandled) \
BASIC_TEST(name##ModifyBeforeResourceLoad, MODIFY_BEFORE_RESOURCE_LOAD, \
custom, unhandled) \
BASIC_TEST(name##RedirectBeforeResourceLoad, REDIRECT_BEFORE_RESOURCE_LOAD, \
custom, unhandled) \
BASIC_TEST(name##RedirectRequestHandler, REDIRECT_REQUEST_HANDLER, custom, \
unhandled) \
BASIC_TEST(name##RedirectResourceRedirect, REDIRECT_RESOURCE_REDIRECT, \
custom, unhandled) \
BASIC_TEST(name##RedirectResourceResponse, REDIRECT_RESOURCE_RESPONSE, \
custom, unhandled) \
BASIC_TEST(name##RestartResourceResponse, RESTART_RESOURCE_RESPONSE, custom, \
unhandled)
// Tests only supported in handled mode.
#define BASIC_TEST_HANDLED_MODES(name, custom) \
BASIC_TEST(name##ImmediateRequestHandlerOpen, \
IMMEDIATE_REQUEST_HANDLER_OPEN, custom, false) \
BASIC_TEST(name##ImmediateRequestHandlerRead, \
IMMEDIATE_REQUEST_HANDLER_READ, custom, false) \
BASIC_TEST(name##ImmediateRequestHandlerAll, IMMEDIATE_REQUEST_HANDLER_ALL, \
custom, false) \
BASIC_TEST(name##DelayedRequestHandlerOpen, DELAYED_REQUEST_HANDLER_OPEN, \
custom, false) \
BASIC_TEST(name##DelayedRequestHandlerRead, DELAYED_REQUEST_HANDLER_READ, \
custom, false) \
BASIC_TEST(name##DelayedRequestHandlerAll, DELAYED_REQUEST_HANDLER_ALL, \
custom, false) \
BASIC_TEST(name##IncompleteBeforeResourceLoad, \
INCOMPLETE_BEFORE_RESOURCE_LOAD, custom, false) \
BASIC_TEST(name##IncompleteRequestHandlerOpen, \
INCOMPLETE_REQUEST_HANDLER_OPEN, custom, false) \
BASIC_TEST(name##IncompleteRequestHandlerRead, \
INCOMPLETE_REQUEST_HANDLER_READ, custom, false)
BASIC_TEST_ALL_MODES(StandardHandled, false, false)
BASIC_TEST_ALL_MODES(StandardUnhandled, false, true)
BASIC_TEST_ALL_MODES(CustomHandled, true, false)
BASIC_TEST_ALL_MODES(CustomUnhandled, true, true)
BASIC_TEST_HANDLED_MODES(StandardHandled, false)
BASIC_TEST_HANDLED_MODES(CustomHandled, true)
namespace {
const char kSubresourceProcessMsg[] = "SubresourceMsg";
class SubresourceResponseTest : public RoutingTestHandler {
public:
enum TestMode {
// Normal load, nothing fancy.
LOAD,
// Don't continue from OnBeforeResourceLoad, then close the browser to
// verify destruction handling of in-progress requests.
INCOMPLETE_BEFORE_RESOURCE_LOAD,
// Modify the request (add headers) in OnBeforeResourceLoad.
MODIFY_BEFORE_RESOURCE_LOAD,
// Redirect the request (change the URL) in OnBeforeResourceLoad.
REDIRECT_BEFORE_RESOURCE_LOAD,
// Return a CefResourceHandler from GetResourceHandler that continues
// immediately by using the callback object instead of the return value.
IMMEDIATE_REQUEST_HANDLER_OPEN,
IMMEDIATE_REQUEST_HANDLER_READ,
IMMEDIATE_REQUEST_HANDLER_ALL,
// Return a CefResourceHandler from GetResourceHandler that continues with
// a delay by using the callback object.
DELAYED_REQUEST_HANDLER_OPEN,
DELAYED_REQUEST_HANDLER_READ,
DELAYED_REQUEST_HANDLER_ALL,
// Return a CefResourceHandler from GetResourceHandler that never completes,
// then close the browser to verify destruction handling of in-progress
// requests.
INCOMPLETE_REQUEST_HANDLER_OPEN,
INCOMPLETE_REQUEST_HANDLER_READ,
// Redirect the request using a CefResourceHandler returned from
// GetResourceHandler.
REDIRECT_REQUEST_HANDLER,
// Redirect the request (change the URL) an additional time in
// OnResourceRedirect after using a CefResourceHandler returned from
// GetResourceHandler for the first redirect.
REDIRECT_RESOURCE_REDIRECT,
// Redirect the request (change the URL) in OnResourceResponse.
REDIRECT_RESOURCE_RESPONSE,
// Restart the request (add headers) in OnResourceResponse.
RESTART_RESOURCE_RESPONSE,
};
// If |custom_scheme| is true all requests will use a custom scheme.
// If |unhandled| is true the final request (after any redirects) will be
// unhandled, meaning that default handling is disabled and GetResourceHandler
// returns null.
// If |subframe| is true the resource will be loaded in an iframe.
SubresourceResponseTest(TestMode mode,
bool custom_scheme,
bool unhandled,
bool subframe)
: mode_(mode),
custom_scheme_(custom_scheme),
unhandled_(unhandled),
subframe_(subframe) {}
void RunTest() override {
CreateBrowser(GetMainURL());
SetTestTimeout();
}
bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
bool is_redirect) override {
EXPECT_UI_THREAD();
if (browser_id_ == 0) {
// This is the first callback that provides a browser ID.
browser_id_ = browser->GetIdentifier();
EXPECT_GT(browser_id_, 0);
} else {
EXPECT_EQ(browser_id_, browser->GetIdentifier());
}
if (IsMainURL(request->GetURL())) {
EXPECT_TRUE(frame->IsMain());
} else if (IsSubURL(request->GetURL())) {
EXPECT_FALSE(frame->IsMain());
EXPECT_TRUE(subframe_);
} else {
EXPECT_FALSE(true); // Not reached.
}
EXPECT_FALSE(user_gesture);
EXPECT_FALSE(is_redirect);
on_before_browse_ct_++;
return false;
}
CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool is_navigation,
bool is_download,
const CefString& request_initiator,
bool& disable_default_handling) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL())) {
EXPECT_TRUE(frame->IsMain());
} else if (IsSubURL(request->GetURL())) {
EXPECT_FALSE(frame->IsMain());
EXPECT_TRUE(subframe_);
}
if (IsMainURL(request->GetURL()) || IsSubURL(request->GetURL())) {
// Track the frame ID that we'll expect for resource callbacks.
// Do this here instead of OnBeforeBrowse because OnBeforeBrowse may
// return -4 (kInvalidFrameId) for the initial navigation.
if (frame_id_ == 0) {
if (subframe_) {
if (IsSubURL(request->GetURL()))
frame_id_ = frame->GetIdentifier();
} else {
frame_id_ = frame->GetIdentifier();
}
}
return this;
}
VerifyFrame(kGetResourceRequestHandler, frame);
if (request_id_ == 0U) {
// This is the first callback that provides a request ID.
request_id_ = request->GetIdentifier();
EXPECT_GT(request_id_, 0U);
}
VerifyState(kGetResourceRequestHandler, request, nullptr);
EXPECT_FALSE(is_navigation);
EXPECT_FALSE(is_download);
EXPECT_STREQ(GetOrigin(), request_initiator.ToString().c_str());
// Check expected default value.
if (custom_scheme_) {
// There is no default handling for custom schemes.
EXPECT_TRUE(disable_default_handling);
} else {
EXPECT_FALSE(disable_default_handling);
// If |unhandled_| is true then we don't want default handling of requests
// (e.g. attempts to resolve over the network).
disable_default_handling = unhandled_;
}
get_resource_request_handler_ct_++;
return this;
}
CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL())) {
EXPECT_TRUE(frame->IsMain());
return nullptr;
} else if (IsSubURL(request->GetURL())) {
EXPECT_FALSE(frame->IsMain());
EXPECT_TRUE(subframe_);
return nullptr;
}
VerifyFrame(kGetCookieAccessFilter, frame);
VerifyState(kGetCookieAccessFilter, request, nullptr);
get_cookie_access_filter_ct_++;
return nullptr;
}
cef_return_value_t OnBeforeResourceLoad(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefRequestCallback> callback) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL())) {
EXPECT_TRUE(frame->IsMain());
return RV_CONTINUE;
} else if (IsSubURL(request->GetURL())) {
EXPECT_FALSE(frame->IsMain());
EXPECT_TRUE(subframe_);
return RV_CONTINUE;
}
VerifyFrame(kOnBeforeResourceLoad, frame);
VerifyState(kOnBeforeResourceLoad, request, nullptr);
on_before_resource_load_ct_++;
if (mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD) {
incomplete_callback_ = callback;
// Close the browser asynchronously to complete the test.
CloseBrowserAsync();
return RV_CONTINUE_ASYNC;
}
if (mode_ == MODIFY_BEFORE_RESOURCE_LOAD) {
// Expect this data in the request for future callbacks.
SetCustomHeader(request);
} else if (mode_ == REDIRECT_BEFORE_RESOURCE_LOAD) {
// Redirect to this URL.
request->SetURL(GetURL(RESULT_JS));
}
// Other continuation modes are tested by
// ResourceRequestHandlerTest.BeforeResourceLoad*.
return RV_CONTINUE;
}
CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL())) {
EXPECT_TRUE(frame->IsMain());
return GetMainResource();
} else if (IsSubURL(request->GetURL())) {
EXPECT_FALSE(frame->IsMain());
EXPECT_TRUE(subframe_);
return GetSubResource();
}
VerifyFrame(kGetResourceHandler, frame);
VerifyState(kGetResourceHandler, request, nullptr);
get_resource_handler_ct_++;
if (IsIncompleteRequestHandler()) {
// Close the browser asynchronously to complete the test.
CloseBrowserAsync();
return GetIncompleteResource();
}
const std::string& url = request->GetURL();
if (url == GetURL(RESULT_JS) && mode_ == RESTART_RESOURCE_RESPONSE) {
if (get_resource_handler_ct_ == 1) {
// First request that will be restarted after response.
return GetOKResource();
} else {
// Restarted request.
if (unhandled_)
return nullptr;
return GetOKResource();
}
} else if (url == GetURL(RESULT_JS)) {
if (unhandled_)
return nullptr;
return GetOKResource();
} else if (url == GetURL(REDIRECT_JS) &&
mode_ == REDIRECT_RESOURCE_RESPONSE) {
if (get_resource_handler_ct_ == 1) {
// First request that will be redirected after response.
return GetOKResource();
} else {
// Redirected request.
if (unhandled_)
return nullptr;
return GetOKResource();
}
} else if (url == GetURL(REDIRECT_JS) || url == GetURL(REDIRECT2_JS)) {
std::string redirect_url;
if (mode_ == REDIRECT_REQUEST_HANDLER ||
mode_ == REDIRECT_RESOURCE_RESPONSE) {
EXPECT_STREQ(GetURL(REDIRECT_JS), url.c_str());
redirect_url = GetURL(RESULT_JS);
} else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
EXPECT_STREQ(GetURL(REDIRECT2_JS), url.c_str());
redirect_url = GetURL(REDIRECT_JS);
} else {
NOTREACHED();
}
return GetRedirectResource(redirect_url);
} else {
NOTREACHED();
return nullptr;
}
}
void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
CefString& new_url) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL()) || IsSubURL(request->GetURL())) {
EXPECT_FALSE(true); // Not reached.
return;
}
VerifyFrame(kOnResourceRedirect, frame);
VerifyState(kOnResourceRedirect, request, response);
if (mode_ == REDIRECT_REQUEST_HANDLER ||
mode_ == REDIRECT_RESOURCE_RESPONSE) {
// The URL redirected to from GetResourceHandler or OnResourceResponse.
EXPECT_STREQ(GetURL(RESULT_JS), new_url.ToString().c_str());
} else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
if (on_resource_redirect_ct_ == 0) {
// The URL redirected to from GetResourceHandler.
EXPECT_STREQ(GetURL(REDIRECT_JS), new_url.ToString().c_str());
// Redirect again.
new_url = GetURL(RESULT_JS);
} else {
NOTREACHED();
}
}
on_resource_redirect_ct_++;
}
bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL())) {
EXPECT_TRUE(frame->IsMain());
return false;
} else if (IsSubURL(request->GetURL())) {
EXPECT_FALSE(frame->IsMain());
EXPECT_TRUE(subframe_);
return false;
}
VerifyFrame(kOnResourceResponse, frame);
VerifyState(kOnResourceResponse, request, response);
on_resource_response_ct_++;
if (on_resource_response_ct_ == 1) {
if (mode_ == REDIRECT_RESOURCE_RESPONSE) {
// Redirect the request to this URL.
request->SetURL(GetURL(RESULT_JS));
return true;
} else if (mode_ == RESTART_RESOURCE_RESPONSE) {
// Restart the request loading this data.
SetCustomHeader(request);
return true;
}
}
return false;
}
CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL())) {
EXPECT_TRUE(frame->IsMain());
return nullptr;
} else if (IsSubURL(request->GetURL())) {
EXPECT_FALSE(frame->IsMain());
EXPECT_TRUE(subframe_);
return nullptr;
}
VerifyFrame(kGetResourceResponseFilter, frame);
VerifyState(kGetResourceResponseFilter, request, response);
get_resource_response_filter_ct_++;
return nullptr;
}
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL())) {
EXPECT_TRUE(frame->IsMain());
EXPECT_EQ(UR_SUCCESS, status);
EXPECT_EQ(static_cast<int64>(GetMainResponseBody().length()),
received_content_length);
return;
} else if (IsSubURL(request->GetURL())) {
EXPECT_FALSE(frame->IsMain());
EXPECT_EQ(UR_SUCCESS, status);
EXPECT_EQ(static_cast<int64>(GetSubResponseBody().length()),
received_content_length);
EXPECT_TRUE(subframe_);
return;
}
VerifyFrame(kOnResourceLoadComplete, frame);
VerifyState(kOnResourceLoadComplete, request, response);
if (unhandled_ || IsIncomplete()) {
EXPECT_EQ(UR_FAILED, status);
EXPECT_EQ(0, received_content_length);
} else {
EXPECT_EQ(UR_SUCCESS, status);
EXPECT_EQ(static_cast<int64>(GetResponseBody().length()),
received_content_length);
}
on_resource_load_complete_ct_++;
if (IsIncomplete()) {
MaybeDestroyTest(false);
}
}
void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool& allow_os_execution) override {
EXPECT_IO_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
if (IsMainURL(request->GetURL()) || IsSubURL(request->GetURL())) {
EXPECT_FALSE(true); // Not reached.
return;
}
VerifyFrame(kOnProtocolExecution, frame);
EXPECT_TRUE(custom_scheme_);
EXPECT_TRUE(unhandled_);
// Check expected default value.
EXPECT_FALSE(allow_os_execution);
VerifyState(kOnProtocolExecution, request, nullptr);
on_protocol_execution_ct_++;
}
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) override {
EXPECT_UI_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_EQ(httpStatusCode, 200);
on_load_end_ct_++;
TestHandler::OnLoadEnd(browser, frame, httpStatusCode);
MaybeDestroyTest(false);
}
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
EXPECT_UI_THREAD();
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_STREQ(kSubresourceProcessMsg, request.ToString().c_str());
VerifyFrame(kOnQuery, frame);
callback->Success("");
on_query_ct_++;
MaybeDestroyTest(false);
return true;
}
void DestroyTest() override {
// Only called for the main and/or sub frame load.
if (subframe_) {
EXPECT_EQ(2, on_before_browse_ct_);
} else {
EXPECT_EQ(1, on_before_browse_ct_);
}
if (mode_ == RESTART_RESOURCE_RESPONSE) {
EXPECT_EQ(2, get_resource_request_handler_ct_);
EXPECT_EQ(2, get_cookie_access_filter_ct_);
EXPECT_EQ(2, on_before_resource_load_ct_);
EXPECT_EQ(2, get_resource_handler_ct_);
EXPECT_EQ(0, on_resource_redirect_ct_);
// Unhandled requests won't see a call to GetResourceResponseFilter or
// OnResourceResponse. In this case we're restarting from inside
// OnResourceResponse.
if (unhandled_) {
EXPECT_EQ(0, get_resource_response_filter_ct_);
EXPECT_EQ(1, on_resource_response_ct_);
} else {
EXPECT_EQ(1, get_resource_response_filter_ct_);
EXPECT_EQ(2, on_resource_response_ct_);
}
} else if (IsLoad()) {
EXPECT_EQ(1, get_resource_request_handler_ct_);
EXPECT_EQ(1, get_cookie_access_filter_ct_);
EXPECT_EQ(1, on_before_resource_load_ct_);
EXPECT_EQ(1, get_resource_handler_ct_);
EXPECT_EQ(0, on_resource_redirect_ct_);
// Unhandled requests won't see a call to GetResourceResponseFilter or
// OnResourceResponse.
if (unhandled_) {
EXPECT_EQ(0, get_resource_response_filter_ct_);
EXPECT_EQ(0, on_resource_response_ct_);
} else {
EXPECT_EQ(1, get_resource_response_filter_ct_);
EXPECT_EQ(1, on_resource_response_ct_);
}
} else if (IsRedirect()) {
EXPECT_EQ(2, get_resource_request_handler_ct_);
EXPECT_EQ(2, get_cookie_access_filter_ct_);
EXPECT_EQ(2, on_before_resource_load_ct_);
if (mode_ == REDIRECT_BEFORE_RESOURCE_LOAD) {
EXPECT_EQ(1, get_resource_handler_ct_);
} else {
EXPECT_EQ(2, get_resource_handler_ct_);
}
EXPECT_EQ(1, on_resource_redirect_ct_);
// Unhandled requests won't see a call to GetResourceResponseFilter.
if (unhandled_)
EXPECT_EQ(0, get_resource_response_filter_ct_);
else
EXPECT_EQ(1, get_resource_response_filter_ct_);
// Unhandled requests won't see a call to OnResourceResponse.
if (mode_ == REDIRECT_RESOURCE_RESPONSE) {
// In this case we're redirecting from inside OnResourceResponse.
if (unhandled_)
EXPECT_EQ(1, on_resource_response_ct_);
else
EXPECT_EQ(2, on_resource_response_ct_);
} else {
if (unhandled_)
EXPECT_EQ(0, on_resource_response_ct_);
else
EXPECT_EQ(1, on_resource_response_ct_);
}
} else if (IsIncomplete()) {
EXPECT_EQ(1, get_resource_request_handler_ct_);
EXPECT_EQ(1, get_cookie_access_filter_ct_);
EXPECT_EQ(1, on_before_resource_load_ct_);
if (IsIncompleteRequestHandler()) {
EXPECT_EQ(1, get_resource_handler_ct_);
} else {
EXPECT_EQ(0, get_resource_handler_ct_);
}
EXPECT_EQ(0, on_resource_redirect_ct_);
if (mode_ == INCOMPLETE_REQUEST_HANDLER_READ) {
EXPECT_EQ(1, get_resource_response_filter_ct_);
EXPECT_EQ(1, on_resource_response_ct_);
} else {
EXPECT_EQ(0, get_resource_response_filter_ct_);
EXPECT_EQ(0, on_resource_response_ct_);
}
} else {
NOTREACHED();
}
EXPECT_EQ(resource_handler_created_ct_, resource_handler_destroyed_ct_);
EXPECT_EQ(1, on_resource_load_complete_ct_);
// Only called for the main and/or sub frame load.
if (IsIncomplete()) {
EXPECT_EQ(0, on_load_end_ct_);
} else {
if (subframe_) {
EXPECT_EQ(2, on_load_end_ct_);
} else {
EXPECT_EQ(1, on_load_end_ct_);
}
}
if (unhandled_ || IsIncomplete()) {
EXPECT_EQ(0, on_query_ct_);
} else {
EXPECT_EQ(1, on_query_ct_);
}
if (custom_scheme_ && unhandled_ && !IsIncomplete()) {
EXPECT_EQ(1, on_protocol_execution_ct_);
} else {
EXPECT_EQ(0, on_protocol_execution_ct_);
}
TestHandler::DestroyTest();
if (!SignalCompletionWhenAllBrowsersClose()) {
// Complete asynchronously so the call stack has a chance to unwind.
CefPostTask(TID_UI,
base::Bind(&SubresourceResponseTest::TestComplete, this));
}
}
private:
const char* GetMainURL() const {
if (custom_scheme_) {
return "rrhcustom://test.com/main.html";
} else {
return "http://test.com/main.html";
}
}
const char* GetSubURL() const {
if (custom_scheme_) {
return "rrhcustom://test.com/subframe.html";
} else {
return "http://test.com/subframe.html";
}
}
const char* GetOrigin() const {
if (custom_scheme_) {
return "rrhcustom://test.com";
} else {
return "http://test.com";
}
}
bool IsMainURL(const std::string& url) const { return url == GetMainURL(); }
bool IsSubURL(const std::string& url) const { return url == GetSubURL(); }
enum TestUrl {
RESULT_JS,
REDIRECT_JS,
REDIRECT2_JS,
};
const char* GetURL(TestUrl url) const {
if (custom_scheme_) {
if (url == RESULT_JS)
return "rrhcustom://test.com/result.js";
if (url == REDIRECT_JS)
return "rrhcustom://test.com/redirect.js";
if (url == REDIRECT2_JS)
return "rrhcustom://test.com/redirect2.js";
} else {
if (url == RESULT_JS)
return "http://test.com/result.js";
if (url == REDIRECT_JS)
return "http://test.com/redirect.js";
if (url == REDIRECT2_JS)
return "http://test.com/redirect2.js";
}
NOTREACHED();
return "";
}
const char* GetStartupURL() const {
if (IsLoad() || IsIncomplete()) {
return GetURL(RESULT_JS);
} else if (mode_ == REDIRECT_RESOURCE_REDIRECT) {
return GetURL(REDIRECT2_JS);
} else if (IsRedirect()) {
return GetURL(REDIRECT_JS);
}
NOTREACHED();
return "";
}
std::string GetMainResponseBody() const {
std::stringstream html;
html << "<html><head>";
if (subframe_) {
const std::string& url = GetSubURL();
html << "<iframe src=\"" << url << "\"></iframe>";
} else {
const std::string& url = GetStartupURL();
html << "<script type=\"text/javascript\" src=\"" << url
<< "\"></script>";
}
html << "</head><body><p>Main</p></body></html>";
return html.str();
}
std::string GetSubResponseBody() const {
DCHECK(subframe_);
std::stringstream html;
html << "<html><head>";
const std::string& url = GetStartupURL();
html << "<script type=\"text/javascript\" src=\"" << url << "\"></script>";
html << "</head><body><p>Sub</p></body></html>";
return html.str();
}
std::string GetResponseBody() const {
return "window.testQuery({request:'" + std::string(kSubresourceProcessMsg) +
"'});";
}
std::string GetRedirectBody() const {
return "<html><body>Redirect</body></html>";
}
base::Closure GetResourceDestroyCallback() {
resource_handler_created_ct_++;
return base::Bind(&SubresourceResponseTest::MaybeDestroyTest, this, true);
}
bool GetCallbackResourceHandlerMode(CallbackResourceHandler::Mode& mode) {
switch (mode_) {
case IMMEDIATE_REQUEST_HANDLER_OPEN:
mode = CallbackResourceHandler::IMMEDIATE_OPEN;
return true;
case IMMEDIATE_REQUEST_HANDLER_READ:
mode = CallbackResourceHandler::IMMEDIATE_READ;
return true;
case IMMEDIATE_REQUEST_HANDLER_ALL:
mode = CallbackResourceHandler::IMMEDIATE_ALL;
return true;
case DELAYED_REQUEST_HANDLER_OPEN:
mode = CallbackResourceHandler::DELAYED_OPEN;
return true;
case DELAYED_REQUEST_HANDLER_READ:
mode = CallbackResourceHandler::DELAYED_READ;
return true;
case DELAYED_REQUEST_HANDLER_ALL:
mode = CallbackResourceHandler::DELAYED_ALL;
return true;
default:
break;
}
return false;
}
CefRefPtr<CefResourceHandler> GetResource(int status_code,
const CefString& status_text,
const CefString& mime_type,
CefResponse::HeaderMap header_map,
const std::string& body) {
CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
const_cast<char*>(body.c_str()), body.size());
CallbackResourceHandler::Mode handler_mode;
if (GetCallbackResourceHandlerMode(handler_mode)) {
return new CallbackResourceHandler(handler_mode, status_code, status_text,
mime_type, header_map, stream,
GetResourceDestroyCallback());
}
return new NormalResourceHandler(status_code, status_text, mime_type,
header_map, stream,
GetResourceDestroyCallback());
}
CefRefPtr<CefResourceHandler> GetMainResource() {
return GetResource(200, "OK", "text/html", CefResponse::HeaderMap(),
GetMainResponseBody());
}
CefRefPtr<CefResourceHandler> GetSubResource() {
return GetResource(200, "OK", "text/html", CefResponse::HeaderMap(),
GetSubResponseBody());
}
CefRefPtr<CefResourceHandler> GetOKResource() {
return GetResource(200, "OK", "text/javascript", CefResponse::HeaderMap(),
GetResponseBody());
}
CefRefPtr<CefResourceHandler> GetRedirectResource(
const std::string& redirect_url) {
CefResponse::HeaderMap headerMap;
headerMap.insert(std::make_pair("Location", redirect_url));
return GetResource(307, "Temporary Redirect", "text/javascript", headerMap,
GetRedirectBody());
}
CefRefPtr<CefResourceHandler> GetIncompleteResource() {
if (TestOldResourceAPI()) {
return new IncompleteResourceHandlerOld(
mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN
? IncompleteResourceHandlerOld::BLOCK_PROCESS_REQUEST
: IncompleteResourceHandlerOld::BLOCK_READ_RESPONSE,
"text/javascript", GetResourceDestroyCallback());
}
return new IncompleteResourceHandler(
mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN
? IncompleteResourceHandler::BLOCK_OPEN
: IncompleteResourceHandler::BLOCK_READ,
"text/javascript", GetResourceDestroyCallback());
}
bool IsLoad() const {
return mode_ == LOAD || mode_ == MODIFY_BEFORE_RESOURCE_LOAD ||
mode_ == RESTART_RESOURCE_RESPONSE ||
mode_ == IMMEDIATE_REQUEST_HANDLER_OPEN ||
mode_ == IMMEDIATE_REQUEST_HANDLER_READ ||
mode_ == IMMEDIATE_REQUEST_HANDLER_ALL ||
mode_ == DELAYED_REQUEST_HANDLER_OPEN ||
mode_ == DELAYED_REQUEST_HANDLER_READ ||
mode_ == DELAYED_REQUEST_HANDLER_ALL;
}
bool IsIncompleteRequestHandler() const {
return mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN ||
mode_ == INCOMPLETE_REQUEST_HANDLER_READ;
}
bool IsIncomplete() const {
return mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD ||
IsIncompleteRequestHandler();
}
bool IsRedirect() const {
return mode_ == REDIRECT_BEFORE_RESOURCE_LOAD ||
mode_ == REDIRECT_REQUEST_HANDLER ||
mode_ == REDIRECT_RESOURCE_REDIRECT ||
mode_ == REDIRECT_RESOURCE_RESPONSE;
}
static void SetCustomHeader(CefRefPtr<CefRequest> request) {
EXPECT_FALSE(request->IsReadOnly());
request->SetHeaderByName("X-Custom-Header", "value", false);
}
static std::string GetCustomHeader(CefRefPtr<CefRequest> request) {
return request->GetHeaderByName("X-Custom-Header");
}
// Resource-related callbacks.
enum Callback {
kGetResourceRequestHandler,
kGetCookieAccessFilter,
kOnBeforeResourceLoad,
kGetResourceHandler,
kOnResourceRedirect,
kOnResourceResponse,
kGetResourceResponseFilter,
kOnResourceLoadComplete,
kOnProtocolExecution,
kOnQuery,
};
bool ShouldHaveResponse(Callback callback) const {
return callback >= kOnResourceRedirect &&
callback <= kOnResourceLoadComplete;
}
bool ShouldHaveWritableRequest(Callback callback) const {
return callback == kOnBeforeResourceLoad || callback == kOnResourceResponse;
}
void VerifyFrame(Callback callback, CefRefPtr<CefFrame> frame) const {
EXPECT_TRUE(frame);
if (subframe_)
EXPECT_FALSE(frame->IsMain()) << callback;
else
EXPECT_TRUE(frame->IsMain()) << callback;
EXPECT_EQ(frame_id_, frame->GetIdentifier()) << callback;
}
void VerifyState(Callback callback,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) const {
EXPECT_TRUE(request) << callback;
if (ShouldHaveResponse(callback)) {
EXPECT_TRUE(response) << callback;
EXPECT_TRUE(response->IsReadOnly()) << callback;
} else {
EXPECT_FALSE(response) << callback;
}
if (ShouldHaveWritableRequest(callback)) {
EXPECT_FALSE(request->IsReadOnly()) << callback;
} else {
EXPECT_TRUE(request->IsReadOnly()) << callback;
}
// All resource-related callbacks share the same request ID.
EXPECT_EQ(request_id_, request->GetIdentifier()) << callback;
if (IsLoad() || IsIncomplete()) {
EXPECT_STREQ("GET", request->GetMethod().ToString().c_str()) << callback;
EXPECT_STREQ(GetURL(RESULT_JS), request->GetURL().ToString().c_str())
<< callback;
// Expect the header for all callbacks following the callback that
// initially sets it.
const std::string& custom_header = GetCustomHeader(request);
if ((mode_ == RESTART_RESOURCE_RESPONSE &&
on_resource_response_ct_ > 0) ||
(mode_ == MODIFY_BEFORE_RESOURCE_LOAD &&
on_before_resource_load_ct_ > 0)) {
EXPECT_STREQ("value", custom_header.c_str()) << callback;
} else {
EXPECT_STREQ("", custom_header.c_str()) << callback;
}
if (response)
VerifyOKResponse(callback, response);
} else if (IsRedirect()) {
EXPECT_STREQ("GET", request->GetMethod().ToString().c_str()) << callback;
// Subresource loads don't get OnBeforeBrowse calls, so this check is a
// bit less exact then with main resource loads.
if (on_resource_redirect_ct_ == 0) {
// Before the redirect.
EXPECT_STREQ(GetStartupURL(), request->GetURL().ToString().c_str())
<< callback;
} else {
// After the redirect.
EXPECT_STREQ(GetURL(RESULT_JS), request->GetURL().ToString().c_str())
<< callback;
}
if (response) {
if (callback == kOnResourceRedirect) {
// Before the redirect.
VerifyRedirectResponse(callback, response);
} else {
// After the redirect.
VerifyOKResponse(callback, response);
}
}
} else {
NOTREACHED() << callback;
}
}
void VerifyOKResponse(Callback callback,
CefRefPtr<CefResponse> response) const {
// True for the first response in cases where we're redirecting/restarting
// from inside OnResourceResponse (e.g. the first response always succeeds).
const bool override_unhandled = unhandled_ &&
(mode_ == REDIRECT_RESOURCE_RESPONSE ||
mode_ == RESTART_RESOURCE_RESPONSE) &&
get_resource_handler_ct_ == 1;
// True for tests where the request will be incomplete and never receive a
// response.
const bool incomplete_unhandled =
(mode_ == INCOMPLETE_BEFORE_RESOURCE_LOAD ||
mode_ == INCOMPLETE_REQUEST_HANDLER_OPEN);
if ((unhandled_ && !override_unhandled) || incomplete_unhandled) {
if (incomplete_unhandled) {
EXPECT_EQ(ERR_ABORTED, response->GetError()) << callback;
} else {
EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, response->GetError()) << callback;
}
EXPECT_EQ(0, response->GetStatus()) << callback;
EXPECT_STREQ("", response->GetStatusText().ToString().c_str())
<< callback;
EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
EXPECT_STREQ("", response->GetMimeType().ToString().c_str()) << callback;
EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
} else {
if (mode_ == INCOMPLETE_REQUEST_HANDLER_READ &&
callback == kOnResourceLoadComplete) {
// We got a response, but we also got aborted.
EXPECT_EQ(ERR_ABORTED, response->GetError()) << callback;
} else {
EXPECT_EQ(ERR_NONE, response->GetError()) << callback;
}
EXPECT_EQ(200, response->GetStatus()) << callback;
EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str())
<< callback;
EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
EXPECT_STREQ("text/javascript",
response->GetMimeType().ToString().c_str())
<< callback;
EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
}
}
void VerifyRedirectResponse(Callback callback,
CefRefPtr<CefResponse> response) const {
EXPECT_EQ(ERR_NONE, response->GetError()) << callback;
EXPECT_EQ(307, response->GetStatus()) << callback;
const std::string& status_text = response->GetStatusText();
EXPECT_TRUE(status_text == "Internal Redirect" ||
status_text == "Temporary Redirect")
<< status_text << callback;
EXPECT_STREQ("", response->GetURL().ToString().c_str()) << callback;
EXPECT_STREQ("", response->GetMimeType().ToString().c_str()) << callback;
EXPECT_STREQ("", response->GetCharset().ToString().c_str()) << callback;
}
void CloseBrowserAsync() {
EXPECT_TRUE(IsIncomplete());
SetSignalCompletionWhenAllBrowsersClose(false);
CefPostDelayedTask(
TID_UI, base::Bind(&TestHandler::CloseBrowser, GetBrowser(), false),
100);
}
void MaybeDestroyTest(bool from_handler) {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::Bind(&SubresourceResponseTest::MaybeDestroyTest,
this, from_handler));
return;
}
if (from_handler) {
resource_handler_destroyed_ct_++;
}
bool destroy_test = false;
if (IsIncomplete()) {
// Destroy the test if we got OnResourceLoadComplete and either the
// resource handler will never complete or it was destroyed.
destroy_test =
on_resource_load_complete_ct_ > 0 &&
(!IsIncompleteRequestHandler() ||
resource_handler_destroyed_ct_ == resource_handler_created_ct_);
} else {
// Destroy the test if we got the expected number of OnLoadEnd and
// OnQuery, and the expected number of resource handlers were destroyed.
destroy_test =
on_load_end_ct_ > (subframe_ ? 1 : 0) &&
(on_query_ct_ > 0 || unhandled_) &&
resource_handler_destroyed_ct_ == resource_handler_created_ct_;
}
if (destroy_test) {
DestroyTest();
}
}
const TestMode mode_;
const bool custom_scheme_;
const bool unhandled_;
const bool subframe_;
int browser_id_ = 0;
int64 frame_id_ = 0;
uint64 request_id_ = 0U;
int resource_handler_created_ct_ = 0;
int on_before_browse_ct_ = 0;
int on_load_end_ct_ = 0;
int on_query_ct_ = 0;
int get_resource_request_handler_ct_ = 0;
int get_cookie_access_filter_ct_ = 0;
int on_before_resource_load_ct_ = 0;
int get_resource_handler_ct_ = 0;
int on_resource_redirect_ct_ = 0;
int on_resource_response_ct_ = 0;
int get_resource_response_filter_ct_ = 0;
int on_resource_load_complete_ct_ = 0;
int on_protocol_execution_ct_ = 0;
int resource_handler_destroyed_ct_ = 0;
// Used with INCOMPLETE_BEFORE_RESOURCE_LOAD.
CefRefPtr<CefRequestCallback> incomplete_callback_;
DISALLOW_COPY_AND_ASSIGN(SubresourceResponseTest);
IMPLEMENT_REFCOUNTING(SubresourceResponseTest);
};
} // namespace
#define SUBRESOURCE_TEST(name, test_mode, custom, unhandled, subframe) \
TEST(ResourceRequestHandlerTest, Subresource##name) { \
CefRefPtr<SubresourceResponseTest> handler = new SubresourceResponseTest( \
SubresourceResponseTest::test_mode, custom, unhandled, subframe); \
handler->ExecuteTest(); \
ReleaseAndWaitForDestructor(handler); \
}
#define SUBRESOURCE_TEST_ALL_MODES(name, custom, unhandled, subframe) \
SUBRESOURCE_TEST(name##Load, LOAD, custom, unhandled, subframe) \
SUBRESOURCE_TEST(name##ModifyBeforeResourceLoad, \
MODIFY_BEFORE_RESOURCE_LOAD, custom, unhandled, subframe) \
SUBRESOURCE_TEST(name##RedirectBeforeResourceLoad, \
REDIRECT_BEFORE_RESOURCE_LOAD, custom, unhandled, subframe) \
SUBRESOURCE_TEST(name##RedirectRequestHandler, REDIRECT_REQUEST_HANDLER, \
custom, unhandled, subframe) \
SUBRESOURCE_TEST(name##RedirectResourceRedirect, REDIRECT_RESOURCE_REDIRECT, \
custom, unhandled, subframe) \
SUBRESOURCE_TEST(name##RedirectResourceResponse, REDIRECT_RESOURCE_RESPONSE, \
custom, unhandled, subframe) \
SUBRESOURCE_TEST(name##RestartResourceResponse, RESTART_RESOURCE_RESPONSE, \
custom, unhandled, subframe)
// Tests only supported in handled mode.
#define SUBRESOURCE_TEST_HANDLED_MODES(name, custom, subframe) \
SUBRESOURCE_TEST(name##ImmediateRequestHandlerOpen, \
IMMEDIATE_REQUEST_HANDLER_OPEN, custom, false, subframe) \
SUBRESOURCE_TEST(name##ImmediateRequestHandlerRead, \
IMMEDIATE_REQUEST_HANDLER_READ, custom, false, subframe) \
SUBRESOURCE_TEST(name##ImmediateRequestHandlerAll, \
IMMEDIATE_REQUEST_HANDLER_ALL, custom, false, subframe) \
SUBRESOURCE_TEST(name##DelayedRequestHandlerOpen, \
DELAYED_REQUEST_HANDLER_OPEN, custom, false, subframe) \
SUBRESOURCE_TEST(name##DelayedRequestHandlerRead, \
DELAYED_REQUEST_HANDLER_READ, custom, false, subframe) \
SUBRESOURCE_TEST(name##DelayedRequestHandlerAll, \
DELAYED_REQUEST_HANDLER_ALL, custom, false, subframe) \
SUBRESOURCE_TEST(name##IncompleteBeforeResourceLoad, \
INCOMPLETE_BEFORE_RESOURCE_LOAD, custom, false, subframe) \
SUBRESOURCE_TEST(name##IncompleteRequestHandlerOpen, \
INCOMPLETE_REQUEST_HANDLER_OPEN, custom, false, subframe) \
SUBRESOURCE_TEST(name##IncompleteRequestHandlerRead, \
INCOMPLETE_REQUEST_HANDLER_READ, custom, false, subframe)
SUBRESOURCE_TEST_ALL_MODES(StandardHandledMainFrame, false, false, false)
SUBRESOURCE_TEST_ALL_MODES(StandardUnhandledMainFrame, false, true, false)
SUBRESOURCE_TEST_ALL_MODES(CustomHandledMainFrame, true, false, false)
SUBRESOURCE_TEST_ALL_MODES(CustomUnhandledMainFrame, true, true, false)
SUBRESOURCE_TEST_ALL_MODES(StandardHandledSubFrame, false, false, true)
SUBRESOURCE_TEST_ALL_MODES(StandardUnhandledSubFrame, false, true, true)
SUBRESOURCE_TEST_ALL_MODES(CustomHandledSubFrame, true, false, true)
SUBRESOURCE_TEST_ALL_MODES(CustomUnhandledSubFrame, true, true, true)
SUBRESOURCE_TEST_HANDLED_MODES(StandardHandledMainFrame, false, false)
SUBRESOURCE_TEST_HANDLED_MODES(CustomHandledMainFrame, true, false)
SUBRESOURCE_TEST_HANDLED_MODES(StandardHandledSubFrame, false, true)
SUBRESOURCE_TEST_HANDLED_MODES(CustomHandledSubFrame, true, true)
namespace {
const char kResourceTestHtml[] = "http://test.com/resource.html";
class RedirectResponseTest : public TestHandler {
public:
enum TestMode {
URL,
HEADER,
POST,
};
RedirectResponseTest(TestMode mode, bool via_request_context_handler)
: via_request_context_handler_(via_request_context_handler) {
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");
resource_request_handler_ = new ResourceRequestHandler(this);
CefRefPtr<CefRequestContext> request_context =
CefRequestContext::GetGlobalContext();
if (via_request_context_handler_) {
CefRefPtr<CefRequestContextHandler> request_context_handler =
new RequestContextHandler(resource_request_handler_.get());
CefRequestContextSettings settings;
request_context = CefRequestContext::CreateContext(
request_context, request_context_handler);
}
CreateBrowser(kResourceTestHtml, request_context);
SetTestTimeout();
}
CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool is_navigation,
bool is_download,
const CefString& request_initiator,
bool& disable_default_handling) override {
if (via_request_context_handler_) {
// Use the handler returned by RequestContextHandler.
return nullptr;
}
return resource_request_handler_.get();
}
bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
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());
// Browser-side navigation no longer exposes the actual request information.
EXPECT_EQ(0U, request->GetIdentifier());
return false;
}
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> 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(nullptr);
TestHandler::DestroyTest();
}
private:
std::string GetHtml() const {
std::stringstream html;
html << "<html><head>";
const std::string& url = resource_test_->start_url();
html << "<script type=\"text/javascript\" src=\"" << url << "\"></script>";
html << "</head><body><p>Main</p></body></html>";
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,
size_t expected_resource_load_complete_ct = 1U)
: start_url_(start_url),
expected_resource_response_ct_(expected_resource_response_ct),
expected_before_resource_load_ct_(expected_before_resource_load_ct),
expected_resource_redirect_ct_(expected_resource_redirect_ct),
expected_resource_load_complete_ct_(
expected_resource_load_complete_ct) {}
virtual ~ResourceTest() {}
const std::string& start_url() const { return start_url_; }
virtual bool OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
before_resource_load_ct_++;
return false;
}
virtual CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
get_resource_handler_ct_++;
const std::string& js_content = "<!-- -->";
CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
const_cast<char*>(js_content.c_str()), js_content.size());
return new CefStreamResourceHandler(200, "OK", "text/javascript",
CefResponse::HeaderMap(), stream);
}
virtual void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefString& new_url) {
resource_redirect_ct_++;
}
bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> 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_);
}
CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) {
get_resource_response_filter_ct_++;
return nullptr;
}
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) {
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());
resource_load_complete_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_resource_load_complete_ct_,
get_resource_response_filter_ct_);
EXPECT_EQ(expected_before_resource_load_ct_, before_resource_load_ct_);
EXPECT_EQ(expected_resource_redirect_ct_, resource_redirect_ct_);
EXPECT_EQ(expected_resource_load_complete_ct_,
resource_load_complete_ct_);
}
protected:
virtual void OnResourceReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) {
got_resource_.yes();
}
virtual void OnRetryReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) {
got_resource_retry_.yes();
}
private:
std::string start_url_;
size_t resource_response_ct_ = 0U;
size_t expected_resource_response_ct_;
size_t before_resource_load_ct_ = 0U;
size_t expected_before_resource_load_ct_;
size_t get_resource_handler_ct_ = 0U;
size_t resource_redirect_ct_ = 0U;
size_t expected_resource_redirect_ct_;
size_t get_resource_response_filter_ct_ = 0U;
size_t resource_load_complete_ct_ = 0U;
size_t expected_resource_load_complete_ct_;
TrackCallback got_resource_;
TrackCallback got_resource_retry_;
};
class UrlResourceTest : public ResourceTest {
public:
// With NetworkService we don't get an additional (unnecessary) redirect
// callback.
UrlResourceTest()
: ResourceTest("http://test.com/start_url.js", 2U, 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<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> 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<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
ResourceTest::OnResourceReceived(browser, frame, request, response);
request->SetURL(redirect_url_);
}
void OnRetryReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> 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:
// With NetworkService we restart the request, so we get another call to
// OnBeforeResourceLoad.
HeaderResourceTest()
: ResourceTest("http://test.com/start_header.js", 2U, 2U) {
expected_headers_.insert(std::make_pair("Test-Key1", "Value1"));
expected_headers_.insert(std::make_pair("Test-Key2", "Value2"));
}
private:
void OnResourceReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
ResourceTest::OnResourceReceived(browser, frame, request, response);
request->SetHeaderMap(expected_headers_);
}
void OnRetryReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> 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:
// With NetworkService we restart the request, so we get another call to
// OnBeforeResourceLoad.
PostResourceTest() : ResourceTest("http://test.com/start_post.js", 2U, 2U) {
CefRefPtr<CefPostDataElement> 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<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
ResourceTest::OnResourceReceived(browser, frame, request, response);
request->SetPostData(expected_post_);
}
void OnRetryReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
ResourceTest::OnRetryReceived(browser, frame, request, response);
CefRefPtr<CefPostData> actual_post = request->GetPostData();
TestPostDataEqual(expected_post_, actual_post);
}
CefRefPtr<CefPostData> expected_post_;
};
class RequestContextHandler : public CefRequestContextHandler {
public:
explicit RequestContextHandler(
CefRefPtr<CefResourceRequestHandler> resource_request_handler)
: resource_request_handler_(resource_request_handler) {}
CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool is_navigation,
bool is_download,
const CefString& request_initiator,
bool& disable_default_handling) override {
return resource_request_handler_;
}
private:
CefRefPtr<CefResourceRequestHandler> resource_request_handler_;
IMPLEMENT_REFCOUNTING(RequestContextHandler);
DISALLOW_COPY_AND_ASSIGN(RequestContextHandler);
};
class ResourceRequestHandler : public CefResourceRequestHandler {
public:
explicit ResourceRequestHandler(RedirectResponseTest* test) : test_(test) {}
cef_return_value_t OnBeforeResourceLoad(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,