blob: 8acb5064287cd9b83024a19a843938d331d27e1f [file]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/devtools/devtools_embedder_message_dispatcher.h"
#include <memory>
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/types/optional_util.h"
#include "base/values.h"
#include "chrome/browser/devtools/devtools_dispatch_http_request_params.h"
#include "chrome/browser/devtools/devtools_settings.h"
#include "chrome/browser/devtools/features.h"
#include "chrome/browser/devtools/visual_logging.h"
namespace {
using DispatchCallback = DevToolsEmbedderMessageDispatcher::DispatchCallback;
bool GetValue(const base::Value& value,
DevToolsDispatchHttpRequestParams& params) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
auto parsed_params = DevToolsDispatchHttpRequestParams::FromDict(*dict);
if (!parsed_params) {
return false;
}
params = std::move(*parsed_params);
return true;
}
bool GetValue(const base::Value& value, std::string& result) {
const std::string* str = value.GetIfString();
if (!str) {
return false;
}
result = *str;
return true;
}
bool GetValue(const base::Value& value, int& result) {
return base::OptionalUnwrapTo(value.GetIfInt(), result);
}
bool GetValue(const base::Value& value, double& result) {
return base::OptionalUnwrapTo(value.GetIfDouble(), result);
}
bool GetValue(const base::Value& value, bool& result) {
return base::OptionalUnwrapTo(value.GetIfBool(), result);
}
bool GetValue(const base::Value& value, gfx::Rect& rect) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
std::optional<int> x = dict->FindInt("x");
std::optional<int> y = dict->FindInt("y");
std::optional<int> width = dict->FindInt("width");
std::optional<int> height = dict->FindInt("height");
if (!x.has_value() || !y.has_value() || !width.has_value() ||
!height.has_value()) {
return false;
}
rect.SetRect(x.value(), y.value(), width.value(), height.value());
return true;
}
bool GetValue(const base::Value& value, RegisterOptions& options) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
const bool synced = dict->FindBool("synced").value_or(false);
options.sync_mode = synced ? RegisterOptions::SyncMode::kSync
: RegisterOptions::SyncMode::kDontSync;
return true;
}
bool GetValue(const base::Value& value, ImpressionEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
const base::ListValue* impressions = dict->FindList("impressions");
if (!impressions) {
return false;
}
for (const auto& impression : *impressions) {
const base::DictValue* impression_dict = impression.GetIfDict();
if (!impression_dict) {
return false;
}
std::optional<double> id = impression_dict->FindDouble("id");
std::optional<int> type = impression_dict->FindInt("type");
if (!id || !type) {
return false;
}
auto& back = event.impressions.emplace_back(
VisualElementImpression{static_cast<int64_t>(*id), *type});
base::OptionalUnwrapTo(impression_dict->FindDouble("parent"), back.parent);
base::OptionalUnwrapTo(impression_dict->FindInt("context"), back.context);
base::OptionalUnwrapTo(impression_dict->FindInt("width"), back.width);
base::OptionalUnwrapTo(impression_dict->FindInt("height"), back.height);
}
return true;
}
bool GetValue(const base::Value& value, ResizeEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
if (!base::OptionalUnwrapTo(dict->FindDouble("veid"), event.veid)) {
return false;
}
base::OptionalUnwrapTo(dict->FindInt("width"), event.width);
base::OptionalUnwrapTo(dict->FindInt("height"), event.height);
return true;
}
bool GetValue(const base::Value& value, ClickEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
if (!base::OptionalUnwrapTo(dict->FindDouble("veid"), event.veid)) {
return false;
}
base::OptionalUnwrapTo(dict->FindInt("mouseButton"), event.mouse_button);
base::OptionalUnwrapTo(dict->FindInt("doubleClick"), event.double_click);
base::OptionalUnwrapTo(dict->FindInt("context"), event.context);
return true;
}
bool GetValue(const base::Value& value, HoverEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
if (!base::OptionalUnwrapTo(dict->FindDouble("veid"), event.veid)) {
return false;
}
base::OptionalUnwrapTo(dict->FindInt("time"), event.time);
base::OptionalUnwrapTo(dict->FindInt("context"), event.context);
return true;
}
bool GetValue(const base::Value& value, DragEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
if (!base::OptionalUnwrapTo(dict->FindDouble("veid"), event.veid)) {
return false;
}
base::OptionalUnwrapTo(dict->FindInt("distance"), event.distance);
base::OptionalUnwrapTo(dict->FindInt("context"), event.context);
return true;
}
bool GetValue(const base::Value& value, ChangeEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
if (!base::OptionalUnwrapTo(dict->FindDouble("veid"), event.veid)) {
return false;
}
base::OptionalUnwrapTo(dict->FindInt("context"), event.context);
return true;
}
bool GetValue(const base::Value& value, KeyDownEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
base::OptionalUnwrapTo(dict->FindDouble("veid"), event.veid);
base::OptionalUnwrapTo(dict->FindInt("context"), event.context);
return true;
}
bool GetValue(const base::Value& value, SettingAccessEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
base::OptionalUnwrapTo(dict->FindInt("name"), event.name);
base::OptionalUnwrapTo(dict->FindInt("numeric_value"), event.numeric_value);
base::OptionalUnwrapTo(dict->FindInt("string_value"), event.string_value);
return true;
}
bool GetValue(const base::Value& value, FunctionCallEvent& event) {
const base::DictValue* dict = value.GetIfDict();
if (!dict) {
return false;
}
base::OptionalUnwrapTo(dict->FindInt("name"), event.name);
base::OptionalUnwrapTo(dict->FindInt("context"), event.context);
return true;
}
template <typename T>
struct StorageTraits {
using StorageType = T;
};
template <typename T>
struct StorageTraits<const T&> {
using StorageType = T;
};
template <typename... Ts>
struct ParamTuple {
bool Parse(base::ListValue::const_iterator it) { return true; }
template <typename H, typename... As>
void Apply(const H& handler, As... args) {
handler.Run(std::forward<As>(args)...);
}
};
template <typename T, typename... Ts>
struct ParamTuple<T, Ts...> {
bool Parse(base::ListValue::const_iterator it) {
return GetValue(*it, head) && tail.Parse(it + 1);
}
template <typename H, typename... As>
void Apply(const H& handler, As... args) {
tail.template Apply<H, As..., T>(handler, std::forward<As>(args)..., head);
}
typename StorageTraits<T>::StorageType head;
ParamTuple<Ts...> tail;
};
template <typename... As>
bool ParseAndHandle(const base::RepeatingCallback<void(As...)>& handler,
const std::string& method,
DispatchCallback callback,
const base::ListValue& list) {
ParamTuple<As...> tuple;
if (list.size() != sizeof...(As) || !tuple.Parse(list.begin())) {
LOG(ERROR) << "Failed to parse arguments for " << method
<< " call: " << list.DebugString();
return false;
}
tuple.Apply(handler);
return true;
}
template <typename... As>
bool ParseAndHandleWithCallback(
const base::RepeatingCallback<void(DispatchCallback, As...)>& handler,
const std::string& method,
DispatchCallback callback,
const base::ListValue& list) {
ParamTuple<As...> tuple;
if (list.size() != sizeof...(As) || !tuple.Parse(list.begin())) {
LOG(ERROR) << "Failed to parse arguments for " << method
<< " call: " << list.DebugString();
return false;
}
tuple.Apply(handler, std::move(callback));
return true;
}
} // namespace
/**
* Dispatcher for messages sent from the frontend running in an
* isolated renderer (devtools:// or chrome://inspect) to the embedder
* in the browser.
*
* The messages are sent via InspectorFrontendHost.sendMessageToEmbedder or
* chrome.send method accordingly.
*/
class DispatcherImpl : public DevToolsEmbedderMessageDispatcher {
public:
~DispatcherImpl() override = default;
bool Dispatch(DispatchCallback callback,
const std::string& method,
const base::ListValue& params) override {
auto it = handlers_.find(method);
return it != handlers_.end() && it->second.Run(std::move(callback), params);
}
template<typename... As>
void RegisterHandler(const std::string& method,
void (Delegate::*handler)(As...),
Delegate* delegate) {
handlers_[method] = base::BindRepeating(
&ParseAndHandle<As...>,
base::BindRepeating(handler, base::Unretained(delegate)), method);
}
template <typename... As>
void RegisterHandlerWithCallback(const std::string& method,
void (Delegate::*handler)(DispatchCallback,
As...),
Delegate* delegate) {
handlers_[method] = base::BindRepeating(
&ParseAndHandleWithCallback<As...>,
base::BindRepeating(handler, base::Unretained(delegate)), method);
}
private:
using Handler =
base::RepeatingCallback<bool(DispatchCallback, const base::ListValue&)>;
using HandlerMap = std::map<std::string, Handler>;
HandlerMap handlers_;
};
// static
std::unique_ptr<DevToolsEmbedderMessageDispatcher>
DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(
Delegate* delegate) {
auto d = std::make_unique<DispatcherImpl>();
d->RegisterHandler("bringToFront", &Delegate::ActivateWindow, delegate);
d->RegisterHandler("closeWindow", &Delegate::CloseWindow, delegate);
d->RegisterHandler("loadCompleted", &Delegate::LoadCompleted, delegate);
d->RegisterHandler("setInspectedPageBounds",
&Delegate::SetInspectedPageBounds, delegate);
d->RegisterHandler("inspectElementCompleted",
&Delegate::InspectElementCompleted, delegate);
d->RegisterHandler("inspectedURLChanged",
&Delegate::InspectedURLChanged, delegate);
d->RegisterHandlerWithCallback("setIsDocked",
&Delegate::SetIsDocked, delegate);
d->RegisterHandler("openInNewTab", &Delegate::OpenInNewTab, delegate);
d->RegisterHandler("openSearchResultsInNewTab",
&Delegate::OpenSearchResultsInNewTab, delegate);
d->RegisterHandler("showItemInFolder", &Delegate::ShowItemInFolder, delegate);
d->RegisterHandler("save", &Delegate::SaveToFile, delegate);
d->RegisterHandler("append", &Delegate::AppendToFile, delegate);
d->RegisterHandler("requestFileSystems",
&Delegate::RequestFileSystems, delegate);
d->RegisterHandler("addFileSystem", &Delegate::AddFileSystem, delegate);
d->RegisterHandler("removeFileSystem", &Delegate::RemoveFileSystem, delegate);
d->RegisterHandler("upgradeDraggedFileSystemPermissions",
&Delegate::UpgradeDraggedFileSystemPermissions, delegate);
d->RegisterHandlerWithCallback("connectAutomaticFileSystem",
&Delegate::ConnectAutomaticFileSystem,
delegate);
d->RegisterHandler("disconnectAutomaticFileSystem",
&Delegate::DisconnectAutomaticFileSystem, delegate);
d->RegisterHandler("indexPath", &Delegate::IndexPath, delegate);
d->RegisterHandlerWithCallback("loadNetworkResource",
&Delegate::LoadNetworkResource, delegate);
d->RegisterHandler("stopIndexing", &Delegate::StopIndexing, delegate);
d->RegisterHandler("searchInPath", &Delegate::SearchInPath, delegate);
d->RegisterHandler("setWhitelistedShortcuts",
&Delegate::SetWhitelistedShortcuts, delegate);
d->RegisterHandler("setEyeDropperActive", &Delegate::SetEyeDropperActive,
delegate);
d->RegisterHandler("showCertificateViewer",
&Delegate::ShowCertificateViewer, delegate);
d->RegisterHandler("zoomIn", &Delegate::ZoomIn, delegate);
d->RegisterHandler("zoomOut", &Delegate::ZoomOut, delegate);
d->RegisterHandler("resetZoom", &Delegate::ResetZoom, delegate);
d->RegisterHandler("setDevicesDiscoveryConfig",
&Delegate::SetDevicesDiscoveryConfig, delegate);
d->RegisterHandler("setDevicesUpdatesEnabled",
&Delegate::SetDevicesUpdatesEnabled, delegate);
d->RegisterHandler("openRemotePage", &Delegate::OpenRemotePage, delegate);
d->RegisterHandler("openNodeFrontend", &Delegate::OpenNodeFrontend, delegate);
d->RegisterHandler("dispatchProtocolMessage",
&Delegate::DispatchProtocolMessageFromDevToolsFrontend,
delegate);
d->RegisterHandler("recordCountHistogram", &Delegate::RecordCountHistogram,
delegate);
d->RegisterHandler("recordEnumeratedHistogram",
&Delegate::RecordEnumeratedHistogram, delegate);
d->RegisterHandler("recordPerformanceHistogram",
&Delegate::RecordPerformanceHistogram, delegate);
d->RegisterHandler("recordPerformanceHistogramMedium",
&Delegate::RecordPerformanceHistogramMedium, delegate);
d->RegisterHandler("recordUserMetricsAction",
&Delegate::RecordUserMetricsAction, delegate);
d->RegisterHandler("recordNewBadgeUsage", &Delegate::RecordNewBadgeUsage,
delegate);
d->RegisterHandler("setChromeFlag", &Delegate::SetChromeFlag, delegate);
d->RegisterHandler("recordImpression", &Delegate::RecordImpression, delegate);
d->RegisterHandler("recordResize", &Delegate::RecordResize, delegate);
d->RegisterHandler("recordClick", &Delegate::RecordClick, delegate);
d->RegisterHandler("recordHover", &Delegate::RecordHover, delegate);
d->RegisterHandler("recordDrag", &Delegate::RecordDrag, delegate);
d->RegisterHandler("recordChange", &Delegate::RecordChange, delegate);
d->RegisterHandler("recordKeyDown", &Delegate::RecordKeyDown, delegate);
d->RegisterHandler("recordSettingAccess", &Delegate::RecordSettingAccess,
delegate);
d->RegisterHandler("recordFunctionCall", &Delegate::RecordFunctionCall,
delegate);
d->RegisterHandler("registerPreference", &Delegate::RegisterPreference,
delegate);
d->RegisterHandlerWithCallback("getPreferences",
&Delegate::GetPreferences, delegate);
d->RegisterHandlerWithCallback("getPreference", &Delegate::GetPreference,
delegate);
d->RegisterHandler("setPreference",
&Delegate::SetPreference, delegate);
d->RegisterHandler("removePreference",
&Delegate::RemovePreference, delegate);
d->RegisterHandler("clearPreferences",
&Delegate::ClearPreferences, delegate);
d->RegisterHandlerWithCallback("getSyncInformation",
&Delegate::GetSyncInformation, delegate);
d->RegisterHandlerWithCallback("getHostConfig", &Delegate::GetHostConfig,
delegate);
d->RegisterHandlerWithCallback("reattach",
&Delegate::Reattach, delegate);
d->RegisterHandler("readyForTest",
&Delegate::ReadyForTest, delegate);
d->RegisterHandler("connectionReady", &Delegate::ConnectionReady, delegate);
d->RegisterHandler("setOpenNewWindowForPopups",
&Delegate::SetOpenNewWindowForPopups, delegate);
d->RegisterHandler("registerExtensionsAPI", &Delegate::RegisterExtensionsAPI,
delegate);
d->RegisterHandlerWithCallback("showSurvey", &Delegate::ShowSurvey, delegate);
d->RegisterHandlerWithCallback("canShowSurvey", &Delegate::CanShowSurvey,
delegate);
d->RegisterHandlerWithCallback("doAidaConversation",
&Delegate::DoAidaConversation, delegate);
d->RegisterHandlerWithCallback("aidaCodeComplete",
&Delegate::AidaCodeComplete, delegate);
d->RegisterHandlerWithCallback("registerAidaClientEvent",
&Delegate::RegisterAidaClientEvent, delegate);
d->RegisterHandlerWithCallback("dispatchHttpRequest",
&Delegate::DispatchHttpRequest, delegate);
d->RegisterHandler("requestRestart", &Delegate::RequestRestart, delegate);
return d;
}