ceftests: Refactor test_server to support both HTTP and HTTPS servers (see issue #3348)

This commit is contained in:
Marshall Greenblatt 2022-07-30 19:53:48 -04:00
parent 68be2b8938
commit a4d63010c9
17 changed files with 881 additions and 628 deletions

View File

@ -524,7 +524,15 @@
'tests/ceftests/test_request.h',
'tests/ceftests/test_server.cc',
'tests/ceftests/test_server.h',
'tests/ceftests/test_server_unittest.cc',
'tests/ceftests/test_server_observer.h',
'tests/ceftests/test_server_observer.cc',
'tests/ceftests/test_server_observer_unittest.cc',
'tests/ceftests/test_server_manager.h',
'tests/ceftests/test_server_manager.cc',
'tests/ceftests/test_server_runner.h',
'tests/ceftests/test_server_runner.cc',
'tests/ceftests/test_server_runner_normal.cc',
'tests/ceftests/test_server_runner_test.cc',
'tests/ceftests/test_suite.cc',
'tests/ceftests/test_suite.h',
'tests/ceftests/test_util.cc',

View File

@ -17,7 +17,7 @@
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_server.h"
#include "tests/ceftests/test_server_observer.h"
#include "tests/ceftests/test_suite.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"

View File

@ -13,7 +13,7 @@
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/ceftests/test_request.h"
#include "tests/ceftests/test_server.h"
#include "tests/ceftests/test_server_observer.h"
#include "tests/ceftests/test_util.h"
#include "tests/shared/browser/client_app_browser.h"

View File

@ -4,12 +4,9 @@
#include "tests/ceftests/test_server.h"
#include <vector>
#include "tests/ceftests/test_server_manager.h"
#include "include/cef_server.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/gtest/include/gtest/gtest.h"
#include <vector>
namespace test_server {
@ -20,476 +17,22 @@ const char kServerScheme[] = "http";
const char kServerOrigin[] = "http://127.0.0.1:8098";
const char kIncompleteDoNotSendData[] = "DO NOT SEND";
namespace {
class ServerManager;
ServerManager* g_manager = nullptr;
// True if Stop() has been called.
bool g_stopping = false;
// Created on the UI thread and called on the dedicated server thread.
class ServerHandler : public CefServerHandler {
public:
ServerHandler() {
CefServer::CreateServer(kServerAddress, kServerPort, 10, this);
}
~ServerHandler() override {
EXPECT_FALSE(server_);
NotifyServerHandlerDeleted();
}
void Shutdown() { server_->Shutdown(); }
protected:
// CefServerHandler methods:
void OnServerCreated(CefRefPtr<CefServer> server) override {
server_ = server;
NotifyServerCreated(kServerOrigin);
}
void OnServerDestroyed(CefRefPtr<CefServer> server) override {
server_ = nullptr;
NotifyServerDestroyed();
}
void OnClientConnected(CefRefPtr<CefServer> server,
int connection_id) override {
EXPECT_TRUE(server->HasConnection());
EXPECT_TRUE(server->IsValidConnection(connection_id));
}
void OnClientDisconnected(CefRefPtr<CefServer> server,
int connection_id) override {
EXPECT_FALSE(server->IsValidConnection(connection_id));
}
void OnHttpRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request) override {
NotifyHttpRequest(server, connection_id, client_address, request);
}
void OnWebSocketRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request,
CefRefPtr<CefCallback> callback) override {}
void OnWebSocketConnected(CefRefPtr<CefServer> server,
int connection_id) override {}
void OnWebSocketMessage(CefRefPtr<CefServer> server,
int connection_id,
const void* data,
size_t data_size) override {}
private:
static void NotifyServerCreated(const std::string& server_origin);
static void NotifyServerDestroyed();
static void NotifyServerHandlerDeleted();
static void NotifyHttpRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request);
CefRefPtr<CefServer> server_;
IMPLEMENT_REFCOUNTING(ServerHandler);
DISALLOW_COPY_AND_ASSIGN(ServerHandler);
};
// Only accessed on the UI thread. Deletes itself after the server is stopped.
class ServerManager {
public:
ServerManager() {
CEF_REQUIRE_UI_THREAD();
EXPECT_FALSE(g_manager);
g_manager = this;
}
~ServerManager() {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(observer_list_.empty());
EXPECT_TRUE(start_callback_list_.empty());
EXPECT_TRUE(stop_callback_.is_null());
g_manager = nullptr;
}
void Start(StartDoneCallback callback) {
CEF_REQUIRE_UI_THREAD();
if (!origin_.empty()) {
// The server is already running.
std::move(callback).Run(origin_);
return;
}
// If tests run in parallel, and the server is starting, then there may be
// multiple pending callbacks.
start_callback_list_.push_back(std::move(callback));
// Only create the handler a single time.
if (!handler_) {
handler_ = new ServerHandler();
}
}
void Stop(DoneCallback callback) {
CEF_REQUIRE_UI_THREAD();
if (!handler_) {
// The server is not currently running.
std::move(callback).Run();
return;
}
// Only 1 stop callback supported.
EXPECT_TRUE(stop_callback_.is_null());
stop_callback_ = std::move(callback);
handler_->Shutdown();
}
void AddObserver(Observer* observer) {
CEF_REQUIRE_UI_THREAD();
observer_list_.push_back(observer);
}
void RemoveObserver(Observer* observer) {
CEF_REQUIRE_UI_THREAD();
bool found = false;
ObserverList::iterator it = observer_list_.begin();
for (; it != observer_list_.end(); ++it) {
if (*it == observer) {
observer_list_.erase(it);
found = true;
break;
}
}
EXPECT_TRUE(found);
}
void NotifyServerCreated(const std::string& server_origin) {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(origin_.empty());
origin_ = server_origin;
for (auto& callback : start_callback_list_) {
std::move(callback).Run(origin_);
}
start_callback_list_.clear();
}
void NotifyServerDestroyed() {
CEF_REQUIRE_UI_THREAD();
origin_.clear();
handler_ = nullptr;
}
// All server-related objects have been torn down.
void NotifyServerHandlerDeleted() {
CEF_REQUIRE_UI_THREAD();
EXPECT_FALSE(stop_callback_.is_null());
std::move(stop_callback_).Run();
delete this;
}
void NotifyHttpRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request) {
CEF_REQUIRE_UI_THREAD();
// TODO(chrome-runtime): Debug why favicon requests don't always have the
// correct resource type.
const std::string& url = request->GetURL();
if (request->GetResourceType() == RT_FAVICON ||
url.find("/favicon.ico") != std::string::npos) {
// We don't currently handle favicon requests.
server->SendHttp404Response(connection_id);
return;
}
EXPECT_FALSE(observer_list_.empty()) << url;
// Use a copy in case |observer_list_| is modified during iteration.
ObserverList list = observer_list_;
bool handled = false;
auto response_callback = base::BindRepeating(&ServerManager::SendResponse,
server, connection_id);
ObserverList::const_iterator it = list.begin();
for (; it != list.end(); ++it) {
if ((*it)->OnHttpRequest(request, response_callback)) {
handled = true;
break;
}
}
if (!handled) {
server->SendHttp500Response(connection_id, "Unhandled request.");
}
}
private:
static void SendResponse(CefRefPtr<CefServer> server,
int connection_id,
CefRefPtr<CefResponse> response,
const std::string& response_data) {
// Execute on the server thread because some methods require it.
CefRefPtr<CefTaskRunner> task_runner = server->GetTaskRunner();
if (!task_runner->BelongsToCurrentThread()) {
task_runner->PostTask(CefCreateClosureTask(
base::BindOnce(ServerManager::SendResponse, server, connection_id,
response, response_data)));
return;
}
// No response should be sent yet.
EXPECT_TRUE(server->IsValidConnection(connection_id));
const int response_code = response->GetStatus();
if (response_code <= 0) {
// Intentionally not responding for incomplete request tests.
return;
}
const CefString& content_type = response->GetMimeType();
int64 content_length = static_cast<int64>(response_data.size());
CefResponse::HeaderMap extra_headers;
response->GetHeaderMap(extra_headers);
server->SendHttpResponse(connection_id, response_code, content_type,
content_length, extra_headers);
if (response_data == kIncompleteDoNotSendData) {
// Intentionally not sending data for incomplete request tests.
return;
}
if (content_length != 0) {
server->SendRawData(connection_id, response_data.data(),
response_data.size());
server->CloseConnection(connection_id);
}
// The connection should be closed.
EXPECT_FALSE(server->IsValidConnection(connection_id));
}
CefRefPtr<ServerHandler> handler_;
std::string origin_;
using StartDoneCallbackList = std::vector<StartDoneCallback>;
StartDoneCallbackList start_callback_list_;
DoneCallback stop_callback_;
using ObserverList = std::vector<Observer*>;
ObserverList observer_list_;
DISALLOW_COPY_AND_ASSIGN(ServerManager);
};
ServerManager* GetServerManager() {
return g_manager;
CefRefPtr<CefResponse> Create404Response() {
auto response = CefResponse::Create();
response->SetStatus(404);
response->SetMimeType("text/html");
return response;
}
ServerManager* GetOrCreateServerManager() {
if (!g_manager) {
new ServerManager();
EXPECT_TRUE(g_manager);
}
return g_manager;
}
// static
void ServerHandler::NotifyServerCreated(const std::string& server_origin) {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(ServerHandler::NotifyServerCreated,
server_origin));
return;
}
GetServerManager()->NotifyServerCreated(server_origin);
}
// static
void ServerHandler::NotifyServerDestroyed() {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(ServerHandler::NotifyServerDestroyed));
return;
}
GetServerManager()->NotifyServerDestroyed();
}
// static
void ServerHandler::NotifyServerHandlerDeleted() {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI,
base::BindOnce(ServerHandler::NotifyServerHandlerDeleted));
return;
}
GetServerManager()->NotifyServerHandlerDeleted();
}
// static
void ServerHandler::NotifyHttpRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request) {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(ServerHandler::NotifyHttpRequest, server,
connection_id, client_address, request));
return;
}
GetServerManager()->NotifyHttpRequest(server, connection_id, client_address,
request);
}
// May be created on any thread but will be destroyed on the UI thread.
class ObserverRegistration : public CefRegistration {
public:
explicit ObserverRegistration(Observer* const observer)
: observer_(observer) {
EXPECT_TRUE(observer_);
}
~ObserverRegistration() override {
CEF_REQUIRE_UI_THREAD();
ServerManager* manager = GetServerManager();
if (manager) {
manager->RemoveObserver(observer_);
observer_->OnUnregistered();
}
}
void Initialize() {
CEF_REQUIRE_UI_THREAD();
GetOrCreateServerManager()->AddObserver(observer_);
observer_->OnRegistered();
}
private:
Observer* const observer_;
IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(ObserverRegistration);
DISALLOW_COPY_AND_ASSIGN(ObserverRegistration);
};
void InitializeRegistration(CefRefPtr<ObserverRegistration> registration,
DoneCallback callback) {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(InitializeRegistration, registration,
std::move(callback)));
return;
}
EXPECT_FALSE(g_stopping);
registration->Initialize();
if (!callback.is_null())
std::move(callback).Run();
}
} // namespace
void Start(StartDoneCallback callback) {
EXPECT_FALSE(callback.is_null());
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(Start, std::move(callback)));
return;
}
EXPECT_FALSE(g_stopping);
GetOrCreateServerManager()->Start(std::move(callback));
}
void Stop(DoneCallback callback) {
EXPECT_FALSE(callback.is_null());
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(Stop, std::move(callback)));
return;
}
// Stop will be called one time on test framework shutdown.
EXPECT_FALSE(g_stopping);
g_stopping = true;
ServerManager* manager = GetServerManager();
if (manager) {
manager->Stop(std::move(callback));
} else {
std::move(callback).Run();
}
}
CefRefPtr<CefRegistration> AddObserver(Observer* observer,
DoneCallback callback) {
EXPECT_TRUE(observer);
CefRefPtr<ObserverRegistration> registration =
new ObserverRegistration(observer);
InitializeRegistration(registration, std::move(callback));
return registration.get();
}
CefRefPtr<CefRegistration> AddObserverAndStart(Observer* observer,
StartDoneCallback callback) {
return AddObserver(observer, base::BindOnce(Start, std::move(callback)));
}
// ObserverHelper
ObserverHelper::ObserverHelper() : weak_ptr_factory_(this) {
CEF_REQUIRE_UI_THREAD();
}
ObserverHelper::~ObserverHelper() {
EXPECT_TRUE(state_ == State::NONE);
}
void ObserverHelper::Initialize() {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(state_ == State::NONE);
state_ = State::INITIALIZING;
registration_ =
AddObserverAndStart(this, base::BindOnce(&ObserverHelper::OnStartDone,
weak_ptr_factory_.GetWeakPtr()));
}
void ObserverHelper::Shutdown() {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(state_ == State::INITIALIZED);
state_ = State::SHUTTINGDOWN;
registration_ = nullptr;
}
void ObserverHelper::OnStartDone(const std::string& server_origin) {
EXPECT_TRUE(state_ == State::INITIALIZING);
state_ = State::INITIALIZED;
OnInitialized(server_origin);
}
void ObserverHelper::OnRegistered() {
EXPECT_TRUE(state_ == State::INITIALIZING);
}
void ObserverHelper::OnUnregistered() {
EXPECT_TRUE(state_ == State::SHUTTINGDOWN);
state_ = State::NONE;
OnShutdown();
void Stop(base::OnceClosure callback) {
// Stop both HTTPS and HTTP servers in a chain.
Manager::Stop(base::BindOnce(
[](base::OnceClosure callback) {
Manager::Stop(std::move(callback),
/*https_server=*/false);
},
std::move(callback)),
/*https_server=*/true);
}
} // namespace test_server

