mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-02-28 09:57:50 +01:00
- Add Views header files in a new include/views directory. - Add initial top-level window (CefWindow), control (CefBrowserView, CefLabelButton, CefMenuButton, CefPanel, CefScrollView, CefTextfield) and layout (CefBoxLayout, CefFlowLayout) support. See libcef/browser/views/view_impl.h comments for implementation details. - Add Views example usage in cefclient and cefsimple and Views unit tests in cef_unittests. Pass the `--use-views` command-line flag to cefclient, cefsimple and cef_unittests to run using the Views framework instead of platform APIs. For cefclient and cefsimple this will create the browser window and all related functionality using the Views framework. For cef_unittests this will run all tests (except OSR tests) in a Views-based browser window. Views- specific unit tests (`--gtest_filter=Views*`) will be run even if the the `--use-views` flag is not specified. - Pass the `--hide-frame` command-line flag to cefclient to demo a frameless Views-based browser window. - Pass the `--hide-controls` command-line flag to cefclient to demo a browser window without top controls. This also works in non-Views mode. - Pass the `--enable-high-dpi-support` command-line flag to cef_unittests on Windows to test high-DPI support on a display that supports it. - Add CefImage for reading/writing image file formats. - Add CefBrowser::DownloadImage() for downloading image URLs as a CefImage representation. This is primarily for loading favicons. - Add CefMenuModel::CreateMenuModel() and CefMenuModelDelegate for creating custom menus. This is primarily for use with CefMenuButton. - Add CefBrowser::TryCloseBrowser() helper for closing a browser. Also improve related documentation in cef_life_span_handler.h. - Rename cef_page_range_t to cef_range_t. It is now also used by CefTextfield. - Remove CefLifeSpanHandler::RunModal() which is never called. - Add draggable regions example to cefclient.
484 lines
13 KiB
C++
484 lines
13 KiB
C++
// Copyright (c) 2013 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/unittests/test_handler.h"
|
|
|
|
#include "include/base/cef_bind.h"
|
|
#include "include/cef_command_line.h"
|
|
#include "include/cef_stream.h"
|
|
#include "include/wrapper/cef_closure_task.h"
|
|
#include "include/wrapper/cef_stream_resource_handler.h"
|
|
#include "tests/cefclient/common/client_switches.h"
|
|
|
|
#if defined(USE_AURA)
|
|
#include "include/views/cef_browser_view.h"
|
|
#include "include/views/cef_window.h"
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
#if defined(USE_AURA)
|
|
|
|
// Delegate implementation for the CefWindow that will host the Views-based
|
|
// browser.
|
|
class TestWindowDelegate : public CefWindowDelegate {
|
|
public:
|
|
// Create a new top-level Window hosting |browser_view|.
|
|
static void CreateBrowserWindow(CefRefPtr<CefBrowserView> browser_view,
|
|
const std::string& title) {
|
|
CefWindow::CreateTopLevelWindow(
|
|
new TestWindowDelegate(browser_view, "CefUnitTestViews " + title));
|
|
}
|
|
|
|
// CefWindowDelegate methods:
|
|
|
|
void OnWindowCreated(CefRefPtr<CefWindow> window) override {
|
|
// Add the browser view and show the window.
|
|
window->CenterWindow(CefSize(800, 600));
|
|
window->SetTitle(title_);
|
|
window->AddChildView(browser_view_);
|
|
window->Show();
|
|
}
|
|
|
|
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
|
|
browser_view_ = nullptr;
|
|
}
|
|
|
|
bool CanClose(CefRefPtr<CefWindow> window) override {
|
|
// Allow the window to close if the browser says it's OK.
|
|
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
|
|
if (browser)
|
|
return browser->GetHost()->TryCloseBrowser();
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
TestWindowDelegate(CefRefPtr<CefBrowserView> browser_view,
|
|
const CefString& title)
|
|
: browser_view_(browser_view),
|
|
title_(title) {
|
|
}
|
|
|
|
CefRefPtr<CefBrowserView> browser_view_;
|
|
CefString title_;
|
|
|
|
IMPLEMENT_REFCOUNTING(TestWindowDelegate);
|
|
DISALLOW_COPY_AND_ASSIGN(TestWindowDelegate);
|
|
};
|
|
|
|
// Delegate implementation for the CefBrowserView.
|
|
class TestBrowserViewDelegate : public CefBrowserViewDelegate {
|
|
public:
|
|
TestBrowserViewDelegate() {}
|
|
|
|
// CefBrowserViewDelegate methods:
|
|
|
|
bool OnPopupBrowserViewCreated(
|
|
CefRefPtr<CefBrowserView> browser_view,
|
|
CefRefPtr<CefBrowserView> popup_browser_view,
|
|
bool is_devtools) override {
|
|
// Create our own Window for popups. It will show itself after creation.
|
|
TestWindowDelegate::CreateBrowserWindow(
|
|
popup_browser_view,
|
|
is_devtools ? "DevTools" : "Popup");
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
IMPLEMENT_REFCOUNTING(TestBrowserViewDelegate);
|
|
DISALLOW_COPY_AND_ASSIGN(TestBrowserViewDelegate);
|
|
};
|
|
|
|
#endif // defined(USE_AURA)
|
|
|
|
} // namespace
|
|
|
|
|
|
// TestHandler::CompletionState
|
|
|
|
TestHandler::CompletionState::CompletionState(int total)
|
|
: total_(total),
|
|
count_(0),
|
|
event_(true, false) {
|
|
}
|
|
|
|
void TestHandler::CompletionState::TestComplete() {
|
|
if (++count_ == total_) {
|
|
count_ = 0;
|
|
|
|
// Signal that the test is now complete. Do not access any object members
|
|
// after this call because |this| might be deleted.
|
|
event_.Signal();
|
|
}
|
|
}
|
|
|
|
void TestHandler::CompletionState::WaitForTests() {
|
|
// Wait for the test to complete
|
|
event_.Wait();
|
|
|
|
// Reset the event so the same test can be executed again.
|
|
event_.Reset();
|
|
}
|
|
|
|
|
|
// TestHandler::Collection
|
|
|
|
TestHandler::Collection::Collection(CompletionState* completion_state)
|
|
: completion_state_(completion_state) {
|
|
EXPECT_TRUE(completion_state_);
|
|
}
|
|
|
|
void TestHandler::Collection::AddTestHandler(TestHandler* test_handler) {
|
|
EXPECT_EQ(test_handler->completion_state_, completion_state_);
|
|
handler_list_.push_back(test_handler);
|
|
}
|
|
|
|
void TestHandler::Collection::ExecuteTests() {
|
|
EXPECT_GT(handler_list_.size(), 0UL);
|
|
|
|
TestHandlerList::const_iterator it;
|
|
|
|
it = handler_list_.begin();
|
|
for (; it != handler_list_.end(); ++it)
|
|
(*it)->SetupTest();
|
|
|
|
completion_state_->WaitForTests();
|
|
|
|
it = handler_list_.begin();
|
|
for (; it != handler_list_.end(); ++it)
|
|
(*it)->RunTest();
|
|
|
|
completion_state_->WaitForTests();
|
|
}
|
|
|
|
|
|
// TestHandler::UIThreadHelper
|
|
|
|
TestHandler::UIThreadHelper::UIThreadHelper()
|
|
: weak_ptr_factory_(this) {
|
|
}
|
|
|
|
void TestHandler::UIThreadHelper::PostTask(const base::Closure& task) {
|
|
EXPECT_UI_THREAD();
|
|
CefPostTask(
|
|
TID_UI,
|
|
base::Bind(&UIThreadHelper::TaskHelper,
|
|
weak_ptr_factory_.GetWeakPtr(), task));
|
|
}
|
|
|
|
void TestHandler::UIThreadHelper::PostDelayedTask(
|
|
const base::Closure& task, int delay_ms) {
|
|
EXPECT_UI_THREAD();
|
|
CefPostDelayedTask(
|
|
TID_UI,
|
|
base::Bind(&UIThreadHelper::TaskHelper,
|
|
weak_ptr_factory_.GetWeakPtr(), task),
|
|
delay_ms);
|
|
}
|
|
|
|
void TestHandler::UIThreadHelper::TaskHelper(const base::Closure& task) {
|
|
EXPECT_UI_THREAD();
|
|
task.Run();
|
|
}
|
|
|
|
|
|
// TestHandler
|
|
|
|
int TestHandler::browser_count_ = 0;
|
|
|
|
TestHandler::TestHandler(CompletionState* completion_state)
|
|
: first_browser_id_(0),
|
|
signal_completion_when_all_browsers_close_(true),
|
|
destroy_event_(NULL),
|
|
destroy_test_expected_(true),
|
|
destroy_test_called_(false) {
|
|
if (completion_state) {
|
|
completion_state_ = completion_state;
|
|
completion_state_owned_ = false;
|
|
} else {
|
|
completion_state_ = new CompletionState(1);
|
|
completion_state_owned_ = true;
|
|
}
|
|
}
|
|
|
|
TestHandler::~TestHandler() {
|
|
DCHECK(!ui_thread_helper_.get());
|
|
if (destroy_test_expected_)
|
|
EXPECT_TRUE(destroy_test_called_);
|
|
else
|
|
EXPECT_FALSE(destroy_test_called_);
|
|
EXPECT_TRUE(browser_map_.empty());
|
|
|
|
if (completion_state_owned_)
|
|
delete completion_state_;
|
|
|
|
if (destroy_event_)
|
|
destroy_event_->Signal();
|
|
}
|
|
|
|
void TestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
|
|
EXPECT_UI_THREAD();
|
|
|
|
browser_count_++;
|
|
|
|
if (!browser->IsPopup()) {
|
|
// Keep non-popup browsers.
|
|
const int browser_id = browser->GetIdentifier();
|
|
EXPECT_EQ(browser_map_.find(browser_id), browser_map_.end());
|
|
if (browser_map_.empty()) {
|
|
first_browser_id_ = browser_id;
|
|
first_browser_ = browser;
|
|
}
|
|
browser_map_.insert(std::make_pair(browser_id, browser));
|
|
}
|
|
}
|
|
|
|
void TestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
|
|
EXPECT_UI_THREAD();
|
|
|
|
if (!browser->IsPopup()) {
|
|
// Free the browser pointer so that the browser can be destroyed.
|
|
const int browser_id = browser->GetIdentifier();
|
|
BrowserMap::iterator it = browser_map_.find(browser_id);
|
|
EXPECT_NE(it, browser_map_.end());
|
|
browser_map_.erase(it);
|
|
|
|
if (browser_id == first_browser_id_) {
|
|
first_browser_id_ = 0;
|
|
first_browser_ = NULL;
|
|
}
|
|
|
|
if (browser_map_.empty() &&
|
|
signal_completion_when_all_browsers_close_) {
|
|
// Signal that the test is now complete.
|
|
TestComplete();
|
|
}
|
|
}
|
|
|
|
browser_count_--;
|
|
}
|
|
|
|
CefRefPtr<CefResourceHandler> TestHandler::GetResourceHandler(
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request) {
|
|
EXPECT_IO_THREAD();
|
|
|
|
if (resource_map_.size() > 0) {
|
|
CefString url = request->GetURL();
|
|
|
|
// Ignore the query component, if any.
|
|
std::string urlStr = url;
|
|
size_t idx = urlStr.find('?');
|
|
if (idx > 0)
|
|
urlStr = urlStr.substr(0, idx);
|
|
|
|
ResourceMap::const_iterator it = resource_map_.find(urlStr);
|
|
if (it != resource_map_.end()) {
|
|
// Return the previously mapped resource
|
|
CefRefPtr<CefStreamReader> stream =
|
|
CefStreamReader::CreateForData(
|
|
static_cast<void*>(const_cast<char*>(it->second.first.c_str())),
|
|
it->second.first.length());
|
|
return new CefStreamResourceHandler(it->second.second, stream);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CefRefPtr<CefBrowser> TestHandler::GetBrowser() {
|
|
return first_browser_;
|
|
}
|
|
|
|
int TestHandler::GetBrowserId() {
|
|
return first_browser_id_;
|
|
}
|
|
|
|
void TestHandler::GetAllBrowsers(BrowserMap* map) {
|
|
EXPECT_UI_THREAD();
|
|
EXPECT_TRUE(map);
|
|
*map = browser_map_;
|
|
}
|
|
|
|
void TestHandler::ExecuteTest() {
|
|
EXPECT_EQ(completion_state_->total(), 1);
|
|
|
|
// Reset any state from the previous run.
|
|
if (destroy_test_called_)
|
|
destroy_test_called_ = false;
|
|
|
|
// Run the test.
|
|
RunTest();
|
|
|
|
// Wait for the test to complete.
|
|
completion_state_->WaitForTests();
|
|
}
|
|
|
|
void TestHandler::SetupComplete() {
|
|
// Signal that the test setup is complete.
|
|
completion_state_->TestComplete();
|
|
}
|
|
|
|
void TestHandler::DestroyTest() {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
CefPostTask(TID_UI, base::Bind(&TestHandler::DestroyTest, this));
|
|
return;
|
|
}
|
|
|
|
EXPECT_TRUE(destroy_test_expected_);
|
|
if (destroy_test_called_)
|
|
return;
|
|
destroy_test_called_ = true;
|
|
|
|
if (!browser_map_.empty()) {
|
|
// Use a copy of the map since the original may be modified while we're
|
|
// iterating.
|
|
BrowserMap browser_map = browser_map_;
|
|
|
|
// Tell all non-popup browsers to close.
|
|
BrowserMap::const_iterator it = browser_map.begin();
|
|
for (; it != browser_map.end(); ++it)
|
|
CloseBrowser(it->second, false);
|
|
}
|
|
|
|
if (ui_thread_helper_.get())
|
|
ui_thread_helper_.reset(NULL);
|
|
}
|
|
|
|
void TestHandler::OnTestTimeout(int timeout_ms, bool treat_as_error) {
|
|
EXPECT_UI_THREAD();
|
|
if (treat_as_error)
|
|
EXPECT_TRUE(false) << "Test timed out after " << timeout_ms << "ms";
|
|
DestroyTest();
|
|
}
|
|
|
|
void TestHandler::CreateBrowser(
|
|
const CefString& url,
|
|
CefRefPtr<CefRequestContext> request_context) {
|
|
#if defined(USE_AURA)
|
|
const bool use_views = CefCommandLine::GetGlobalCommandLine()->HasSwitch(
|
|
client::switches::kUseViews);
|
|
if (use_views && !CefCurrentlyOn(TID_UI)) {
|
|
// Views classes must be accessed on the UI thread.
|
|
CefPostTask(TID_UI,
|
|
base::Bind(&TestHandler::CreateBrowser, this, url, request_context));
|
|
return;
|
|
}
|
|
#endif // defined(USE_AURA)
|
|
|
|
CefWindowInfo windowInfo;
|
|
CefBrowserSettings settings;
|
|
PopulateBrowserSettings(&settings);
|
|
|
|
#if defined(USE_AURA)
|
|
if (use_views) {
|
|
// Create the BrowserView.
|
|
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
|
|
this, url, settings, request_context, new TestBrowserViewDelegate());
|
|
|
|
// Create the Window. It will show itself after creation.
|
|
TestWindowDelegate::CreateBrowserWindow(browser_view, std::string());
|
|
} else
|
|
#endif // defined(USE_AURA)
|
|
{
|
|
#if defined(OS_WIN)
|
|
windowInfo.SetAsPopup(NULL, "CefUnitTest");
|
|
windowInfo.style |= WS_VISIBLE;
|
|
#endif
|
|
CefBrowserHost::CreateBrowser(windowInfo, this, url, settings,
|
|
request_context);
|
|
}
|
|
}
|
|
|
|
// static
|
|
void TestHandler::CloseBrowser(CefRefPtr<CefBrowser> browser,
|
|
bool force_close) {
|
|
browser->GetHost()->CloseBrowser(force_close);
|
|
}
|
|
|
|
void TestHandler::AddResource(const std::string& url,
|
|
const std::string& content,
|
|
const std::string& mimeType) {
|
|
if (!CefCurrentlyOn(TID_IO)) {
|
|
CefPostTask(TID_IO,
|
|
base::Bind(&TestHandler::AddResource, this, url, content, mimeType));
|
|
return;
|
|
}
|
|
|
|
// Ignore the query component, if any.
|
|
std::string urlStr = url;
|
|
size_t idx = urlStr.find('?');
|
|
if (idx > 0)
|
|
urlStr = urlStr.substr(0, idx);
|
|
|
|
resource_map_.insert(
|
|
std::make_pair(urlStr, std::make_pair(content, mimeType)));
|
|
}
|
|
|
|
void TestHandler::ClearResources() {
|
|
if (!CefCurrentlyOn(TID_IO)) {
|
|
CefPostTask(TID_IO, base::Bind(&TestHandler::ClearResources, this));
|
|
return;
|
|
}
|
|
|
|
resource_map_.clear();
|
|
}
|
|
|
|
void TestHandler::SetTestTimeout(int timeout_ms, bool treat_as_error) {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
CefPostTask(TID_UI, base::Bind(&TestHandler::SetTestTimeout, this,
|
|
timeout_ms, treat_as_error));
|
|
return;
|
|
}
|
|
|
|
if (treat_as_error &&
|
|
CefCommandLine::GetGlobalCommandLine()->HasSwitch(
|
|
"disable-test-timeout")) {
|
|
return;
|
|
}
|
|
|
|
// Use a weak reference to |this| via UIThreadHelper so that the TestHandler
|
|
// can be destroyed before the timeout expires.
|
|
GetUIThreadHelper()->PostDelayedTask(
|
|
base::Bind(&TestHandler::OnTestTimeout, base::Unretained(this),
|
|
timeout_ms, treat_as_error),
|
|
timeout_ms);
|
|
}
|
|
|
|
void TestHandler::TestComplete() {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
CefPostTask(TID_UI, base::Bind(&TestHandler::TestComplete, this));
|
|
return;
|
|
}
|
|
|
|
EXPECT_TRUE(browser_map_.empty());
|
|
completion_state_->TestComplete();
|
|
}
|
|
|
|
TestHandler::UIThreadHelper* TestHandler::GetUIThreadHelper() {
|
|
EXPECT_UI_THREAD();
|
|
EXPECT_FALSE(destroy_test_called_);
|
|
|
|
if (!ui_thread_helper_.get())
|
|
ui_thread_helper_.reset(new UIThreadHelper());
|
|
return ui_thread_helper_.get();
|
|
}
|
|
|
|
|
|
// global functions
|
|
|
|
bool TestFailed() {
|
|
CefRefPtr<CefCommandLine> command_line =
|
|
CefCommandLine::GetGlobalCommandLine();
|
|
if (command_line->HasSwitch("single-process")) {
|
|
// Check for a failure on the current test only.
|
|
return ::testing::UnitTest::GetInstance()->current_test_info()->result()->
|
|
Failed();
|
|
} else {
|
|
// Check for any global failure.
|
|
return ::testing::UnitTest::GetInstance()->Failed();
|
|
}
|
|
}
|