2928 lines
92 KiB
C++
2928 lines
92 KiB
C++
// Copyright (c) 2014 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 <cstdlib>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#include "include/base/cef_bind.h"
|
|
#include "include/base/cef_weak_ptr.h"
|
|
#include "include/cef_v8.h"
|
|
#include "include/wrapper/cef_closure_task.h"
|
|
#include "tests/ceftests/routing_test_handler.h"
|
|
#include "tests/gtest/include/gtest/gtest.h"
|
|
#include "tests/shared/renderer/client_app_renderer.h"
|
|
|
|
using client::ClientAppRenderer;
|
|
|
|
namespace {
|
|
|
|
const char kTestDomainRoot[] = "http://tests-mr";
|
|
const char kTestDomain1[] = "http://tests-mr1.com/";
|
|
const char kTestDomain2[] = "http://tests-mr2.com/";
|
|
const char kTestDomain3[] = "http://tests-mr3.com/";
|
|
|
|
const char kDoneMessageName[] = "mrtNotifyMsg";
|
|
|
|
const char kJSNotifyFunc[] = "mrtNotify";
|
|
const char kJSAssertTotalCountFunc[] = "mrtAssertTotalCount";
|
|
const char kJSAssertBrowserCountFunc[] = "mrtAssertBrowserCount";
|
|
const char kJSAssertContextCountFunc[] = "mrtAssertContextCount";
|
|
|
|
void SetRouterConfig(CefMessageRouterConfig& config) {
|
|
config.js_query_function = "mrtQuery";
|
|
config.js_cancel_function = "mrtQueryCancel";
|
|
}
|
|
|
|
// Handle the renderer side of the routing implementation.
|
|
class MRRenderDelegate : public ClientAppRenderer::Delegate {
|
|
public:
|
|
class V8HandlerImpl : public CefV8Handler {
|
|
public:
|
|
explicit V8HandlerImpl(CefRefPtr<MRRenderDelegate> delegate)
|
|
: delegate_(delegate) {}
|
|
|
|
bool Execute(const CefString& name,
|
|
CefRefPtr<CefV8Value> object,
|
|
const CefV8ValueList& arguments,
|
|
CefRefPtr<CefV8Value>& retval,
|
|
CefString& exception) override {
|
|
const std::string& message_name = name;
|
|
if (message_name == kJSNotifyFunc) {
|
|
EXPECT_EQ(1U, arguments.size());
|
|
EXPECT_TRUE(arguments[0]->IsString());
|
|
|
|
const CefString& msg = arguments[0]->GetStringValue();
|
|
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
|
|
CefRefPtr<CefFrame> frame = context->GetFrame();
|
|
|
|
CefRefPtr<CefProcessMessage> message =
|
|
CefProcessMessage::Create(kDoneMessageName);
|
|
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
|
args->SetString(0, msg);
|
|
frame->SendProcessMessage(PID_BROWSER, message);
|
|
return true;
|
|
} else {
|
|
EXPECT_EQ(1U, arguments.size());
|
|
EXPECT_TRUE(arguments[0]->IsInt());
|
|
|
|
const int expected_count = arguments[0]->GetIntValue();
|
|
int actual_count = -1;
|
|
|
|
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
|
|
CefRefPtr<CefBrowser> browser = context->GetBrowser();
|
|
|
|
if (name == kJSAssertTotalCountFunc) {
|
|
actual_count =
|
|
delegate_->message_router_->GetPendingCount(NULL, NULL);
|
|
} else if (name == kJSAssertBrowserCountFunc) {
|
|
actual_count =
|
|
delegate_->message_router_->GetPendingCount(browser, NULL);
|
|
} else if (name == kJSAssertContextCountFunc) {
|
|
actual_count =
|
|
delegate_->message_router_->GetPendingCount(browser, context);
|
|
}
|
|
|
|
if (expected_count != actual_count) {
|
|
std::stringstream ss;
|
|
ss << message_name << " failed; expected " << expected_count
|
|
<< ", got " << actual_count;
|
|
exception = ss.str();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
CefRefPtr<MRRenderDelegate> delegate_;
|
|
|
|
IMPLEMENT_REFCOUNTING(V8HandlerImpl);
|
|
};
|
|
|
|
MRRenderDelegate() {}
|
|
|
|
void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) override {
|
|
// Create the renderer-side router for query handling.
|
|
CefMessageRouterConfig config;
|
|
SetRouterConfig(config);
|
|
message_router_ = CefMessageRouterRendererSide::Create(config);
|
|
}
|
|
|
|
void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefV8Context> context) override {
|
|
const std::string& url = frame->GetURL();
|
|
if (url.find(kTestDomainRoot) != 0)
|
|
return;
|
|
|
|
message_router_->OnContextCreated(browser, frame, context);
|
|
|
|
// Register function handlers with the 'window' object.
|
|
CefRefPtr<CefV8Value> window = context->GetGlobal();
|
|
|
|
CefRefPtr<V8HandlerImpl> handler = new V8HandlerImpl(this);
|
|
CefV8Value::PropertyAttribute attributes =
|
|
static_cast<CefV8Value::PropertyAttribute>(
|
|
V8_PROPERTY_ATTRIBUTE_READONLY | V8_PROPERTY_ATTRIBUTE_DONTENUM |
|
|
V8_PROPERTY_ATTRIBUTE_DONTDELETE);
|
|
|
|
CefRefPtr<CefV8Value> notify_func =
|
|
CefV8Value::CreateFunction(kJSNotifyFunc, handler.get());
|
|
window->SetValue(kJSNotifyFunc, notify_func, attributes);
|
|
|
|
CefRefPtr<CefV8Value> total_count_func =
|
|
CefV8Value::CreateFunction(kJSAssertTotalCountFunc, handler.get());
|
|
window->SetValue(kJSAssertTotalCountFunc, total_count_func, attributes);
|
|
|
|
CefRefPtr<CefV8Value> browser_count_func =
|
|
CefV8Value::CreateFunction(kJSAssertBrowserCountFunc, handler.get());
|
|
window->SetValue(kJSAssertBrowserCountFunc, browser_count_func, attributes);
|
|
|
|
CefRefPtr<CefV8Value> context_count_func =
|
|
CefV8Value::CreateFunction(kJSAssertContextCountFunc, handler.get());
|
|
window->SetValue(kJSAssertContextCountFunc, context_count_func, attributes);
|
|
}
|
|
|
|
void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefV8Context> context) override {
|
|
const std::string& url = frame->GetURL();
|
|
if (url.find(kTestDomainRoot) != 0)
|
|
return;
|
|
|
|
message_router_->OnContextReleased(browser, frame, context);
|
|
}
|
|
|
|
bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefProcessId source_process,
|
|
CefRefPtr<CefProcessMessage> message) override {
|
|
const std::string& url = browser->GetMainFrame()->GetURL();
|
|
if (url.find(kTestDomainRoot) != 0)
|
|
return false;
|
|
|
|
return message_router_->OnProcessMessageReceived(browser, frame,
|
|
source_process, message);
|
|
}
|
|
|
|
private:
|
|
CefRefPtr<CefMessageRouterRendererSide> message_router_;
|
|
|
|
IMPLEMENT_REFCOUNTING(MRRenderDelegate);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Entry point for creating the test delegate.
|
|
// Called from client_app_delegates.cc.
|
|
void CreateMessageRouterRendererTests(
|
|
ClientAppRenderer::DelegateSet& delegates) {
|
|
delegates.insert(new MRRenderDelegate);
|
|
}
|
|
|
|
namespace {
|
|
|
|
class MRTestHandler : public TestHandler {
|
|
public:
|
|
MRTestHandler() {}
|
|
|
|
void RunTest() override {
|
|
RunMRTest();
|
|
|
|
// Time out the test after a reasonable period of time.
|
|
SetTestTimeout(10000);
|
|
}
|
|
|
|
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
|
|
if (!message_router_.get()) {
|
|
// Create the browser-side router for query handling.
|
|
CefMessageRouterConfig config;
|
|
SetRouterConfig(config);
|
|
message_router_ = CefMessageRouterBrowserSide::Create(config);
|
|
AddHandlers(message_router_);
|
|
}
|
|
TestHandler::OnAfterCreated(browser);
|
|
}
|
|
|
|
void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
|
|
message_router_->OnBeforeClose(browser);
|
|
TestHandler::OnBeforeClose(browser);
|
|
}
|
|
|
|
void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
|
|
TerminationStatus status) override {
|
|
message_router_->OnRenderProcessTerminated(browser);
|
|
}
|
|
|
|
// Only call this method if the navigation isn't canceled.
|
|
bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request,
|
|
bool user_gesture,
|
|
bool is_redirect) override {
|
|
message_router_->OnBeforeBrowse(browser, frame);
|
|
return false;
|
|
}
|
|
|
|
// Returns true if the router handled the navigation.
|
|
bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefProcessId source_process,
|
|
CefRefPtr<CefProcessMessage> message) override {
|
|
const std::string& message_name = message->GetName();
|
|
if (message_name == kDoneMessageName) {
|
|
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
|
EXPECT_EQ(1U, args->GetSize());
|
|
EXPECT_EQ(VTYPE_STRING, args->GetType(0));
|
|
OnNotify(browser, frame, args->GetString(0));
|
|
return true;
|
|
}
|
|
|
|
return message_router_->OnProcessMessageReceived(browser, frame,
|
|
source_process, message);
|
|
}
|
|
|
|
CefRefPtr<CefMessageRouterBrowserSide> GetRouter() const {
|
|
return message_router_;
|
|
}
|
|
|
|
protected:
|
|
virtual void RunMRTest() = 0;
|
|
|
|
virtual void AddHandlers(
|
|
CefRefPtr<CefMessageRouterBrowserSide> message_router) = 0;
|
|
|
|
virtual void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) = 0;
|
|
|
|
bool AssertQueryCount(CefRefPtr<CefBrowser> browser,
|
|
CefMessageRouterBrowserSide::Handler* handler,
|
|
int expected_count) {
|
|
int actual_count = message_router_->GetPendingCount(browser, handler);
|
|
EXPECT_EQ(expected_count, actual_count);
|
|
return (expected_count == actual_count);
|
|
}
|
|
|
|
void AssertMainBrowser(CefRefPtr<CefBrowser> browser) {
|
|
EXPECT_TRUE(browser.get());
|
|
EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
|
|
}
|
|
|
|
private:
|
|
CefRefPtr<CefMessageRouterBrowserSide> message_router_;
|
|
|
|
IMPLEMENT_REFCOUNTING(MRTestHandler);
|
|
};
|
|
|
|
// Implementation of MRTestHandler that loads a single page.
|
|
class SingleLoadTestHandler : public MRTestHandler,
|
|
public CefMessageRouterBrowserSide::Handler {
|
|
public:
|
|
SingleLoadTestHandler()
|
|
: main_url_(std::string(kTestDomain1) + "main.html") {}
|
|
|
|
const std::string& GetMainURL() { return main_url_; }
|
|
|
|
protected:
|
|
void RunMRTest() override {
|
|
AddOtherResources();
|
|
AddResource(main_url_, GetMainHTML(), "text/html");
|
|
|
|
CreateBrowser(main_url_, NULL);
|
|
}
|
|
|
|
void AddHandlers(
|
|
CefRefPtr<CefMessageRouterBrowserSide> message_router) override {
|
|
message_router->AddHandler(this, false);
|
|
}
|
|
|
|
virtual void AddOtherResources() {}
|
|
|
|
virtual std::string GetMainHTML() = 0;
|
|
|
|
void AssertMainFrame(CefRefPtr<CefFrame> frame) {
|
|
EXPECT_TRUE(frame.get());
|
|
EXPECT_TRUE(frame->IsMain());
|
|
EXPECT_STREQ(main_url_.c_str(), frame->GetURL().ToString().c_str());
|
|
}
|
|
|
|
private:
|
|
const std::string main_url_;
|
|
};
|
|
|
|
// Used to verify that the test harness (bound functions) behave correctly.
|
|
class HarnessTestHandler : public SingleLoadTestHandler {
|
|
public:
|
|
HarnessTestHandler(bool test_success) : test_success_(test_success) {}
|
|
|
|
std::string GetMainHTML() override {
|
|
std::string html;
|
|
if (test_success_) {
|
|
// All assertions should pass.
|
|
html =
|
|
"<html><body><script>\n"
|
|
"var fail_ct = 0;\n"
|
|
"try { window.mrtAssertTotalCount(0); } catch (e) { fail_ct++; }\n"
|
|
"try { window.mrtAssertBrowserCount(0); } catch (e) { fail_ct++; }\n"
|
|
"try { window.mrtAssertContextCount(0); } catch (e) { fail_ct++; }\n"
|
|
"window.mrtNotify('' + (fail_ct == 0));"
|
|
"</script></body></html>";
|
|
} else {
|
|
// All assertions should fail.
|
|
html =
|
|
"<html><body><script>\n"
|
|
"var fail_ct = 0;\n"
|
|
"try { window.mrtAssertTotalCount(1); } catch (e) { fail_ct++; }\n"
|
|
"try { window.mrtAssertBrowserCount(1); } catch (e) { fail_ct++; }\n"
|
|
"try { window.mrtAssertContextCount(1); } catch (e) { fail_ct++; }\n"
|
|
"window.mrtNotify('' + (fail_ct == 3));"
|
|
"</script></body></html>";
|
|
}
|
|
return html;
|
|
}
|
|
|
|
void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
|
|
got_done_.yes();
|
|
EXPECT_STREQ("true", message.c_str());
|
|
DestroyTest();
|
|
}
|
|
|
|
void DestroyTest() override {
|
|
EXPECT_TRUE(got_done_);
|
|
TestHandler::DestroyTest();
|
|
}
|
|
|
|
private:
|
|
const bool test_success_;
|
|
TrackCallback got_done_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Verify that the test harness works with successful assertions.
|
|
TEST(MessageRouterTest, HarnessSuccess) {
|
|
CefRefPtr<HarnessTestHandler> handler = new HarnessTestHandler(true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Verify that the test harness works with failed assertions.
|
|
TEST(MessageRouterTest, HarnessFailure) {
|
|
CefRefPtr<HarnessTestHandler> handler = new HarnessTestHandler(false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
namespace {
|
|
|
|
const char kSingleQueryRequest[] = "request_context";
|
|
const char kSingleQueryResponse[] = "success_response";
|
|
const int kSingleQueryErrorCode = 5;
|
|
const char kSingleQueryErrorMessage[] = "error_message";
|
|
|
|
// Test a single query in a single page load.
|
|
class SingleQueryTestHandler : public SingleLoadTestHandler {
|
|
public:
|
|
enum TestType {
|
|
SUCCESS,
|
|
FAILURE,
|
|
CANCEL,
|
|
};
|
|
|
|
SingleQueryTestHandler(TestType type, bool sync_callback)
|
|
: test_type_(type), sync_callback_(sync_callback), query_id_(0) {}
|
|
|
|
std::string GetMainHTML() override {
|
|
std::string html;
|
|
|
|
std::stringstream ss;
|
|
ss << kSingleQueryErrorCode;
|
|
const std::string& errorCodeStr = ss.str();
|
|
|
|
html =
|
|
"<html><body><script>\n"
|
|
// No requests should exist.
|
|
"window.mrtAssertTotalCount(0);\n"
|
|
"window.mrtAssertBrowserCount(0);\n"
|
|
"window.mrtAssertContextCount(0);\n"
|
|
// Send the query.
|
|
"var request_id = window.mrtQuery({\n"
|
|
" request: '" +
|
|
std::string(kSingleQueryRequest) +
|
|
"',\n"
|
|
" persistent: false,\n"
|
|
" onSuccess: function(response) {\n"
|
|
// Request should be removed before callback is executed.
|
|
" window.mrtAssertTotalCount(0);\n"
|
|
" window.mrtAssertBrowserCount(0);\n"
|
|
" window.mrtAssertContextCount(0);\n"
|
|
" if (response == '" +
|
|
std::string(kSingleQueryResponse) +
|
|
"')\n"
|
|
" window.mrtNotify('success');\n"
|
|
" else\n"
|
|
" window.mrtNotify('error-onSuccess');\n"
|
|
" },\n"
|
|
" onFailure: function(error_code, error_message) {\n"
|
|
// Request should be removed before callback is executed.
|
|
" window.mrtAssertTotalCount(0);\n"
|
|
" window.mrtAssertBrowserCount(0);\n"
|
|
" window.mrtAssertContextCount(0);\n"
|
|
" if (error_code == " +
|
|
errorCodeStr + " && error_message == '" +
|
|
std::string(kSingleQueryErrorMessage) +
|
|
"')\n"
|
|
" window.mrtNotify('failure');\n"
|
|
" else\n"
|
|
" window.mrtNotify('error-onFailure');\n"
|
|
" }\n"
|
|
"});\n"
|
|
// Request should exist.
|
|
"window.mrtAssertTotalCount(1);\n"
|
|
"window.mrtAssertBrowserCount(1);\n"
|
|
"window.mrtAssertContextCount(1);\n";
|
|
|
|
if (test_type_ == CANCEL) {
|
|
html +=
|
|
"window.mrtQueryCancel(request_id);\n"
|
|
// Request should be removed immediately.
|
|
"window.mrtAssertTotalCount(0);\n"
|
|
"window.mrtAssertBrowserCount(0);\n"
|
|
"window.mrtAssertContextCount(0);\n"
|
|
"window.mrtNotify('cancel');\n";
|
|
}
|
|
|
|
html += "</script></body></html>";
|
|
return html;
|
|
}
|
|
|
|
void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
|
|
// OnNotify only be called once.
|
|
EXPECT_FALSE(got_notify_);
|
|
got_notify_.yes();
|
|
|
|
if (test_type_ == SUCCESS) {
|
|
EXPECT_STREQ("success", message.c_str());
|
|
} else if (test_type_ == FAILURE) {
|
|
EXPECT_STREQ("failure", message.c_str());
|
|
} else if (test_type_ == CANCEL) {
|
|
EXPECT_STREQ("cancel", message.c_str());
|
|
}
|
|
|
|
DestroyTestIfDone();
|
|
}
|
|
|
|
void ExecuteCallback() {
|
|
EXPECT_TRUE(callback_.get());
|
|
if (test_type_ == SUCCESS) {
|
|
callback_->Success(kSingleQueryResponse);
|
|
} else if (test_type_ == FAILURE) {
|
|
callback_->Failure(kSingleQueryErrorCode, kSingleQueryErrorMessage);
|
|
} else {
|
|
EXPECT_TRUE(false); // Not reached.
|
|
}
|
|
callback_ = NULL;
|
|
}
|
|
|
|
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> callback) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
EXPECT_NE(0, query_id);
|
|
EXPECT_FALSE(persistent);
|
|
EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
|
|
|
|
got_on_query_.yes();
|
|
|
|
query_id_ = query_id;
|
|
callback_ = callback;
|
|
|
|
if (test_type_ == SUCCESS || test_type_ == FAILURE) {
|
|
if (sync_callback_) {
|
|
ExecuteCallback();
|
|
} else {
|
|
CefPostTask(TID_UI,
|
|
base::Bind(&SingleQueryTestHandler::ExecuteCallback, this));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
EXPECT_EQ(test_type_, CANCEL);
|
|
EXPECT_EQ(query_id_, query_id);
|
|
EXPECT_TRUE(got_on_query_);
|
|
EXPECT_TRUE(callback_.get());
|
|
|
|
got_on_query_canceled_.yes();
|
|
callback_ = NULL;
|
|
|
|
DestroyTestIfDone();
|
|
}
|
|
|
|
void DestroyTestIfDone() {
|
|
bool destroy_test = false;
|
|
if (test_type_ == CANCEL)
|
|
destroy_test = got_notify_ && got_on_query_canceled_;
|
|
else
|
|
destroy_test = got_notify_;
|
|
if (destroy_test)
|
|
DestroyTest();
|
|
}
|
|
|
|
void DestroyTest() override {
|
|
EXPECT_TRUE(got_notify_);
|
|
EXPECT_TRUE(got_on_query_);
|
|
EXPECT_FALSE(callback_.get());
|
|
|
|
if (test_type_ == CANCEL)
|
|
EXPECT_TRUE(got_on_query_canceled_);
|
|
else
|
|
EXPECT_FALSE(got_on_query_canceled_);
|
|
|
|
TestHandler::DestroyTest();
|
|
}
|
|
|
|
private:
|
|
const TestType test_type_;
|
|
const bool sync_callback_;
|
|
|
|
int64 query_id_;
|
|
CefRefPtr<Callback> callback_;
|
|
|
|
TrackCallback got_on_query_;
|
|
TrackCallback got_on_query_canceled_;
|
|
TrackCallback got_notify_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Test that a single query with successful result delivered synchronously.
|
|
TEST(MessageRouterTest, SingleQuerySuccessSyncCallback) {
|
|
CefRefPtr<SingleQueryTestHandler> handler =
|
|
new SingleQueryTestHandler(SingleQueryTestHandler::SUCCESS, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that a single query with successful result delivered asynchronously.
|
|
TEST(MessageRouterTest, SingleQuerySuccessAsyncCallback) {
|
|
CefRefPtr<SingleQueryTestHandler> handler =
|
|
new SingleQueryTestHandler(SingleQueryTestHandler::SUCCESS, false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that a single query with failure result delivered synchronously.
|
|
TEST(MessageRouterTest, SingleQueryFailureSyncCallback) {
|
|
CefRefPtr<SingleQueryTestHandler> handler =
|
|
new SingleQueryTestHandler(SingleQueryTestHandler::FAILURE, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that a single query with failure result delivered asynchronously.
|
|
TEST(MessageRouterTest, SingleQueryFailureAsyncCallback) {
|
|
CefRefPtr<SingleQueryTestHandler> handler =
|
|
new SingleQueryTestHandler(SingleQueryTestHandler::FAILURE, false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that a single query with cancellation.
|
|
TEST(MessageRouterTest, SingleQueryCancel) {
|
|
CefRefPtr<SingleQueryTestHandler> handler =
|
|
new SingleQueryTestHandler(SingleQueryTestHandler::CANCEL, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
namespace {
|
|
|
|
const int kSinglePersistentQueryResponseCount = 10;
|
|
|
|
// Test a single persistent query in a single page load.
|
|
class SinglePersistentQueryTestHandler : public SingleLoadTestHandler {
|
|
public:
|
|
enum TestType {
|
|
SUCCESS,
|
|
FAILURE,
|
|
};
|
|
|
|
SinglePersistentQueryTestHandler(TestType test_type, bool sync_callback)
|
|
: test_type_(test_type), sync_callback_(sync_callback), query_id_(0) {}
|
|
|
|
std::string GetMainHTML() override {
|
|
std::string html;
|
|
|
|
std::stringstream ss;
|
|
ss << kSinglePersistentQueryResponseCount;
|
|
const std::string& responseCountStr = ss.str();
|
|
ss.str("");
|
|
ss << kSingleQueryErrorCode;
|
|
const std::string& errorCodeStr = ss.str();
|
|
|
|
html =
|
|
"<html><body><script>\n"
|
|
// No requests should exist.
|
|
"window.mrtAssertTotalCount(0);\n"
|
|
"window.mrtAssertBrowserCount(0);\n"
|
|
"window.mrtAssertContextCount(0);\n"
|
|
// Keep track of the number of responses.
|
|
"var count = 0;\n"
|
|
// Send the query.
|
|
"var request_id = window.mrtQuery({\n"
|
|
" request: '" +
|
|
std::string(kSingleQueryRequest) +
|
|
"',\n"
|
|
" persistent: true,\n"
|
|
" onSuccess: function(response) {\n"
|
|
// Request should not be removed.
|
|
" window.mrtAssertTotalCount(1);\n"
|
|
" window.mrtAssertBrowserCount(1);\n"
|
|
" window.mrtAssertContextCount(1);\n"
|
|
" if (response == '" +
|
|
std::string(kSingleQueryResponse) +
|
|
"') {\n"
|
|
" if (++count == " +
|
|
responseCountStr +
|
|
") {\n"
|
|
" window.mrtNotify('success');\n"
|
|
" window.mrtQueryCancel(request_id);\n"
|
|
// Request should be removed immediately.
|
|
" window.mrtAssertTotalCount(0);\n"
|
|
" window.mrtAssertBrowserCount(0);\n"
|
|
" window.mrtAssertContextCount(0);\n"
|
|
" }\n"
|
|
" } else {\n"
|
|
" window.mrtNotify('error-onSuccess');\n"
|
|
" }\n"
|
|
" },\n"
|
|
" onFailure: function(error_code, error_message) {\n"
|
|
// Request should be removed before callback is executed.
|
|
" window.mrtAssertTotalCount(0);\n"
|
|
" window.mrtAssertBrowserCount(0);\n"
|
|
" window.mrtAssertContextCount(0);\n"
|
|
" if (error_code == " +
|
|
errorCodeStr + " && error_message == '" +
|
|
std::string(kSingleQueryErrorMessage) +
|
|
"') {\n"
|
|
" window.mrtNotify('failure');\n"
|
|
" } else {\n"
|
|
" window.mrtNotify('error-onFailure');\n"
|
|
" }\n"
|
|
" }\n"
|
|
"});\n"
|
|
// Request should exist.
|
|
"window.mrtAssertTotalCount(1);\n"
|
|
"window.mrtAssertBrowserCount(1);\n"
|
|
"window.mrtAssertContextCount(1);\n";
|
|
|
|
html += "</script></body></html>";
|
|
return html;
|
|
}
|
|
|
|
void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
|
|
if (test_type_ == SUCCESS) {
|
|
EXPECT_STREQ("success", message.c_str());
|
|
} else if (test_type_ == FAILURE) {
|
|
EXPECT_STREQ("failure", message.c_str());
|
|
}
|
|
|
|
got_notify_.yes();
|
|
|
|
DestroyTestIfDone();
|
|
}
|
|
|
|
void ExecuteCallback() {
|
|
EXPECT_TRUE(callback_.get());
|
|
if (test_type_ == SUCCESS) {
|
|
callback_->Success(kSingleQueryResponse);
|
|
} else {
|
|
callback_->Failure(kSingleQueryErrorCode, kSingleQueryErrorMessage);
|
|
callback_ = NULL;
|
|
}
|
|
}
|
|
|
|
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> callback) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
EXPECT_NE(0, query_id);
|
|
EXPECT_TRUE(persistent);
|
|
EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
|
|
|
|
got_on_query_.yes();
|
|
|
|
query_id_ = query_id;
|
|
callback_ = callback;
|
|
|
|
int repeat =
|
|
(test_type_ == SUCCESS ? kSinglePersistentQueryResponseCount : 1);
|
|
|
|
for (int i = 0; i < repeat; ++i) {
|
|
if (sync_callback_) {
|
|
ExecuteCallback();
|
|
} else {
|
|
CefPostTask(
|
|
TID_UI,
|
|
base::Bind(&SinglePersistentQueryTestHandler::ExecuteCallback,
|
|
this));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
EXPECT_EQ(query_id_, query_id);
|
|
EXPECT_TRUE(got_on_query_);
|
|
EXPECT_TRUE(callback_.get());
|
|
|
|
got_on_query_canceled_.yes();
|
|
callback_ = NULL;
|
|
|
|
DestroyTestIfDone();
|
|
}
|
|
|
|
void DestroyTestIfDone() {
|
|
bool destroy_test = false;
|
|
if (test_type_ == SUCCESS) {
|
|
if (got_on_query_ && got_on_query_canceled_ && got_notify_)
|
|
destroy_test = true;
|
|
} else if (got_on_query_ && got_notify_) {
|
|
destroy_test = true;
|
|
}
|
|
|
|
if (destroy_test)
|
|
DestroyTest();
|
|
}
|
|
|
|
void DestroyTest() override {
|
|
EXPECT_TRUE(got_notify_);
|
|
EXPECT_TRUE(got_on_query_);
|
|
EXPECT_FALSE(callback_.get());
|
|
|
|
if (test_type_ == SUCCESS)
|
|
EXPECT_TRUE(got_on_query_canceled_);
|
|
else
|
|
EXPECT_FALSE(got_on_query_canceled_);
|
|
|
|
TestHandler::DestroyTest();
|
|
}
|
|
|
|
private:
|
|
const TestType test_type_;
|
|
const bool sync_callback_;
|
|
|
|
int64 query_id_;
|
|
CefRefPtr<Callback> callback_;
|
|
|
|
TrackCallback got_on_query_;
|
|
TrackCallback got_on_query_canceled_;
|
|
TrackCallback got_notify_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Test that a single query with successful result delivered synchronously.
|
|
TEST(MessageRouterTest, SinglePersistentQuerySuccessSyncCallback) {
|
|
CefRefPtr<SinglePersistentQueryTestHandler> handler =
|
|
new SinglePersistentQueryTestHandler(
|
|
SinglePersistentQueryTestHandler::SUCCESS, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that a single query with successful result delivered asynchronously.
|
|
TEST(MessageRouterTest, SinglePersistentQuerySuccessAsyncCallback) {
|
|
CefRefPtr<SinglePersistentQueryTestHandler> handler =
|
|
new SinglePersistentQueryTestHandler(
|
|
SinglePersistentQueryTestHandler::SUCCESS, false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that a single query with failure result delivered synchronously.
|
|
TEST(MessageRouterTest, SinglePersistentQueryFailureSyncCallback) {
|
|
CefRefPtr<SinglePersistentQueryTestHandler> handler =
|
|
new SinglePersistentQueryTestHandler(
|
|
SinglePersistentQueryTestHandler::FAILURE, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that a single query with failure result delivered asynchronously.
|
|
TEST(MessageRouterTest, SinglePersistentQueryFailureAsyncCallback) {
|
|
CefRefPtr<SinglePersistentQueryTestHandler> handler =
|
|
new SinglePersistentQueryTestHandler(
|
|
SinglePersistentQueryTestHandler::FAILURE, false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
namespace {
|
|
|
|
// Test a single unhandled query in a single page load.
|
|
class SingleUnhandledQueryTestHandler : public SingleLoadTestHandler {
|
|
public:
|
|
SingleUnhandledQueryTestHandler() {}
|
|
|
|
std::string GetMainHTML() override {
|
|
std::string html;
|
|
|
|
html =
|
|
"<html><body><script>\n"
|
|
// No requests should exist.
|
|
"window.mrtAssertTotalCount(0);\n"
|
|
"window.mrtAssertBrowserCount(0);\n"
|
|
"window.mrtAssertContextCount(0);\n"
|
|
// Keep track of the number of responses.
|
|
"var count = 0;\n"
|
|
// Send the query.
|
|
"var request_id = window.mrtQuery({\n"
|
|
" request: '" +
|
|
std::string(kSingleQueryRequest) +
|
|
"',\n"
|
|
" persistent: false,\n"
|
|
" onSuccess: function(response) {\n"
|
|
" window.mrtNotify('error-onSuccess');\n"
|
|
" },\n"
|
|
" onFailure: function(error_code, error_message) {\n"
|
|
// Request should be removed before callback is executed.
|
|
" window.mrtAssertTotalCount(0);\n"
|
|
" window.mrtAssertBrowserCount(0);\n"
|
|
" window.mrtAssertContextCount(0);\n"
|
|
" if (error_code == -1 && "
|
|
"error_message == 'The query has been canceled') {\n"
|
|
" window.mrtNotify('failure');\n"
|
|
" } else {\n"
|
|
" window.mrtNotify('error-onFailure');\n"
|
|
" }\n"
|
|
" }\n"
|
|
"});\n"
|
|
// Request should exist.
|
|
"window.mrtAssertTotalCount(1);\n"
|
|
"window.mrtAssertBrowserCount(1);\n"
|
|
"window.mrtAssertContextCount(1);\n";
|
|
|
|
html += "</script></body></html>";
|
|
return html;
|
|
}
|
|
|
|
void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
EXPECT_STREQ("failure", message.c_str());
|
|
|
|
got_notify_.yes();
|
|
|
|
DestroyTest();
|
|
}
|
|
|
|
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> callback) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
EXPECT_NE(0, query_id);
|
|
EXPECT_FALSE(persistent);
|
|
EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
|
|
|
|
got_on_query_.yes();
|
|
|
|
return false;
|
|
}
|
|
|
|
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id) override {
|
|
EXPECT_FALSE(true); // Not reached.
|
|
}
|
|
|
|
void DestroyTest() override {
|
|
EXPECT_TRUE(got_on_query_);
|
|
EXPECT_TRUE(got_notify_);
|
|
|
|
TestHandler::DestroyTest();
|
|
}
|
|
|
|
private:
|
|
TrackCallback got_on_query_;
|
|
TrackCallback got_notify_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Test that a single unhandled query results in a call to onFailure.
|
|
TEST(MessageRouterTest, SingleUnhandledQuery) {
|
|
CefRefPtr<SingleUnhandledQueryTestHandler> handler =
|
|
new SingleUnhandledQueryTestHandler();
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
namespace {
|
|
|
|
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 = "<html><body>" + label_ + "<script>\n";
|
|
|
|
// No requests should exist.
|
|
if (assert_total)
|
|
html += "window.mrtAssertTotalCount(0);\n";
|
|
if (assert_browser)
|
|
html += "window.mrtAssertBrowserCount(0);\n";
|
|
html += "window.mrtAssertContextCount(0);\n";
|
|
|
|
if (synchronous_) {
|
|
// Run all of the queries synchronously. None will complete before the
|
|
// last one begins.
|
|
for (size_t i = 0; i < test_query_vector_.size(); ++i) {
|
|
const TestQuery& query = test_query_vector_[i];
|
|
html += GetQueryHTML(static_cast<int>(i), query);
|
|
}
|
|
|
|
const int total_ct = static_cast<int>(test_query_vector_.size());
|
|
|
|
// Pending requests should match the total created.
|
|
const std::string& total_val = GetIntString(total_ct);
|
|
if (assert_total)
|
|
html += "window.mrtAssertTotalCount(" + total_val + ");\n";
|
|
if (assert_browser)
|
|
html += "window.mrtAssertBrowserCount(" + total_val + ");\n";
|
|
html += "window.mrtAssertContextCount(" + total_val + ");\n";
|
|
|
|
int cancel_ct = 0;
|
|
|
|
// Cancel all of the queries with type CANCEL.
|
|
for (size_t i = 0; i < test_query_vector_.size(); ++i) {
|
|
const TestQuery& query = test_query_vector_[i];
|
|
if (query.type == CANCEL) {
|
|
html += GetCancelHTML(static_cast<int>(i), query);
|
|
cancel_ct++;
|
|
}
|
|
}
|
|
|
|
if (cancel_ct > 0) {
|
|
// Pending requests should match the total not canceled.
|
|
const std::string& cancel_val = GetIntString(total_ct - cancel_ct);
|
|
if (assert_total)
|
|
html += "window.mrtAssertTotalCount(" + cancel_val + ");\n";
|
|
if (assert_browser)
|
|
html += "window.mrtAssertBrowserCount(" + cancel_val + ");\n";
|
|
html += "window.mrtAssertContextCount(" + cancel_val + ");\n";
|
|
}
|
|
} else {
|
|
// Run all of the queries asynchronously. Some may complete before
|
|
// others begin.
|
|
for (size_t i = 0; i < test_query_vector_.size(); ++i) {
|
|
const TestQuery& query = test_query_vector_[i];
|
|
|
|
const int index = static_cast<int>(i);
|
|
|
|
// Each request is delayed by 10ms from the previous request.
|
|
const std::string& delay_val = GetIntString(index);
|
|
const std::string& query_html = GetQueryHTML(index, query);
|
|
|
|
html += "window.setTimeout(function() {\n" + query_html;
|
|
|
|
if (query.type == CANCEL) {
|
|
// Cancel the query asynchronously with a 10ms delay.
|
|
const std::string& request_id_var =
|
|
GetIDString(kMultiQueryRequestId, index);
|
|
html +=
|
|
" window.setTimeout(function() {\n"
|
|
" window.mrtQueryCancel(" +
|
|
request_id_var +
|
|
");\n"
|
|
" }, 1);\n";
|
|
}
|
|
|
|
html += "\n}, " + delay_val + ");\n";
|
|
}
|
|
}
|
|
|
|
html += "</script></body></html>";
|
|
|
|
return html;
|
|
}
|
|
|
|
void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 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 = NULL;
|
|
|
|
// 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<int>(test_query_vector_.size()));
|
|
}
|
|
|
|
bool HasAutoQueries() const {
|
|
return (manual_total_ != static_cast<int>(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 frame_id;
|
|
bool is_main_frame;
|
|
|
|
// Used when a query is canceled.
|
|
int64 query_id;
|
|
CefRefPtr<Callback> callback;
|
|
|
|
TrackCallback got_query;
|
|
TrackCallback got_query_canceled;
|
|
TrackCallback got_success;
|
|
TrackCallback got_error;
|
|
};
|
|
|
|
class NotifyTask : public CefTask {
|
|
public:
|
|
NotifyTask(base::WeakPtr<MultiQueryManager> 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<MultiQueryManager> 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<int>(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<int>(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<int>(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<TestQuery> TestQueryVector;
|
|
TestQueryVector test_query_vector_;
|
|
|
|
typedef std::set<Observer*> 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<MultiQueryManager> 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
|
|
manager_.OnNotify(browser, frame, message);
|
|
}
|
|
|
|
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> callback) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
|
|
return manager_.OnQuery(browser, frame, query_id, request, persistent,
|
|
callback);
|
|
}
|
|
|
|
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 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(NULL, NULL, 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(NULL, NULL, 0);
|
|
|
|
DestroyTest();
|
|
|
|
if (!SignalCompletionWhenAllBrowsersClose()) {
|
|
// Complete asynchronously so the call stack has a chance to unwind.
|
|
CefPostTask(
|
|
TID_UI,
|
|
base::Bind(&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<MultiQuerySingleFrameTestHandler> 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<MultiQuerySingleFrameTestHandler> 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<MultiQuerySingleFrameTestHandler> 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<MultiQuerySingleFrameTestHandler> 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<MultiQuerySingleFrameTestHandler> 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<MultiQuerySingleFrameTestHandler> 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<MultiQuerySingleFrameTestHandler> 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 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_;
|
|
int 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
|
|
manager_.OnNotify(browser, frame, message);
|
|
}
|
|
|
|
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> callback) override {
|
|
AssertMainBrowser(browser);
|
|
AssertMainFrame(frame);
|
|
|
|
return manager_.OnQuery(browser, frame, query_id, request, persistent,
|
|
callback);
|
|
}
|
|
|
|
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 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<CefMessageRouterBrowserSide> 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(NULL, NULL, 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<CefMessageRouterBrowserSide> 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<MultiQueryMultiHandlerTestHandler> handler =
|
|
new MultiQueryMultiHandlerTestHandler(false, false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that multiple handlers behave correctly. Cancel by removing the
|
|
// handlers.
|
|
TEST(MessageRouterTest, MultiQueryMultiHandlerCancelByRemovingHandler) {
|
|
CefRefPtr<MultiQueryMultiHandlerTestHandler> 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<int>(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 = "<html><body>\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 += "<iframe id=\"" + name + "\" src=\"" + url + "\"></iframe>\n";
|
|
}
|
|
|
|
html += "</body></html>";
|
|
return html;
|
|
}
|
|
|
|
void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 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<int>(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<int>(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<CefBrowser> browser, CefRefPtr<CefFrame> 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 frame_id = frame->IsMain() ? -1 : frame->GetIdentifier();
|
|
|
|
const std::pair<int, int64>& 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame) const {
|
|
const int browser_id = browser->GetIdentifier();
|
|
// Always use the same ID for the main frame.
|
|
const int64 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<int>(url.rfind("/"));
|
|
int pos2 = static_cast<int>(url.rfind("."));
|
|
EXPECT_TRUE(pos1 >= 0 && pos2 >= 0 && pos1 < pos2);
|
|
return url.substr(pos1 + 1, pos2 - pos1 - 1);
|
|
}
|
|
|
|
private:
|
|
typedef std::vector<MultiQueryManager*> ManagerList;
|
|
// Map of (browser ID, frame ID) to manager.
|
|
typedef std::map<std::pair<int, int64>, 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<Observer*> 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_, "<html><body>cancel</body></html>", "text/html");
|
|
}
|
|
}
|
|
|
|
std::string GetMainHTML() override { return manager_map_.GetMainHTML(); }
|
|
|
|
void OnLoadStart(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
TransitionType transition_type) override {
|
|
AssertMainBrowser(browser);
|
|
if (!frame->IsMain())
|
|
manager_map_.OnLoadStart(browser, frame);
|
|
}
|
|
|
|
void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) override {
|
|
AssertMainBrowser(browser);
|
|
EXPECT_FALSE(frame->IsMain());
|
|
|
|
manager_map_.OnNotify(browser, frame, message);
|
|
}
|
|
|
|
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> callback) override {
|
|
AssertMainBrowser(browser);
|
|
EXPECT_FALSE(frame->IsMain());
|
|
|
|
return manager_map_.OnQuery(browser, frame, query_id, request, persistent,
|
|
callback);
|
|
}
|
|
|
|
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 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<CefFrame> 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<MultiQueryMultiFrameTestHandler> 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<MultiQueryMultiFrameTestHandler> 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<MultiQueryMultiFrameTestHandler> 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<MultiQueryMultiFrameTestHandler> 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<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
TransitionType transition_type) override {
|
|
manager_map_.OnLoadStart(browser, frame);
|
|
}
|
|
|
|
void OnNotify(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
const std::string& message) override {
|
|
manager_map_.OnNotify(browser, frame, message);
|
|
}
|
|
|
|
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> callback) override {
|
|
return manager_map_.OnQuery(browser, frame, query_id, request, persistent,
|
|
callback);
|
|
}
|
|
|
|
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 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<CefMessageRouterBrowserSide> 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_, "<html><body>cancel</body></html>", "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, NULL);
|
|
CreateBrowser(url2, NULL);
|
|
CreateBrowser(url3, NULL);
|
|
}
|
|
|
|
private:
|
|
bool same_origin_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Test that multiple browsers can query simultaniously from the same origin.
|
|
TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginSync) {
|
|
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
|
|
new MultiQueryMultiBrowserTestHandler(true, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that multiple browsers can query simultaniously from the same origin.
|
|
TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginAsync) {
|
|
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
|
|
new MultiQueryMultiBrowserTestHandler(false, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that multiple browsers can query simultaniously from different origins.
|
|
TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginSync) {
|
|
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
|
|
new MultiQueryMultiBrowserTestHandler(true, false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that multiple browsers can query simultaniously from different origins.
|
|
TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginAsync) {
|
|
CefRefPtr<MultiQueryMultiBrowserTestHandler> 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";
|
|
|
|
AddManagedResource(url1_, true, true);
|
|
AddManagedResource(url2_, true, true);
|
|
AddManagedResource(url3_, true, true);
|
|
Finalize();
|
|
|
|
// 1. Load the 1st url.
|
|
CreateBrowser(url1_, NULL);
|
|
}
|
|
|
|
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<MultiQueryMultiNavigateTestHandler> handler =
|
|
new MultiQueryMultiNavigateTestHandler(true, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that multiple navigations can query from the same origin.
|
|
TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginAsync) {
|
|
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
|
|
new MultiQueryMultiNavigateTestHandler(false, true);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that multiple navigations can query from different origins.
|
|
TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginSync) {
|
|
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
|
|
new MultiQueryMultiNavigateTestHandler(true, false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test that multiple navigations can query from different origins.
|
|
TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginAsync) {
|
|
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
|
|
new MultiQueryMultiNavigateTestHandler(false, false);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|