cef/tests/cefclient/browser/test_runner.cc

907 lines
28 KiB
C++
Raw Normal View History

// Copyright (c) 2015 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/cefclient/browser/test_runner.h"
#include <algorithm>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include "include/base/cef_callback.h"
#include "include/base/cef_dump_without_crashing.h"
#include "include/cef_parser.h"
#include "include/cef_task.h"
#include "include/cef_trace.h"
views: Add support for modal browser dialogs A modal dialog is a child CefWindow that implements some special behaviors relative to a parent CefWindow. Like any CefWindow it can be framed with titlebar or frameless, and optionally contain draggable regions (subject to platform limitations described below). Modal dialogs are shown centered on the parent window (inside a single display) and always stay on top of the parent window in z-order. Sizing behavior and available window buttons are controlled via the usual CefWindowDelegate callbacks. For example, the dialog can have a preferred size with resize, minimize and maximize disabled (via GetPreferredSize, CanResize, CanMinimize and CanMaximize respectively). This change adds support for two modality modes. With window modality all controls in the parent window are disabled. With browser modality only the browser view in the parent window is disabled. Both modality modes require that a valid parent window be returned via GetParentWindow. For window modality return true from IsWindowModalDialog and call CefWindow::Show. For browser modality return false from IsWindowModalDialog (the default value) and call CefWindow::ShowAsBrowserModalDialog with a reference to the parent window's browser view. Window modal dialog behavior depends on the platform. On Windows and Linux these dialogs have a titlebar and can be moved independent of the parent window. On macOS these dialogs do not have a titlebar, move with the parent window, and do not support draggable regions (because they are implemented using sheets). On Linux disabling the parent window controls requires a window manager the supports _NET_WM_STATE_MODAL. Browser modal dialog behavior is similar on all platforms. The dialog will be automatically sized and positioned relative to the parent window's browser view. Closing the parent window or navigating the parent browser view will dismiss the dialog. The dialog can also be moved independent of the parent window though it will be recentered when the parent window itself is resized or redisplayed. On MacOS the dialog will move along with the parent window while on Windows and Linux the parent window can be moved independently. To test: Use the Tests > Dialog Window menu option in cefclient with Views enabled (`--use-views` or `--enable-chrome-runtime` command-line flag). Browser modal dialog is the default behavior. For window modal dialog add the `--use-window-modal-dialog` command-line flag.
2023-06-14 10:20:02 +02:00
#include "include/views/cef_browser_view.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include "tests/cefclient/browser/base_client_handler.h"
#include "tests/cefclient/browser/binary_transfer_test.h"
#include "tests/cefclient/browser/binding_test.h"
#include "tests/cefclient/browser/dialog_test.h"
#include "tests/cefclient/browser/hang_test.h"
#include "tests/cefclient/browser/main_context.h"
#include "tests/cefclient/browser/media_router_test.h"
#include "tests/cefclient/browser/preferences_test.h"
#include "tests/cefclient/browser/resource.h"
#include "tests/cefclient/browser/response_filter_test.h"
#include "tests/cefclient/browser/root_window_manager.h"
#include "tests/cefclient/browser/scheme_test.h"
#include "tests/cefclient/browser/server_test.h"
#include "tests/cefclient/browser/task_manager_test.h"
#include "tests/cefclient/browser/urlrequest_test.h"
#include "tests/cefclient/browser/window_test.h"
#include "tests/shared/browser/resource_util.h"
#include "tests/shared/common/string_util.h"
namespace client::test_runner {
namespace {
const char kTestHost[] = "tests";
const char kLocalHost[] = "localhost";
const char kTestOrigin[] = "https://tests/";
// Pages handled via StringResourceProvider.
const char kTestGetSourcePage[] = "get_source.html";
const char kTestGetTextPage[] = "get_text.html";
// Set page data and navigate the browser. Used in combination with
// StringResourceProvider.
void LoadStringResourcePage(CefRefPtr<CefBrowser> browser,
const std::string& page,
const std::string& data) {
BaseClientHandler::GetForBrowser(browser)->SetStringResource(page, data);
browser->GetMainFrame()->LoadURL(kTestOrigin + page);
}
void RunGetSourceTest(CefRefPtr<CefBrowser> browser) {
class Visitor : public CefStringVisitor {
public:
explicit Visitor(CefRefPtr<CefBrowser> browser) : browser_(browser) {}
void Visit(const CefString& string) override {
std::string source = AsciiStrReplace(string, "<", "&lt;");
source = AsciiStrReplace(source, ">", "&gt;");
std::stringstream ss;
ss << "<html><body bgcolor=\"white\">Source:<pre>" << source
<< "</pre></body></html>";
LoadStringResourcePage(browser_, kTestGetSourcePage, ss.str());
}
private:
CefRefPtr<CefBrowser> browser_;
IMPLEMENT_REFCOUNTING(Visitor);
};
browser->GetMainFrame()->GetSource(new Visitor(browser));
}
void RunGetTextTest(CefRefPtr<CefBrowser> browser) {
class Visitor : public CefStringVisitor {
public:
explicit Visitor(CefRefPtr<CefBrowser> browser) : browser_(browser) {}
void Visit(const CefString& string) override {
std::string text = AsciiStrReplace(string, "<", "&lt;");
text = AsciiStrReplace(text, ">", "&gt;");
std::stringstream ss;
ss << "<html><body bgcolor=\"white\">Text:<pre>" << text
<< "</pre></body></html>";
LoadStringResourcePage(browser_, kTestGetTextPage, ss.str());
}
private:
CefRefPtr<CefBrowser> browser_;
IMPLEMENT_REFCOUNTING(Visitor);
};
browser->GetMainFrame()->GetText(new Visitor(browser));
}
void RunRequestTest(CefRefPtr<CefBrowser> browser) {
// Create a new request
CefRefPtr<CefRequest> request(CefRequest::Create());
if (browser->GetMainFrame()->GetURL().ToString().find(kTestOrigin) != 0) {
// The LoadRequest method will fail with "bad IPC message" reason
// INVALID_INITIATOR_ORIGIN (213) unless you first navigate to the
// request origin using some other mechanism (LoadURL, link click, etc).
Alert(browser, "Please first navigate to a " + std::string(kTestOrigin) +
" URL. "
"For example, first load Tests > Other Tests.");
return;
}
// Set the request URL
request->SetURL(GetTestURL("request"));
// Add post data to the request. The correct method and content-
// type headers will be set by CEF.
CefRefPtr<CefPostDataElement> postDataElement(CefPostDataElement::Create());
std::string data = "arg1=val1&arg2=val2";
postDataElement->SetToBytes(data.length(), data.c_str());
CefRefPtr<CefPostData> postData(CefPostData::Create());
postData->AddElement(postDataElement);
request->SetPostData(postData);
// Add a custom header
CefRequest::HeaderMap headerMap;
headerMap.insert(std::make_pair("X-My-Header", "My Header Value"));
request->SetHeaderMap(headerMap);
// Load the request
browser->GetMainFrame()->LoadRequest(request);
}
Add support for complete isolation of storage and permissions (cache, cookies, localStorage, access grants, etc) on a per-request-context basis (issue #1044). - CefRequestContext instances can be configured using a new CefRequestContextSettings structure passed to CefRequestContext::CreateContext. - Scheme registration is now per-request-context using new CefRequestContext::RegisterSchemeHandlerFactory and ClearSchemeHandlerFactories methods. - Cookie managers are now per-request-context by default and can be retrieved using a new CefRequestContext::GetDefaultCookieManager method. - CefURLRequest::Create now accepts an optional CefRequestContext argument for associating a URL request with a context (browser process only). - The CefRequestContextHandler associated with a CefRequestContext will not be released until all objects related to that context have been destroyed. - When the cache path is empty an in-memory cache ("incognito mode") will be used for storage and no data will be persisted to disk. - Add CefSettings.user_data_path which specifies the location where user data such as spell checking dictionary files will be stored on disk. - Add asynchronous callbacks for all CefCookieManager methods. - Add PK_LOCAL_APP_DATA and PK_USER_DATA path keys for retrieving user directories via CefGetPath. - cefclient: Add "New Window" test that creates a new window unrelated to existing windows. When used in combination with `--request-context-per-browser` the new window will be given a new and isolated request context. git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@2040 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
2015-03-02 21:25:14 +01:00
void RunNewWindowTest(CefRefPtr<CefBrowser> browser) {
auto config = std::make_unique<RootWindowConfig>();
config->with_controls = true;
config->with_osr = browser->GetHost()->IsWindowRenderingDisabled();
MainContext::Get()->GetRootWindowManager()->CreateRootWindow(
std::move(config));
Add support for complete isolation of storage and permissions (cache, cookies, localStorage, access grants, etc) on a per-request-context basis (issue #1044). - CefRequestContext instances can be configured using a new CefRequestContextSettings structure passed to CefRequestContext::CreateContext. - Scheme registration is now per-request-context using new CefRequestContext::RegisterSchemeHandlerFactory and ClearSchemeHandlerFactories methods. - Cookie managers are now per-request-context by default and can be retrieved using a new CefRequestContext::GetDefaultCookieManager method. - CefURLRequest::Create now accepts an optional CefRequestContext argument for associating a URL request with a context (browser process only). - The CefRequestContextHandler associated with a CefRequestContext will not be released until all objects related to that context have been destroyed. - When the cache path is empty an in-memory cache ("incognito mode") will be used for storage and no data will be persisted to disk. - Add CefSettings.user_data_path which specifies the location where user data such as spell checking dictionary files will be stored on disk. - Add asynchronous callbacks for all CefCookieManager methods. - Add PK_LOCAL_APP_DATA and PK_USER_DATA path keys for retrieving user directories via CefGetPath. - cefclient: Add "New Window" test that creates a new window unrelated to existing windows. When used in combination with `--request-context-per-browser` the new window will be given a new and isolated request context. git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@2040 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
2015-03-02 21:25:14 +01:00
}
void RunPopupWindowTest(CefRefPtr<CefBrowser> browser) {
browser->GetMainFrame()->ExecuteJavaScript(
"window.open('https://www.google.com');", "about:blank", 0);
}
views: Add support for modal browser dialogs A modal dialog is a child CefWindow that implements some special behaviors relative to a parent CefWindow. Like any CefWindow it can be framed with titlebar or frameless, and optionally contain draggable regions (subject to platform limitations described below). Modal dialogs are shown centered on the parent window (inside a single display) and always stay on top of the parent window in z-order. Sizing behavior and available window buttons are controlled via the usual CefWindowDelegate callbacks. For example, the dialog can have a preferred size with resize, minimize and maximize disabled (via GetPreferredSize, CanResize, CanMinimize and CanMaximize respectively). This change adds support for two modality modes. With window modality all controls in the parent window are disabled. With browser modality only the browser view in the parent window is disabled. Both modality modes require that a valid parent window be returned via GetParentWindow. For window modality return true from IsWindowModalDialog and call CefWindow::Show. For browser modality return false from IsWindowModalDialog (the default value) and call CefWindow::ShowAsBrowserModalDialog with a reference to the parent window's browser view. Window modal dialog behavior depends on the platform. On Windows and Linux these dialogs have a titlebar and can be moved independent of the parent window. On macOS these dialogs do not have a titlebar, move with the parent window, and do not support draggable regions (because they are implemented using sheets). On Linux disabling the parent window controls requires a window manager the supports _NET_WM_STATE_MODAL. Browser modal dialog behavior is similar on all platforms. The dialog will be automatically sized and positioned relative to the parent window's browser view. Closing the parent window or navigating the parent browser view will dismiss the dialog. The dialog can also be moved independent of the parent window though it will be recentered when the parent window itself is resized or redisplayed. On MacOS the dialog will move along with the parent window while on Windows and Linux the parent window can be moved independently. To test: Use the Tests > Dialog Window menu option in cefclient with Views enabled (`--use-views` or `--enable-chrome-runtime` command-line flag). Browser modal dialog is the default behavior. For window modal dialog add the `--use-window-modal-dialog` command-line flag.
2023-06-14 10:20:02 +02:00
void RunDialogWindowTest(CefRefPtr<CefBrowser> browser) {
auto browser_view = CefBrowserView::GetForBrowser(browser);
if (!browser_view) {
LOG(ERROR) << "Dialog windows require Views";
return;
}
auto config = std::make_unique<RootWindowConfig>();
config->window_type = WindowType::DIALOG;
config->parent_window = browser_view->GetWindow();
MainContext::Get()->GetRootWindowManager()->CreateRootWindow(
std::move(config));
}
const char kPrompt[] = "Prompt.";
const char kPromptFPS[] = "FPS";
const char kPromptDSF[] = "DSF";
// Handles execution of prompt results.
class PromptHandler : public CefMessageRouterBrowserSide::Handler {
public:
PromptHandler() = default;
// Called due to cefQuery execution.
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64_t query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
// Parse |request| which takes the form "Prompt.[type]:[value]".
const std::string& request_str = request;
2023-01-02 23:59:03 +01:00
if (request_str.find(kPrompt) != 0) {
return false;
2023-01-02 23:59:03 +01:00
}
std::string type = request_str.substr(sizeof(kPrompt) - 1);
size_t delim = type.find(':');
2023-01-02 23:59:03 +01:00
if (delim == std::string::npos) {
return false;
2023-01-02 23:59:03 +01:00
}
const std::string& value = type.substr(delim + 1);
type = type.substr(0, delim);
// Canceling the prompt dialog returns a value of "null".
if (value != "null") {
2023-01-02 23:59:03 +01:00
if (type == kPromptFPS) {
SetFPS(browser, atoi(value.c_str()));
2023-01-02 23:59:03 +01:00
} else if (type == kPromptDSF) {
SetDSF(browser, static_cast<float>(atof(value.c_str())));
2023-01-02 23:59:03 +01:00
}
}
// Nothing is done with the response.
callback->Success(CefString());
return true;
}
private:
void SetFPS(CefRefPtr<CefBrowser> browser, int fps) {
if (fps <= 0) {
// Reset to the default value.
CefBrowserSettings settings;
MainContext::Get()->PopulateBrowserSettings(&settings);
fps = settings.windowless_frame_rate;
}
browser->GetHost()->SetWindowlessFrameRate(fps);
}
void SetDSF(CefRefPtr<CefBrowser> browser, float dsf) {
MainMessageLoop::Get()->PostClosure(
base::BindOnce(&PromptHandler::SetDSFOnMainThread, browser, dsf));
}
static void SetDSFOnMainThread(CefRefPtr<CefBrowser> browser, float dsf) {
RootWindow::GetForBrowser(browser->GetIdentifier())
->SetDeviceScaleFactor(dsf);
}
};
void Prompt(CefRefPtr<CefBrowser> browser,
const std::string& type,
const std::string& label,
const std::string& default_value) {
// Prompt the user for a new value. Works as follows:
// 1. Show a prompt() dialog via JavaScript.
// 2. Pass the result to window.cefQuery().
// 3. Handle the result in PromptHandler::OnQuery.
const std::string& code = "window.cefQuery({'request': '" +
std::string(kPrompt) + type + ":' + prompt('" +
label + "', '" + default_value + "')});";
browser->GetMainFrame()->ExecuteJavaScript(
code, browser->GetMainFrame()->GetURL(), 0);
}
void PromptFPS(CefRefPtr<CefBrowser> browser) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the UI thread.
CefPostTask(TID_UI, base::BindOnce(&PromptFPS, browser));
return;
}
// Format the default value string.
std::stringstream ss;
ss << browser->GetHost()->GetWindowlessFrameRate();
Prompt(browser, kPromptFPS, "Enter FPS", ss.str());
}
void PromptDSF(CefRefPtr<CefBrowser> browser) {
if (!MainMessageLoop::Get()->RunsTasksOnCurrentThread()) {
// Execute on the main thread.
MainMessageLoop::Get()->PostClosure(base::BindOnce(&PromptDSF, browser));
return;
}
// Format the default value string.
std::stringstream ss;
ss << RootWindow::GetForBrowser(browser->GetIdentifier())
->GetDeviceScaleFactor();
Prompt(browser, kPromptDSF, "Enter Device Scale Factor", ss.str());
}
void BeginTracing() {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the UI thread.
CefPostTask(TID_UI, base::BindOnce(&BeginTracing));
return;
}
CefBeginTracing(CefString(), nullptr);
}
void EndTracing(CefRefPtr<CefBrowser> browser) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the UI thread.
CefPostTask(TID_UI, base::BindOnce(&EndTracing, browser));
return;
}
class Client : public CefEndTracingCallback, public CefRunFileDialogCallback {
public:
explicit Client(CefRefPtr<CefBrowser> browser) : browser_(browser) {
RunDialog();
}
void RunDialog() {
static const char kDefaultFileName[] = "trace.txt";
std::string path = MainContext::Get()->GetDownloadPath(kDefaultFileName);
2023-01-02 23:59:03 +01:00
if (path.empty()) {
path = kDefaultFileName;
2023-01-02 23:59:03 +01:00
}
// Results in a call to OnFileDialogDismissed.
browser_->GetHost()->RunFileDialog(
Use Chrome file dialogs on all platforms and runtimes (fixes issue #3314) All file dialogs irrespective of source, platform and runtime will now be routed through CefFileDialogManager and trigger CefDialogHandler callbacks (see issue #3293). Adds Chrome runtime support for CefBrowserHost::RunFileDialog and CefDialogHandler callbacks. Adds Alloy runtime support for internal GTK file and print dialogs on Linux subject to the following limitations: 1. Internal GTK implementation: - Cannot be used with multi-threaded-message-loop because Chromium's internal GTK implementation is not thread-safe (does not use GDK threads). - Dialogs will not be modal to application windows when used with off-screen rendering due to lack of access to the client's top-level GtkWindow. 2. Cefclient CefDialogHandler implementation: - Cannot be used with Views because it requires a top-level GtkWindow. Due to the above limitations no dialog implementation is currently provided for Views + multi-threaded-message-loop on Linux. In cases where both implementations are supported the cefclient version is now behind an optional `--use-client-dialogs` command-line flag. Expressly forbids multiple simultaneous file dialogs with the internal platform implementation which uses modal dialogs. CefDialogHandler will still be notified and can optionally handle each request without a modal dialog (see issue #3154). Removes some RunFileDialog parameters that are not supported by the Chrome file dialog implementation (selected_accept_filter parameter, cef_file_dialog_mode_t overwrite/read-only flags).
2022-04-15 21:55:23 +02:00
FILE_DIALOG_SAVE,
/*title=*/CefString(), path,
/*accept_filters=*/std::vector<CefString>(), this);
}
void OnFileDialogDismissed(
const std::vector<CefString>& file_paths) override {
if (!file_paths.empty()) {
// File selected. Results in a call to OnEndTracingComplete.
CefEndTracing(file_paths.front(), this);
} else {
// No file selected. Discard the trace data.
CefEndTracing(CefString(), nullptr);
}
}
void OnEndTracingComplete(const CefString& tracing_file) override {
Alert(browser_,
"File \"" + tracing_file.ToString() + "\" saved successfully.");
}
private:
CefRefPtr<CefBrowser> browser_;
IMPLEMENT_REFCOUNTING(Client);
};
new Client(browser);
}
void PrintToPDF(CefRefPtr<CefBrowser> browser) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the UI thread.
CefPostTask(TID_UI, base::BindOnce(&PrintToPDF, browser));
return;
}
class Client : public CefPdfPrintCallback, public CefRunFileDialogCallback {
public:
explicit Client(CefRefPtr<CefBrowser> browser) : browser_(browser) {
RunDialog();
}
void RunDialog() {
static const char kDefaultFileName[] = "output.pdf";
std::string path = MainContext::Get()->GetDownloadPath(kDefaultFileName);
2023-01-02 23:59:03 +01:00
if (path.empty()) {
path = kDefaultFileName;
2023-01-02 23:59:03 +01:00
}
std::vector<CefString> accept_filters;
accept_filters.push_back(".pdf");
// Results in a call to OnFileDialogDismissed.
Use Chrome file dialogs on all platforms and runtimes (fixes issue #3314) All file dialogs irrespective of source, platform and runtime will now be routed through CefFileDialogManager and trigger CefDialogHandler callbacks (see issue #3293). Adds Chrome runtime support for CefBrowserHost::RunFileDialog and CefDialogHandler callbacks. Adds Alloy runtime support for internal GTK file and print dialogs on Linux subject to the following limitations: 1. Internal GTK implementation: - Cannot be used with multi-threaded-message-loop because Chromium's internal GTK implementation is not thread-safe (does not use GDK threads). - Dialogs will not be modal to application windows when used with off-screen rendering due to lack of access to the client's top-level GtkWindow. 2. Cefclient CefDialogHandler implementation: - Cannot be used with Views because it requires a top-level GtkWindow. Due to the above limitations no dialog implementation is currently provided for Views + multi-threaded-message-loop on Linux. In cases where both implementations are supported the cefclient version is now behind an optional `--use-client-dialogs` command-line flag. Expressly forbids multiple simultaneous file dialogs with the internal platform implementation which uses modal dialogs. CefDialogHandler will still be notified and can optionally handle each request without a modal dialog (see issue #3154). Removes some RunFileDialog parameters that are not supported by the Chrome file dialog implementation (selected_accept_filter parameter, cef_file_dialog_mode_t overwrite/read-only flags).
2022-04-15 21:55:23 +02:00
browser_->GetHost()->RunFileDialog(FILE_DIALOG_SAVE,
/*title=*/CefString(), path,
accept_filters, this);
}
void OnFileDialogDismissed(
const std::vector<CefString>& file_paths) override {
if (!file_paths.empty()) {
CefPdfPrintSettings settings;
// Display a header and footer.
settings.display_header_footer = true;
CefString(&settings.header_template) =
"<div style=\"width: 100%; font-size: 9px; position: relative;\">"
"<div style=\"position: absolute; left: 5px;\">"
"<span class=\"title\"></span></div>"
"</div>";
CefString(&settings.footer_template) =
"<div style=\"width: 100%; font-size: 9px; position: relative;\">"
"<div style=\"position: absolute; left: 5px;\">"
"<span class=\"date\"></span></div>"
"<div style=\"position: absolute; right: 5px;\">"
"<span class=\"pageNumber\"></span>/"
"<span class=\"totalPages\"></span></div>"
"</div>";
// Print to the selected PDF file.
browser_->GetHost()->PrintToPDF(file_paths[0], settings, this);
}
}
void OnPdfPrintFinished(const CefString& path, bool ok) override {
Alert(browser_, "File \"" + path.ToString() + "\" " +
(ok ? "saved successfully." : "failed to save."));
}
private:
CefRefPtr<CefBrowser> browser_;
IMPLEMENT_REFCOUNTING(Client);
};
new Client(browser);
}
void MuteAudio(CefRefPtr<CefBrowser> browser, bool mute) {
CefRefPtr<CefBrowserHost> host = browser->GetHost();
host->SetAudioMuted(mute);
}
void RunOtherTests(CefRefPtr<CefBrowser> browser) {
browser->GetMainFrame()->LoadURL(GetTestURL("other_tests"));
}
// Provider that dumps the request contents.
class RequestDumpResourceProvider : public CefResourceManager::Provider {
public:
explicit RequestDumpResourceProvider(const std::string& url) : url_(url) {
DCHECK(!url.empty());
}
bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
CEF_REQUIRE_IO_THREAD();
const std::string& url = request->url();
if (url != url_) {
// Not handled by this provider.
return false;
}
CefResponse::HeaderMap response_headers;
CefRefPtr<CefStreamReader> response =
GetDumpResponse(request->request(), response_headers);
request->Continue(new CefStreamResourceHandler(200, "OK", "text/html",
response_headers, response));
return true;
}
private:
std::string url_;
DISALLOW_COPY_AND_ASSIGN(RequestDumpResourceProvider);
};
// Provider that returns string data for specific pages. Used in combination
// with LoadStringResourcePage().
class StringResourceProvider : public CefResourceManager::Provider {
public:
StringResourceProvider(const std::set<std::string>& pages,
StringResourceMap* string_resource_map)
: pages_(pages), string_resource_map_(string_resource_map) {
DCHECK(!pages.empty());
}
bool OnRequest(scoped_refptr<CefResourceManager::Request> request) override {
CEF_REQUIRE_IO_THREAD();
const std::string& url = request->url();
if (url.find(kTestOrigin) != 0U) {
// Not handled by this provider.
return false;
}
const std::string& page = url.substr(strlen(kTestOrigin));
if (pages_.find(page) == pages_.end()) {
// Not handled by this provider.
return false;
}
std::string value;
StringResourceMap::const_iterator it = string_resource_map_->find(page);
if (it != string_resource_map_->end()) {
value = it->second;
} else {
value = "<html><body>No data available</body></html>";
}
CefRefPtr<CefStreamReader> response = CefStreamReader::CreateForData(
static_cast<void*>(const_cast<char*>(value.c_str())), value.size());
request->Continue(new CefStreamResourceHandler(
200, "OK", "text/html", CefResponse::HeaderMap(), response));
return true;
}
private:
const std::set<std::string> pages_;
// Only accessed on the IO thread.
StringResourceMap* string_resource_map_;
DISALLOW_COPY_AND_ASSIGN(StringResourceProvider);
};
// Add a file extension to |url| if none is currently specified.
std::string RequestUrlFilter(const std::string& url) {
if (url.find(kTestOrigin) != 0U) {
// Don't filter anything outside of the test origin.
return url;
}
// Identify where the query or fragment component, if any, begins.
size_t suffix_pos = url.find('?');
2023-01-02 23:59:03 +01:00
if (suffix_pos == std::string::npos) {
suffix_pos = url.find('#');
2023-01-02 23:59:03 +01:00
}
std::string url_base, url_suffix;
if (suffix_pos == std::string::npos) {
url_base = url;
} else {
url_base = url.substr(0, suffix_pos);
url_suffix = url.substr(suffix_pos);
}
// Identify the last path component.
size_t path_pos = url_base.rfind('/');
2023-01-02 23:59:03 +01:00
if (path_pos == std::string::npos) {
return url;
2023-01-02 23:59:03 +01:00
}
const std::string& path_component = url_base.substr(path_pos);
// Identify if a file extension is currently specified.
size_t ext_pos = path_component.rfind(".");
2023-01-02 23:59:03 +01:00
if (ext_pos != std::string::npos) {
return url;
2023-01-02 23:59:03 +01:00
}
// Rebuild the URL with a file extension.
return url_base + ".html" + url_suffix;
}
} // namespace
void RunTest(CefRefPtr<CefBrowser> browser, int id) {
2023-01-02 23:59:03 +01:00
if (!browser) {
return;
2023-01-02 23:59:03 +01:00
}
switch (id) {
case ID_TESTS_GETSOURCE:
RunGetSourceTest(browser);
break;
case ID_TESTS_GETTEXT:
RunGetTextTest(browser);
break;
Add support for complete isolation of storage and permissions (cache, cookies, localStorage, access grants, etc) on a per-request-context basis (issue #1044). - CefRequestContext instances can be configured using a new CefRequestContextSettings structure passed to CefRequestContext::CreateContext. - Scheme registration is now per-request-context using new CefRequestContext::RegisterSchemeHandlerFactory and ClearSchemeHandlerFactories methods. - Cookie managers are now per-request-context by default and can be retrieved using a new CefRequestContext::GetDefaultCookieManager method. - CefURLRequest::Create now accepts an optional CefRequestContext argument for associating a URL request with a context (browser process only). - The CefRequestContextHandler associated with a CefRequestContext will not be released until all objects related to that context have been destroyed. - When the cache path is empty an in-memory cache ("incognito mode") will be used for storage and no data will be persisted to disk. - Add CefSettings.user_data_path which specifies the location where user data such as spell checking dictionary files will be stored on disk. - Add asynchronous callbacks for all CefCookieManager methods. - Add PK_LOCAL_APP_DATA and PK_USER_DATA path keys for retrieving user directories via CefGetPath. - cefclient: Add "New Window" test that creates a new window unrelated to existing windows. When used in combination with `--request-context-per-browser` the new window will be given a new and isolated request context. git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@2040 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
2015-03-02 21:25:14 +01:00
case ID_TESTS_WINDOW_NEW:
RunNewWindowTest(browser);
break;
case ID_TESTS_WINDOW_POPUP:
RunPopupWindowTest(browser);
break;
views: Add support for modal browser dialogs A modal dialog is a child CefWindow that implements some special behaviors relative to a parent CefWindow. Like any CefWindow it can be framed with titlebar or frameless, and optionally contain draggable regions (subject to platform limitations described below). Modal dialogs are shown centered on the parent window (inside a single display) and always stay on top of the parent window in z-order. Sizing behavior and available window buttons are controlled via the usual CefWindowDelegate callbacks. For example, the dialog can have a preferred size with resize, minimize and maximize disabled (via GetPreferredSize, CanResize, CanMinimize and CanMaximize respectively). This change adds support for two modality modes. With window modality all controls in the parent window are disabled. With browser modality only the browser view in the parent window is disabled. Both modality modes require that a valid parent window be returned via GetParentWindow. For window modality return true from IsWindowModalDialog and call CefWindow::Show. For browser modality return false from IsWindowModalDialog (the default value) and call CefWindow::ShowAsBrowserModalDialog with a reference to the parent window's browser view. Window modal dialog behavior depends on the platform. On Windows and Linux these dialogs have a titlebar and can be moved independent of the parent window. On macOS these dialogs do not have a titlebar, move with the parent window, and do not support draggable regions (because they are implemented using sheets). On Linux disabling the parent window controls requires a window manager the supports _NET_WM_STATE_MODAL. Browser modal dialog behavior is similar on all platforms. The dialog will be automatically sized and positioned relative to the parent window's browser view. Closing the parent window or navigating the parent browser view will dismiss the dialog. The dialog can also be moved independent of the parent window though it will be recentered when the parent window itself is resized or redisplayed. On MacOS the dialog will move along with the parent window while on Windows and Linux the parent window can be moved independently. To test: Use the Tests > Dialog Window menu option in cefclient with Views enabled (`--use-views` or `--enable-chrome-runtime` command-line flag). Browser modal dialog is the default behavior. For window modal dialog add the `--use-window-modal-dialog` command-line flag.
2023-06-14 10:20:02 +02:00
case ID_TESTS_WINDOW_DIALOG:
RunDialogWindowTest(browser);
break;
case ID_TESTS_REQUEST:
RunRequestTest(browser);
break;
case ID_TESTS_ZOOM_IN:
browser->GetHost()->Zoom(CEF_ZOOM_COMMAND_IN);
break;
case ID_TESTS_ZOOM_OUT:
browser->GetHost()->Zoom(CEF_ZOOM_COMMAND_OUT);
break;
case ID_TESTS_ZOOM_RESET:
browser->GetHost()->Zoom(CEF_ZOOM_COMMAND_RESET);
break;
case ID_TESTS_OSR_FPS:
PromptFPS(browser);
break;
case ID_TESTS_OSR_DSF:
PromptDSF(browser);
break;
case ID_TESTS_TRACING_BEGIN:
BeginTracing();
break;
case ID_TESTS_TRACING_END:
EndTracing(browser);
break;
case ID_TESTS_PRINT:
browser->GetHost()->Print();
break;
case ID_TESTS_PRINT_TO_PDF:
PrintToPDF(browser);
break;
case ID_TESTS_MUTE_AUDIO:
MuteAudio(browser, true);
break;
case ID_TESTS_UNMUTE_AUDIO:
MuteAudio(browser, false);
break;
case ID_TESTS_OTHER_TESTS:
RunOtherTests(browser);
break;
case ID_TESTS_DUMP_WITHOUT_CRASHING:
CefDumpWithoutCrashing();
break;
}
}
std::string DumpRequestContents(CefRefPtr<CefRequest> request) {
std::stringstream ss;
ss << "URL: " << std::string(request->GetURL());
ss << "\nMethod: " << std::string(request->GetMethod());
CefRequest::HeaderMap headerMap;
request->GetHeaderMap(headerMap);
if (headerMap.size() > 0) {
ss << "\nHeaders:";
CefRequest::HeaderMap::const_iterator it = headerMap.begin();
for (; it != headerMap.end(); ++it) {
ss << "\n\t" << std::string((*it).first) << ": "
<< std::string((*it).second);
}
}
CefRefPtr<CefPostData> postData = request->GetPostData();
if (postData.get()) {
CefPostData::ElementVector elements;
postData->GetElements(elements);
if (elements.size() > 0) {
ss << "\nPost Data:";
CefRefPtr<CefPostDataElement> element;
CefPostData::ElementVector::const_iterator it = elements.begin();
for (; it != elements.end(); ++it) {
element = (*it);
if (element->GetType() == PDE_TYPE_BYTES) {
// the element is composed of bytes
ss << "\n\tBytes: ";
if (element->GetBytesCount() == 0) {
ss << "(empty)";
} else {
// retrieve the data.
size_t size = element->GetBytesCount();
char* bytes = new char[size];
element->GetBytes(size, bytes);
ss << std::string(bytes, size);
delete[] bytes;
}
} else if (element->GetType() == PDE_TYPE_FILE) {
ss << "\n\tFile: " << std::string(element->GetFile());
}
}
}
}
return ss.str();
}
CefRefPtr<CefStreamReader> GetDumpResponse(
CefRefPtr<CefRequest> request,
CefResponse::HeaderMap& response_headers) {
std::string origin;
// Extract the origin request header, if any. It will be specified for
// cross-origin requests.
{
CefRequest::HeaderMap requestMap;
request->GetHeaderMap(requestMap);
CefRequest::HeaderMap::const_iterator it = requestMap.begin();
for (; it != requestMap.end(); ++it) {
const std::string& key = AsciiStrToLower(it->first);
if (key == "origin") {
origin = it->second;
break;
}
}
}
if (!origin.empty() &&
(origin.find("https://" + std::string(kTestHost)) == 0 ||
origin.find("http://" + std::string(kLocalHost)) == 0)) {
// Allow cross-origin XMLHttpRequests from test origins.
response_headers.insert(
std::make_pair("Access-Control-Allow-Origin", origin));
// Allow the custom header from the xmlhttprequest.html example.
response_headers.insert(
std::make_pair("Access-Control-Allow-Headers", "My-Custom-Header"));
}
const std::string& dump = DumpRequestContents(request);
std::string str =
"<html><body bgcolor=\"white\"><pre>" + dump + "</pre></body></html>";
CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
static_cast<void*>(const_cast<char*>(str.c_str())), str.size());
DCHECK(stream);
return stream;
}
std::string GetDataURI(const std::string& data, const std::string& mime_type) {
return "data:" + mime_type + ";base64," +
CefURIEncode(CefBase64Encode(data.data(), data.size()), false)
.ToString();
}
std::string GetErrorString(cef_errorcode_t code) {
// Case condition that returns |code| as a string.
#define CASE(code) \
case code: \
return #code
switch (code) {
CASE(ERR_NONE);
CASE(ERR_FAILED);
CASE(ERR_ABORTED);
CASE(ERR_INVALID_ARGUMENT);
CASE(ERR_INVALID_HANDLE);
CASE(ERR_FILE_NOT_FOUND);
CASE(ERR_TIMED_OUT);
CASE(ERR_FILE_TOO_BIG);
CASE(ERR_UNEXPECTED);
CASE(ERR_ACCESS_DENIED);
CASE(ERR_NOT_IMPLEMENTED);
CASE(ERR_CONNECTION_CLOSED);
CASE(ERR_CONNECTION_RESET);
CASE(ERR_CONNECTION_REFUSED);
CASE(ERR_CONNECTION_ABORTED);
CASE(ERR_CONNECTION_FAILED);
CASE(ERR_NAME_NOT_RESOLVED);
CASE(ERR_INTERNET_DISCONNECTED);
CASE(ERR_SSL_PROTOCOL_ERROR);
CASE(ERR_ADDRESS_INVALID);
CASE(ERR_ADDRESS_UNREACHABLE);
CASE(ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
CASE(ERR_TUNNEL_CONNECTION_FAILED);
CASE(ERR_NO_SSL_VERSIONS_ENABLED);
CASE(ERR_SSL_VERSION_OR_CIPHER_MISMATCH);
CASE(ERR_SSL_RENEGOTIATION_REQUESTED);
CASE(ERR_CERT_COMMON_NAME_INVALID);
CASE(ERR_CERT_DATE_INVALID);
CASE(ERR_CERT_AUTHORITY_INVALID);
CASE(ERR_CERT_CONTAINS_ERRORS);
CASE(ERR_CERT_NO_REVOCATION_MECHANISM);
CASE(ERR_CERT_UNABLE_TO_CHECK_REVOCATION);
CASE(ERR_CERT_REVOKED);
CASE(ERR_CERT_INVALID);
CASE(ERR_CERT_END);
CASE(ERR_INVALID_URL);
CASE(ERR_DISALLOWED_URL_SCHEME);
CASE(ERR_UNKNOWN_URL_SCHEME);
CASE(ERR_TOO_MANY_REDIRECTS);
CASE(ERR_UNSAFE_REDIRECT);
CASE(ERR_UNSAFE_PORT);
CASE(ERR_INVALID_RESPONSE);
CASE(ERR_INVALID_CHUNKED_ENCODING);
CASE(ERR_METHOD_NOT_SUPPORTED);
CASE(ERR_UNEXPECTED_PROXY_AUTH);
CASE(ERR_EMPTY_RESPONSE);
CASE(ERR_RESPONSE_HEADERS_TOO_BIG);
CASE(ERR_CACHE_MISS);
CASE(ERR_INSECURE_RESPONSE);
default:
return std::to_string(static_cast<int>(code));
}
}
std::string GetErrorString(cef_termination_status_t status) {
switch (status) {
case TS_ABNORMAL_TERMINATION:
return "ABNORMAL_TERMINATION";
case TS_PROCESS_WAS_KILLED:
return "PROCESS_WAS_KILLED";
case TS_PROCESS_CRASHED:
return "PROCESS_CRASHED";
case TS_PROCESS_OOM:
return "PROCESS_OOM";
case TS_LAUNCH_FAILED:
return "LAUNCH_FAILED";
case TS_INTEGRITY_FAILURE:
return "INTEGRITY_FAILURE";
}
NOTREACHED();
return std::string();
}
void SetupResourceManager(CefRefPtr<CefResourceManager> resource_manager,
StringResourceMap* string_resource_map) {
if (!CefCurrentlyOn(TID_IO)) {
// Execute on the browser IO thread.
CefPostTask(TID_IO, base::BindOnce(SetupResourceManager, resource_manager,
string_resource_map));
return;
}
const std::string& test_origin = kTestOrigin;
// Add the URL filter.
resource_manager->SetUrlFilter(base::BindRepeating(RequestUrlFilter));
// Add provider for resource dumps.
resource_manager->AddProvider(
new RequestDumpResourceProvider(test_origin + "request.html"), 0,
std::string());
// Set of supported string pages.
std::set<std::string> string_pages;
string_pages.insert(kTestGetSourcePage);
string_pages.insert(kTestGetTextPage);
if (string_resource_map) {
// Add provider for string resources.
resource_manager->AddProvider(
new StringResourceProvider(string_pages, string_resource_map), 0,
std::string());
}
// Add provider for bundled resource files.
#if defined(OS_WIN)
// Read resources from the binary.
resource_manager->AddProvider(
CreateBinaryResourceProvider(test_origin, std::string()), 100,
std::string());
#elif defined(OS_POSIX)
// Read resources from a directory on disk.
std::string resource_dir;
if (GetResourceDir(resource_dir)) {
resource_manager->AddDirectoryProvider(test_origin, resource_dir, 100,
std::string());
}
#endif
}
void Alert(CefRefPtr<CefBrowser> browser, const std::string& message) {
// Escape special characters in the message.
std::string msg = AsciiStrReplace(message, "\\", "\\\\");
msg = AsciiStrReplace(msg, "'", "\\'");
// Execute a JavaScript alert().
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('" + msg + "');", frame->GetURL(), 0);
}
std::string GetTestURL(const std::string& path) {
return kTestOrigin + path;
}
bool IsTestURL(const std::string& url, const std::string& path) {
CefURLParts parts;
CefParseURL(url, parts);
const std::string& url_host = CefString(&parts.host);
2023-01-02 23:59:03 +01:00
if (url_host != kTestHost && url_host != kLocalHost) {
return false;
2023-01-02 23:59:03 +01:00
}
const std::string& url_path = CefString(&parts.path);
return url_path.find(path) == 0;
}
void CreateMessageHandlers(MessageHandlerSet& handlers) {
handlers.insert(new PromptHandler);
// Create the binary trasfer test handlers.
binary_transfer_test::CreateMessageHandlers(handlers);
// Create the binding test handlers.
binding_test::CreateMessageHandlers(handlers);
// Create the dialog test handlers.
dialog_test::CreateMessageHandlers(handlers);
// Create the hang test handlers.
hang_test::CreateMessageHandlers(handlers);
// Create the media router test handlers.
media_router_test::CreateMessageHandlers(handlers);
// Create the preferences test handlers.
preferences_test::CreateMessageHandlers(handlers);
// Create the server test handlers.
server_test::CreateMessageHandlers(handlers);
// Create the task manager handlers.
task_manager_test::CreateMessageHandlers(handlers);
// Create the urlrequest test handlers.
urlrequest_test::CreateMessageHandlers(handlers);
// Create the window test handlers.
window_test::CreateMessageHandlers(handlers);
}
void RegisterSchemeHandlers() {
// Register the scheme handler.
scheme_test::RegisterSchemeHandlers();
}
CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) {
// Create the response filter.
return response_filter_test::GetResourceResponseFilter(browser, frame,
request, response);
}
} // namespace client::test_runner