mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-01-30 11:04:52 +01:00
dca0435d2f
Split the Alloy runtime into bootstrap and style components. Support creation of Alloy style browsers and windows with the Chrome runtime. Chrome runtime (`--enable-chrome-runtime`) + Alloy style (`--use-alloy-style`) supports Views (`--use-views`), native parent (`--use-native`) and windowless rendering (`--off-screen-rendering-enabled`). Print preview is supported in all cases except with windowless rendering on all platforms and native parent on MacOS. It is disabled by default with Alloy style for legacy compatibility. Where supported it can be enabled or disabled globally using `--[enable|disable]-print-preview` or configured on a per-RequestContext basis using the `printing.print_preview_disabled` preference. It also behaves as expected when triggered via the PDF viewer print button. Chrome runtime + Alloy style behavior differs from Alloy runtime in the following significant ways: - Supports Chrome error pages by default. - DevTools popups are Chrome style only (cannot be windowless). - The Alloy extension API will not supported. Chrome runtime + Alloy style passes all expected Alloy ceftests except the following: - `DisplayTest.AutoResize` (Alloy extension API not supported) - `DownloadTest.*` (Download API not yet supported) - `ExtensionTest.*` (Alloy extension API not supported) This change also adds Chrome runtime support for CefContextMenuHandler::RunContextMenu (see #3293). This change also explicitly blocks (and doesn't retry) FrameAttached requests from PDF viewer and print preview excluded frames (see #3664). Known issues specific to Chrome runtime + Alloy style: - DevTools popup with windowless rendering doesn't load successfully. Use windowed rendering or remote debugging as a workaround. - Chrome style Window with Alloy style BrowserView (`--use-alloy-style --use-chrome-style-window`) does not show Chrome theme changes. To test: - Run `ceftests --enable-chrome-runtime --use-alloy-style [--use-chrome-style-window] [--use-views|--use-native] --gtest_filter=...` - Run `cefclient --enable-chrome-runtime --use-alloy-style [--use-chrome-style-window] [--use-views|--use-native|--off-screen-rendering-enabled]` - Run `cefsimple --enable-chrome-runtime --use-alloy-style [--use-views]`
811 lines
24 KiB
C++
811 lines
24 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/ceftests/test_handler.h"
|
|
|
|
#include <memory>
|
|
#include <sstream>
|
|
|
|
#include "include/base/cef_callback.h"
|
|
#include "include/base/cef_logging.h"
|
|
#include "include/cef_command_line.h"
|
|
#include "include/cef_stream.h"
|
|
#include "include/views/cef_browser_view.h"
|
|
#include "include/views/cef_window.h"
|
|
#include "include/wrapper/cef_closure_task.h"
|
|
#include "include/wrapper/cef_stream_resource_handler.h"
|
|
#include "tests/ceftests/test_request.h"
|
|
#include "tests/ceftests/test_util.h"
|
|
|
|
// Set to 1 to enable verbose debugging info logging.
|
|
#define VERBOSE_DEBUGGING 0
|
|
|
|
namespace {
|
|
|
|
cef_runtime_style_t GetExpectedRuntimeStyle(TestHandler* handler,
|
|
bool is_devtools_popup,
|
|
bool is_window) {
|
|
if (!IsChromeBootstrap()) {
|
|
// Alloy runtime always uses Alloy style.
|
|
return CEF_RUNTIME_STYLE_ALLOY;
|
|
}
|
|
|
|
const bool alloy_requested = is_window ? handler->use_alloy_style_window()
|
|
: handler->use_alloy_style_browser();
|
|
|
|
// Alloy style is not supported with Chrome DevTools popups.
|
|
if (alloy_requested && !is_devtools_popup) {
|
|
return CEF_RUNTIME_STYLE_ALLOY;
|
|
}
|
|
return CEF_RUNTIME_STYLE_CHROME;
|
|
}
|
|
|
|
// 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(TestHandler* handler,
|
|
CefRefPtr<CefBrowserView> browser_view,
|
|
bool is_devtools_popup) {
|
|
auto window = CefWindow::CreateTopLevelWindow(
|
|
new TestWindowDelegate(handler, browser_view, is_devtools_popup));
|
|
EXPECT_EQ(
|
|
GetExpectedRuntimeStyle(handler, is_devtools_popup, /*is_window=*/true),
|
|
window->GetRuntimeStyle());
|
|
}
|
|
|
|
// CefWindowDelegate methods:
|
|
|
|
void OnWindowCreated(CefRefPtr<CefWindow> window) override {
|
|
// Add the browser view and show the window.
|
|
window->CenterWindow(CefSize(800, 600));
|
|
window->SetTitle(ComputeViewsWindowTitle(window, browser_view_));
|
|
window->AddChildView(browser_view_);
|
|
window->Show();
|
|
|
|
// With Chrome runtime, the Browser is not created until after the
|
|
// BrowserView is assigned to the Window.
|
|
browser_id_ = browser_view_->GetBrowser()->GetIdentifier();
|
|
handler_->OnWindowCreated(browser_id_);
|
|
}
|
|
|
|
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
|
|
auto browser = browser_view_->GetBrowser();
|
|
browser_view_ = nullptr;
|
|
handler_->OnWindowDestroyed(browser_id_);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
cef_runtime_style_t GetWindowRuntimeStyle() override {
|
|
// Alloy style is not supported with Chrome DevTools popups.
|
|
if (handler_->use_alloy_style_window() && !is_devtools_popup_) {
|
|
return CEF_RUNTIME_STYLE_ALLOY;
|
|
}
|
|
return CEF_RUNTIME_STYLE_DEFAULT;
|
|
}
|
|
|
|
private:
|
|
TestWindowDelegate(TestHandler* handler,
|
|
CefRefPtr<CefBrowserView> browser_view,
|
|
bool is_devtools_popup)
|
|
: handler_(handler),
|
|
browser_view_(browser_view),
|
|
is_devtools_popup_(is_devtools_popup) {}
|
|
|
|
TestHandler* const handler_;
|
|
CefRefPtr<CefBrowserView> browser_view_;
|
|
const bool is_devtools_popup_;
|
|
int browser_id_ = 0;
|
|
|
|
IMPLEMENT_REFCOUNTING(TestWindowDelegate);
|
|
DISALLOW_COPY_AND_ASSIGN(TestWindowDelegate);
|
|
};
|
|
|
|
// Delegate implementation for the CefBrowserView.
|
|
class TestBrowserViewDelegate : public CefBrowserViewDelegate {
|
|
public:
|
|
TestBrowserViewDelegate(TestHandler* handler, bool is_devtools_popup)
|
|
: handler_(handler), is_devtools_popup_(is_devtools_popup) {}
|
|
|
|
// CefBrowserViewDelegate methods:
|
|
|
|
void OnBrowserDestroyed(CefRefPtr<CefBrowserView> browser_view,
|
|
CefRefPtr<CefBrowser> browser) override {
|
|
#if VERBOSE_DEBUGGING
|
|
LOG(INFO) << handler_->debug_string_prefix() << browser->GetIdentifier()
|
|
<< ": OnBrowserDestroyed";
|
|
#endif
|
|
// Always close the containing Window when the browser is destroyed.
|
|
if (auto window = browser_view->GetWindow()) {
|
|
#if VERBOSE_DEBUGGING
|
|
LOG(INFO) << handler_->debug_string_prefix() << browser->GetIdentifier()
|
|
<< ": OnBrowserDestroyed Close";
|
|
#endif
|
|
window->Close();
|
|
}
|
|
}
|
|
|
|
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
|
|
CefRefPtr<CefBrowserView> browser_view,
|
|
const CefBrowserSettings& settings,
|
|
CefRefPtr<CefClient> client,
|
|
bool is_devtools) override {
|
|
auto* handler = static_cast<TestHandler*>(client.get());
|
|
if (handler == handler_) {
|
|
// Use the same Delegate when using the same TestHandler instance if
|
|
// allowed (e.g. runtime style is also the same).
|
|
if (GetExpectedRuntimeStyle(handler_, is_devtools, /*is_window=*/false) ==
|
|
browser_view->GetRuntimeStyle()) {
|
|
return this;
|
|
}
|
|
}
|
|
|
|
// Otherwise return a new Delegate instance.
|
|
return new TestBrowserViewDelegate(handler, is_devtools);
|
|
}
|
|
|
|
bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
|
|
CefRefPtr<CefBrowserView> popup_browser_view,
|
|
bool is_devtools) override {
|
|
// The popup may use a different TestHandler instance.
|
|
auto* handler = static_cast<TestHandler*>(
|
|
popup_browser_view->GetBrowser()->GetHost()->GetClient().get());
|
|
|
|
// Create our own Window for popups. It will show itself after creation.
|
|
TestWindowDelegate::CreateBrowserWindow(handler, popup_browser_view,
|
|
is_devtools);
|
|
return true;
|
|
}
|
|
|
|
cef_runtime_style_t GetBrowserRuntimeStyle() override {
|
|
// Alloy style is not supported with Chrome DevTools popups.
|
|
if (handler_->use_alloy_style_browser() && !is_devtools_popup_) {
|
|
return CEF_RUNTIME_STYLE_ALLOY;
|
|
}
|
|
return CEF_RUNTIME_STYLE_DEFAULT;
|
|
}
|
|
|
|
private:
|
|
TestHandler* const handler_;
|
|
const bool is_devtools_popup_;
|
|
|
|
IMPLEMENT_REFCOUNTING(TestBrowserViewDelegate);
|
|
DISALLOW_COPY_AND_ASSIGN(TestBrowserViewDelegate);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// TestHandler::CompletionState
|
|
|
|
TestHandler::CompletionState::CompletionState(int total) : total_(total) {
|
|
event_ = CefWaitableEvent::CreateWaitableEvent(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(base::OnceClosure task) {
|
|
EXPECT_UI_THREAD();
|
|
CefPostTask(TID_UI,
|
|
base::BindOnce(&UIThreadHelper::TaskHelper,
|
|
weak_ptr_factory_.GetWeakPtr(), std::move(task)));
|
|
}
|
|
|
|
void TestHandler::UIThreadHelper::PostDelayedTask(base::OnceClosure task,
|
|
int delay_ms) {
|
|
EXPECT_UI_THREAD();
|
|
CefPostDelayedTask(
|
|
TID_UI,
|
|
base::BindOnce(&UIThreadHelper::TaskHelper,
|
|
weak_ptr_factory_.GetWeakPtr(), std::move(task)),
|
|
delay_ms);
|
|
}
|
|
|
|
void TestHandler::UIThreadHelper::TaskHelper(base::OnceClosure task) {
|
|
EXPECT_UI_THREAD();
|
|
std::move(task).Run();
|
|
}
|
|
|
|
// TestHandler
|
|
|
|
// static
|
|
std::atomic<size_t> TestHandler::test_handler_count_{0U};
|
|
|
|
TestHandler::TestHandler(CompletionState* completion_state)
|
|
: debug_string_prefix_(MakeDebugStringPrefix()),
|
|
use_views_(UseViewsGlobal()),
|
|
use_alloy_style_browser_(UseAlloyStyleBrowserGlobal()),
|
|
use_alloy_style_window_(UseAlloyStyleWindowGlobal()) {
|
|
test_handler_count_++;
|
|
|
|
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());
|
|
EXPECT_TRUE(browser_status_map_.empty());
|
|
|
|
if (completion_state_owned_) {
|
|
delete completion_state_;
|
|
}
|
|
|
|
if (destroy_event_) {
|
|
destroy_event_->Signal();
|
|
}
|
|
|
|
test_handler_count_--;
|
|
}
|
|
|
|
void TestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
|
|
EXPECT_UI_THREAD();
|
|
|
|
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));
|
|
|
|
const bool views_hosted = !!CefBrowserView::GetForBrowser(browser);
|
|
OnCreated(browser_id, NT_BROWSER, views_hosted);
|
|
}
|
|
|
|
void TestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
|
|
EXPECT_UI_THREAD();
|
|
|
|
// 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_ = nullptr;
|
|
}
|
|
|
|
OnClosed(browser_id, NT_BROWSER);
|
|
}
|
|
|
|
void TestHandler::OnWindowCreated(int browser_id) {
|
|
CHECK(use_views_);
|
|
EXPECT_UI_THREAD();
|
|
OnCreated(browser_id, NT_WINDOW, /*views_hosted=*/true);
|
|
}
|
|
|
|
void TestHandler::OnWindowDestroyed(int browser_id) {
|
|
CHECK(use_views_);
|
|
EXPECT_UI_THREAD();
|
|
OnClosed(browser_id, NT_WINDOW);
|
|
}
|
|
|
|
void TestHandler::OnCreated(int browser_id,
|
|
NotifyType type,
|
|
bool views_hosted) {
|
|
CHECK(use_views_ || !views_hosted);
|
|
|
|
const bool has_value =
|
|
browser_status_map_.find(browser_id) != browser_status_map_.end();
|
|
|
|
auto& browser_status = browser_status_map_[browser_id];
|
|
if (has_value) {
|
|
CHECK_EQ(browser_status.views_hosted, views_hosted);
|
|
} else {
|
|
browser_status.views_hosted = views_hosted;
|
|
}
|
|
|
|
EXPECT_FALSE(browser_status.got_created[type])
|
|
<< "Duplicate call to OnCreated(" << browser_id << ", "
|
|
<< (type == NT_BROWSER ? "BROWSER" : "WINDOW") << ")";
|
|
browser_status.got_created[type].yes();
|
|
|
|
#if VERBOSE_DEBUGGING
|
|
bool creation_complete = false;
|
|
|
|
// When using Views, wait for both Browser and Window notifications.
|
|
if (browser_status.views_hosted) {
|
|
creation_complete = browser_status.got_created[NT_BROWSER] &&
|
|
browser_status.got_created[NT_WINDOW];
|
|
} else {
|
|
creation_complete = browser_status.got_created[NT_BROWSER];
|
|
}
|
|
|
|
LOG(INFO) << debug_string_prefix_ << browser_id << ": OnCreated type="
|
|
<< (type == NT_BROWSER ? "BROWSER" : "WINDOW")
|
|
<< " creation_complete=" << creation_complete;
|
|
#endif // VERBOSE_DEBUGGING
|
|
}
|
|
|
|
void TestHandler::OnClosed(int browser_id, NotifyType type) {
|
|
bool close_complete = false;
|
|
|
|
auto& browser_status = browser_status_map_[browser_id];
|
|
EXPECT_FALSE(browser_status.got_closed[type])
|
|
<< "Duplicate call to OnClosed(" << browser_id << ", "
|
|
<< (type == NT_BROWSER ? "BROWSER" : "WINDOW") << ")";
|
|
browser_status.got_closed[type].yes();
|
|
|
|
// When using Views, wait for both Browser and Window notifications.
|
|
if (browser_status.views_hosted) {
|
|
close_complete = browser_status.got_closed[NT_BROWSER] &&
|
|
browser_status.got_closed[NT_WINDOW];
|
|
} else {
|
|
close_complete = browser_status.got_closed[NT_BROWSER];
|
|
}
|
|
|
|
if (close_complete) {
|
|
browser_status_map_.erase(browser_id);
|
|
}
|
|
|
|
// Test may be complete if no Browsers/Windows are remaining.
|
|
const bool all_browsers_closed = AllBrowsersClosed();
|
|
|
|
#if VERBOSE_DEBUGGING
|
|
LOG(INFO) << debug_string_prefix_ << browser_id
|
|
<< ": OnClosed type=" << (type == NT_BROWSER ? "BROWSER" : "WINDOW")
|
|
<< " close_complete=" << close_complete
|
|
<< " all_browsers_closed=" << all_browsers_closed;
|
|
#endif
|
|
|
|
if (all_browsers_closed) {
|
|
// May result in |this| being deleted.
|
|
MaybeTestComplete();
|
|
}
|
|
}
|
|
|
|
std::string TestHandler::MakeDebugStringPrefix() const {
|
|
#if VERBOSE_DEBUGGING
|
|
std::stringstream ss;
|
|
ss << "TestHandler [0x" << std::hex << this << "]: ";
|
|
return ss.str();
|
|
#else
|
|
return std::string();
|
|
#endif
|
|
}
|
|
|
|
namespace {
|
|
|
|
CefResponse::HeaderMap ToCefHeaderMap(
|
|
const ResourceContent::HeaderMap& headerMap) {
|
|
CefResponse::HeaderMap result;
|
|
ResourceContent::HeaderMap::const_iterator it = headerMap.begin();
|
|
for (; it != headerMap.end(); ++it) {
|
|
result.insert(std::pair<CefString, CefString>(it->first, it->second));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CefRefPtr<CefResourceHandler> TestHandler::GetResourceHandler(
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request) {
|
|
EXPECT_IO_THREAD();
|
|
|
|
if (resource_map_.size() > 0) {
|
|
const std::string& url = test_request::GetPathURL(request->GetURL());
|
|
ResourceMap::const_iterator it = resource_map_.find(url);
|
|
if (it != resource_map_.end()) {
|
|
// Return the previously mapped resource
|
|
CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
|
|
static_cast<void*>(const_cast<char*>(it->second.content().c_str())),
|
|
it->second.content().length());
|
|
return new CefStreamResourceHandler(
|
|
200, "OK", it->second.mimeType(),
|
|
ToCefHeaderMap(it->second.headerMap()), stream);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void TestHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
|
|
TerminationStatus status,
|
|
int error_code,
|
|
const CefString& error_string) {
|
|
LOG(WARNING) << "OnRenderProcessTerminated: status = " << status
|
|
<< ", error = " << error_string.ToString() << ".";
|
|
}
|
|
|
|
CefRefPtr<CefBrowser> TestHandler::GetBrowser() const {
|
|
return first_browser_;
|
|
}
|
|
|
|
int TestHandler::GetBrowserId() const {
|
|
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 (test_timeout_called_) {
|
|
test_timeout_called_ = false;
|
|
}
|
|
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::BindOnce(&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 browsers to close.
|
|
BrowserMap::const_iterator it = browser_map.begin();
|
|
for (; it != browser_map.end(); ++it) {
|
|
CloseBrowser(it->second, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TestHandler::OnTestTimeout(int timeout_ms, bool treat_as_error) {
|
|
EXPECT_UI_THREAD();
|
|
|
|
EXPECT_FALSE(test_timeout_called_);
|
|
test_timeout_called_ = true;
|
|
|
|
if (treat_as_error) {
|
|
EXPECT_TRUE(false) << "Test timed out after " << timeout_ms << "ms";
|
|
}
|
|
|
|
EXPECT_FALSE(AllBrowsersClosed() && AllowTestCompletionWhenAllBrowsersClose())
|
|
<< "Test timed out unexpectedly; should be complete";
|
|
|
|
// Keep |this| alive until after the method completes.
|
|
CefRefPtr<TestHandler> self(this);
|
|
|
|
// Close any remaining browsers.
|
|
DestroyTest();
|
|
|
|
// Reset signal completion count.
|
|
if (signal_completion_count_ > 0) {
|
|
signal_completion_count_ = 0;
|
|
MaybeTestComplete();
|
|
}
|
|
}
|
|
|
|
void TestHandler::CreateBrowser(const CefString& url,
|
|
CefRefPtr<CefRequestContext> request_context,
|
|
CefRefPtr<CefDictionaryValue> extra_info) {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
CefPostTask(TID_UI, base::BindOnce(&TestHandler::CreateBrowser, this, url,
|
|
request_context, extra_info));
|
|
return;
|
|
}
|
|
|
|
CefWindowInfo windowInfo;
|
|
CefBrowserSettings settings;
|
|
|
|
if (use_views_) {
|
|
// Create the BrowserView.
|
|
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
|
|
this, url, settings, extra_info, request_context,
|
|
new TestBrowserViewDelegate(this, /*is_devtools_popup=*/false));
|
|
EXPECT_EQ(GetExpectedRuntimeStyle(this, /*is_devtools_popup=*/false,
|
|
/*is_window=*/false),
|
|
browser_view->GetRuntimeStyle());
|
|
|
|
// Create the Window. It will show itself after creation.
|
|
TestWindowDelegate::CreateBrowserWindow(this, browser_view,
|
|
/*is_devtools_popup=*/false);
|
|
} else {
|
|
#if defined(OS_WIN)
|
|
windowInfo.SetAsPopup(nullptr,
|
|
ComputeNativeWindowTitle(use_alloy_style_browser_));
|
|
windowInfo.style |= WS_VISIBLE;
|
|
#endif
|
|
if (use_alloy_style_browser_) {
|
|
windowInfo.runtime_style = CEF_RUNTIME_STYLE_ALLOY;
|
|
}
|
|
CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, extra_info,
|
|
request_context);
|
|
}
|
|
}
|
|
|
|
// static
|
|
void TestHandler::CloseBrowser(CefRefPtr<CefBrowser> browser,
|
|
bool force_close) {
|
|
#if VERBOSE_DEBUGGING
|
|
LOG(INFO) << "TestHandler: " << browser->GetIdentifier()
|
|
<< ": CloseBrowser force_close=" << force_close;
|
|
#endif
|
|
browser->GetHost()->CloseBrowser(force_close);
|
|
}
|
|
|
|
void TestHandler::AddResource(const std::string& url,
|
|
const std::string& content,
|
|
const std::string& mime_type,
|
|
const ResourceContent::HeaderMap& header_map) {
|
|
ResourceContent rc = ResourceContent(content, mime_type, header_map);
|
|
AddResourceEx(url, rc);
|
|
}
|
|
|
|
void TestHandler::AddResourceEx(const std::string& url,
|
|
const ResourceContent& content) {
|
|
if (!CefCurrentlyOn(TID_IO)) {
|
|
CefPostTask(TID_IO, base::BindOnce(&TestHandler::AddResourceEx, this, url,
|
|
content));
|
|
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, content));
|
|
}
|
|
|
|
void TestHandler::ClearResources() {
|
|
if (!CefCurrentlyOn(TID_IO)) {
|
|
CefPostTask(TID_IO, base::BindOnce(&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::BindOnce(&TestHandler::SetTestTimeout, this,
|
|
timeout_ms, treat_as_error));
|
|
return;
|
|
}
|
|
|
|
if (destroy_test_called_) {
|
|
// No need to set the timeout if the test has already completed.
|
|
return;
|
|
}
|
|
|
|
const auto timeout = GetConfiguredTestTimeout(timeout_ms);
|
|
if (treat_as_error && !timeout) {
|
|
return;
|
|
}
|
|
|
|
// Use a weak reference to |this| via UIThreadHelper so that the TestHandler
|
|
// can be destroyed before the timeout expires.
|
|
GetUIThreadHelper()->PostDelayedTask(
|
|
base::BindOnce(&TestHandler::OnTestTimeout, base::Unretained(this),
|
|
*timeout, treat_as_error),
|
|
*timeout);
|
|
}
|
|
|
|
void TestHandler::SetUseViews(bool use_views) {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
CefPostTask(TID_UI,
|
|
base::BindOnce(&TestHandler::SetUseViews, this, use_views));
|
|
return;
|
|
}
|
|
|
|
use_views_ = use_views;
|
|
}
|
|
|
|
void TestHandler::SetUseAlloyStyle(bool use_alloy_style_browser,
|
|
bool use_alloy_style_window) {
|
|
if (!IsChromeBootstrap()) {
|
|
LOG(WARNING) << "SetUseAlloyStyle is ignored with the Alloy runtime";
|
|
return;
|
|
}
|
|
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
CefPostTask(TID_UI, base::BindOnce(&TestHandler::SetUseAlloyStyle, this,
|
|
use_alloy_style_browser,
|
|
use_alloy_style_window));
|
|
return;
|
|
}
|
|
|
|
use_alloy_style_browser_ = use_alloy_style_browser;
|
|
use_alloy_style_window_ = use_alloy_style_window;
|
|
}
|
|
|
|
void TestHandler::SetSignalTestCompletionCount(size_t count) {
|
|
#if VERBOSE_DEBUGGING
|
|
LOG(INFO) << debug_string_prefix_
|
|
<< "SetSignalTestCompletionCount count=" << count;
|
|
#endif
|
|
signal_completion_count_ = count;
|
|
}
|
|
|
|
void TestHandler::SignalTestCompletion() {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
CefPostTask(TID_UI,
|
|
base::BindOnce(&TestHandler::SignalTestCompletion, this));
|
|
return;
|
|
}
|
|
|
|
if (test_timeout_called_) {
|
|
// Ignore any signals that arrive after test timeout.
|
|
return;
|
|
}
|
|
|
|
CHECK_GT(signal_completion_count_, 0U);
|
|
signal_completion_count_--;
|
|
|
|
#if VERBOSE_DEBUGGING
|
|
LOG(INFO) << debug_string_prefix_
|
|
<< "SignalTestComplete remaining=" << signal_completion_count_;
|
|
#endif
|
|
|
|
if (signal_completion_count_ == 0) {
|
|
// May result in |this| being deleted.
|
|
MaybeTestComplete();
|
|
}
|
|
}
|
|
|
|
bool TestHandler::AllowTestCompletionWhenAllBrowsersClose() const {
|
|
EXPECT_UI_THREAD();
|
|
return signal_completion_count_ == 0U;
|
|
}
|
|
|
|
bool TestHandler::AllBrowsersClosed() const {
|
|
EXPECT_UI_THREAD();
|
|
return browser_status_map_.empty();
|
|
}
|
|
|
|
void TestHandler::MaybeTestComplete() {
|
|
EXPECT_UI_THREAD();
|
|
|
|
const bool all_browsers_closed = AllBrowsersClosed();
|
|
const bool allow_test_completion = AllowTestCompletionWhenAllBrowsersClose();
|
|
|
|
#if VERBOSE_DEBUGGING
|
|
LOG(INFO) << debug_string_prefix_
|
|
<< "MaybeTestComplete all_browsers_closed=" << all_browsers_closed
|
|
<< " allow_test_completion=" << allow_test_completion;
|
|
#endif
|
|
|
|
if (all_browsers_closed && allow_test_completion) {
|
|
TestComplete();
|
|
}
|
|
}
|
|
|
|
void TestHandler::TestComplete() {
|
|
EXPECT_UI_THREAD();
|
|
EXPECT_TRUE(AllBrowsersClosed());
|
|
EXPECT_TRUE(AllowTestCompletionWhenAllBrowsersClose());
|
|
|
|
#if VERBOSE_DEBUGGING
|
|
LOG(INFO) << debug_string_prefix_ << "TestComplete";
|
|
#endif
|
|
|
|
// Cancel any pending tasks posted via UIThreadHelper.
|
|
ui_thread_helper_.reset();
|
|
|
|
completion_state_->TestComplete();
|
|
}
|
|
|
|
TestHandler::UIThreadHelper* TestHandler::GetUIThreadHelper() {
|
|
EXPECT_UI_THREAD();
|
|
CHECK(!destroy_test_called_);
|
|
|
|
if (!ui_thread_helper_.get()) {
|
|
ui_thread_helper_ = std::make_unique<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();
|
|
}
|
|
}
|