View File

@ -23,92 +23,16 @@ extern const char kServerOrigin[];
// Used with incomplete tests for data that should not be sent.
extern const char kIncompleteDoNotSendData[];
using DoneCallback = base::OnceClosure;
// Create a 404 response for passing to ResponseCallback.
CefRefPtr<CefResponse> Create404Response();
using StartDoneCallback =
base::OnceCallback<void(const std::string& server_origin)>;
using ResponseCallback =
base::RepeatingCallback<void(CefRefPtr<CefResponse> response,
const std::string& response_data)>;
// Starts the server if it is not currently running, and executes |callback| on
// the UI thread. This method should be called by each test case that relies on
// the server.
void Start(StartDoneCallback callback);
// Stops the server if it is currently running, and executes |callback| on the
// Stops all servers that are currently running and executes |callback| on the
// UI thread. This method will be called by the test framework on shutdown.
void Stop(DoneCallback callback);
// Observer for server callbacks. Methods will be called on the UI thread.
class Observer {
public:
// Called when this Observer is registered.
virtual void OnRegistered() = 0;
// Called when this Observer is unregistered.
virtual void OnUnregistered() = 0;
using ResponseCallback =
base::RepeatingCallback<void(CefRefPtr<CefResponse> response,
const std::string& response_data)>;
// Return true and execute |response_callback| either synchronously or
// asynchronously if the request was handled. Do not execute
// |response_callback| when returning false.
virtual bool OnHttpRequest(CefRefPtr<CefRequest> request,
const ResponseCallback& response_callback) = 0;
protected:
virtual ~Observer() {}
};
// Add an observer for server callbacks. Remains registered until the returned
// CefRegistration object is destroyed. Registered observers will be executed in
// the order of registration until one returns true to indicate that it handled
// the callback. |callback| will be executed on the UI thread after registration
// is complete.
CefRefPtr<CefRegistration> AddObserver(Observer* observer,
DoneCallback callback);
// Combination of AddObserver() followed by Start().
CefRefPtr<CefRegistration> AddObserverAndStart(Observer* observer,
StartDoneCallback callback);
// Helper for managing Observer registration and callbacks. Only used on the UI
// thread.
class ObserverHelper : public Observer {
public:
ObserverHelper();
~ObserverHelper() override;
// Initialize the registration. Results in a call to OnInitialized().
void Initialize();
// Shut down the registration. Results in a call to OnShutdown().
void Shutdown();
// Implement this method to start sending server requests after Initialize().
virtual void OnInitialized(const std::string& server_origin) = 0;
// Implement this method to continue the test after Shutdown().
virtual void OnShutdown() = 0;
private:
void OnStartDone(const std::string& server_origin);
void OnRegistered() override;
void OnUnregistered() override;
CefRefPtr<CefRegistration> registration_;
enum class State {
NONE,
INITIALIZING,
INITIALIZED,
SHUTTINGDOWN,
} state_ = State::NONE;
base::WeakPtrFactory<ObserverHelper> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ObserverHelper);
};
void Stop(base::OnceClosure callback);
} // namespace test_server

