blob: 449675c6e0f560c944724ac12832418327a9d37f [file] [log] [blame]
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/debug/backend/overlay_agent.h"
#include "cobalt/math/clamp.h"
#include "cobalt/math/rect_f.h"
#include "cobalt/render_tree/brush.h"
#include "cobalt/render_tree/color_rgba.h"
#include "cobalt/render_tree/composition_node.h"
#include "cobalt/render_tree/rect_node.h"
namespace cobalt {
namespace debug {
namespace backend {
using base::Value;
using math::Clamp;
using render_tree::ColorRGBA;
namespace {
// Definitions from the set specified here:
// https://chromedevtools.github.io/devtools-protocol/tot/Overlay
constexpr char kInspectorDomain[] = "Overlay";
// File to load JavaScript Overlay DevTools domain implementation from.
constexpr char kScriptFile[] = "overlay_agent.js";
// Returns the float value of a param, or 0.0 if undefined or non-numeric.
float GetFloatParam(const Value* params, base::StringPiece key) {
if (!params || !params->is_dict()) return 0.0f;
const Value* v = params->FindKey(key);
if (!v || !(v->is_double() || v->is_int())) return 0.0f;
return static_cast<float>(v->GetDouble());
}
// Returns an RGBA color defined by a param, or transparent if undefined.
ColorRGBA RenderColor(const Value* params) {
float r = GetFloatParam(params, "r") / 255.0f;
float g = GetFloatParam(params, "g") / 255.0f;
float b = GetFloatParam(params, "b") / 255.0f;
float a = GetFloatParam(params, "a");
return ColorRGBA(Clamp(r, 0.0f, 1.0f), Clamp(g, 0.0f, 1.0f),
Clamp(b, 0.0f, 1.0f), Clamp(a, 0.0f, 1.0f));
}
// Returns a rectangle to render according to the params for the DevTools
// "Overlay.highlightRect" command.
// https://chromedevtools.github.io/devtools-protocol/tot/Overlay#method-highlightRect
scoped_refptr<render_tree::RectNode> RenderHighlightRect(const Value* params) {
float x = GetFloatParam(params, "x");
float y = GetFloatParam(params, "y");
float width = GetFloatParam(params, "width");
float height = GetFloatParam(params, "height");
ColorRGBA color(RenderColor(params->FindKey("color")));
const Value* outline_param = params->FindKey("outlineColor");
ColorRGBA outline_color(RenderColor(outline_param));
float outline_width = outline_param ? 1.0f : 0.0f;
return base::MakeRefCounted<render_tree::RectNode>(
math::RectF(x, y, width, height),
std::make_unique<render_tree::SolidColorBrush>(color),
std::make_unique<render_tree::Border>(render_tree::BorderSide(
outline_width, render_tree::kBorderStyleSolid, outline_color)));
}
} // namespace
OverlayAgent::OverlayAgent(DebugDispatcher* dispatcher,
std::unique_ptr<RenderLayer> render_layer)
: dispatcher_(dispatcher),
render_layer_(std::move(render_layer)),
ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)) {
DCHECK(dispatcher_);
DCHECK(render_layer_);
commands_["disable"] = &OverlayAgent::Disable;
commands_["enable"] = &OverlayAgent::Enable;
commands_["highlightNode"] = &OverlayAgent::HighlightNode;
commands_["highlightRect"] = &OverlayAgent::HighlightRect;
commands_["hideHighlight"] = &OverlayAgent::HideHighlight;
}
void OverlayAgent::Thaw(JSONObject agent_state) {
dispatcher_->AddDomain(kInspectorDomain, commands_.Bind());
script_loaded_ = dispatcher_->RunScriptFile(kScriptFile);
DLOG_IF(ERROR, !script_loaded_) << "Failed to load " << kScriptFile;
}
JSONObject OverlayAgent::Freeze() {
dispatcher_->RemoveDomain(kInspectorDomain);
return JSONObject();
}
void OverlayAgent::Enable(const Command& command) {
if (script_loaded_) {
enabled_ = true;
command.SendResponse();
} else {
command.SendErrorResponse(Command::kInternalError,
"Cannot create Overlay inspector.");
}
}
void OverlayAgent::Disable(const Command& command) {
enabled_ = false;
command.SendResponse();
}
void OverlayAgent::HighlightNode(const Command& command) {
if (!enabled_) {
command.SendErrorResponse(Command::kInvalidRequest,
"Overlay inspector not enabled.");
return;
}
// Use the injected JavaScript helper to get the rectangles to highlight for
// the specified node.
JSONObject rects_response = dispatcher_->RunScriptCommand(
"Overlay._highlightNodeRects", command.GetParams());
const Value* highlight_rects =
rects_response->FindPath({"result", "highlightRects"});
if (!highlight_rects) {
command.SendErrorResponse(Command::kInvalidParams,
"Can't get node highlights.");
return;
}
// Render all the highlight rects as children of a CompositionNode.
render_tree::CompositionNode::Builder builder;
for (const Value& rect_params : highlight_rects->GetList()) {
builder.AddChild(RenderHighlightRect(&rect_params));
}
render_layer_->SetFrontLayer(
base::MakeRefCounted<render_tree::CompositionNode>(builder));
command.SendResponse();
}
void OverlayAgent::HighlightRect(const Command& command) {
JSONObject params = JSONParse(command.GetParams());
render_layer_->SetFrontLayer(RenderHighlightRect(params.get()));
command.SendResponse();
}
void OverlayAgent::HideHighlight(const Command& command) {
render_layer_->SetFrontLayer(scoped_refptr<render_tree::Node>());
command.SendResponse();
}
} // namespace backend
} // namespace debug
} // namespace cobalt