// Copyright (c) 2022 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 "tests/ceftests/message_router_unittest_utils.h"
namespace {
const char kTestDomain1[] = "https://tests-mr1.com/";
const char kTestDomain2[] = "https://tests-mr2.com/";
const char kTestDomain3[] = "https://tests-mr3.com/";
const char kMultiQueryRequestId[] = "request_id";
const char kMultiQueryRepeatCt[] = "repeat_ct";
const char kMultiQueryRequest[] = "request";
const char kMultiQueryResponse[] = "response";
const char kMultiQuerySuccess[] = "success";
const char kMultiQueryError[] = "error";
const char kMultiQueryErrorMessage[] = "errormsg";
const int kMultiQueryPersistentResponseCount = 5;
// Generates HTML and verifies results for multiple simultanious queries.
class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
public:
enum TestType {
// Initiates a non-persistent query with a successful response.
// OnQuery and OnNotify will be called.
SUCCESS,
// Initiates a non-persistent query with a failure response.
// OnQuery and OnNotify will be called.
FAILURE,
// Initiates a persistent query with multiple successful responses.
// OnQuery, OnNotify and OnQueryCanceled will be called.
PERSISTENT_SUCCESS,
// Initiates a persistent query with multiple successful responses and one
// failure response.
// OnQuery and OnNotify will be called.
PERSISTENT_FAILURE,
// Initiates a non-persistent query that will be canceled via JavaScript.
// No JavaScript callbacks will be executed.
// OnQuery and OnQueryCanceled will be called.
CANCEL,
// Initiates a non-persistent query that will not be manually canceled.
// No JavaScript callbacks will be executed.
// OnQuery and OnQueryCanceled will be called.
AUTOCANCEL,
// Initiates a persistent query with multiple successful responses that will
// not be manually canceled.
// OnQuery, OnNotify and OnQueryCanceled will be called.
PERSISTENT_AUTOCANCEL,
};
class Observer {
public:
// Called when all manual queries are complete.
virtual void OnManualQueriesCompleted(MultiQueryManager* manager) {}
// Called when all queries are complete.
virtual void OnAllQueriesCompleted(MultiQueryManager* manager) {}
protected:
virtual ~Observer() {}
};
MultiQueryManager(const std::string& label,
bool synchronous,
int id_offset = 0)
: label_(label),
synchronous_(synchronous),
id_offset_(id_offset),
finalized_(false),
running_(false),
manual_total_(0),
received_count_(0),
manual_complete_count_(0),
auto_complete_count_(0),
will_cancel_by_removing_handler_(false),
weak_ptr_factory_(this) {}
virtual ~MultiQueryManager() {}
std::string label() const { return label_; }
void AddObserver(Observer* observer) {
EXPECT_FALSE(running_);
observer_set_.insert(observer);
}
void RemoveObserver(Observer* observer) {
EXPECT_FALSE(running_);
EXPECT_TRUE(observer_set_.erase(observer));
}
// Can be called from any thread, but should always be called from the same
// thread.
void AddTestQuery(TestType type) {
EXPECT_FALSE(finalized_);
test_query_vector_.push_back(TestQuery(type));
if (!IsAuto(type)) {
manual_total_++;
}
}
// Must be called after AddTestQuery and before the manager is used.
void Finalize() {
EXPECT_FALSE(finalized_);
finalized_ = true;
}
// Call after all manual queries have completed if you intend to cancel auto
// queries by removing the handler.
void WillCancelByRemovingHandler() {
EXPECT_TRUE(IsManualComplete());
will_cancel_by_removing_handler_ = true;
}
std::string GetHTML(bool assert_total, bool assert_browser) const {
EXPECT_TRUE(finalized_);
EXPECT_FALSE(running_);
std::string html;
html = "
" + label_ + "";
return html;
}
void OnNotify(CefRefPtr browser,
CefRefPtr frame,
const std::string& message) {
EXPECT_TRUE(finalized_);
EXPECT_UI_THREAD();
if (!running_) {
running_ = true;
}
EXPECT_TRUE(browser.get());
EXPECT_TRUE(frame.get());
std::string value;
int index = 0;
EXPECT_TRUE(SplitIDString(message, &value, &index));
TestQuery& query = test_query_vector_[index];
// Verify that browser and frame are the same.
EXPECT_EQ(query.browser_id, browser->GetIdentifier()) << index;
EXPECT_EQ(query.frame_id, frame->GetIdentifier()) << index;
// Verify a successful/expected result.
if (will_cancel_by_removing_handler_) {
// Auto queries receive an onFailure callback which will notify with error
// when the handler is removed.
EXPECT_STREQ(kMultiQueryError, value.c_str()) << index;
EXPECT_TRUE(IsAuto(query.type)) << index;
EXPECT_TRUE(query.got_query) << index;
if (query.type == PERSISTENT_AUTOCANCEL) {
EXPECT_TRUE(query.got_success) << index;
} else {
EXPECT_FALSE(query.got_success) << index;
}
query.got_error.yes();
// There's a race between OnQueryCanceled and OnNotification. Only call
// OnQueryCompleted a single time.
if (query.got_query_canceled) {
OnQueryCompleted(query.type);
}
} else {
EXPECT_STREQ(kMultiQuerySuccess, value.c_str()) << index;
EXPECT_TRUE(WillNotify(query.type)) << index;
EXPECT_TRUE(query.got_query) << index;
EXPECT_FALSE(query.got_query_canceled) << index;
EXPECT_FALSE(query.got_success) << index;
query.got_success.yes();
// PERSISTENT_AUTOCANCEL doesn't call OnReceiveCompleted from OnQuery.
if (query.type == PERSISTENT_AUTOCANCEL) {
OnReceiveCompleted(query.type);
}
// Call OnQueryCompleted for types that don't get OnQueryCanceled.
if (!WillCancel(query.type)) {
OnQueryCompleted(query.type);
}
}
}
bool OnQuery(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id,
const CefString& request,
bool persistent,
CefRefPtr callback) override {
EXPECT_TRUE(finalized_);
EXPECT_UI_THREAD();
if (!running_) {
running_ = true;
}
EXPECT_TRUE(browser.get());
EXPECT_TRUE(frame.get());
EXPECT_NE(0, query_id);
std::string value;
int index = 0;
EXPECT_TRUE(SplitIDString(request, &value, &index));
TestQuery& query = test_query_vector_[index];
if (IsPersistent(query.type)) {
EXPECT_TRUE(persistent);
} else {
EXPECT_FALSE(persistent);
}
// Verify expected request.
EXPECT_STREQ(kMultiQueryRequest, value.c_str()) << index;
// Verify that call order is correct.
EXPECT_FALSE(query.got_query) << index;
EXPECT_FALSE(query.got_query_canceled) << index;
EXPECT_FALSE(query.got_success) << index;
EXPECT_FALSE(query.got_error) << index;
query.got_query.yes();
query.browser_id = browser->GetIdentifier();
query.frame_id = frame->GetIdentifier();
query.is_main_frame = frame->IsMain();
if (query.type == SUCCESS) {
// Send the single success response.
callback->Success(GetIDString(kMultiQueryResponse, index));
} else if (IsPersistent(query.type)) {
// Send the required number of successful responses.
const std::string& response = GetIDString(kMultiQueryResponse, index);
for (int i = 0; i < kMultiQueryPersistentResponseCount; ++i) {
callback->Success(response);
}
}
if (WillFail(query.type)) {
// Send the single failure response.
callback->Failure(index, GetIDString(kMultiQueryErrorMessage, index));
}
if (WillCancel(query.type)) {
// Hold onto the callback until the query is canceled.
query.query_id = query_id;
query.callback = callback;
}
// PERSISTENT_AUTOCANCEL will call OnReceiveCompleted once the success
// notification is received.
if (query.type != PERSISTENT_AUTOCANCEL) {
OnReceiveCompleted(query.type);
}
return true;
}
void OnQueryCanceled(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id) override {
EXPECT_TRUE(finalized_);
EXPECT_UI_THREAD();
if (!running_) {
running_ = true;
}
EXPECT_TRUE(browser.get());
EXPECT_TRUE(frame.get());
EXPECT_NE(0, query_id);
bool found = false;
for (size_t i = 0; i < test_query_vector_.size(); ++i) {
TestQuery& query = test_query_vector_[i];
if (query.query_id == query_id) {
// Verify that browser and frame are the same.
EXPECT_EQ(query.browser_id, browser->GetIdentifier()) << i;
if (query.is_main_frame) {
EXPECT_TRUE(frame->IsMain()) << i;
} else {
EXPECT_FALSE(frame->IsMain()) << i;
EXPECT_EQ(query.frame_id, frame->GetIdentifier()) << i;
}
// Verify a successful/expected result.
EXPECT_TRUE(WillCancel(query.type)) << i;
EXPECT_TRUE(query.callback.get()) << i;
// Release the callback.
query.callback = nullptr;
// Verify that call order is correct.
EXPECT_TRUE(query.got_query) << i;
if (query.type == CANCEL || query.type == AUTOCANCEL) {
// No JavaScript onSuccess callback executes.
EXPECT_FALSE(query.got_success) << i;
} else {
// JavaScript onSuccess does execute before cancellation.
EXPECT_TRUE(query.got_success) << i;
}
query.got_query_canceled.yes();
if (will_cancel_by_removing_handler_) {
// There's a race between OnQueryCanceled and OnNotification. Only
// call OnQueryCompleted a single time.
if (query.got_error) {
OnQueryCompleted(query.type);
}
} else {
EXPECT_FALSE(query.got_error) << i;
// Cancellation is always completion.
OnQueryCompleted(query.type);
}
found = true;
break;
}
}
EXPECT_TRUE(found);
}
// Asserts that all queries have completed.
void AssertAllComplete() const {
EXPECT_TRUE(finalized_);
EXPECT_FALSE(running_);
EXPECT_UI_THREAD();
for (size_t i = 0; i < test_query_vector_.size(); ++i) {
const TestQuery& query = test_query_vector_[i];
EXPECT_TRUE(query.got_query) << i;
if (WillCancel(query.type)) {
EXPECT_TRUE(query.got_query_canceled) << i;
} else {
EXPECT_FALSE(query.got_query_canceled) << i;
}
if (WillNotify(query.type)) {
EXPECT_TRUE(query.got_success) << i;
} else {
EXPECT_FALSE(query.got_success) << i;
}
if (IsAuto(query.type) && will_cancel_by_removing_handler_) {
EXPECT_TRUE(query.got_error);
} else {
EXPECT_FALSE(query.got_error);
}
EXPECT_FALSE(query.callback.get()) << i;
}
}
// Returns true if all manual queries have completed.
bool IsManualComplete() const {
EXPECT_TRUE(finalized_);
EXPECT_UI_THREAD();
return (manual_complete_count_ == manual_total_);
}
// Returns true if all queries have completed.
bool IsAllComplete() const {
EXPECT_TRUE(finalized_);
EXPECT_UI_THREAD();
return (manual_complete_count_ + auto_complete_count_ ==
static_cast(test_query_vector_.size()));
}
bool HasAutoQueries() const {
return (manual_total_ != static_cast(test_query_vector_.size()));
}
private:
struct TestQuery {
explicit TestQuery(TestType test_type)
: type(test_type),
browser_id(0),
frame_id(0),
is_main_frame(false),
query_id(0) {}
TestType type;
// Set in OnQuery and verified in OnNotify or OnQueryCanceled.
int browser_id;
int64_t frame_id;
bool is_main_frame;
// Used when a query is canceled.
int64_t query_id;
CefRefPtr callback;
TrackCallback got_query;
TrackCallback got_query_canceled;
TrackCallback got_success;
TrackCallback got_error;
};
class NotifyTask : public CefTask {
public:
NotifyTask(base::WeakPtr weak_ptr, bool notify_all)
: weak_ptr_(weak_ptr), notify_all_(notify_all) {}
void Execute() override {
if (weak_ptr_) {
if (notify_all_) {
weak_ptr_->NotifyAllQueriesCompleted();
} else {
weak_ptr_->NotifyManualQueriesCompleted();
}
}
}
private:
base::WeakPtr weak_ptr_;
const bool notify_all_;
IMPLEMENT_REFCOUNTING(NotifyTask);
};
static bool IsAuto(TestType type) {
return (type == AUTOCANCEL || type == PERSISTENT_AUTOCANCEL);
}
static bool IsPersistent(TestType type) {
return (type == PERSISTENT_SUCCESS || type == PERSISTENT_FAILURE ||
type == PERSISTENT_AUTOCANCEL);
}
static bool WillFail(TestType type) {
return (type == FAILURE || type == PERSISTENT_FAILURE);
}
static bool WillCancel(TestType type) {
return (type == PERSISTENT_SUCCESS || type == CANCEL ||
type == AUTOCANCEL || type == PERSISTENT_AUTOCANCEL);
}
static bool WillNotify(TestType type) {
return (type == SUCCESS || type == PERSISTENT_SUCCESS || type == FAILURE ||
type == PERSISTENT_FAILURE || type == PERSISTENT_AUTOCANCEL);
}
void OnReceiveCompleted(TestType type) {
const int total_count = static_cast(test_query_vector_.size());
if (++received_count_ == total_count && manual_total_ == 0) {
// There aren't any manual queries so notify here.
CefPostTask(TID_UI,
new NotifyTask(weak_ptr_factory_.GetWeakPtr(), false));
}
}
void OnQueryCompleted(TestType type) {
const int total_count = static_cast(test_query_vector_.size());
EXPECT_LT(manual_complete_count_ + auto_complete_count_, total_count);
EXPECT_LE(manual_complete_count_, manual_total_);
const bool is_auto = IsAuto(type);
if (is_auto) {
auto_complete_count_++;
} else if (++manual_complete_count_ == manual_total_) {
CefPostTask(TID_UI,
new NotifyTask(weak_ptr_factory_.GetWeakPtr(), false));
}
if (auto_complete_count_ + manual_complete_count_ == total_count) {
running_ = false;
CefPostTask(TID_UI, new NotifyTask(weak_ptr_factory_.GetWeakPtr(), true));
}
}
void NotifyManualQueriesCompleted() {
if (observer_set_.empty()) {
return;
}
// Use a copy of the set in case an Observer is removed while we're
// iterating.
ObserverSet observer_set = observer_set_;
ObserverSet::const_iterator it = observer_set.begin();
for (; it != observer_set.end(); ++it) {
(*it)->OnManualQueriesCompleted(this);
}
}
void NotifyAllQueriesCompleted() {
if (observer_set_.empty()) {
return;
}
// Use a copy of the set in case an Observer is removed while we're
// iterating.
ObserverSet observer_set = observer_set_;
ObserverSet::const_iterator it = observer_set.begin();
for (; it != observer_set.end(); ++it) {
(*it)->OnAllQueriesCompleted(this);
}
}
std::string GetQueryHTML(const int index, const TestQuery& query) const {
const std::string& request_id_var =
GetIDString(kMultiQueryRequestId, index);
const std::string& repeat_ct_var = GetIDString(kMultiQueryRepeatCt, index);
const std::string& request_val =
GetIDString(std::string(kMultiQueryRequest) + ":", index);
const std::string& success_val =
GetIDString(std::string(kMultiQuerySuccess) + ":", index);
const std::string& error_val =
GetIDString(std::string(kMultiQueryError) + ":", index);
std::string html;
const bool persistent = IsPersistent(query.type);
if (persistent) {
html += "var " + repeat_ct_var + " = 0;\n";
}
html += "var " + request_id_var +
" = window.mrtQuery({\n"
" request: '" +
request_val +
"',\n"
" persistent: " +
(persistent ? "true" : "false") + ",\n";
if (query.type == SUCCESS) {
const std::string& response_val = GetIDString(kMultiQueryResponse, index);
html +=
" onSuccess: function(response) {\n"
" if (response == '" +
response_val +
"')\n"
" window.mrtNotify('" +
success_val +
"');\n"
" else\n"
" window.mrtNotify('" +
error_val +
"');\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
" window.mrtNotify('" +
error_val +
"');\n"
" }\n";
} else if (query.type == FAILURE) {
const std::string& error_code_val = GetIntString(index);
const std::string& error_message_val =
GetIDString(kMultiQueryErrorMessage, index);
html +=
" onSuccess: function(response) {\n"
" window.mrtNotify('" +
error_val +
"');\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
" if (error_code == " +
error_code_val + " && error_message == '" + error_message_val +
"')\n"
" window.mrtNotify('" +
success_val +
"');\n"
" else\n"
" window.mrtNotify('" +
error_val +
"');\n"
" }\n";
} else if (query.type == PERSISTENT_SUCCESS ||
query.type == PERSISTENT_AUTOCANCEL) {
const std::string& response_val = GetIDString(kMultiQueryResponse, index);
const std::string& repeat_ct =
GetIntString(kMultiQueryPersistentResponseCount);
html +=
" onSuccess: function(response) {\n"
" if (response == '" +
response_val +
"') {\n"
// Should get repeat_ct number of successful responses.
" if (++" +
repeat_ct_var + " == " + repeat_ct +
") {\n"
" window.mrtNotify('" +
success_val + "');\n";
if (query.type == PERSISTENT_SUCCESS) {
// Manually cancel the request.
html += " window.mrtQueryCancel(" + request_id_var + ");\n";
}
html +=
" }\n"
" } else {\n"
" window.mrtNotify('" +
error_val +
"');\n"
" }\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
" window.mrtNotify('" +
error_val +
"');\n"
" }\n";
} else if (query.type == PERSISTENT_FAILURE) {
const std::string& error_code_val = GetIntString(index);
const std::string& error_message_val =
GetIDString(kMultiQueryErrorMessage, index);
const std::string& repeat_ct =
GetIntString(kMultiQueryPersistentResponseCount);
html +=
" onSuccess: function(response) {\n"
// Should get some successful responses before failure.
" if (++" +
repeat_ct_var + " > " + repeat_ct +
") {\n"
" window.mrtNotify('" +
error_val +
"');\n"
" }\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
" if (error_code == " +
error_code_val + " && error_message == '" + error_message_val +
"'"
" && " +
repeat_ct_var + " == " + repeat_ct +
")\n"
" window.mrtNotify('" +
success_val +
"');\n"
" else\n"
" window.mrtNotify('" +
error_val +
"');\n"
" }\n";
} else if (query.type == CANCEL || query.type == AUTOCANCEL) {
html +=
" onSuccess: function(response) {\n"
" window.mrtNotify('" +
error_val +
"');\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
" window.mrtNotify('" +
error_val +
"');\n"
" }\n";
}
html += "});\n";
return html;
}
std::string GetCancelHTML(const int index, const TestQuery& query) const {
const std::string& request_id_var =
GetIDString(kMultiQueryRequestId, index);
return "window.mrtQueryCancel(" + request_id_var + ");\n";
}
std::string GetIDString(const std::string& prefix, int index) const {
EXPECT_TRUE(!prefix.empty());
std::stringstream ss;
ss << prefix << GetIDFromIndex(index);
return ss.str();
}
bool SplitIDString(const std::string& str,
std::string* value,
int* index) const {
size_t pos = str.find(':');
if (pos != std::string::npos) {
*value = str.substr(0, pos);
*index = GetIndexFromID(atoi(str.substr(pos + 1).c_str()));
return (*index >= 0 &&
*index < static_cast(test_query_vector_.size()));
}
return false;
}
std::string GetIntString(int val) const {
std::stringstream ss;
ss << val;
return ss.str();
}
int GetIDFromIndex(int index) const { return id_offset_ + index; }
int GetIndexFromID(int id) const { return id - id_offset_; }
const std::string label_;
const bool synchronous_;
const int id_offset_;
typedef std::vector TestQueryVector;
TestQueryVector test_query_vector_;
typedef std::set ObserverSet;
ObserverSet observer_set_;
// Set to true after all queries have been added.
bool finalized_;
// Set to true while queries are pending.
bool running_;
// Total number of queries that will manually complete.
int manual_total_;
// Number of queries that have been received.
int received_count_;
// Number of queries that have completed successfully.
int manual_complete_count_;
int auto_complete_count_;
// If true any pending queries will receive an onFailure callback in addition
// to be canceled.
bool will_cancel_by_removing_handler_;
// Should always be the last member.
base::WeakPtrFactory weak_ptr_factory_;
};
void MakeTestQueries(MultiQueryManager* manager,
bool some,
int many_count = 200) {
if (some) {
// Test some queries of arbitrary types.
// Use a hard-coded list so the behavior is deterministic across test runs.
MultiQueryManager::TestType types[] = {
MultiQueryManager::PERSISTENT_AUTOCANCEL,
MultiQueryManager::SUCCESS,
MultiQueryManager::AUTOCANCEL,
MultiQueryManager::PERSISTENT_FAILURE,
MultiQueryManager::CANCEL,
MultiQueryManager::FAILURE,
MultiQueryManager::AUTOCANCEL,
MultiQueryManager::SUCCESS,
MultiQueryManager::PERSISTENT_SUCCESS,
MultiQueryManager::SUCCESS,
MultiQueryManager::PERSISTENT_AUTOCANCEL,
MultiQueryManager::CANCEL,
MultiQueryManager::PERSISTENT_SUCCESS,
MultiQueryManager::FAILURE,
};
for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
manager->AddTestQuery(types[i]);
}
} else {
// Test every type of query.
for (int i = 0; i < many_count; ++i) {
MultiQueryManager::TestType type = MultiQueryManager::SUCCESS;
switch (i % 7) {
case 0:
type = MultiQueryManager::SUCCESS;
break;
case 1:
type = MultiQueryManager::FAILURE;
break;
case 2:
type = MultiQueryManager::PERSISTENT_SUCCESS;
break;
case 3:
type = MultiQueryManager::PERSISTENT_FAILURE;
break;
case 4:
type = MultiQueryManager::CANCEL;
break;
case 5:
type = MultiQueryManager::AUTOCANCEL;
break;
case 6:
type = MultiQueryManager::PERSISTENT_AUTOCANCEL;
break;
}
manager->AddTestQuery(type);
}
}
manager->Finalize();
}
// Test multiple queries in a single page load with a single frame.
class MultiQuerySingleFrameTestHandler : public SingleLoadTestHandler,
public MultiQueryManager::Observer {
public:
enum CancelType {
CANCEL_BY_NAVIGATION,
CANCEL_BY_REMOVING_HANDLER,
CANCEL_BY_CLOSING_BROWSER,
};
MultiQuerySingleFrameTestHandler(
bool synchronous,
CancelType cancel_type = CANCEL_BY_NAVIGATION)
: manager_(std::string(), synchronous), cancel_type_(cancel_type) {
manager_.AddObserver(this);
}
std::string GetMainHTML() override { return manager_.GetHTML(true, true); }
void OnNotify(CefRefPtr browser,
CefRefPtr frame,
const std::string& message) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
manager_.OnNotify(browser, frame, message);
}
bool OnQuery(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id,
const CefString& request,
bool persistent,
CefRefPtr callback) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
return manager_.OnQuery(browser, frame, query_id, request, persistent,
callback);
}
void OnQueryCanceled(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
manager_.OnQueryCanceled(browser, frame, query_id);
}
void OnManualQueriesCompleted(MultiQueryManager* manager) override {
EXPECT_EQ(manager, &manager_);
if (manager_.HasAutoQueries()) {
if (cancel_type_ == CANCEL_BY_NAVIGATION) {
// Navigate somewhere else to terminate the auto queries.
GetBrowser()->GetMainFrame()->LoadURL(std::string(kTestDomain1) +
"cancel.html");
} else if (cancel_type_ == CANCEL_BY_REMOVING_HANDLER) {
// Change the expected behavior in the manager.
manager_.WillCancelByRemovingHandler();
GetRouter()->RemoveHandler(this);
// All queries should be immediately canceled.
AssertQueryCount(nullptr, nullptr, 0);
} else if (cancel_type_ == CANCEL_BY_CLOSING_BROWSER) {
// Change the expected behavior in the handler.
SetSignalCompletionWhenAllBrowsersClose(false);
CloseBrowser(GetBrowser(), false);
}
}
}
void OnAllQueriesCompleted(MultiQueryManager* manager) override {
EXPECT_EQ(manager, &manager_);
// All queries should be canceled.
AssertQueryCount(nullptr, nullptr, 0);
DestroyTest();
if (!SignalCompletionWhenAllBrowsersClose()) {
// Complete asynchronously so the call stack has a chance to unwind.
CefPostTask(TID_UI,
base::BindOnce(
&MultiQuerySingleFrameTestHandler::TestComplete, this));
}
}
void DestroyTest() override {
manager_.AssertAllComplete();
TestHandler::DestroyTest();
}
MultiQueryManager* GetManager() { return &manager_; }
private:
MultiQueryManager manager_;
const CancelType cancel_type_;
};
} // namespace
#define MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(name, type, synchronous) \
TEST(MessageRouterTest, name) { \
CefRefPtr handler = \
new MultiQuerySingleFrameTestHandler(synchronous); \
MultiQueryManager* manager = handler->GetManager(); \
manager->AddTestQuery(MultiQueryManager::type); \
manager->Finalize(); \
handler->ExecuteTest(); \
ReleaseAndWaitForDestructor(handler); \
}
// Test the query types individually.
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncSuccess,
SUCCESS,
true)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncSuccess,
SUCCESS,
false)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncFailure,
FAILURE,
true)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncFailure,
FAILURE,
false)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncPersistentSuccess,
PERSISTENT_SUCCESS,
true)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncPersistentSuccess,
PERSISTENT_SUCCESS,
false)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncPersistentFailure,
PERSISTENT_FAILURE,
true)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncPersistentFailure,
PERSISTENT_FAILURE,
false)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameCancel, CANCEL, true)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAutoCancel,
AUTOCANCEL,
true)
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFramePersistentAutoCancel,
PERSISTENT_AUTOCANCEL,
true)
// Test that one frame can run some queries successfully in a synchronous
// manner.
TEST(MessageRouterTest, MultiQuerySingleFrameSyncSome) {
CefRefPtr handler =
new MultiQuerySingleFrameTestHandler(true);
MakeTestQueries(handler->GetManager(), true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that one frame can run some queries successfully in an asynchronous
// manner.
TEST(MessageRouterTest, MultiQuerySingleFrameAsyncSome) {
CefRefPtr handler =
new MultiQuerySingleFrameTestHandler(false);
MakeTestQueries(handler->GetManager(), true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that one frame can run many queries successfully in a synchronous
// manner.
TEST(MessageRouterTest, MultiQuerySingleFrameSyncMany) {
CefRefPtr handler =
new MultiQuerySingleFrameTestHandler(true);
MakeTestQueries(handler->GetManager(), false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that one frame can run many queries successfully in an asynchronous
// manner.
TEST(MessageRouterTest, MultiQuerySingleFrameAsyncMany) {
CefRefPtr handler =
new MultiQuerySingleFrameTestHandler(false);
MakeTestQueries(handler->GetManager(), false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that pending queries can be canceled by removing the handler.
TEST(MessageRouterTest, MultiQuerySingleFrameCancelByRemovingHandler) {
CefRefPtr handler =
new MultiQuerySingleFrameTestHandler(
false, MultiQuerySingleFrameTestHandler::CANCEL_BY_REMOVING_HANDLER);
MakeTestQueries(handler->GetManager(), false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that pending queries can be canceled by closing the browser.
TEST(MessageRouterTest, MultiQuerySingleFrameCancelByClosingBrowser) {
CefRefPtr handler =
new MultiQuerySingleFrameTestHandler(
false, MultiQuerySingleFrameTestHandler::CANCEL_BY_CLOSING_BROWSER);
MakeTestQueries(handler->GetManager(), false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
namespace {
// Test multiple handlers.
class MultiQueryMultiHandlerTestHandler : public SingleLoadTestHandler,
public MultiQueryManager::Observer {
public:
class Handler : public CefMessageRouterBrowserSide::Handler {
public:
Handler(MultiQueryMultiHandlerTestHandler* test_handler, int index)
: test_handler_(test_handler), index_(index), query_id_(0) {}
bool OnQuery(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id,
const CefString& request,
bool persistent,
CefRefPtr callback) override {
// Each handler only handles a single request.
std::stringstream ss;
ss << kMultiQueryRequest << ":" << index_;
const std::string& handled_request = ss.str();
if (request != handled_request) {
return false;
}
// Verify that handlers are called in the correct order.
if (index_ == 0) {
EXPECT_FALSE(test_handler_->got_query0_);
EXPECT_FALSE(test_handler_->got_query1_);
EXPECT_FALSE(test_handler_->got_query2_);
test_handler_->got_query0_.yes();
} else if (index_ == 1) {
EXPECT_TRUE(test_handler_->got_query0_);
EXPECT_FALSE(test_handler_->got_query1_);
EXPECT_FALSE(test_handler_->got_query2_);
test_handler_->got_query1_.yes();
} else if (index_ == 2) {
EXPECT_TRUE(test_handler_->got_query0_);
EXPECT_TRUE(test_handler_->got_query1_);
EXPECT_FALSE(test_handler_->got_query2_);
test_handler_->got_query2_.yes();
}
query_id_ = query_id;
return test_handler_->OnQuery(browser, frame, query_id, request,
persistent, callback);
}
void OnQueryCanceled(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id) override {
// Verify that the correct handler is called for cancellation.
EXPECT_EQ(query_id_, query_id);
if (index_ == 0) {
EXPECT_FALSE(test_handler_->got_query_canceled0_);
test_handler_->got_query_canceled0_.yes();
} else if (index_ == 1) {
EXPECT_FALSE(test_handler_->got_query_canceled1_);
test_handler_->got_query_canceled1_.yes();
} else if (index_ == 2) {
EXPECT_FALSE(test_handler_->got_query_canceled2_);
test_handler_->got_query_canceled2_.yes();
}
test_handler_->OnQueryCanceled(browser, frame, query_id);
}
private:
MultiQueryMultiHandlerTestHandler* test_handler_;
const int index_;
int64_t query_id_;
};
MultiQueryMultiHandlerTestHandler(bool synchronous,
bool cancel_by_removing_handler)
: manager_(std::string(), synchronous, 0),
handler0_(this, 0),
handler1_(this, 1),
handler2_(this, 2),
cancel_by_removing_handler_(cancel_by_removing_handler) {
manager_.AddObserver(this);
// Each handler will handle one of the queries.
manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL);
manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL);
manager_.AddTestQuery(MultiQueryManager::PERSISTENT_AUTOCANCEL);
manager_.Finalize();
}
std::string GetMainHTML() override { return manager_.GetHTML(true, true); }
void OnNotify(CefRefPtr browser,
CefRefPtr frame,
const std::string& message) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
manager_.OnNotify(browser, frame, message);
}
bool OnQuery(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id,
const CefString& request,
bool persistent,
CefRefPtr callback) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
return manager_.OnQuery(browser, frame, query_id, request, persistent,
callback);
}
void OnQueryCanceled(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
manager_.OnQueryCanceled(browser, frame, query_id);
}
void OnManualQueriesCompleted(MultiQueryManager* manager) override {
EXPECT_EQ(manager, &manager_);
EXPECT_TRUE(got_query0_);
EXPECT_TRUE(got_query1_);
EXPECT_TRUE(got_query2_);
EXPECT_FALSE(got_query_canceled0_);
EXPECT_FALSE(got_query_canceled1_);
EXPECT_FALSE(got_query_canceled2_);
EXPECT_TRUE(manager_.HasAutoQueries());
CefRefPtr router = GetRouter();
// Remove one handler to cancel a query.
if (cancel_by_removing_handler_) {
manager_.WillCancelByRemovingHandler();
// Each query should be canceled as the handler is removed.
EXPECT_TRUE(router->RemoveHandler(&handler1_));
EXPECT_FALSE(got_query_canceled0_);
EXPECT_TRUE(got_query_canceled1_);
EXPECT_FALSE(got_query_canceled2_);
EXPECT_TRUE(router->RemoveHandler(&handler2_));
EXPECT_FALSE(got_query_canceled0_);
EXPECT_TRUE(got_query_canceled2_);
EXPECT_TRUE(router->RemoveHandler(&handler0_));
EXPECT_TRUE(got_query_canceled0_);
} else {
GetBrowser()->GetMainFrame()->LoadURL(std::string(kTestDomain1) +
"cancel.html");
}
}
void OnAllQueriesCompleted(MultiQueryManager* manager) override {
EXPECT_EQ(manager, &manager_);
// All queries should be canceled.
AssertQueryCount(nullptr, nullptr, 0);
DestroyTest();
}
void DestroyTest() override {
EXPECT_TRUE(got_query0_);
EXPECT_TRUE(got_query1_);
EXPECT_TRUE(got_query2_);
EXPECT_TRUE(got_query_canceled0_);
EXPECT_TRUE(got_query_canceled1_);
EXPECT_TRUE(got_query_canceled2_);
manager_.AssertAllComplete();
TestHandler::DestroyTest();
}
protected:
void AddHandlers(
CefRefPtr message_router) override {
// OnQuery call order will verify that the first/last ordering works as
// expected.
EXPECT_TRUE(message_router->AddHandler(&handler1_, true));
EXPECT_TRUE(message_router->AddHandler(&handler0_, true));
EXPECT_TRUE(message_router->AddHandler(&handler2_, false));
// Can't add the same handler multiple times.
EXPECT_FALSE(message_router->AddHandler(&handler1_, true));
}
private:
MultiQueryManager manager_;
Handler handler0_;
Handler handler1_;
Handler handler2_;
const bool cancel_by_removing_handler_;
TrackCallback got_query0_;
TrackCallback got_query1_;
TrackCallback got_query2_;
TrackCallback got_query_canceled0_;
TrackCallback got_query_canceled1_;
TrackCallback got_query_canceled2_;
};
} // namespace
// Test that multiple handlers behave correctly.
TEST(MessageRouterTest, MultiQueryMultiHandler) {
CefRefPtr handler =
new MultiQueryMultiHandlerTestHandler(false, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple handlers behave correctly. Cancel by removing the
// handlers.
TEST(MessageRouterTest, MultiQueryMultiHandlerCancelByRemovingHandler) {
CefRefPtr handler =
new MultiQueryMultiHandlerTestHandler(false, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
namespace {
// Map of managers on a per-URL basis.
class MultiQueryManagerMap : public CefMessageRouterBrowserSide::Handler,
public MultiQueryManager::Observer {
public:
class Observer {
public:
// Called when all manual queries are complete.
virtual void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) {}
// Called when all queries are complete.
virtual void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) {}
protected:
virtual ~Observer() {}
};
MultiQueryManagerMap()
: finalized_(false),
running_(false),
manual_complete_count_(0),
total_complete_count_(0) {}
virtual ~MultiQueryManagerMap() { RemoveAllManagers(); }
void AddObserver(Observer* observer) {
EXPECT_FALSE(running_);
observer_set_.insert(observer);
}
void RemoveObserver(Observer* observer) {
EXPECT_FALSE(running_);
EXPECT_TRUE(observer_set_.erase(observer));
}
MultiQueryManager* CreateManager(const std::string& url, bool synchronous) {
EXPECT_FALSE(finalized_);
MultiQueryManager* manager = new MultiQueryManager(
url, synchronous, static_cast(manager_map_.size()) * 1000);
manager->AddObserver(this);
all_managers_.push_back(manager);
pending_managers_.push_back(manager);
return manager;
}
void Finalize() {
EXPECT_FALSE(finalized_);
finalized_ = true;
}
std::string GetMainHTML() const {
EXPECT_TRUE(finalized_);
EXPECT_FALSE(running_);
std::string html = "\n";
for (size_t i = 0; i < all_managers_.size(); ++i) {
const std::string& url = all_managers_[i]->label();
const std::string& name = GetNameForURL(url);
html += "\n";
}
html += "";
return html;
}
void OnNotify(CefRefPtr browser,
CefRefPtr frame,
const std::string& message) {
EXPECT_TRUE(finalized_);
if (!running_) {
running_ = true;
}
MultiQueryManager* manager = GetManager(browser, frame);
manager->OnNotify(browser, frame, message);
}
bool OnQuery(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id,
const CefString& request,
bool persistent,
CefRefPtr callback) override {
EXPECT_TRUE(finalized_);
if (!running_) {
running_ = true;
}
MultiQueryManager* manager = GetManager(browser, frame);
return manager->OnQuery(browser, frame, query_id, request, persistent,
callback);
}
void OnQueryCanceled(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id) override {
EXPECT_TRUE(finalized_);
if (!running_) {
running_ = true;
}
MultiQueryManager* manager = GetManager(browser, frame);
manager->OnQueryCanceled(browser, frame, query_id);
}
void OnManualQueriesCompleted(MultiQueryManager* manager) override {
const int size = static_cast(all_managers_.size());
EXPECT_LT(manual_complete_count_, size);
if (++manual_complete_count_ == size) {
running_ = false;
// Notify observers.
if (!observer_set_.empty()) {
// Use a copy of the set in case an Observer is removed while we're
// iterating.
ObserverSet observer_set = observer_set_;
ObserverSet::const_iterator it = observer_set.begin();
for (; it != observer_set.end(); ++it) {
(*it)->OnMapManualQueriesCompleted(this);
}
}
}
}
void OnAllQueriesCompleted(MultiQueryManager* manager) override {
const int size = static_cast(all_managers_.size());
EXPECT_LT(total_complete_count_, size);
if (++total_complete_count_ == size) {
running_ = false;
// Notify observers.
if (!observer_set_.empty()) {
// Use a copy of the set in case an Observer is removed while we're
// iterating.
ObserverSet observer_set = observer_set_;
ObserverSet::const_iterator it = observer_set.begin();
for (; it != observer_set.end(); ++it) {
(*it)->OnMapAllQueriesCompleted(this);
}
}
}
}
bool AllComplete() const {
EXPECT_TRUE(finalized_);
for (size_t i = 0; i < all_managers_.size(); ++i) {
if (!all_managers_[i]->IsAllComplete()) {
return false;
}
}
return true;
}
void AssertAllComplete() const {
EXPECT_TRUE(finalized_);
EXPECT_TRUE(pending_managers_.empty());
EXPECT_FALSE(running_);
for (size_t i = 0; i < all_managers_.size(); ++i) {
all_managers_[i]->AssertAllComplete();
}
}
bool HasAutoQueries() const {
for (size_t i = 0; i < all_managers_.size(); ++i) {
if (all_managers_[i]->HasAutoQueries()) {
return true;
}
}
return false;
}
void OnLoadStart(CefRefPtr browser, CefRefPtr frame) {
if (pending_managers_.empty()) {
return;
}
const std::string& expected_url = frame->GetURL();
MultiQueryManager* next_manager = nullptr;
// Find the pending manager that matches the expected URL.
ManagerList::iterator it = pending_managers_.begin();
for (; it != pending_managers_.end(); ++it) {
if ((*it)->label() == expected_url) {
next_manager = *it;
pending_managers_.erase(it);
break;
}
}
EXPECT_TRUE(next_manager);
const int browser_id = browser->GetIdentifier();
// Always use the same ID for the main frame.
const int64_t frame_id = frame->IsMain() ? -1 : frame->GetIdentifier();
const std::pair& id = std::make_pair(browser_id, frame_id);
// Remove the currently active manager, if any.
ManagerMap::iterator it2 = manager_map_.find(id);
if (it2 != manager_map_.end()) {
manager_map_.erase(it2);
}
// Add the next manager to the active map.
manager_map_.insert(std::make_pair(id, next_manager));
}
MultiQueryManager* GetManager(CefRefPtr browser,
CefRefPtr frame) const {
const int browser_id = browser->GetIdentifier();
// Always use the same ID for the main frame.
const int64_t frame_id = frame->IsMain() ? -1 : frame->GetIdentifier();
// Find the manager in the active map.
ManagerMap::const_iterator it =
manager_map_.find(std::make_pair(browser_id, frame_id));
EXPECT_NE(it, manager_map_.end())
<< "browser_id = " << browser_id << ", frame_id = " << frame_id;
return it->second;
}
void RemoveAllManagers() {
EXPECT_TRUE(pending_managers_.empty());
if (all_managers_.empty()) {
return;
}
for (size_t i = 0; i < all_managers_.size(); ++i) {
delete all_managers_[i];
}
all_managers_.clear();
manager_map_.clear();
}
static std::string GetNameForURL(const std::string& url) {
// Extract the file name without extension.
int pos1 = static_cast(url.rfind("/"));
int pos2 = static_cast(url.rfind("."));
EXPECT_TRUE(pos1 >= 0 && pos2 >= 0 && pos1 < pos2);
return url.substr(pos1 + 1, pos2 - pos1 - 1);
}
private:
typedef std::vector ManagerList;
// Map of (browser ID, frame ID) to manager.
typedef std::map, MultiQueryManager*> ManagerMap;
// All managers that have been created.
ManagerList all_managers_;
// Managers that have not yet associated with a frame.
ManagerList pending_managers_;
// Managers that are currently active.
ManagerMap manager_map_;
typedef std::set ObserverSet;
ObserverSet observer_set_;
// Set to true after all query managers have been added.
bool finalized_;
// Set to true while queries are pending.
bool running_;
// Number of managers that have completed.
int manual_complete_count_;
int total_complete_count_;
};
// Test multiple queries in a single page load with multiple frames.
class MultiQueryMultiFrameTestHandler : public SingleLoadTestHandler,
public MultiQueryManagerMap::Observer {
public:
MultiQueryMultiFrameTestHandler(bool synchronous, bool cancel_with_subnav)
: synchronous_(synchronous), cancel_with_subnav_(cancel_with_subnav) {
manager_map_.AddObserver(this);
}
void AddOtherResources() override {
AddSubFrameResource("sub1");
AddSubFrameResource("sub2");
AddSubFrameResource("sub3");
manager_map_.Finalize();
if (manager_map_.HasAutoQueries()) {
cancel_url_ = std::string(kTestDomain1) + "cancel.html";
AddResource(cancel_url_, "cancel", "text/html");
}
}
std::string GetMainHTML() override { return manager_map_.GetMainHTML(); }
void OnLoadStart(CefRefPtr browser,
CefRefPtr frame,
TransitionType transition_type) override {
AssertMainBrowser(browser);
if (!frame->IsMain()) {
manager_map_.OnLoadStart(browser, frame);
}
}
void OnNotify(CefRefPtr browser,
CefRefPtr frame,
const std::string& message) override {
AssertMainBrowser(browser);
EXPECT_FALSE(frame->IsMain());
manager_map_.OnNotify(browser, frame, message);
}
bool OnQuery(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id,
const CefString& request,
bool persistent,
CefRefPtr callback) override {
AssertMainBrowser(browser);
EXPECT_FALSE(frame->IsMain());
return manager_map_.OnQuery(browser, frame, query_id, request, persistent,
callback);
}
void OnQueryCanceled(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id) override {
AssertMainBrowser(browser);
EXPECT_FALSE(frame->IsMain());
manager_map_.OnQueryCanceled(browser, frame, query_id);
}
void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) override {
EXPECT_EQ(map, &manager_map_);
if (manager_map_.HasAutoQueries()) {
CefRefPtr frame = GetBrowser()->GetMainFrame();
// Navigate somewhere else to terminate the auto queries.
if (cancel_with_subnav_) {
// Navigate each subframe individually.
const std::string js = "document.getElementById('sub1').src = '" +
cancel_url_ +
"';"
"document.getElementById('sub2').src = '" +
cancel_url_ +
"';"
"document.getElementById('sub3').src = '" +
cancel_url_ + "';";
frame->ExecuteJavaScript(js, frame->GetURL(), 0);
} else {
// Navigate the main frame.
frame->LoadURL(cancel_url_);
}
}
}
void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) override {
EXPECT_EQ(map, &manager_map_);
DestroyTest();
}
void DestroyTest() override {
manager_map_.AssertAllComplete();
TestHandler::DestroyTest();
}
private:
void AddSubFrameResource(const std::string& name) {
const std::string& url = std::string(kTestDomain1) + name + ".html";
MultiQueryManager* manager = manager_map_.CreateManager(url, synchronous_);
MakeTestQueries(manager, false, 100);
const std::string& html = manager->GetHTML(false, false);
AddResource(url, html, "text/html");
}
const bool synchronous_;
const bool cancel_with_subnav_;
MultiQueryManagerMap manager_map_;
std::string cancel_url_;
};
} // namespace
// Test that multiple frames can run many queries successfully in a synchronous
// manner.
TEST(MessageRouterTest, MultiQueryMultiFrameSync) {
CefRefPtr handler =
new MultiQueryMultiFrameTestHandler(true, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple frames can run many queries successfully in an
// asynchronous manner.
TEST(MessageRouterTest, MultiQueryMultiFrameAsync) {
CefRefPtr handler =
new MultiQueryMultiFrameTestHandler(false, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple frames can run many queries successfully in a synchronous
// manner. Cancel auto queries with sub-frame navigation.
TEST(MessageRouterTest, MultiQueryMultiFrameSyncSubnavCancel) {
CefRefPtr handler =
new MultiQueryMultiFrameTestHandler(true, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple frames can run many queries successfully in an
// asynchronous manner. Cancel auto queries with sub-frame navigation.
TEST(MessageRouterTest, MultiQueryMultiFrameAsyncSubnavCancel) {
CefRefPtr handler =
new MultiQueryMultiFrameTestHandler(false, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
namespace {
// Implementation of MRTestHandler that loads multiple pages and/or browsers and
// executes multiple queries.
class MultiQueryMultiLoadTestHandler
: public MRTestHandler,
public CefMessageRouterBrowserSide::Handler,
public MultiQueryManagerMap::Observer,
public MultiQueryManager::Observer {
public:
MultiQueryMultiLoadTestHandler(bool some, bool synchronous)
: some_(some), synchronous_(synchronous) {
manager_map_.AddObserver(this);
}
void OnLoadStart(CefRefPtr browser,
CefRefPtr frame,
TransitionType transition_type) override {
manager_map_.OnLoadStart(browser, frame);
}
void OnNotify(CefRefPtr browser,
CefRefPtr frame,
const std::string& message) override {
manager_map_.OnNotify(browser, frame, message);
}
bool OnQuery(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id,
const CefString& request,
bool persistent,
CefRefPtr callback) override {
return manager_map_.OnQuery(browser, frame, query_id, request, persistent,
callback);
}
void OnQueryCanceled(CefRefPtr browser,
CefRefPtr frame,
int64_t query_id) override {
manager_map_.OnQueryCanceled(browser, frame, query_id);
}
void OnMapManualQueriesCompleted(MultiQueryManagerMap* map) override {
EXPECT_EQ(map, &manager_map_);
if (manager_map_.HasAutoQueries()) {
// Navigate all browsers somewhere else to terminate the auto queries.
BrowserMap browser_map;
GetAllBrowsers(&browser_map);
BrowserMap::const_iterator it = browser_map.begin();
for (; it != browser_map.end(); ++it) {
it->second->GetMainFrame()->LoadURL(cancel_url_);
}
}
}
void OnMapAllQueriesCompleted(MultiQueryManagerMap* map) override {
EXPECT_EQ(map, &manager_map_);
DestroyTest();
}
void DestroyTest() override {
manager_map_.AssertAllComplete();
TestHandler::DestroyTest();
}
protected:
void AddHandlers(
CefRefPtr message_router) override {
message_router->AddHandler(this, false);
}
void AddManagedResource(const std::string& url,
bool assert_total,
bool assert_browser) {
MultiQueryManager* manager = manager_map_.CreateManager(url, synchronous_);
manager->AddObserver(this);
MakeTestQueries(manager, some_, 75);
const std::string& html = manager->GetHTML(assert_total, assert_browser);
AddResource(url, html, "text/html");
}
void Finalize() {
manager_map_.Finalize();
if (manager_map_.HasAutoQueries()) {
cancel_url_ = std::string(kTestDomain1) + "cancel.html";
AddResource(cancel_url_, "cancel", "text/html");
}
}
MultiQueryManagerMap manager_map_;
private:
const bool some_;
const bool synchronous_;
std::string cancel_url_;
};
// Test multiple browsers that send queries at the same time.
class MultiQueryMultiBrowserTestHandler
: public MultiQueryMultiLoadTestHandler {
public:
MultiQueryMultiBrowserTestHandler(bool synchronous, bool same_origin)
: MultiQueryMultiLoadTestHandler(false, synchronous),
same_origin_(same_origin) {}
protected:
void RunMRTest() override {
const std::string& url1 = std::string(kTestDomain1) + "browser1.html";
const std::string& url2 =
std::string(same_origin_ ? kTestDomain1 : kTestDomain2) +
"browser2.html";
const std::string& url3 =
std::string(same_origin_ ? kTestDomain1 : kTestDomain3) +
"browser3.html";
AddManagedResource(url1, false, true);
AddManagedResource(url2, false, true);
AddManagedResource(url3, false, true);
Finalize();
// Create 2 browsers simultaniously.
CreateBrowser(url1, nullptr);
CreateBrowser(url2, nullptr);
CreateBrowser(url3, nullptr);
}
private:
bool same_origin_;
};
} // namespace
// Test that multiple browsers can query simultaniously from the same origin.
TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginSync) {
CefRefPtr handler =
new MultiQueryMultiBrowserTestHandler(true, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple browsers can query simultaniously from the same origin.
TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginAsync) {
CefRefPtr handler =
new MultiQueryMultiBrowserTestHandler(false, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple browsers can query simultaniously from different origins.
TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginSync) {
CefRefPtr handler =
new MultiQueryMultiBrowserTestHandler(true, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple browsers can query simultaniously from different origins.
TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginAsync) {
CefRefPtr handler =
new MultiQueryMultiBrowserTestHandler(false, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
namespace {
// Test multiple navigations that send queries sequentially.
class MultiQueryMultiNavigateTestHandler
: public MultiQueryMultiLoadTestHandler {
public:
MultiQueryMultiNavigateTestHandler(bool synchronous, bool same_origin)
: MultiQueryMultiLoadTestHandler(false, synchronous),
same_origin_(same_origin) {}
void OnManualQueriesCompleted(MultiQueryManager* manager) override {
const std::string& url = manager->label();
if (url == url1_) { // 2. Load the 2nd url.
GetBrowser()->GetMainFrame()->LoadURL(url2_);
} else if (url == url2_) { // 3. Load the 3rd url.
GetBrowser()->GetMainFrame()->LoadURL(url3_);
}
}
protected:
void RunMRTest() override {
url1_ = std::string(kTestDomain1) + "browser1.html";
url2_ = std::string(same_origin_ ? kTestDomain1 : kTestDomain2) +
"browser2.html";
url3_ = std::string(same_origin_ ? kTestDomain1 : kTestDomain3) +
"browser3.html";
// With same-site BFCache enabled a new browser will be created for each
// same-site navigation in the renderer process, resulting in "total count"
// values that potentially span multiple navigations.
const bool should_assert = !(same_origin_ && IsSameSiteBFCacheEnabled());
AddManagedResource(url1_, should_assert, should_assert);
AddManagedResource(url2_, should_assert, should_assert);
AddManagedResource(url3_, should_assert, should_assert);
Finalize();
// 1. Load the 1st url.
CreateBrowser(url1_, nullptr);
}
private:
bool same_origin_;
std::string url1_;
std::string url2_;
std::string url3_;
};
} // namespace
// Test that multiple navigations can query from the same origin.
TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginSync) {
CefRefPtr handler =
new MultiQueryMultiNavigateTestHandler(true, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple navigations can query from the same origin.
TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginAsync) {
CefRefPtr handler =
new MultiQueryMultiNavigateTestHandler(false, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple navigations can query from different origins.
TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginSync) {
CefRefPtr handler =
new MultiQueryMultiNavigateTestHandler(true, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that multiple navigations can query from different origins.
TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginAsync) {
CefRefPtr handler =
new MultiQueryMultiNavigateTestHandler(false, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}