View File

@ -0,0 +1,282 @@
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/ceftests/test_server_manager.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/gtest/include/gtest/gtest.h"
namespace test_server {
namespace {
Manager* g_http_manager = nullptr;
Manager* g_https_manager = nullptr;
} // namespace
// May be created on any thread but will be destroyed on the UI thread.
class ObserverRegistration : public CefRegistration {
public:
ObserverRegistration(Observer* observer, bool https_server)
: observer_(observer), https_server_(https_server) {
EXPECT_TRUE(observer_);
}
~ObserverRegistration() override {
CEF_REQUIRE_UI_THREAD();
if (auto manager = Manager::GetInstance(https_server_)) {
manager->RemoveObserver(observer_);
observer_->OnUnregistered();
}
}
void Initialize() {
CEF_REQUIRE_UI_THREAD();
Manager::GetOrCreateInstance(https_server_)->AddObserver(observer_);
observer_->OnRegistered();
}
static void InitializeRegistration(
CefRefPtr<ObserverRegistration> registration,
Manager::DoneCallback callback) {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(InitializeRegistration, registration,
std::move(callback)));
return;
}
registration->Initialize();
if (!callback.is_null())
std::move(callback).Run();
}
private:
Observer* const observer_;
const bool https_server_;
IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(ObserverRegistration);
DISALLOW_COPY_AND_ASSIGN(ObserverRegistration);
};
Manager::Manager(bool https_server) : https_server_(https_server) {
CEF_REQUIRE_UI_THREAD();
if (https_server) {
DCHECK(!g_https_manager);
g_https_manager = this;
} else {
DCHECK(!g_http_manager);
g_http_manager = this;
}
}
Manager::~Manager() {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(observer_list_.empty());
EXPECT_TRUE(start_callback_list_.empty());
EXPECT_TRUE(stop_callback_.is_null());
if (https_server_) {
g_https_manager = nullptr;
} else {
g_http_manager = nullptr;
}
}
// static
Manager* Manager::GetInstance(bool https_server) {
return https_server ? g_https_manager : g_http_manager;
}
// static
Manager* Manager::GetOrCreateInstance(bool https_server) {
if (auto manager = GetInstance(https_server)) {
return manager;
}
new Manager(https_server);
auto manager = GetInstance(https_server);
DCHECK(manager);
return manager;
}
// static
void Manager::Start(StartDoneCallback callback, bool https_server) {
EXPECT_FALSE(callback.is_null());
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(Manager::Start, std::move(callback),
https_server));
return;
}
Manager::GetOrCreateInstance(https_server)->StartImpl(std::move(callback));
}
// static
void Manager::Stop(DoneCallback callback, bool https_server) {
EXPECT_FALSE(callback.is_null());
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(Manager::Stop, std::move(callback),
https_server));
return;
}
if (auto manager = Manager::GetInstance(https_server)) {
manager->StopImpl(std::move(callback));
} else {
std::move(callback).Run();
}
}
// static
CefRefPtr<CefRegistration> Manager::AddObserver(Observer* observer,
DoneCallback callback,
bool https_server) {
EXPECT_TRUE(observer);
CefRefPtr<ObserverRegistration> registration =
new ObserverRegistration(observer, https_server);
ObserverRegistration::InitializeRegistration(registration,
std::move(callback));
return registration.get();
}
// static
CefRefPtr<CefRegistration> Manager::AddObserverAndStart(
Observer* observer,
StartDoneCallback callback,
bool https_server) {
return AddObserver(
observer,
base::BindOnce(Manager::Start, std::move(callback), https_server),
https_server);
}
void Manager::StartImpl(StartDoneCallback callback) {
CEF_REQUIRE_UI_THREAD();
EXPECT_FALSE(stopping_);
if (!origin_.empty()) {
// The server is already running.
std::move(callback).Run(origin_);
return;
}
// If tests run in parallel, and the server is starting, then there may be
// multiple pending callbacks.
start_callback_list_.push_back(std::move(callback));
// Only create the runner a single time.
if (!runner_) {
runner_ = Runner::Create(this, https_server_);
runner_->StartServer();
}
}
void Manager::StopImpl(DoneCallback callback) {
CEF_REQUIRE_UI_THREAD();
if (!runner_) {
// The server is not currently running.
std::move(callback).Run();
return;
}
// Stop will be called one time on test framework shutdown.
EXPECT_FALSE(stopping_);
stopping_ = true;
// Only 1 stop callback supported.
EXPECT_TRUE(stop_callback_.is_null());
stop_callback_ = std::move(callback);
runner_->ShutdownServer();
}
void Manager::AddObserver(Observer* observer) {
CEF_REQUIRE_UI_THREAD();
EXPECT_FALSE(stopping_);
observer_list_.push_back(observer);
}
void Manager::RemoveObserver(Observer* observer) {
CEF_REQUIRE_UI_THREAD();
bool found = false;
ObserverList::iterator it = observer_list_.begin();
for (; it != observer_list_.end(); ++it) {
if (*it == observer) {
observer_list_.erase(it);
found = true;
break;
}
}
EXPECT_TRUE(found);
}
void Manager::OnServerCreated(const std::string& server_origin) {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(origin_.empty());
origin_ = server_origin;
for (auto& callback : start_callback_list_) {
std::move(callback).Run(origin_);
}
start_callback_list_.clear();
}
void Manager::OnServerDestroyed() {
CEF_REQUIRE_UI_THREAD();
origin_.clear();
runner_.reset();
}
// All server-related objects have been torn down.
void Manager::OnServerHandlerDeleted() {
CEF_REQUIRE_UI_THREAD();
EXPECT_FALSE(stop_callback_.is_null());
std::move(stop_callback_).Run();
delete this;
}
void Manager::OnHttpRequest(CefRefPtr<CefRequest> request,
const ResponseCallback& response_callback) {
CEF_REQUIRE_UI_THREAD();
// TODO(chrome-runtime): Debug why favicon requests don't always have the
// correct resource type.
const std::string& url = request->GetURL();
if (request->GetResourceType() == RT_FAVICON ||
url.find("/favicon.ico") != std::string::npos) {
// We don't currently handle favicon requests.
response_callback.Run(Create404Response(), std::string());
return;
}
EXPECT_FALSE(observer_list_.empty()) << url;
// Use a copy in case |observer_list_| is modified during iteration.
ObserverList list = observer_list_;
bool handled = false;
ObserverList::const_iterator it = list.begin();
for (; it != list.end(); ++it) {
if ((*it)->OnHttpRequest(request, response_callback)) {
handled = true;
break;
}
}
if (!handled) {
LOG(WARNING) << "Unhandled request for: " << url;
response_callback.Run(Create404Response(), std::string());
}
}
} // namespace test_server

