cef/tests/ceftests/thread_unittest.cc
Marshall Greenblatt 4fbd247231 Add chrome runtime support for more callbacks and ceftests (see issue #2969)
This change adds support for:
- Protocol and request handling.
- Loading and navigation events.
- Display and focus events.
- Mouse/keyboard events.
- Popup browsers.
- Callbacks in the renderer process.
- Misc. functionality required for ceftests.

This change also adds a new CefBrowserProcessHandler::GetCookieableSchemes
callback for configuring global state that will be applied to all
CefCookieManagers by default. This global callback is currently required by the
chrome runtime because the primary ProfileImpl is created via
ChromeBrowserMainParts::PreMainMessageLoopRun (CreatePrimaryProfile) before
OnContextCreated can be called.

ProfileImpl will use the "C:\Users\[user]\AppData\Local\CEF\User Data\Default"
directory by default (on Windows). Cookies may persist in this directory when
running ceftests and may need to be manually deleted if those tests fail.

Remaining work includes:
- Support for client-created request contexts.
- Embedding the browser in a Views hierarchy (cefclient support).
- TryCloseBrowser and DoClose support.
- Most of the CefSettings configuration.
- DevTools protocol and window control (ShowDevTools, ExecuteDevToolsMethod).
- CEF-specific WebUI pages (about, license, webui-hosts).
- Context menu customization (CefContextMenuHandler).
- Auto resize (SetAutoResizeEnabled).
- Zoom settings (SetZoomLevel).
- File dialog runner (RunFileDialog).
- File and JS dialog handlers (CefDialogHandler, CefJSDialogHandler).
- Extension loading (LoadExtension, etc).
- Plugin loading (OnBeforePluginLoad).
- Widevine loading (CefRegisterWidevineCdm).
- PDF and print preview does not display.
- Crash reporting is untested.
- Mac: Web content loads but does not display.

The following ceftests are now passing when run with the
"--enable-chrome-runtime" command-line flag:

CorsTest.*
DisplayTest.*:-DisplayTest.AutoResize
DOMTest.*
DraggableRegionsTest.*
ImageTest.*
MessageRouterTest.*
NavigationTest.*
ParserTest.*
RequestContextTest.*Global*
RequestTest.*
ResourceManagerTest.*
ResourceRequestHandlerTest.*
ResponseTest.*
SchemeHandlerTest.*
ServerTest.*
StreamResourceHandlerTest.*
StreamTest.*
StringTest.*
TaskTest.*
TestServerTest.*
ThreadTest.*
URLRequestTest.*Global*
V8Test.*:-V8Test.OnUncaughtExceptionDevTools
ValuesTest.*
WaitableEventTest.*
XmlReaderTest.*
ZipReaderTest.*
2020-09-29 18:31:43 -04:00

440 lines
13 KiB
C++

// Copyright 2016 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 "include/base/cef_bind.h"
#include "include/cef_task.h"
#include "include/cef_thread.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/client_app_browser.h"
#include "tests/shared/renderer/client_app_renderer.h"
using client::ClientAppBrowser;
using client::ClientAppRenderer;
namespace {
// Base class for creating and testing threads.
class ThreadTest : public base::RefCountedThreadSafe<ThreadTest> {
public:
ThreadTest() {}
virtual ~ThreadTest() {}
// Create the test thread. Should only be called one time.
void CreateTestThread() {
EXPECT_TRUE(!thread_.get());
owner_task_runner_ = CefTaskRunner::GetForCurrentThread();
EXPECT_TRUE(owner_task_runner_.get());
EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
thread_ = CefThread::CreateThread("test_thread");
EXPECT_TRUE(thread_.get());
EXPECT_TRUE(thread_->IsRunning());
thread_id_ = thread_->GetPlatformThreadId();
EXPECT_NE(thread_id_, kInvalidPlatformThreadId);
thread_task_runner_ = thread_->GetTaskRunner();
EXPECT_TRUE(thread_task_runner_.get());
AssertOwnerThread();
}
// Destroy the test thread. Should only be called one time.
void DestroyTestThread() {
EXPECT_TRUE(thread_.get());
AssertOwnerThread();
EXPECT_TRUE(thread_->IsRunning());
thread_->Stop();
EXPECT_FALSE(thread_->IsRunning());
AssertOwnerThread();
thread_ = nullptr;
}
// Execute |test_task| on the test thread. After execution |callback| will be
// posted to |callback_task_runner|.
void PostOnTestThreadAndCallback(
const base::Closure& test_task,
CefRefPtr<CefTaskRunner> callback_task_runner,
const base::Closure& callback) {
EXPECT_TRUE(thread_.get());
thread_task_runner_->PostTask(CefCreateClosureTask(
base::Bind(&ThreadTest::ExecuteOnTestThread, this, test_task,
callback_task_runner, callback)));
}
CefRefPtr<CefTaskRunner> owner_task_runner() const {
return owner_task_runner_;
}
CefRefPtr<CefTaskRunner> thread_task_runner() const {
return thread_task_runner_;
}
// Assert that we're running on the owner thread.
void AssertOwnerThread() {
EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
EXPECT_FALSE(thread_task_runner_->BelongsToCurrentThread());
EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner()));
EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId());
}
// Assert that we're running on the test thread.
void AssertTestThread() {
EXPECT_FALSE(owner_task_runner_->BelongsToCurrentThread());
EXPECT_TRUE(thread_task_runner_->BelongsToCurrentThread());
EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner()));
EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId());
}
private:
// Helper for PostOnTestThreadAndCallback().
void ExecuteOnTestThread(const base::Closure& test_task,
CefRefPtr<CefTaskRunner> callback_task_runner,
const base::Closure& callback) {
AssertTestThread();
test_task.Run();
callback_task_runner->PostTask(CefCreateClosureTask(callback));
}
CefRefPtr<CefTaskRunner> owner_task_runner_;
CefRefPtr<CefThread> thread_;
cef_platform_thread_id_t thread_id_;
CefRefPtr<CefTaskRunner> thread_task_runner_;
DISALLOW_COPY_AND_ASSIGN(ThreadTest);
};
} // namespace
// Test thread creation and destruction without any task execution.
TEST(ThreadTest, Create) {
scoped_refptr<ThreadTest> thread_test = new ThreadTest();
thread_test->CreateTestThread();
thread_test->DestroyTestThread();
thread_test = nullptr;
}
namespace {
// Simple implementation of ThreadTest that creates a thread, executes tasks
// on the thread, then destroys the thread after all tasks have completed.
class SimpleThreadTest : public ThreadTest {
public:
SimpleThreadTest(size_t expected_task_count,
const base::Closure& task_callback,
const base::Closure& done_callback)
: expected_task_count_(expected_task_count),
task_callback_(task_callback),
done_callback_(done_callback),
got_task_count_(0U),
got_done_count_(0U) {}
void RunTest() {
// Create the test thread.
CreateTestThread();
for (size_t i = 0U; i < expected_task_count_; ++i) {
// Execute Task() on the test thread and then call Done() on this thread.
PostOnTestThreadAndCallback(base::Bind(&SimpleThreadTest::Task, this),
owner_task_runner(),
base::Bind(&SimpleThreadTest::Done, this));
}
}
void DestroyTest() {
EXPECT_EQ(expected_task_count_, got_task_count_);
EXPECT_EQ(expected_task_count_, got_done_count_);
// Destroy the test thread.
DestroyTestThread();
}
private:
void Task() {
AssertTestThread();
got_task_count_++;
if (!task_callback_.is_null())
task_callback_.Run();
}
void Done() {
AssertOwnerThread();
if (++got_done_count_ == expected_task_count_ && !done_callback_.is_null())
done_callback_.Run();
}
const size_t expected_task_count_;
base::Closure task_callback_;
base::Closure done_callback_;
size_t got_task_count_;
size_t got_done_count_;
DISALLOW_COPY_AND_ASSIGN(SimpleThreadTest);
};
// Test creation/execution of threads in the browser process.
const char kBrowserThreadTestHtml[] = "http://test.com/browserthread.html";
// Browser side.
class BrowserThreadTestHandler : public TestHandler {
public:
explicit BrowserThreadTestHandler(CefThreadId owner_thread_id)
: owner_thread_id_(owner_thread_id) {}
void RunTest() override {
AddResource(kBrowserThreadTestHtml, "<html><body>Test</body></html>",
"text/html");
CreateBrowser(kBrowserThreadTestHtml);
// Time out the test after a reasonable period of time.
SetTestTimeout();
}
void RunThreadTestOnOwnerThread() {
if (!CefCurrentlyOn(owner_thread_id_)) {
// Run the test on the desired owner thread.
CefPostTask(
owner_thread_id_,
base::Bind(&BrowserThreadTestHandler::RunThreadTestOnOwnerThread,
this));
return;
}
EXPECT_FALSE(thread_test_.get());
thread_test_ = new SimpleThreadTest(
3, base::Closure(),
base::Bind(&BrowserThreadTestHandler::DoneOnOwnerThread, this));
thread_test_->RunTest();
}
void DoneOnOwnerThread() {
// Let the call stack unwind before destroying |thread_test_|.
CefPostTask(
owner_thread_id_,
base::Bind(&BrowserThreadTestHandler::DestroyTestOnOwnerThread, this));
}
void DestroyTestOnOwnerThread() {
EXPECT_TRUE(CefCurrentlyOn(owner_thread_id_));
EXPECT_TRUE(thread_test_.get());
if (thread_test_) {
thread_test_->DestroyTest();
thread_test_ = nullptr;
}
got_test_done_.yes();
// Call DestroyTest() on the UI thread.
CefPostTask(TID_UI,
base::Bind(&BrowserThreadTestHandler::DestroyTest, this));
}
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) override {
if (!isLoading)
RunThreadTestOnOwnerThread();
}
private:
void DestroyTest() override {
EXPECT_FALSE(thread_test_.get());
EXPECT_TRUE(got_test_done_);
TestHandler::DestroyTest();
}
const CefThreadId owner_thread_id_;
scoped_refptr<SimpleThreadTest> thread_test_;
TrackCallback got_test_done_;
IMPLEMENT_REFCOUNTING(BrowserThreadTestHandler);
DISALLOW_COPY_AND_ASSIGN(BrowserThreadTestHandler);
};
} // namespace
// Test creation of new threads from the browser UI thread.
TEST(ThreadTest, CreateFromBrowserUIThread) {
CefRefPtr<BrowserThreadTestHandler> handler =
new BrowserThreadTestHandler(TID_UI);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test creation of new threads from the browser IO thread.
TEST(ThreadTest, CreateFromBrowserIOThread) {
CefRefPtr<BrowserThreadTestHandler> handler =
new BrowserThreadTestHandler(TID_IO);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test creation of new threads from the browser FILE thread.
TEST(ThreadTest, CreateFromBrowserFILEThread) {
// Use a FILE thread that will run tasks relatively quickly.
CefRefPtr<BrowserThreadTestHandler> handler =
new BrowserThreadTestHandler(TID_FILE_USER_VISIBLE);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
namespace {
// Test creation/execution of threads in the render process.
const char kRenderThreadTestHtml[] = "http://test.com/renderthread.html";
const char kRenderThreadTestMsg[] = "ThreadTest.RenderThreadTest";
// Browser side.
class RenderThreadTestHandler : public TestHandler {
public:
RenderThreadTestHandler() {}
void RunTest() override {
AddResource(kRenderThreadTestHtml, "<html><body>Test</body></html>",
"text/html");
CreateBrowser(kRenderThreadTestHtml);
// Time out the test after a reasonable period of time.
SetTestTimeout();
}
void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) override {
if (!isLoading) {
// Return the test in the render process.
CefRefPtr<CefProcessMessage> msg =
CefProcessMessage::Create(kRenderThreadTestMsg);
browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, msg);
}
}
bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override {
EXPECT_TRUE(browser.get());
EXPECT_TRUE(frame.get());
EXPECT_EQ(PID_RENDERER, source_process);
EXPECT_TRUE(message.get());
EXPECT_TRUE(message->IsReadOnly());
const std::string& message_name = message->GetName();
EXPECT_STREQ(kRenderThreadTestMsg, message_name.c_str());
got_message_.yes();
if (message->GetArgumentList()->GetBool(0))
got_success_.yes();
// Test is complete.
DestroyTest();
return true;
}
protected:
void DestroyTest() override {
EXPECT_TRUE(got_message_);
EXPECT_TRUE(got_success_);
TestHandler::DestroyTest();
}
TrackCallback got_message_;
TrackCallback got_success_;
IMPLEMENT_REFCOUNTING(RenderThreadTestHandler);
DISALLOW_COPY_AND_ASSIGN(RenderThreadTestHandler);
};
// Renderer side.
class RenderThreadRendererTest : public ClientAppRenderer::Delegate {
public:
RenderThreadRendererTest() {}
bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override {
if (message->GetName().ToString() == kRenderThreadTestMsg) {
browser_ = browser;
EXPECT_FALSE(thread_test_.get());
thread_test_ = new SimpleThreadTest(
3, base::Closure(),
base::Bind(&RenderThreadRendererTest::Done, this));
thread_test_->RunTest();
return true;
}
// Message not handled.
return false;
}
private:
void Done() {
// Let the call stack unwind before destroying |thread_test_|.
CefPostTask(TID_RENDERER,
base::Bind(&RenderThreadRendererTest::DestroyTest, this));
}
void DestroyTest() {
EXPECT_TRUE(thread_test_.get());
if (thread_test_) {
thread_test_->DestroyTest();
thread_test_ = nullptr;
}
// Check if the test has failed.
bool result = !TestFailed();
// Return the result to the browser process.
CefRefPtr<CefProcessMessage> return_msg =
CefProcessMessage::Create(kRenderThreadTestMsg);
EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
browser_ = nullptr;
}
CefRefPtr<CefBrowser> browser_;
scoped_refptr<SimpleThreadTest> thread_test_;
IMPLEMENT_REFCOUNTING(RenderThreadRendererTest);
DISALLOW_COPY_AND_ASSIGN(RenderThreadRendererTest);
};
} // namespace
TEST(ThreadTest, CreateFromRenderThread) {
CefRefPtr<RenderThreadTestHandler> handler = new RenderThreadTestHandler();
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Entry point for creating request handler renderer test objects.
// Called from client_app_delegates.cc.
void CreateThreadRendererTests(ClientAppRenderer::DelegateSet& delegates) {
delegates.insert(new RenderThreadRendererTest);
}