#include <algorithm>
#include <sstream>
#include "include/base/cef_callback.h"
#include "include/base/cef_callback_helpers.h"
#include "include/cef_callback.h"
#include "include/cef_devtools_message_observer.h"
#include "include/cef_parser.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
namespace {
const char kTestUrl1[] = "http://tests/DevToolsMessage1";
const char kTestUrl2[] = "http://tests/DevToolsMessage2";
class DevToolsMessageTestHandler : public TestHandler {
public:
DevToolsMessageTestHandler() {}
void RunTest() override {
AddResource(kTestUrl1, "<html><body>Test1</body></html>", "text/html");
AddResource(kTestUrl2, "<html><body>Test2</body></html>", "text/html");
CreateBrowser(kTestUrl1);
SetTestTimeout();
}
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
TestHandler::OnAfterCreated(browser);
registration_ = browser->GetHost()->AddDevToolsMessageObserver(
new TestMessageObserver(this));
EXPECT_TRUE(registration_);
}
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) override {
if (!isLoading) {
load_ct_++;
if (load_ct_ == 1) {
ExecuteMethod(
"Page.enable", "",
base::BindOnce(&DevToolsMessageTestHandler::Navigate, this));
} else if (load_ct_ == 2) {
MaybeDestroyTest();
}
}
}
void DestroyTest() override {
EXPECT_TRUE(registration_);
registration_ = nullptr;
EXPECT_TRUE(observer_destroyed_);
EXPECT_GE(method_send_ct_, 1);
EXPECT_GE(method_execute_ct_, 1);
EXPECT_EQ(expected_method_ct_, method_send_ct_ + method_execute_ct_);
EXPECT_EQ(expected_method_ct_, result_ct_);
EXPECT_EQ(expected_method_ct_, last_result_id_);
EXPECT_EQ(message_ct_, result_ct_ + event_ct_);
EXPECT_GE(event_ct_, 1);
EXPECT_EQ(expected_load_ct_, load_ct_);
EXPECT_EQ(1, attached_ct_);
EXPECT_EQ(0, detached_ct_);
TestHandler::DestroyTest();
}
private:
struct MethodResult {
int message_id;
bool success;
std::string result;
};
struct Event {
std::string method;
std::string params;
};
class TestMessageObserver : public CefDevToolsMessageObserver {
public:
explicit TestMessageObserver(DevToolsMessageTestHandler* handler)
: handler_(handler) {}
virtual ~TestMessageObserver() { handler_->observer_destroyed_.yes(); }
bool OnDevToolsMessage(CefRefPtr<CefBrowser> browser,
const void* message,
size_t message_size) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->message_ct_++;
return false;
}
void OnDevToolsMethodResult(CefRefPtr<CefBrowser> browser,
int message_id,
bool success,
const void* result,
size_t result_size) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->result_ct_++;
MethodResult mr;
mr.message_id = message_id;
mr.success = success;
if (result) {
mr.result = std::string(static_cast<const char*>(result),
std::min(static_cast<int>(result_size), 80));
}
handler_->OnMethodResult(mr);
}
void OnDevToolsEvent(CefRefPtr<CefBrowser> browser,
const CefString& method,
const void* params,
size_t params_size) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->event_ct_++;
Event ev;
ev.method = method;
if (params) {
ev.params = std::string(static_cast<const char*>(params),
std::min(static_cast<int>(params_size), 80));
}
handler_->OnEvent(ev);
}
void OnDevToolsAgentAttached(CefRefPtr<CefBrowser> browser) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->attached_ct_++;
}
void OnDevToolsAgentDetached(CefRefPtr<CefBrowser> browser) override {
EXPECT_TRUE(browser.get());
EXPECT_EQ(handler_->GetBrowserId(), browser->GetIdentifier());
handler_->detached_ct_++;
}
private:
DevToolsMessageTestHandler* handler_;
IMPLEMENT_REFCOUNTING(TestMessageObserver);
DISALLOW_COPY_AND_ASSIGN(TestMessageObserver);
};
void ExecuteMethod(const std::string& method,
const std::string& params,
base::OnceClosure next_step,
const std::string& expected_result = "{}",
bool expected_success = true) {
CHECK(!method.empty());
CHECK(!next_step.is_null());
int message_id = next_message_id_++;
std::stringstream message;
message << "{\"id\":" << message_id << ",\"method\":\"" << method << "\"";
if (!params.empty()) {
message << ",\"params\":" << params;
}
message << "}";
pending_message_ = message.str();
pending_result_next_ = std::move(next_step);
pending_result_ = {message_id, expected_success, expected_result};
if (message_id % 2 == 0) {
method_send_ct_++;
GetBrowser()->GetHost()->SendDevToolsMessage(pending_message_.data(),
pending_message_.size());
} else {
method_execute_ct_++;
CefRefPtr<CefDictionaryValue> dict;
if (!params.empty()) {
CefRefPtr<CefValue> value =
CefParseJSON(params.data(), params.size(), JSON_PARSER_RFC);
EXPECT_TRUE(value && value->GetType() == VTYPE_DICTIONARY)
<< "failed to parse: " << params;
if (value && value->GetType() == VTYPE_DICTIONARY) {
dict = value->GetDictionary();
}
}
GetBrowser()->GetHost()->ExecuteDevToolsMethod(message_id, method, dict);
}
}
void OnMethodResult(const MethodResult& result) {
EXPECT_EQ(pending_result_.message_id, result.message_id)
<< "with message=" << pending_message_;
if (result.message_id != pending_result_.message_id)
return;
EXPECT_EQ(pending_result_.success, result.success)
<< "with message=" << pending_message_;
EXPECT_TRUE(result.result.find(pending_result_.result) == 0)
<< "with message=" << pending_message_
<< "\nand actual result=" << result.result
<< "\nand expected result=" << pending_result_.result;
last_result_id_ = result.message_id;
CefPostTask(TID_UI, std::move(pending_result_next_));
pending_message_.clear();
pending_result_ = {};
}
void OnEvent(const Event& event) {
if (event.method != pending_event_.method)
return;
EXPECT_TRUE(event.params.find(pending_event_.params) == 0)
<< "with method=" << event.method
<< "\nand actual params=" << event.params
<< "\nand expected params=" << pending_event_.params;
CefPostTask(TID_UI, std::move(pending_event_next_));
pending_event_ = {};
}
void Navigate() {
pending_event_ = {"Page.frameNavigated", "{\"frame\":"};
pending_event_next_ =
base::BindOnce(&DevToolsMessageTestHandler::AfterNavigate, this);
std::stringstream params;
params << "{\"url\":\"" << kTestUrl2 << "\"}";
ExecuteMethod("Page.navigate", params.str(), base::DoNothing(),
"{\"frameId\":");
}
void AfterNavigate() {
ExecuteMethod(
"Page.disable", "",
base::BindOnce(&DevToolsMessageTestHandler::AfterPageDisabled, this));
}
void AfterPageDisabled() {
ExecuteMethod(
"Foo.doesNotExist", "",
base::BindOnce(&DevToolsMessageTestHandler::MaybeDestroyTest, this),
"{\"code\":-32601,\"message\":\"'Foo.doesNotExist' wasn't found\"}",
false);
}
void MaybeDestroyTest() {
if (result_ct_ == expected_method_ct_ && load_ct_ == expected_load_ct_) {
DestroyTest();
}
}
const int expected_method_ct_ = 4;
const int expected_load_ct_ = 2;
int next_message_id_ = 1;
std::string pending_message_;
int method_send_ct_ = 0;
int method_execute_ct_ = 0;
MethodResult pending_result_;
int last_result_id_ = -1;
base::OnceClosure pending_result_next_;
Event pending_event_;
base::OnceClosure pending_event_next_;
CefRefPtr<CefRegistration> registration_;
int message_ct_ = 0;
int result_ct_ = 0;
int event_ct_ = 0;
int attached_ct_ = 0;
int detached_ct_ = 0;
int load_ct_ = 0;
TrackCallback observer_destroyed_;
IMPLEMENT_REFCOUNTING(DevToolsMessageTestHandler);
DISALLOW_COPY_AND_ASSIGN(DevToolsMessageTestHandler);
};
}
TEST(DevToolsMessageTest, Messages) {
CefRefPtr<DevToolsMessageTestHandler> handler =
new DevToolsMessageTestHandler();
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}