View File

@ -0,0 +1,98 @@
// Copyright (c) 2020 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.
#ifndef CEF_TESTS_CEFTESTS_TEST_SERVER_MANAGER_H_
#define CEF_TESTS_CEFTESTS_TEST_SERVER_MANAGER_H_
#pragma once
#include <string>
#include <vector>
#include "include/base/cef_callback.h"
#include "include/cef_registration.h"
#include "include/cef_request.h"
#include "include/cef_response.h"
#include "tests/ceftests/test_server_observer.h"
#include "tests/ceftests/test_server_runner.h"
namespace test_server {
class ObserverRegistration;
// Static methods are safe to call on any thread. Non-static methods are only
// called on the UI thread. Deletes itself after the server is stopped. Use
// ObserverHelper instead of calling these methods directly.
class Manager : public Runner::Delegate {
public:
using StartDoneCallback =
base::OnceCallback<void(const std::string& server_origin)>;
using DoneCallback = base::OnceClosure;
// Starts the server if it is not currently running, and executes |callback|
// on the UI thread.
static void Start(StartDoneCallback callback, bool https_server);
// Stops the server if it is currently running, and executes |callback| on the
// UI thread. This method will be called by the test framework on shutdown.
static void Stop(DoneCallback callback, bool https_server);
// Add an observer for server callbacks. Remains registered until the returned
// CefRegistration object is destroyed. Registered observers will be executed
// in the order of registration until one returns true to indicate that it
// handled the callback. |callback| will be executed on the UI thread after
// registration is complete.
static CefRefPtr<CefRegistration> AddObserver(Observer* observer,
DoneCallback callback,
bool https_server);
// Combination of AddObserver() followed by Start().
static CefRefPtr<CefRegistration> AddObserverAndStart(
Observer* observer,
StartDoneCallback callback,
bool https_server);
private:
friend class ObserverRegistration;
explicit Manager(bool https_server);
~Manager();
static Manager* GetInstance(bool https_server);
static Manager* GetOrCreateInstance(bool https_server);
void StartImpl(StartDoneCallback callback);
void StopImpl(DoneCallback callback);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Runner::Delegate methods:
void OnServerCreated(const std::string& server_origin) override;
void OnServerDestroyed() override;
void OnServerHandlerDeleted() override;
void OnHttpRequest(CefRefPtr<CefRequest> request,
const ResponseCallback& response_callback) override;
private:
const bool https_server_;
std::unique_ptr<Runner> runner_;
std::string origin_;
using StartDoneCallbackList = std::vector<StartDoneCallback>;
StartDoneCallbackList start_callback_list_;
DoneCallback stop_callback_;
using ObserverList = std::vector<Observer*>;
ObserverList observer_list_;
bool stopping_ = false;
DISALLOW_COPY_AND_ASSIGN(Manager);
};
} // namespace test_server
#endif // CEF_TESTS_CEFTESTS_TEST_SERVER_MANAGER_H_

