chrome: Add support for Alloy style browsers and windows (see #3681)

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]`
This commit is contained in:
Marshall Greenblatt
2024-04-17 12:01:26 -04:00
parent 62c93f01f4
commit dca0435d2f
216 changed files with 3388 additions and 1565 deletions

View File

@@ -1,207 +0,0 @@
// Copyright 2019 The Chromium Embedded Framework Authors. Portions copyright
// 2013 The Chromium 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 "libcef/browser/devtools/devtools_file_manager.h"
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/hash/md5.h"
#include "base/json/json_writer.h"
#include "base/json/values_util.h"
#include "base/lazy_instance.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/web_contents.h"
namespace {
base::LazyInstance<base::FilePath>::Leaky g_last_save_path =
LAZY_INSTANCE_INITIALIZER;
void WriteToFile(const base::FilePath& path, const std::string& content) {
DCHECK(!path.empty());
base::WriteFile(path, content.c_str(), content.length());
}
void AppendToFile(const base::FilePath& path, const std::string& content) {
DCHECK(!path.empty());
base::AppendToFile(path, base::StringPiece(content));
}
} // namespace
CefDevToolsFileManager::CefDevToolsFileManager(
AlloyBrowserHostImpl* browser_impl,
PrefService* prefs)
: browser_impl_(browser_impl),
prefs_(prefs),
file_task_runner_(
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})),
weak_factory_(this) {}
void CefDevToolsFileManager::SaveToFile(const std::string& url,
const std::string& content,
bool save_as) {
Save(url, content, save_as,
base::BindOnce(&CefDevToolsFileManager::FileSavedAs,
weak_factory_.GetWeakPtr(), url),
base::BindOnce(&CefDevToolsFileManager::CanceledFileSaveAs,
weak_factory_.GetWeakPtr(), url));
}
void CefDevToolsFileManager::AppendToFile(const std::string& url,
const std::string& content) {
Append(url, content,
base::BindOnce(&CefDevToolsFileManager::AppendedTo,
weak_factory_.GetWeakPtr(), url));
}
void CefDevToolsFileManager::Save(const std::string& url,
const std::string& content,
bool save_as,
SaveCallback saveCallback,
CancelCallback cancelCallback) {
auto it = saved_files_.find(url);
if (it != saved_files_.end() && !save_as) {
SaveAsFileSelected(url, content, std::move(saveCallback), it->second);
return;
}
const base::Value::Dict& file_map =
prefs_->GetDict(prefs::kDevToolsEditedFiles);
base::FilePath initial_path;
if (const base::Value* path_value = file_map.Find(base::MD5String(url))) {
std::optional<base::FilePath> path = base::ValueToFilePath(*path_value);
if (path) {
initial_path = std::move(*path);
}
}
if (initial_path.empty()) {
GURL gurl(url);
std::string suggested_file_name =
gurl.is_valid() ? gurl.ExtractFileName() : url;
if (suggested_file_name.length() > 64) {
suggested_file_name = suggested_file_name.substr(0, 64);
}
if (!g_last_save_path.Pointer()->empty()) {
initial_path = g_last_save_path.Pointer()->DirName().AppendASCII(
suggested_file_name);
} else {
// Use the temp directory. It may be an empty value.
base::PathService::Get(base::DIR_TEMP, &initial_path);
initial_path = initial_path.AppendASCII(suggested_file_name);
}
}
blink::mojom::FileChooserParams params;
params.mode = blink::mojom::FileChooserParams::Mode::kSave;
if (!initial_path.empty()) {
params.default_file_name = initial_path;
if (!initial_path.Extension().empty()) {
params.accept_types.push_back(CefString(initial_path.Extension()));
}
}
browser_impl_->RunFileChooserForBrowser(
params,
base::BindOnce(&CefDevToolsFileManager::SaveAsDialogDismissed,
weak_factory_.GetWeakPtr(), url, content,
std::move(saveCallback), std::move(cancelCallback)));
}
void CefDevToolsFileManager::SaveAsDialogDismissed(
const std::string& url,
const std::string& content,
SaveCallback saveCallback,
CancelCallback cancelCallback,
const std::vector<base::FilePath>& file_paths) {
if (file_paths.size() == 1) {
SaveAsFileSelected(url, content, std::move(saveCallback), file_paths[0]);
} else {
std::move(cancelCallback).Run();
}
}
void CefDevToolsFileManager::SaveAsFileSelected(const std::string& url,
const std::string& content,
SaveCallback callback,
const base::FilePath& path) {
*g_last_save_path.Pointer() = path;
saved_files_[url] = path;
ScopedDictPrefUpdate update(prefs_, prefs::kDevToolsEditedFiles);
update->Set(base::MD5String(url), base::FilePathToValue(path));
std::string file_system_path = path.AsUTF8Unsafe();
std::move(callback).Run(file_system_path);
file_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&::WriteToFile, path, content));
}
void CefDevToolsFileManager::FileSavedAs(const std::string& url,
const std::string& file_system_path) {
base::Value url_value(url);
base::Value file_system_path_value(file_system_path);
CallClientFunction("DevToolsAPI.savedURL", &url_value,
&file_system_path_value, nullptr);
}
void CefDevToolsFileManager::CanceledFileSaveAs(const std::string& url) {
base::Value url_value(url);
CallClientFunction("DevToolsAPI.canceledSaveURL", &url_value, nullptr,
nullptr);
}
void CefDevToolsFileManager::Append(const std::string& url,
const std::string& content,
AppendCallback callback) {
auto it = saved_files_.find(url);
if (it == saved_files_.end()) {
return;
}
std::move(callback).Run();
file_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&::AppendToFile, it->second, content));
}
void CefDevToolsFileManager::AppendedTo(const std::string& url) {
base::Value url_value(url);
CallClientFunction("DevToolsAPI.appendedToURL", &url_value, nullptr, nullptr);
}
void CefDevToolsFileManager::CallClientFunction(
const std::string& function_name,
const base::Value* arg1,
const base::Value* arg2,
const base::Value* arg3) {
std::string javascript = function_name + "(";
if (arg1) {
std::string json;
base::JSONWriter::Write(*arg1, &json);
javascript.append(json);
if (arg2) {
base::JSONWriter::Write(*arg2, &json);
javascript.append(", ").append(json);
if (arg3) {
base::JSONWriter::Write(*arg3, &json);
javascript.append(", ").append(json);
}
}
}
javascript.append(");");
browser_impl_->web_contents()->GetPrimaryMainFrame()->ExecuteJavaScript(
base::UTF8ToUTF16(javascript), base::NullCallback());
}

View File

@@ -1,82 +0,0 @@
// Copyright 2019 The Chromium Embedded Framework Authors. Portions copyright
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FILE_MANAGER_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FILE_MANAGER_H_
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include <map>
#include <string>
namespace base {
class FilePath;
class SequencedTaskRunner;
class Value;
} // namespace base
class AlloyBrowserHostImpl;
class PrefService;
// File management helper for DevTools.
// Based on chrome/browser/devtools/devtools_ui_bindings.cc and
// chrome/browser/devtools/devtools_file_helper.cc.
class CefDevToolsFileManager {
public:
CefDevToolsFileManager(AlloyBrowserHostImpl* browser_impl,
PrefService* prefs);
CefDevToolsFileManager(const CefDevToolsFileManager&) = delete;
CefDevToolsFileManager& operator=(const CefDevToolsFileManager&) = delete;
void SaveToFile(const std::string& url,
const std::string& content,
bool save_as);
void AppendToFile(const std::string& url, const std::string& content);
private:
// SaveToFile implementation:
using SaveCallback = base::OnceCallback<void(const std::string&)>;
using CancelCallback = base::OnceCallback<void()>;
void Save(const std::string& url,
const std::string& content,
bool save_as,
SaveCallback saveCallback,
CancelCallback cancelCallback);
void SaveAsDialogDismissed(const std::string& url,
const std::string& content,
SaveCallback saveCallback,
CancelCallback cancelCallback,
const std::vector<base::FilePath>& file_paths);
void SaveAsFileSelected(const std::string& url,
const std::string& content,
SaveCallback callback,
const base::FilePath& path);
void FileSavedAs(const std::string& url, const std::string& file_system_path);
void CanceledFileSaveAs(const std::string& url);
// AppendToFile implementation:
using AppendCallback = base::OnceCallback<void(void)>;
void Append(const std::string& url,
const std::string& content,
AppendCallback callback);
void AppendedTo(const std::string& url);
void CallClientFunction(const std::string& function_name,
const base::Value* arg1,
const base::Value* arg2,
const base::Value* arg3);
// Guaranteed to outlive this object.
AlloyBrowserHostImpl* browser_impl_;
PrefService* prefs_;
using PathsMap = std::map<std::string, base::FilePath>;
PathsMap saved_files_;
scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
base::WeakPtrFactory<CefDevToolsFileManager> weak_factory_;
};
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FILE_MANAGER_H_

View File

@@ -1,665 +0,0 @@
// Copyright 2013 The Chromium 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 "libcef/browser/devtools/devtools_frontend.h"
#include <stddef.h>
#include <iomanip>
#include <memory>
#include <utility>
#include "libcef/browser/browser_context.h"
#include "libcef/browser/devtools/devtools_manager_delegate.h"
#include "libcef/browser/net/devtools_scheme_handler.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/cef_switches.h"
#include "libcef/common/task_runner_manager.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/json/string_escape.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/uuid.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/file_url_loader.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "ipc/ipc_channel.h"
#include "net/base/completion_once_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "storage/browser/file_system/native_file_util.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#elif BUILDFLAG(IS_POSIX)
#include <time.h>
#endif
namespace {
// This constant should be in sync with the constant in
// chrome/browser/devtools/devtools_ui_bindings.cc.
constexpr size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
constexpr int kMaxLogLineLength = 1024;
static std::string GetFrontendURL() {
return base::StringPrintf("%s://%s/devtools_app.html",
content::kChromeDevToolsScheme,
scheme::kChromeDevToolsHost);
}
base::Value::Dict BuildObjectForResponse(const net::HttpResponseHeaders* rh,
bool success,
int net_error) {
base::Value::Dict response;
int responseCode = 200;
if (rh) {
responseCode = rh->response_code();
} else if (!success) {
// In case of no headers, assume file:// URL and failed to load
responseCode = 404;
}
response.Set("statusCode", responseCode);
response.Set("netError", net_error);
response.Set("netErrorName", net::ErrorToString(net_error));
base::Value::Dict headers;
size_t iterator = 0;
std::string name;
std::string value;
// TODO(caseq): this probably needs to handle duplicate header names
// correctly by folding them.
while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value)) {
headers.Set(name, value);
}
response.Set("headers", std::move(headers));
return response;
}
void WriteTimestamp(std::stringstream& stream) {
#if BUILDFLAG(IS_WIN)
SYSTEMTIME local_time;
GetLocalTime(&local_time);
stream << std::setfill('0') << std::setw(2) << local_time.wMonth
<< std::setw(2) << local_time.wDay << '/' << std::setw(2)
<< local_time.wHour << std::setw(2) << local_time.wMinute
<< std::setw(2) << local_time.wSecond << '.' << std::setw(3)
<< local_time.wMilliseconds;
#elif BUILDFLAG(IS_POSIX)
timeval tv;
gettimeofday(&tv, nullptr);
time_t t = tv.tv_sec;
struct tm local_time;
localtime_r(&t, &local_time);
struct tm* tm_time = &local_time;
stream << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
<< std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
<< tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
<< tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec;
#else
#error Unsupported platform
#endif
}
void LogProtocolMessage(const base::FilePath& log_file,
ProtocolMessageType type,
std::string to_log) {
// Track if logging has failed, in which case we don't keep trying.
static bool log_error = false;
if (log_error) {
return;
}
if (storage::NativeFileUtil::EnsureFileExists(log_file, nullptr) !=
base::File::FILE_OK) {
LOG(ERROR) << "Failed to create file " << log_file.value();
log_error = true;
return;
}
std::string type_label;
switch (type) {
case ProtocolMessageType::METHOD:
type_label = "METHOD";
break;
case ProtocolMessageType::RESULT:
type_label = "RESULT";
break;
case ProtocolMessageType::EVENT:
type_label = "EVENT";
break;
}
std::stringstream stream;
WriteTimestamp(stream);
stream << ": " << type_label << ": " << to_log << "\n";
const std::string& str = stream.str();
if (!base::AppendToFile(log_file, base::StringPiece(str))) {
LOG(ERROR) << "Failed to write file " << log_file.value();
log_error = true;
}
}
} // namespace
class CefDevToolsFrontend::NetworkResourceLoader
: public network::SimpleURLLoaderStreamConsumer {
public:
NetworkResourceLoader(int stream_id,
CefDevToolsFrontend* bindings,
std::unique_ptr<network::SimpleURLLoader> loader,
network::mojom::URLLoaderFactory* url_loader_factory,
int request_id)
: stream_id_(stream_id),
bindings_(bindings),
loader_(std::move(loader)),
request_id_(request_id) {
loader_->SetOnResponseStartedCallback(base::BindOnce(
&NetworkResourceLoader::OnResponseStarted, base::Unretained(this)));
loader_->DownloadAsStream(url_loader_factory, this);
}
NetworkResourceLoader(const NetworkResourceLoader&) = delete;
NetworkResourceLoader& operator=(const NetworkResourceLoader&) = delete;
private:
void OnResponseStarted(const GURL& final_url,
const network::mojom::URLResponseHead& response_head) {
response_headers_ = response_head.headers;
}
void OnDataReceived(base::StringPiece chunk,
base::OnceClosure resume) override {
base::Value chunkValue;
bool encoded = !base::IsStringUTF8(chunk);
if (encoded) {
chunkValue = base::Value(base::Base64Encode(chunk));
} else {
chunkValue = base::Value(chunk);
}
base::Value id(stream_id_);
base::Value encodedValue(encoded);
bindings_->CallClientFunction("DevToolsAPI", "streamWrite", std::move(id),
std::move(chunkValue),
std::move(encodedValue));
std::move(resume).Run();
}
void OnComplete(bool success) override {
auto response = BuildObjectForResponse(response_headers_.get(), success,
loader_->NetError());
bindings_->SendMessageAck(request_id_, std::move(response));
bindings_->loaders_.erase(bindings_->loaders_.find(this));
}
void OnRetry(base::OnceClosure start_retry) override { DCHECK(false); }
const int stream_id_;
CefDevToolsFrontend* const bindings_;
std::unique_ptr<network::SimpleURLLoader> loader_;
int request_id_;
scoped_refptr<net::HttpResponseHeaders> response_headers_;
};
// static
CefDevToolsFrontend* CefDevToolsFrontend::Show(
AlloyBrowserHostImpl* inspected_browser,
const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefBrowserSettings& settings,
const CefPoint& inspect_element_at,
base::OnceClosure frontend_destroyed_callback) {
CefBrowserSettings new_settings = settings;
if (!windowInfo.windowless_rendering_enabled &&
CefColorGetA(new_settings.background_color) != SK_AlphaOPAQUE) {
// Use white as the default background color for windowed DevTools instead
// of the CefSettings.background_color value.
new_settings.background_color = SK_ColorWHITE;
}
CefBrowserCreateParams create_params;
if (inspected_browser->is_views_hosted()) {
create_params.popup_with_views_hosted_opener = true;
} else {
create_params.window_info = std::make_unique<CefWindowInfo>(windowInfo);
}
create_params.client = client;
create_params.settings = new_settings;
create_params.devtools_opener = inspected_browser;
create_params.request_context = inspected_browser->GetRequestContext();
create_params.extra_info = inspected_browser->browser_info()->extra_info();
CefRefPtr<AlloyBrowserHostImpl> frontend_browser =
AlloyBrowserHostImpl::Create(create_params);
content::WebContents* inspected_contents = inspected_browser->web_contents();
// CefDevToolsFrontend will delete itself when the frontend WebContents is
// destroyed.
CefDevToolsFrontend* devtools_frontend = new CefDevToolsFrontend(
static_cast<AlloyBrowserHostImpl*>(frontend_browser.get()),
inspected_contents, inspect_element_at,
std::move(frontend_destroyed_callback));
// Need to load the URL after creating the DevTools objects.
frontend_browser->GetMainFrame()->LoadURL(GetFrontendURL());
return devtools_frontend;
}
void CefDevToolsFrontend::Activate() {
frontend_browser_->ActivateContents(web_contents());
}
void CefDevToolsFrontend::Focus() {
frontend_browser_->SetFocus(true);
}
void CefDevToolsFrontend::InspectElementAt(int x, int y) {
if (inspect_element_at_.x != x || inspect_element_at_.y != y) {
inspect_element_at_.Set(x, y);
}
if (agent_host_) {
agent_host_->InspectElement(inspected_contents_->GetFocusedFrame(), x, y);
}
}
void CefDevToolsFrontend::Close() {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&AlloyBrowserHostImpl::CloseBrowser,
frontend_browser_.get(), true));
}
CefDevToolsFrontend::CefDevToolsFrontend(
AlloyBrowserHostImpl* frontend_browser,
content::WebContents* inspected_contents,
const CefPoint& inspect_element_at,
base::OnceClosure frontend_destroyed_callback)
: content::WebContentsObserver(frontend_browser->web_contents()),
frontend_browser_(frontend_browser),
inspected_contents_(inspected_contents),
inspect_element_at_(inspect_element_at),
frontend_destroyed_callback_(std::move(frontend_destroyed_callback)),
file_manager_(frontend_browser, GetPrefs()),
protocol_log_file_(
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kDevToolsProtocolLogFile)),
weak_factory_(this) {
DCHECK(!frontend_destroyed_callback_.is_null());
}
CefDevToolsFrontend::~CefDevToolsFrontend() = default;
void CefDevToolsFrontend::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
content::RenderFrameHost* frame = navigation_handle->GetRenderFrameHost();
if (navigation_handle->IsInMainFrame()) {
frontend_host_ = content::DevToolsFrontendHost::Create(
frame, base::BindRepeating(
&CefDevToolsFrontend::HandleMessageFromDevToolsFrontend,
base::Unretained(this)));
return;
}
std::string origin =
navigation_handle->GetURL().DeprecatedGetOriginAsURL().spec();
auto it = extensions_api_.find(origin);
if (it == extensions_api_.end()) {
return;
}
std::string script = base::StringPrintf(
"%s(\"%s\")", it->second.c_str(),
base::Uuid::GenerateRandomV4().AsLowercaseString().c_str());
content::DevToolsFrontendHost::SetupExtensionsAPI(frame, script);
}
void CefDevToolsFrontend::PrimaryMainDocumentElementAvailable() {
// Don't call AttachClient multiple times for the same DevToolsAgentHost.
// Otherwise it will call AgentHostClosed which closes the DevTools window.
// This may happen in cases where the DevTools content fails to load.
scoped_refptr<content::DevToolsAgentHost> agent_host =
content::DevToolsAgentHost::GetOrCreateFor(inspected_contents_);
if (agent_host != agent_host_) {
if (agent_host_) {
agent_host_->DetachClient(this);
}
agent_host_ = agent_host;
agent_host_->AttachClient(this);
if (!inspect_element_at_.IsEmpty()) {
agent_host_->InspectElement(inspected_contents_->GetFocusedFrame(),
inspect_element_at_.x, inspect_element_at_.y);
}
}
}
void CefDevToolsFrontend::WebContentsDestroyed() {
if (agent_host_) {
agent_host_->DetachClient(this);
agent_host_ = nullptr;
}
std::move(frontend_destroyed_callback_).Run();
delete this;
}
void CefDevToolsFrontend::HandleMessageFromDevToolsFrontend(
base::Value::Dict message) {
const std::string* method = message.FindString("method");
if (!method) {
return;
}
int request_id = message.FindInt("id").value_or(0);
base::Value::List* params_value = message.FindList("params");
// Since we've received message by value, we can take the list.
base::Value::List params;
if (params_value) {
params = std::move(*params_value);
}
if (*method == "dispatchProtocolMessage") {
if (params.size() < 1) {
return;
}
const std::string* protocol_message = params[0].GetIfString();
if (!agent_host_ || !protocol_message) {
return;
}
if (ProtocolLoggingEnabled()) {
LogProtocolMessage(ProtocolMessageType::METHOD, *protocol_message);
}
agent_host_->DispatchProtocolMessage(
this, base::as_bytes(base::make_span(*protocol_message)));
} else if (*method == "loadCompleted") {
web_contents()->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"DevToolsAPI.setUseSoftMenu(true);", base::NullCallback());
} else if (*method == "loadNetworkResource") {
if (params.size() < 3) {
return;
}
// TODO(pfeldman): handle some of the embedder messages in content.
const std::string* url = params[0].GetIfString();
const std::string* headers = params[1].GetIfString();
std::optional<const int> stream_id = params[2].GetIfInt();
if (!url || !headers || !stream_id.has_value()) {
return;
}
GURL gurl(*url);
if (!gurl.is_valid()) {
base::Value::Dict response;
response.Set("statusCode", 404);
response.Set("urlValid", false);
SendMessageAck(request_id, std::move(response));
return;
}
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation(
"devtools_handle_front_end_messages", R"(
semantics {
sender: "Developer Tools"
description:
"When user opens Developer Tools, the browser may fetch "
"additional resources from the network to enrich the debugging "
"experience (e.g. source map resources)."
trigger: "User opens Developer Tools to debug a web page."
data: "Any resources requested by Developer Tools."
destination: OTHER
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting:
"It's not possible to disable this feature from settings."
chrome_policy {
DeveloperToolsAvailability {
policy_options {mode: MANDATORY}
DeveloperToolsAvailability: 2
}
}
})");
// Based on DevToolsUIBindings::LoadNetworkResource.
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = gurl;
// TODO(caseq): this preserves behavior of URLFetcher-based
// implementation. We really need to pass proper first party origin from
// the front-end.
resource_request->site_for_cookies = net::SiteForCookies::FromUrl(gurl);
resource_request->headers.AddHeadersFromString(*headers);
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
if (gurl.SchemeIsFile()) {
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote =
content::CreateFileURLLoaderFactory(
base::FilePath() /* profile_path */,
nullptr /* shared_cors_origin_access_list */);
url_loader_factory = network::SharedURLLoaderFactory::Create(
std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
std::move(pending_remote)));
} else if (content::HasWebUIScheme(gurl)) {
base::Value::Dict response;
response.Set("statusCode", 403);
SendMessageAck(request_id, std::move(response));
return;
} else {
auto* partition =
inspected_contents_->GetPrimaryMainFrame()->GetStoragePartition();
url_loader_factory = partition->GetURLLoaderFactoryForBrowserProcess();
}
auto simple_url_loader = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
auto resource_loader = std::make_unique<NetworkResourceLoader>(
*stream_id, this, std::move(simple_url_loader),
url_loader_factory.get(), request_id);
loaders_.insert(std::move(resource_loader));
return;
} else if (*method == "getPreferences") {
SendMessageAck(request_id,
GetPrefs()->GetDict(prefs::kDevToolsPreferences).Clone());
return;
} else if (*method == "setPreference") {
if (params.size() < 2) {
return;
}
const std::string* name = params[0].GetIfString();
// We're just setting params[1] as a value anyways, so just make sure it's
// the type we want, but don't worry about getting it.
if (!name || !params[1].is_string()) {
return;
}
ScopedDictPrefUpdate update(GetPrefs(), prefs::kDevToolsPreferences);
update->Set(*name, std::move(params[1]));
} else if (*method == "removePreference") {
const std::string* name = params[0].GetIfString();
if (!name) {
return;
}
ScopedDictPrefUpdate update(GetPrefs(), prefs::kDevToolsPreferences);
update->Remove(*name);
} else if (*method == "requestFileSystems") {
web_contents()->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"DevToolsAPI.fileSystemsLoaded([]);", base::NullCallback());
} else if (*method == "reattach") {
if (!agent_host_) {
return;
}
agent_host_->DetachClient(this);
agent_host_->AttachClient(this);
} else if (*method == "registerExtensionsAPI") {
if (params.size() < 2) {
return;
}
const std::string* origin = params[0].GetIfString();
const std::string* script = params[1].GetIfString();
if (!origin || !script) {
return;
}
extensions_api_[*origin + "/"] = *script;
} else if (*method == "save") {
if (params.size() < 3) {
return;
}
const std::string* url = params[0].GetIfString();
const std::string* content = params[1].GetIfString();
std::optional<bool> save_as = params[2].GetIfBool();
if (!url || !content || !save_as.has_value()) {
return;
}
file_manager_.SaveToFile(*url, *content, *save_as);
} else if (*method == "append") {
if (params.size() < 2) {
return;
}
const std::string* url = params[0].GetIfString();
const std::string* content = params[1].GetIfString();
if (!url || !content) {
return;
}
file_manager_.AppendToFile(*url, *content);
} else {
return;
}
if (request_id) {
SendMessageAck(request_id, base::Value::Dict());
}
}
void CefDevToolsFrontend::DispatchProtocolMessage(
content::DevToolsAgentHost* agent_host,
base::span<const uint8_t> message) {
if (!frontend_browser_->GetWebContents() ||
frontend_browser_->GetWebContents()->IsBeingDestroyed()) {
return;
}
base::StringPiece str_message(reinterpret_cast<const char*>(message.data()),
message.size());
if (ProtocolLoggingEnabled()) {
// Quick check to avoid parsing the JSON object. Events begin with a
// "method" value whereas method results begin with an "id" value.
LogProtocolMessage(base::StartsWith(str_message, "{\"method\":")
? ProtocolMessageType::EVENT
: ProtocolMessageType::RESULT,
str_message);
}
if (str_message.length() < kMaxMessageChunkSize) {
CallClientFunction("DevToolsAPI", "dispatchMessage",
base::Value(std::string(str_message)));
} else {
size_t total_size = str_message.length();
for (size_t pos = 0; pos < str_message.length();
pos += kMaxMessageChunkSize) {
base::StringPiece str_message_chunk =
str_message.substr(pos, kMaxMessageChunkSize);
CallClientFunction(
"DevToolsAPI", "dispatchMessageChunk",
base::Value(std::string(str_message_chunk)),
base::Value(base::NumberToString(pos ? 0 : total_size)));
}
}
}
void CefDevToolsFrontend::CallClientFunction(
const std::string& object_name,
const std::string& method_name,
base::Value arg1,
base::Value arg2,
base::Value arg3,
base::OnceCallback<void(base::Value)> cb) {
std::string javascript;
web_contents()->GetPrimaryMainFrame()->AllowInjectingJavaScript();
base::Value::List arguments;
if (!arg1.is_none()) {
arguments.Append(std::move(arg1));
if (!arg2.is_none()) {
arguments.Append(std::move(arg2));
if (!arg3.is_none()) {
arguments.Append(std::move(arg3));
}
}
}
web_contents()->GetPrimaryMainFrame()->ExecuteJavaScriptMethod(
base::ASCIIToUTF16(object_name), base::ASCIIToUTF16(method_name),
std::move(arguments), std::move(cb));
}
void CefDevToolsFrontend::SendMessageAck(int request_id,
base::Value::Dict arg) {
CallClientFunction("DevToolsAPI", "embedderMessageAck",
base::Value(request_id), base::Value(std::move(arg)));
}
bool CefDevToolsFrontend::ProtocolLoggingEnabled() const {
return !protocol_log_file_.empty();
}
void CefDevToolsFrontend::LogProtocolMessage(ProtocolMessageType type,
const base::StringPiece& message) {
DCHECK(ProtocolLoggingEnabled());
std::string to_log(message.substr(0, kMaxLogLineLength));
// Execute in an ordered context that allows blocking.
auto task_runner = CefTaskRunnerManager::Get()->GetBackgroundTaskRunner();
task_runner->PostTask(
FROM_HERE, base::BindOnce(::LogProtocolMessage, protocol_log_file_, type,
std::move(to_log)));
}
void CefDevToolsFrontend::AgentHostClosed(
content::DevToolsAgentHost* agent_host) {
DCHECK(agent_host == agent_host_.get());
agent_host_ = nullptr;
Close();
}
PrefService* CefDevToolsFrontend::GetPrefs() const {
return CefBrowserContext::FromBrowserContext(
frontend_browser_->web_contents()->GetBrowserContext())
->AsProfile()
->GetPrefs();
}

View File

@@ -1,114 +0,0 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_H_
#include <memory>
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "libcef/browser/devtools/devtools_file_manager.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_frontend_host.h"
#include "content/public/browser/web_contents_observer.h"
namespace base {
class Value;
}
namespace content {
class NavigationHandle;
class RenderViewHost;
class WebContents;
} // namespace content
class PrefService;
enum class ProtocolMessageType {
METHOD,
RESULT,
EVENT,
};
class CefDevToolsFrontend : public content::WebContentsObserver,
public content::DevToolsAgentHostClient {
public:
CefDevToolsFrontend(const CefDevToolsFrontend&) = delete;
CefDevToolsFrontend& operator=(const CefDevToolsFrontend&) = delete;
static CefDevToolsFrontend* Show(
AlloyBrowserHostImpl* inspected_browser,
const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefBrowserSettings& settings,
const CefPoint& inspect_element_at,
base::OnceClosure frontend_destroyed_callback);
void Activate();
void Focus();
void InspectElementAt(int x, int y);
void Close();
void CallClientFunction(
const std::string& object_name,
const std::string& method_name,
const base::Value arg1 = {},
const base::Value arg2 = {},
const base::Value arg3 = {},
base::OnceCallback<void(base::Value)> cb = base::NullCallback());
private:
CefDevToolsFrontend(AlloyBrowserHostImpl* frontend_browser,
content::WebContents* inspected_contents,
const CefPoint& inspect_element_at,
base::OnceClosure destroyed_callback);
~CefDevToolsFrontend() override;
// content::DevToolsAgentHostClient implementation.
void AgentHostClosed(content::DevToolsAgentHost* agent_host) override;
void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
base::span<const uint8_t> message) override;
void HandleMessageFromDevToolsFrontend(base::Value::Dict message);
private:
// WebContentsObserver overrides
void ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) override;
void PrimaryMainDocumentElementAvailable() override;
void WebContentsDestroyed() override;
void SendMessageAck(int request_id, base::Value::Dict arg);
bool ProtocolLoggingEnabled() const;
void LogProtocolMessage(ProtocolMessageType type,
const base::StringPiece& message);
PrefService* GetPrefs() const;
CefRefPtr<AlloyBrowserHostImpl> frontend_browser_;
content::WebContents* inspected_contents_;
scoped_refptr<content::DevToolsAgentHost> agent_host_;
CefPoint inspect_element_at_;
base::OnceClosure frontend_destroyed_callback_;
std::unique_ptr<content::DevToolsFrontendHost> frontend_host_;
class NetworkResourceLoader;
std::set<std::unique_ptr<NetworkResourceLoader>, base::UniquePtrComparator>
loaders_;
using ExtensionsAPIs = std::map<std::string, std::string>;
ExtensionsAPIs extensions_api_;
CefDevToolsFileManager file_manager_;
const base::FilePath protocol_log_file_;
base::WeakPtrFactory<CefDevToolsFrontend> weak_factory_;
};
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_H_

View File

@@ -1,148 +0,0 @@
// Copyright 2013 The Chromium 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 "libcef/browser/devtools/devtools_manager_delegate.h"
#include <stdint.h>
#include <vector>
#include "base/atomicops.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "cef/grit/cef_resources.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_frontend_host.h"
#include "content/public/browser/devtools_socket_factory.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/user_agent.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_source.h"
#include "net/socket/tcp_server_socket.h"
#include "ui/base/resource/resource_bundle.h"
namespace {
const int kBackLog = 10;
class TCPServerSocketFactory : public content::DevToolsSocketFactory {
public:
TCPServerSocketFactory(const std::string& address, uint16_t port)
: address_(address), port_(port) {}
TCPServerSocketFactory(const TCPServerSocketFactory&) = delete;
TCPServerSocketFactory& operator=(const TCPServerSocketFactory&) = delete;
private:
// content::DevToolsSocketFactory.
std::unique_ptr<net::ServerSocket> CreateForHttpServer() override {
std::unique_ptr<net::ServerSocket> socket(
new net::TCPServerSocket(nullptr, net::NetLogSource()));
if (socket->ListenWithAddressAndPort(address_, port_, kBackLog) !=
net::OK) {
return std::unique_ptr<net::ServerSocket>();
}
return socket;
}
std::unique_ptr<net::ServerSocket> CreateForTethering(
std::string* out_name) override {
return nullptr;
}
std::string address_;
uint16_t port_;
};
std::unique_ptr<content::DevToolsSocketFactory> CreateSocketFactory() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
// See if the user specified a port on the command line. Specifying 0 will
// result in the selection of an ephemeral port and the port number will be
// printed as part of the WebSocket endpoint URL to stderr. If a cache
// directory path is provided the port will also be written to the
// <cache-dir>/DevToolsActivePort file.
//
// It's not allowed to bind ports between 0 and 1024 exclusive because
// they're normally restricted to root on Posix-based systems.
if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) {
int port = 0;
std::string port_str =
command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort);
if (base::StringToInt(port_str, &port) &&
(0 == port || (port >= 1024 && port < 65535))) {
return std::unique_ptr<content::DevToolsSocketFactory>(
new TCPServerSocketFactory("127.0.0.1", port));
} else {
DLOG(WARNING) << "Invalid http debugger port number '" << port_str << "'";
}
}
return nullptr;
}
} // namespace
// CefDevToolsManagerDelegate ----------------------------------------------
// static
void CefDevToolsManagerDelegate::StartHttpHandler(
content::BrowserContext* browser_context) {
std::unique_ptr<content::DevToolsSocketFactory> socket_factory =
CreateSocketFactory();
if (!socket_factory) {
return;
}
content::DevToolsAgentHost::StartRemoteDebuggingServer(
std::move(socket_factory), browser_context->GetPath(), base::FilePath());
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kRemoteDebuggingPipe)) {
content::DevToolsAgentHost::StartRemoteDebuggingPipeHandler(
base::OnceClosure());
}
}
// static
void CefDevToolsManagerDelegate::StopHttpHandler() {
// This is a no-op if the server was never started.
content::DevToolsAgentHost::StopRemoteDebuggingServer();
}
CefDevToolsManagerDelegate::CefDevToolsManagerDelegate() = default;
CefDevToolsManagerDelegate::~CefDevToolsManagerDelegate() = default;
scoped_refptr<content::DevToolsAgentHost>
CefDevToolsManagerDelegate::CreateNewTarget(
const GURL& url,
content::DevToolsManagerDelegate::TargetType target_type) {
// This is reached when the user selects "Open link in new tab" from the
// DevTools interface.
// TODO(cef): Consider exposing new API to support this.
return nullptr;
}
std::string CefDevToolsManagerDelegate::GetDiscoveryPageHTML() {
return ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
IDR_CEF_DEVTOOLS_DISCOVERY_PAGE);
}
bool CefDevToolsManagerDelegate::HasBundledFrontendResources() {
return true;
}

View File

@@ -1,35 +0,0 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_DELEGATE_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_DELEGATE_H_
#include "content/public/browser/devtools_manager_delegate.h"
namespace content {
class BrowserContext;
}
class CefDevToolsManagerDelegate : public content::DevToolsManagerDelegate {
public:
static void StartHttpHandler(content::BrowserContext* browser_context);
static void StopHttpHandler();
CefDevToolsManagerDelegate();
CefDevToolsManagerDelegate(const CefDevToolsManagerDelegate&) = delete;
CefDevToolsManagerDelegate& operator=(const CefDevToolsManagerDelegate&) =
delete;
~CefDevToolsManagerDelegate() override;
// DevToolsManagerDelegate implementation.
scoped_refptr<content::DevToolsAgentHost> CreateNewTarget(
const GURL& url,
content::DevToolsManagerDelegate::TargetType target_type) override;
std::string GetDiscoveryPageHTML() override;
bool HasBundledFrontendResources() override;
};
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_DELEGATE_H_

View File

@@ -2,13 +2,11 @@
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "libcef/browser/devtools/devtools_manager.h"
#include <memory>
#include "libcef/browser/devtools/devtools_protocol_manager.h"
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/devtools/devtools_controller.h"
#include "libcef/browser/devtools/devtools_frontend.h"
#include "libcef/features/runtime.h"
#include "libcef/browser/thread_util.h"
#include "content/public/browser/web_contents.h"
@@ -99,55 +97,18 @@ class CefDevToolsRegistrationImpl : public CefRegistration,
} // namespace
CefDevToolsManager::CefDevToolsManager(CefBrowserHostBase* inspected_browser)
: inspected_browser_(inspected_browser), weak_ptr_factory_(this) {
CefDevToolsProtocolManager::CefDevToolsProtocolManager(
CefBrowserHostBase* inspected_browser)
: inspected_browser_(inspected_browser) {
CEF_REQUIRE_UIT();
}
CefDevToolsManager::~CefDevToolsManager() {
CefDevToolsProtocolManager::~CefDevToolsProtocolManager() {
CEF_REQUIRE_UIT();
}
void CefDevToolsManager::ShowDevTools(const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefBrowserSettings& settings,
const CefPoint& inspect_element_at) {
CEF_REQUIRE_UIT();
if (devtools_frontend_) {
if (!inspect_element_at.IsEmpty()) {
devtools_frontend_->InspectElementAt(inspect_element_at.x,
inspect_element_at.y);
}
devtools_frontend_->Focus();
return;
}
if (cef::IsChromeRuntimeEnabled()) {
NOTIMPLEMENTED();
} else {
auto alloy_browser = static_cast<AlloyBrowserHostImpl*>(inspected_browser_);
devtools_frontend_ = CefDevToolsFrontend::Show(
alloy_browser, windowInfo, client, settings, inspect_element_at,
base::BindOnce(&CefDevToolsManager::OnFrontEndDestroyed,
weak_ptr_factory_.GetWeakPtr()));
}
}
void CefDevToolsManager::CloseDevTools() {
CEF_REQUIRE_UIT();
if (!devtools_frontend_) {
return;
}
devtools_frontend_->Close();
}
bool CefDevToolsManager::HasDevTools() {
CEF_REQUIRE_UIT();
return !!devtools_frontend_;
}
bool CefDevToolsManager::SendDevToolsMessage(const void* message,
size_t message_size) {
bool CefDevToolsProtocolManager::SendDevToolsMessage(const void* message,
size_t message_size) {
CEF_REQUIRE_UIT();
if (!message || message_size == 0) {
return false;
@@ -161,7 +122,7 @@ bool CefDevToolsManager::SendDevToolsMessage(const void* message,
base::StringPiece(static_cast<const char*>(message), message_size));
}
int CefDevToolsManager::ExecuteDevToolsMethod(
int CefDevToolsProtocolManager::ExecuteDevToolsMethod(
int message_id,
const CefString& method,
CefRefPtr<CefDictionaryValue> params) {
@@ -187,13 +148,13 @@ int CefDevToolsManager::ExecuteDevToolsMethod(
}
// static
CefRefPtr<CefRegistration> CefDevToolsManager::CreateRegistration(
CefRefPtr<CefRegistration> CefDevToolsProtocolManager::CreateRegistration(
CefRefPtr<CefDevToolsMessageObserver> observer) {
DCHECK(observer);
return new CefDevToolsRegistrationImpl(observer);
}
void CefDevToolsManager::InitializeRegistrationOnUIThread(
void CefDevToolsProtocolManager::InitializeRegistrationOnUIThread(
CefRefPtr<CefRegistration> registration) {
CEF_REQUIRE_UIT();
@@ -205,11 +166,7 @@ void CefDevToolsManager::InitializeRegistrationOnUIThread(
->Initialize(inspected_browser_, devtools_controller_->GetWeakPtr());
}
void CefDevToolsManager::OnFrontEndDestroyed() {
devtools_frontend_ = nullptr;
}
bool CefDevToolsManager::EnsureController() {
bool CefDevToolsProtocolManager::EnsureController() {
if (!devtools_controller_) {
devtools_controller_ = std::make_unique<CefDevToolsController>(
inspected_browser_->contents_delegate()->web_contents());

View File

@@ -2,41 +2,35 @@
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_MANAGER_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_MANAGER_H_
#pragma once
#include <memory>
#include "include/cef_browser.h"
#include "base/memory/weak_ptr.h"
class CefBrowserHostBase;
class CefDevToolsController;
class CefDevToolsFrontend;
namespace content {
class WebContents;
}
// Manages DevTools instances. Methods must be called on the UI thread unless
// otherwise indicated.
class CefDevToolsManager {
// Manages DevTools protocol messages without an active frontend. Methods must
// be called on the UI thread unless otherwise indicated.
class CefDevToolsProtocolManager {
public:
// |inspected_browser| will outlive this object.
explicit CefDevToolsManager(CefBrowserHostBase* inspected_browser);
explicit CefDevToolsProtocolManager(CefBrowserHostBase* inspected_browser);
CefDevToolsManager(const CefDevToolsManager&) = delete;
CefDevToolsManager& operator=(const CefDevToolsManager&) = delete;
CefDevToolsProtocolManager(const CefDevToolsProtocolManager&) = delete;
CefDevToolsProtocolManager& operator=(const CefDevToolsProtocolManager&) =
delete;
~CefDevToolsManager();
~CefDevToolsProtocolManager();
// See CefBrowserHost methods of the same name for documentation.
void ShowDevTools(const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefBrowserSettings& settings,
const CefPoint& inspect_element_at);
void CloseDevTools();
bool HasDevTools();
bool SendDevToolsMessage(const void* message, size_t message_size);
int ExecuteDevToolsMethod(int message_id,
const CefString& method,
@@ -52,20 +46,11 @@ class CefDevToolsManager {
CefRefPtr<CefRegistration> registration);
private:
void OnFrontEndDestroyed();
bool EnsureController();
CefBrowserHostBase* const inspected_browser_;
// CefDevToolsFrontend will delete itself when the frontend WebContents is
// destroyed.
CefDevToolsFrontend* devtools_frontend_ = nullptr;
// Used for sending DevTools protocol messages without an active frontend.
std::unique_ptr<CefDevToolsController> devtools_controller_;
base::WeakPtrFactory<CefDevToolsManager> weak_ptr_factory_;
};
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_H_
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_MANAGER_H_

View File

@@ -0,0 +1,47 @@
// Copyright 2024 The Chromium Embedded Framework Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_RUNNER_H_
#define CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_RUNNER_H_
#pragma once
#include <memory>
#include "include/cef_client.h"
class CefBrowserHostBase;
// Parameters passed to ShowDevTools.
struct CefShowDevToolsParams {
CefShowDevToolsParams(const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefBrowserSettings& settings,
const CefPoint& inspect_element_at)
: window_info_(windowInfo),
client_(client),
settings_(settings),
inspect_element_at_(inspect_element_at) {}
CefWindowInfo window_info_;
CefRefPtr<CefClient> client_;
CefBrowserSettings settings_;
CefPoint inspect_element_at_;
};
// Creates and runs a DevTools window instance. Only accessed on the UI thread.
class CefDevToolsWindowRunner {
public:
// Creates the appropriate runner type based on the current runtime.
static std::unique_ptr<CefDevToolsWindowRunner> Create();
// See documentation on CefBrowserHost methods of the same name.
virtual void ShowDevTools(CefBrowserHostBase* opener,
std::unique_ptr<CefShowDevToolsParams> params) = 0;
virtual void CloseDevTools() = 0;
virtual bool HasDevTools() = 0;
virtual ~CefDevToolsWindowRunner() = default;
};
#endif // CEF_LIBCEF_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_RUNNER_H_

View File

@@ -0,0 +1,17 @@
// Copyright 2024 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 "libcef/browser/devtools/devtools_window_runner.h"
#include "libcef/browser/alloy/devtools/alloy_devtools_window_runner.h"
#include "libcef/browser/chrome/chrome_devtools_window_runner.h"
#include "libcef/features/runtime.h"
// static
std::unique_ptr<CefDevToolsWindowRunner> CefDevToolsWindowRunner::Create() {
if (cef::IsChromeRuntimeEnabled()) {
return std::make_unique<ChromeDevToolsWindowRunner>();
}
return std::make_unique<AlloyDevToolsWindowRunner>();
}