View File

@ -0,0 +1,57 @@
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/ceftests/test_server.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/ceftests/test_server_manager.h"
#include "tests/gtest/include/gtest/gtest.h"
#include <vector>
namespace test_server {
ObserverHelper::ObserverHelper() : weak_ptr_factory_(this) {
CEF_REQUIRE_UI_THREAD();
}
ObserverHelper::~ObserverHelper() {
EXPECT_TRUE(state_ == State::NONE);
}
void ObserverHelper::Initialize(bool https_server) {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(state_ == State::NONE);
state_ = State::INITIALIZING;
registration_ = Manager::AddObserverAndStart(
this,
base::BindOnce(&ObserverHelper::OnStartDone,
weak_ptr_factory_.GetWeakPtr()),
https_server);
}
void ObserverHelper::Shutdown() {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(state_ == State::INITIALIZED);
state_ = State::SHUTTINGDOWN;
registration_ = nullptr;
}
void ObserverHelper::OnStartDone(const std::string& server_origin) {
EXPECT_TRUE(state_ == State::INITIALIZING);
state_ = State::INITIALIZED;
OnInitialized(server_origin);
}
void ObserverHelper::OnRegistered() {
EXPECT_TRUE(state_ == State::INITIALIZING);
}
void ObserverHelper::OnUnregistered() {
EXPECT_TRUE(state_ == State::SHUTTINGDOWN);
state_ = State::NONE;
OnShutdown();
}
} // namespace test_server

View File

@ -0,0 +1,82 @@
// Copyright (c) 2020 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.
#ifndef CEF_TESTS_CEFTESTS_TEST_SERVER_OBSERVER_H_
#define CEF_TESTS_CEFTESTS_TEST_SERVER_OBSERVER_H_
#pragma once
#include <string>
#include "include/base/cef_callback.h"
#include "include/base/cef_weak_ptr.h"
#include "include/cef_registration.h"
#include "include/cef_request.h"
#include "tests/ceftests/test_server.h"
namespace test_server {
// Observer for server callbacks. Methods will be called on the UI thread.
class Observer {
public:
using ResponseCallback = test_server::ResponseCallback;
// Called when this Observer is registered.
virtual void OnRegistered() = 0;
// Called when this Observer is unregistered.
virtual void OnUnregistered() = 0;
// Return true and execute |response_callback| either synchronously or
// asynchronously if the request was handled. Do not execute
// |response_callback| when returning false.
virtual bool OnHttpRequest(CefRefPtr<CefRequest> request,
const ResponseCallback& response_callback) = 0;
protected:
virtual ~Observer() = default;
};
// Helper for managing Observer registration and callbacks. Only used on the UI
// thread.
class ObserverHelper : public Observer {
public:
ObserverHelper();
~ObserverHelper() override;
// Initialize the registration. If |https_server| is true an HTTPS server will
// be used, otherwise an HTTP server will be used. Results in a call to
// OnInitialized().
void Initialize(bool https_server = false);
// Shut down the registration. Results in a call to OnShutdown().
void Shutdown();
// Implement this method to start sending server requests after Initialize().
virtual void OnInitialized(const std::string& server_origin) = 0;
// Implement this method to continue the test after Shutdown().
virtual void OnShutdown() = 0;
private:
void OnStartDone(const std::string& server_origin);
void OnRegistered() override;
void OnUnregistered() override;
CefRefPtr<CefRegistration> registration_;
enum class State {
NONE,
INITIALIZING,
INITIALIZED,
SHUTTINGDOWN,
} state_ = State::NONE;
base::WeakPtrFactory<ObserverHelper> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ObserverHelper);
};
} // namespace test_server
#endif // CEF_TESTS_CEFTESTS_TEST_SERVER_OBSERVER_H_

View File

@ -10,7 +10,7 @@
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/ceftests/test_request.h"
#include "tests/ceftests/test_server.h"
#include "tests/ceftests/test_server_observer.h"
#include "tests/ceftests/track_callback.h"
#include "tests/gtest/include/gtest/gtest.h"
@ -18,21 +18,16 @@ namespace {
struct TestState {
TrackCallback got_initialized_;
TrackCallback got_connected_;
TrackCallback got_request_;
TrackCallback got_response_;
TrackCallback got_disconnected_;
TrackCallback got_shutdown_;
bool ExpectAll() {
EXPECT_TRUE(got_initialized_);
EXPECT_TRUE(got_connected_);
EXPECT_TRUE(got_request_);
EXPECT_TRUE(got_response_);
EXPECT_TRUE(got_disconnected_);
EXPECT_TRUE(got_shutdown_);
return got_initialized_ && got_connected_ && got_request_ &&
got_response_ && got_disconnected_ && got_shutdown_;
return got_initialized_ && got_request_ && got_response_ && got_shutdown_;
}
};
@ -58,10 +53,8 @@ class TestServerObserver : public test_server::ObserverHelper {
void OnInitialized(const std::string& server_origin) override {
CEF_REQUIRE_UI_THREAD();
EXPECT_FALSE(state_->got_initialized_);
EXPECT_FALSE(state_->got_connected_);
EXPECT_FALSE(state_->got_request_);
EXPECT_FALSE(state_->got_response_);
EXPECT_FALSE(state_->got_disconnected_);
EXPECT_FALSE(state_->got_shutdown_);
state_->got_initialized_.yes();
@ -77,49 +70,25 @@ class TestServerObserver : public test_server::ObserverHelper {
weak_ptr_factory_.GetWeakPtr()));
}
bool OnClientConnected(CefRefPtr<CefServer> server,
int connection_id) override {
CEF_REQUIRE_UI_THREAD();
if (state_->got_connected_) {
// We already got the callback once. Let the next observer get the
// callback.
return false;
}
EXPECT_TRUE(state_->got_initialized_);
EXPECT_FALSE(state_->got_request_);
EXPECT_FALSE(state_->got_response_);
EXPECT_FALSE(state_->got_disconnected_);
EXPECT_FALSE(state_->got_shutdown_);
state_->got_connected_.yes();
// We don't know if this connection is the one that we're going to
// handle, so continue propagating the callback.
return false;
}
bool OnHttpRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request) override {
bool OnHttpRequest(CefRefPtr<CefRequest> request,
const ResponseCallback& response_callback) override {
CEF_REQUIRE_UI_THREAD();
const std::string& url = request->GetURL();
if (url != url_)
return false;
EXPECT_TRUE(state_->got_initialized_);
EXPECT_TRUE(state_->got_connected_);
EXPECT_FALSE(state_->got_request_);
EXPECT_FALSE(state_->got_response_);
EXPECT_FALSE(state_->got_disconnected_);
EXPECT_FALSE(state_->got_shutdown_);
state_->got_request_.yes();
connection_id_ = connection_id;
server->SendHttp200Response(connection_id, "text/plain", kResponseData,
sizeof(kResponseData) - 1);
auto response = CefResponse::Create();
response->SetStatus(200);
response->SetMimeType("text/plain");
response_callback.Run(response, kResponseData);
// Stop propagating the callback.
return true;
@ -129,7 +98,6 @@ class TestServerObserver : public test_server::ObserverHelper {
CEF_REQUIRE_UI_THREAD();
// Don't test for disconnected, which may race response.
EXPECT_TRUE(state_->got_initialized_);
EXPECT_TRUE(state_->got_connected_);
EXPECT_TRUE(state_->got_request_);
EXPECT_FALSE(state_->got_response_);
EXPECT_FALSE(state_->got_shutdown_);
@ -147,35 +115,11 @@ class TestServerObserver : public test_server::ObserverHelper {
weak_ptr_factory_.GetWeakPtr()));
}
bool OnClientDisconnected(CefRefPtr<CefServer> server,
int connection_id) override {
CEF_REQUIRE_UI_THREAD();
if (connection_id != connection_id_) {
// Not the connection that we handled. Let the next observer get the
// callback.
return false;
}
// Don't test for response, which may race disconnected.
EXPECT_TRUE(state_->got_initialized_);
EXPECT_TRUE(state_->got_connected_);
EXPECT_TRUE(state_->got_request_);
EXPECT_FALSE(state_->got_disconnected_);
EXPECT_FALSE(state_->got_shutdown_);
state_->got_disconnected_.yes();
// Stop propagating the callback.
return true;
}
void OnShutdown() override {
CEF_REQUIRE_UI_THREAD();
EXPECT_TRUE(state_->got_initialized_);
EXPECT_TRUE(state_->got_connected_);
EXPECT_TRUE(state_->got_request_);
EXPECT_TRUE(state_->got_response_);
EXPECT_TRUE(state_->got_disconnected_);
EXPECT_FALSE(state_->got_shutdown_);
state_->got_shutdown_.yes();
@ -190,7 +134,6 @@ class TestServerObserver : public test_server::ObserverHelper {
base::OnceClosure done_callback_;
std::string url_;
int connection_id_ = -1;
base::WeakPtrFactory<TestServerObserver> weak_ptr_factory_;
@ -220,7 +163,7 @@ void SignalIfDone(CefRefPtr<CefWaitableEvent> event,
} // namespace
TEST(TestServerTest, ObserverHelperSingle) {
TEST(TestServerObserverTest, HelperSingle) {
CefRefPtr<CefWaitableEvent> event =
CefWaitableEvent::CreateWaitableEvent(true, false);
@ -232,7 +175,7 @@ TEST(TestServerTest, ObserverHelperSingle) {
EXPECT_TRUE(state.ExpectAll());
}
TEST(TestServerTest, ObserverHelperMultiple) {
TEST(TestServerObserverTest, HelperMultiple) {
CefRefPtr<CefWaitableEvent> event =
CefWaitableEvent::CreateWaitableEvent(true, false);

View File

@ -0,0 +1,33 @@
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/ceftests/test_server_runner.h"
#include "tests/shared/common/client_switches.h"
#include "include/base/cef_logging.h"
#include "include/cef_command_line.h"
namespace test_server {
Runner::Runner(Delegate* delegate) : delegate_(delegate) {
DCHECK(delegate_);
}
// static
std::unique_ptr<Runner> Runner::Create(Runner::Delegate* delegate,
bool https_server) {
static bool use_test_http_server = [] {
auto command_line = CefCommandLine::GetGlobalCommandLine();
return command_line->HasSwitch(client::switches::kUseTestHttpServer);
}();
if (https_server || use_test_http_server) {
return CreateTest(delegate, https_server);
}
return CreateNormal(delegate);
}
} // namespace test_server

View File

@ -0,0 +1,68 @@
// Copyright (c) 2020 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.
#ifndef CEF_TESTS_CEFTESTS_TEST_SERVER_RUNNER_H_
#define CEF_TESTS_CEFTESTS_TEST_SERVER_RUNNER_H_
#pragma once
#include <memory>
#include <string>
#include "include/cef_request.h"
#include "include/internal/cef_types.h"
#include "tests/ceftests/test_server.h"
namespace test_server {
// Runs the server. All methods will be called on the UI thread.
class Runner {
public:
// Interface implemented by the Manager that creates/owns the Runner.
class Delegate {
public:
// Server created notification.
virtual void OnServerCreated(const std::string& server_origin) = 0;
// Server destroyed notification. May delete the Runner.
virtual void OnServerDestroyed() = 0;
// Server handler deleted notification. May delete the Manager.
virtual void OnServerHandlerDeleted() = 0;
// Server request notification.
virtual void OnHttpRequest(CefRefPtr<CefRequest> request,
const ResponseCallback& response_callback) = 0;
protected:
virtual ~Delegate() = default;
};
// |delegate| will outlive this object.
explicit Runner(Delegate* delegate);
virtual ~Runner() = default;
// Called by the Manager to create the Runner.
static std::unique_ptr<Runner> Create(Delegate* delegate, bool https_server);
// Called by the Manager to create/destroy the server handler.
virtual void StartServer() = 0;
virtual void ShutdownServer() = 0;
private:
// Create a Runner based on CefServer.
static std::unique_ptr<Runner> CreateNormal(Delegate* delegate);
// Create a Runner based on CefTestServer.
static std::unique_ptr<Runner> CreateTest(Delegate* delegate,
bool https_server);
protected:
Delegate* const delegate_;
DISALLOW_COPY_AND_ASSIGN(Runner);
};
} // namespace test_server
#endif // CEF_TESTS_CEFTESTS_TEST_SERVER_RUNNER_H_

View File

@ -0,0 +1,196 @@
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/ceftests/test_server_runner.h"
#include "include/cef_server.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/gtest/include/gtest/gtest.h"
namespace test_server {
namespace {
const int kServerBacklog = 10;
// Created on the UI thread and called on the dedicated server thread.
class ServerHandler : public CefServerHandler {
public:
explicit ServerHandler(Runner::Delegate* delegate) : delegate_(delegate) {}
~ServerHandler() override {
CEF_REQUIRE_UI_THREAD();
EXPECT_FALSE(server_);
delegate_->OnServerHandlerDeleted();
}
void Shutdown() { server_->Shutdown(); }
// CefServerHandler methods:
void OnServerCreated(CefRefPtr<CefServer> server) override {
EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread());
server_ = server;
NotifyServerCreated(kServerOrigin);
}
void OnServerDestroyed(CefRefPtr<CefServer> server) override {
EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread());
server_ = nullptr;
NotifyServerDestroyed();
}
void OnClientConnected(CefRefPtr<CefServer> server,
int connection_id) override {
EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread());
EXPECT_TRUE(server->HasConnection());
EXPECT_TRUE(server->IsValidConnection(connection_id));
}
void OnClientDisconnected(CefRefPtr<CefServer> server,
int connection_id) override {
EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread());
EXPECT_FALSE(server->IsValidConnection(connection_id));
}
void OnHttpRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request) override {
EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread());
NotifyHttpRequest(server, connection_id, client_address, request);
}
void OnWebSocketRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request,
CefRefPtr<CefCallback> callback) override {}
void OnWebSocketConnected(CefRefPtr<CefServer> server,
int connection_id) override {}
void OnWebSocketMessage(CefRefPtr<CefServer> server,
int connection_id,
const void* data,
size_t data_size) override {}
private:
void NotifyServerCreated(const std::string& server_origin) {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(&ServerHandler::NotifyServerCreated,
this, server_origin));
return;
}
delegate_->OnServerCreated(server_origin);
}
void NotifyServerDestroyed() {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI,
base::BindOnce(&ServerHandler::NotifyServerDestroyed, this));
return;
}
delegate_->OnServerDestroyed();
}
void NotifyHttpRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request) {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, base::BindOnce(&ServerHandler::NotifyHttpRequest,
this, server, connection_id,
client_address, request));
return;
}
auto response_callback = base::BindRepeating(&ServerHandler::SendResponse,
server, connection_id);
delegate_->OnHttpRequest(request, response_callback);
}
static void SendResponse(CefRefPtr<CefServer> server,
int connection_id,
CefRefPtr<CefResponse> response,
const std::string& response_data) {
// Execute on the server thread because some methods require it.
CefRefPtr<CefTaskRunner> task_runner = server->GetTaskRunner();
if (!task_runner->BelongsToCurrentThread()) {
task_runner->PostTask(CefCreateClosureTask(
base::BindOnce(ServerHandler::SendResponse, server, connection_id,
response, response_data)));
return;
}
// No response should be sent yet.
EXPECT_TRUE(server->IsValidConnection(connection_id));
const int response_code = response->GetStatus();
if (response_code <= 0) {
// Intentionally not responding for incomplete request tests.
return;
}
const CefString& content_type = response->GetMimeType();
int64 content_length = static_cast<int64>(response_data.size());
CefResponse::HeaderMap extra_headers;
response->GetHeaderMap(extra_headers);
server->SendHttpResponse(connection_id, response_code, content_type,
content_length, extra_headers);
if (response_data == kIncompleteDoNotSendData) {
// Intentionally not sending data for incomplete request tests.
return;
}
if (content_length != 0) {
server->SendRawData(connection_id, response_data.data(),
response_data.size());
server->CloseConnection(connection_id);
}
// The connection should be closed.
EXPECT_FALSE(server->IsValidConnection(connection_id));
}
Runner::Delegate* const delegate_;
CefRefPtr<CefServer> server_;
IMPLEMENT_REFCOUNTING(ServerHandler);
DISALLOW_COPY_AND_ASSIGN(ServerHandler);
};
class ServerRunner : public Runner {
public:
explicit ServerRunner(Delegate* delegate) : Runner(delegate) {}
void StartServer() override {
CEF_REQUIRE_UI_THREAD();
DCHECK(!handler_);
handler_ = new ServerHandler(delegate_);
CefServer::CreateServer(kServerAddress, kServerPort, kServerBacklog,
handler_);
}
void ShutdownServer() override {
CEF_REQUIRE_UI_THREAD();
DCHECK(handler_);
handler_->Shutdown();
handler_ = nullptr;
}
private:
CefRefPtr<ServerHandler> handler_;
};
} // namespace
std::unique_ptr<Runner> Runner::CreateNormal(Delegate* delegate) {
return std::make_unique<ServerRunner>(delegate);
}
} // namespace test_server

View File

@ -0,0 +1,17 @@
// Copyright (c) 2022 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/ceftests/test_server_runner.h"
#include "include/base/cef_logging.h"
namespace test_server {
std::unique_ptr<Runner> Runner::CreateTest(Delegate* delegate,
bool https_server) {
NOTREACHED();
return nullptr;
}
} // namespace test_server

View File

@ -19,7 +19,7 @@
#include "include/wrapper/cef_scoped_temp_dir.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_request.h"
#include "tests/ceftests/test_server.h"
#include "tests/ceftests/test_server_observer.h"
#include "tests/ceftests/test_suite.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"

View File

@ -51,6 +51,7 @@ const char kInitialShowState[] = "initial-show-state";
const char kHideChromeStatusBubble[] = "hide-chrome-status-bubble";
const char kUseDefaultPopup[] = "use-default-popup";
const char kUseClientDialogs[] = "use-client-dialogs";
const char kUseTestHttpServer[] = "use-test-http-server";
} // namespace switches
} // namespace client

View File

@ -45,6 +45,7 @@ extern const char kInitialShowState[];
extern const char kHideChromeStatusBubble[];
extern const char kUseDefaultPopup[];
extern const char kUseClientDialogs[];
extern const char kUseTestHttpServer[];
} // namespace switches
} // namespace client