blob: c14f8a875e8deddc619d067f7f7b158c404d2318 [file] [log] [blame]
// Copyright 2016 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 "components/ui_devtools/views/dom_agent.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/ui_devtools/devtools_server.h"
#include "components/ui_devtools/views/overlay_agent.h"
#include "components/ui_devtools/views/ui_element.h"
#include "components/ui_devtools/views/view_element.h"
#include "components/ui_devtools/views/widget_element.h"
#include "components/ui_devtools/views/window_element.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/effects/SkDashPathEffect.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/render_text.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/window_util.h"
namespace ui_devtools {
namespace {
using namespace ui_devtools::protocol;
// TODO(mhashmi): Make ids reusable
std::unique_ptr<DOM::Node> BuildNode(
const std::string& name,
std::unique_ptr<Array<std::string>> attributes,
std::unique_ptr<Array<DOM::Node>> children,
int node_ids) {
constexpr int kDomElementNodeType = 1;
std::unique_ptr<DOM::Node> node = DOM::Node::create()
.setNodeId(node_ids)
.setBackendNodeId(node_ids)
.setNodeName(name)
.setNodeType(kDomElementNodeType)
.setAttributes(std::move(attributes))
.build();
node->setChildNodeCount(static_cast<int>(children->length()));
node->setChildren(std::move(children));
return node;
}
// TODO(thanhph): Move this function to UIElement::GetAttributes().
std::unique_ptr<Array<std::string>> GetAttributes(UIElement* ui_element) {
std::unique_ptr<Array<std::string>> attributes = Array<std::string>::create();
attributes->addItem("name");
switch (ui_element->type()) {
case UIElementType::WINDOW: {
aura::Window* window =
UIElement::GetBackingElement<aura::Window, WindowElement>(ui_element);
attributes->addItem(window->GetName());
attributes->addItem("active");
attributes->addItem(::wm::IsActiveWindow(window) ? "true" : "false");
break;
}
case UIElementType::WIDGET: {
views::Widget* widget =
UIElement::GetBackingElement<views::Widget, WidgetElement>(
ui_element);
attributes->addItem(widget->GetName());
attributes->addItem("active");
attributes->addItem(widget->IsActive() ? "true" : "false");
break;
}
case UIElementType::VIEW: {
attributes->addItem(
UIElement::GetBackingElement<views::View, ViewElement>(ui_element)
->GetClassName());
break;
}
default:
DCHECK(false);
}
return attributes;
}
views::Widget* GetWidgetFromWindow(aura::Window* window) {
return views::Widget::GetWidgetForNativeView(window);
}
std::unique_ptr<DOM::Node> BuildDomNodeFromUIElement(UIElement* root) {
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
for (auto* it : root->children())
children->addItem(BuildDomNodeFromUIElement(it));
return BuildNode(root->GetTypeName(), GetAttributes(root),
std::move(children), root->node_id());
}
void DrawRulerText(const base::string16& utf16_text,
const gfx::Point& p,
gfx::Canvas* canvas,
gfx::RenderText* render_text_) {
render_text_->SetText(utf16_text);
render_text_->SetColor(SK_ColorRED);
const gfx::Rect text_rect(gfx::Rect(p, render_text_->GetStringSize()));
canvas->FillRect(text_rect, SK_ColorWHITE, SkBlendMode::kColor);
render_text_->SetDisplayRect(text_rect);
render_text_->Draw(canvas);
}
void DrawRulers(const gfx::Rect& screen_bounds,
gfx::Canvas* canvas,
gfx::RenderText* render_text_) {
// Top horizontal ruler from left to right.
canvas->Draw1pxLine(gfx::PointF(0.0f, 0.0f),
gfx::PointF(screen_bounds.right(), 0.0f),
SK_ColorMAGENTA);
// Left veritical ruler from top to bottom.
canvas->Draw1pxLine(gfx::PointF(0.0f, 0.0f),
gfx::PointF(0.0f, screen_bounds.bottom()),
SK_ColorMAGENTA);
int short_stroke = 5;
int long_stroke = 10;
int gap_between_strokes = 4;
int gap_between_long_stroke = 100;
// Draw top horizontal ruler.
for (int x = gap_between_strokes; x < screen_bounds.right();
x += gap_between_strokes) {
if (x % gap_between_long_stroke == 0) {
canvas->Draw1pxLine(gfx::PointF(x, 0.0f), gfx::PointF(x, long_stroke),
SK_ColorMAGENTA);
// Draw ruler marks.
base::string16 utf16_text = base::UTF8ToUTF16(std::to_string(x));
DrawRulerText(utf16_text, gfx::Point(x + 2, long_stroke), canvas,
render_text_);
} else {
canvas->Draw1pxLine(gfx::PointF(x, 0.0f), gfx::PointF(x, short_stroke),
SK_ColorMAGENTA);
}
}
// Draw left vertical ruler.
for (int y = 0; y < screen_bounds.bottom(); y += gap_between_strokes) {
if (y % gap_between_long_stroke == 0) {
canvas->Draw1pxLine(gfx::PointF(0.0f, y), gfx::PointF(long_stroke, y),
SK_ColorMAGENTA);
// Draw ruler marks.
base::string16 utf16_text = base::UTF8ToUTF16(std::to_string(y));
DrawRulerText(utf16_text, gfx::Point(short_stroke + 1, y + 2), canvas,
render_text_);
} else {
canvas->Draw1pxLine(gfx::PointF(0.0f, y), gfx::PointF(short_stroke, y),
SK_ColorMAGENTA);
}
}
}
// Draw width() x height() of a rectangle if not empty. Otherwise, draw either
// width() or height() if any of them is not empty.
void DrawSizeOfRectangle(const gfx::Rect& hovered_rect,
const RectSide drawing_side,
gfx::Canvas* canvas,
gfx::RenderText* render_text_) {
base::string16 utf16_text;
const std::string unit = "dp";
if (!hovered_rect.IsEmpty()) {
utf16_text = base::UTF8ToUTF16(hovered_rect.size().ToString() + unit);
} else if (hovered_rect.height()) {
// Draw only height() if height() is not empty.
utf16_text =
base::UTF8ToUTF16(std::to_string(hovered_rect.height()) + unit);
} else if (hovered_rect.width()) {
// Draw only width() if width() is not empty.
utf16_text = base::UTF8ToUTF16(std::to_string(hovered_rect.width()) + unit);
} else {
// If both width() and height() are empty, canvas won't draw size.
return;
}
render_text_->SetText(utf16_text);
render_text_->SetColor(SK_ColorRED);
const gfx::Size& text_size = render_text_->GetStringSize();
gfx::Rect text_rect;
if (drawing_side == RectSide::LEFT_SIDE) {
const gfx::Point text_left_side(
hovered_rect.x() + 1,
hovered_rect.height() / 2 - text_size.height() / 2 + hovered_rect.y());
text_rect = gfx::Rect(text_left_side,
gfx::Size(text_size.width(), text_size.height()));
} else if (drawing_side == RectSide::RIGHT_SIDE) {
const gfx::Point text_right_side(
hovered_rect.right() - 1 - text_size.width(),
hovered_rect.height() / 2 - text_size.height() / 2 + hovered_rect.y());
text_rect = gfx::Rect(text_right_side,
gfx::Size(text_size.width(), text_size.height()));
} else if (drawing_side == RectSide::TOP_SIDE) {
const gfx::Point text_top_side(
hovered_rect.x() + hovered_rect.width() / 2 - text_size.width() / 2,
hovered_rect.y() + 1);
text_rect = gfx::Rect(text_top_side,
gfx::Size(text_size.width(), text_size.height()));
} else if (drawing_side == RectSide::BOTTOM_SIDE) {
const gfx::Point text_top_side(
hovered_rect.x() + hovered_rect.width() / 2 - text_size.width() / 2,
hovered_rect.bottom() - 1 - text_size.height());
text_rect = gfx::Rect(text_top_side,
gfx::Size(text_size.width(), text_size.height()));
}
canvas->FillRect(text_rect, SK_ColorWHITE, SkBlendMode::kColor);
render_text_->SetDisplayRect(text_rect);
render_text_->Draw(canvas);
}
void DrawRectGuideLinesOnCanvas(const gfx::Rect& screen_bounds,
const gfx::RectF& rect_f,
cc::PaintFlags flags,
gfx::Canvas* canvas) {
// Top horizontal dotted line from left to right.
canvas->DrawLine(gfx::PointF(0.0f, rect_f.y()),
gfx::PointF(screen_bounds.right(), rect_f.y()), flags);
// Bottom horizontal dotted line from left to right.
canvas->DrawLine(gfx::PointF(0.0f, rect_f.bottom()),
gfx::PointF(screen_bounds.right(), rect_f.bottom()), flags);
// Left vertical dotted line from top to bottom.
canvas->DrawLine(gfx::PointF(rect_f.x(), 0.0f),
gfx::PointF(rect_f.x(), screen_bounds.bottom()), flags);
// Right vertical dotted line from top to bottom.
canvas->DrawLine(gfx::PointF(rect_f.right(), 0.0f),
gfx::PointF(rect_f.right(), screen_bounds.bottom()), flags);
}
void DrawSizeWithAnyBounds(float x1,
float y1,
float x2,
float y2,
RectSide side,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
if (x2 > x1 || y2 > y1) {
DrawSizeOfRectangle(gfx::Rect(x1, y1, x2 - x1, y2 - y1), side, canvas,
render_text);
} else {
DrawSizeOfRectangle(gfx::Rect(x2, y2, x1 - x2, y1 - y2), side, canvas,
render_text);
}
}
void DrawR1ContainsR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
// Horizontal left distance line.
float x1 = pinned_rect_f.x();
float y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
float x2 = hovered_rect_f.x();
float y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
// Horizontal right distance line.
x1 = hovered_rect_f.right();
y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
x2 = pinned_rect_f.right();
y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
// Vertical top distance line.
x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
y1 = pinned_rect_f.y();
x2 = x1;
y2 = hovered_rect_f.y();
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
// Vertical bottom distance line.
x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
y1 = hovered_rect_f.bottom();
x2 = x1;
y2 = pinned_rect_f.bottom();
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
}
void DrawR1HorizontalFullLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
// Horizontal left distance line.
float x1 = hovered_rect_f.right();
float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
float x2 = pinned_rect_f.x();
float y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
}
void DrawR1TopFullLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas_,
gfx::RenderText* render_text) {
float x1 = hovered_rect_f.x() + hovered_rect_f.width();
float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
float x2 = pinned_rect_f.x();
float y2 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
// Horizontal left dotted line.
canvas_->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas_,
render_text);
x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
y1 = hovered_rect_f.y() + hovered_rect_f.height();
x2 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
y2 = pinned_rect_f.y();
// Vertical left dotted line.
canvas_->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas_,
render_text);
}
void DrawR1BottomFullLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
float x1 = hovered_rect_f.right();
float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
float x2 = pinned_rect_f.x();
float y2 = y1;
// Horizontal left distance line.
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
y1 = pinned_rect_f.bottom();
x2 = x1;
y2 = hovered_rect_f.y();
// Vertical left distance line.
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
}
void DrawR1TopPartialLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
float x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
float y1 = hovered_rect_f.bottom();
float x2 = x1;
float y2 = pinned_rect_f.y();
// Vertical left dotted line.
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
}
void DrawR1BottomPartialLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
float x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
float y1 = pinned_rect_f.bottom();
float x2 = x1;
float y2 = hovered_rect_f.y();
// Vertical left dotted line.
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
}
void DrawR1IntersectsR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
// Vertical dotted line for the top side of the pinned rectangle
float x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
float y1 = pinned_rect_f.y();
float x2 = x1;
float y2 = hovered_rect_f.y();
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
// Vertical dotted line for the bottom side of the pinned rectangle
x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
y1 = pinned_rect_f.bottom();
x2 = x1;
y2 = hovered_rect_f.bottom();
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
// Horizontal dotted line for the left side of the pinned rectangle
x1 = pinned_rect_f.x();
y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
x2 = hovered_rect_f.x();
y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
// Horizontal dotted line for the right side of the pinned rectangle
x1 = pinned_rect_f.right();
y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
x2 = hovered_rect_f.right();
y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
}
} // namespace
DOMAgent::DOMAgent()
: is_building_tree_(false),
show_size_on_canvas_(false),
highlight_rect_config_(HighlightRectsConfiguration::NO_DRAW) {
aura::Env::GetInstance()->AddObserver(this);
}
DOMAgent::~DOMAgent() {
aura::Env::GetInstance()->RemoveObserver(this);
Reset();
}
Response DOMAgent::disable() {
Reset();
return Response::OK();
}
Response DOMAgent::getDocument(std::unique_ptr<DOM::Node>* out_root) {
*out_root = BuildInitialTree();
return Response::OK();
}
Response DOMAgent::hideHighlight() {
if (layer_for_highlighting_ && layer_for_highlighting_->visible())
layer_for_highlighting_->SetVisible(false);
return Response::OK();
}
Response DOMAgent::pushNodesByBackendIdsToFrontend(
std::unique_ptr<protocol::Array<int>> backend_node_ids,
std::unique_ptr<protocol::Array<int>>* result) {
*result = protocol::Array<int>::create();
for (size_t index = 0; index < backend_node_ids->length(); ++index)
(*result)->addItem(backend_node_ids->get(index));
return Response::OK();
}
void DOMAgent::OnUIElementAdded(UIElement* parent, UIElement* child) {
// When parent is null, only need to update |node_id_to_ui_element_|.
if (!parent) {
node_id_to_ui_element_[child->node_id()] = child;
return;
}
// If tree is being built, don't add child to dom tree again.
if (is_building_tree_)
return;
DCHECK(node_id_to_ui_element_.count(parent->node_id()));
const auto& children = parent->children();
auto iter = std::find(children.begin(), children.end(), child);
int prev_node_id =
(iter == children.end() - 1) ? 0 : (*std::next(iter))->node_id();
frontend()->childNodeInserted(parent->node_id(), prev_node_id,
BuildTreeForUIElement(child));
}
void DOMAgent::OnUIElementReordered(UIElement* parent, UIElement* child) {
DCHECK(node_id_to_ui_element_.count(parent->node_id()));
const auto& children = parent->children();
auto iter = std::find(children.begin(), children.end(), child);
int prev_node_id =
(iter == children.begin()) ? 0 : (*std::prev(iter))->node_id();
RemoveDomNode(child);
frontend()->childNodeInserted(parent->node_id(), prev_node_id,
BuildDomNodeFromUIElement(child));
}
void DOMAgent::OnUIElementRemoved(UIElement* ui_element) {
DCHECK(node_id_to_ui_element_.count(ui_element->node_id()));
RemoveDomNode(ui_element);
node_id_to_ui_element_.erase(ui_element->node_id());
}
void DOMAgent::OnUIElementBoundsChanged(UIElement* ui_element) {
for (auto& observer : observers_)
observer.OnElementBoundsChanged(ui_element);
}
void DOMAgent::AddObserver(DOMAgentObserver* observer) {
observers_.AddObserver(observer);
}
void DOMAgent::RemoveObserver(DOMAgentObserver* observer) {
observers_.RemoveObserver(observer);
}
UIElement* DOMAgent::GetElementFromNodeId(int node_id) {
return node_id_to_ui_element_[node_id];
}
Response DOMAgent::HighlightNode(int node_id, bool show_size) {
if (!layer_for_highlighting_) {
layer_for_highlighting_.reset(new ui::Layer(ui::LayerType::LAYER_TEXTURED));
layer_for_highlighting_->set_name("HighlightingLayer");
layer_for_highlighting_->set_delegate(this);
layer_for_highlighting_->SetFillsBoundsOpaquely(false);
}
std::pair<aura::Window*, gfx::Rect> window_and_bounds =
node_id_to_ui_element_.count(node_id)
? node_id_to_ui_element_[node_id]->GetNodeWindowAndBounds()
: std::make_pair<aura::Window*, gfx::Rect>(nullptr, gfx::Rect());
if (!window_and_bounds.first)
return Response::Error("No node found with that id");
highlight_rect_config_ = HighlightRectsConfiguration::NO_DRAW;
show_size_on_canvas_ = show_size;
UpdateHighlight(window_and_bounds);
if (!layer_for_highlighting_->visible())
layer_for_highlighting_->SetVisible(true);
return Response::OK();
}
int DOMAgent::FindElementIdTargetedByPoint(const gfx::Point& p,
aura::Window* root_window) const {
aura::Window* targeted_window = root_window->GetEventHandlerForPoint(p);
if (!targeted_window)
return 0;
views::Widget* targeted_widget =
views::Widget::GetWidgetForNativeWindow(targeted_window);
if (!targeted_widget) {
return window_element_root_->FindUIElementIdForBackendElement<aura::Window>(
targeted_window);
}
views::View* root_view = targeted_widget->GetRootView();
DCHECK(root_view);
gfx::Point point_in_targeted_window(p);
aura::Window::ConvertPointToTarget(root_window, targeted_window,
&point_in_targeted_window);
views::View* targeted_view =
root_view->GetEventHandlerForPoint(point_in_targeted_window);
DCHECK(targeted_view);
return window_element_root_->FindUIElementIdForBackendElement<views::View>(
targeted_view);
}
void DOMAgent::ShowDistancesInHighlightOverlay(int pinned_id, int element_id) {
const std::pair<aura::Window*, gfx::Rect> pair_r2(
node_id_to_ui_element_[element_id]->GetNodeWindowAndBounds());
const std::pair<aura::Window*, gfx::Rect> pair_r1(
node_id_to_ui_element_[pinned_id]->GetNodeWindowAndBounds());
gfx::Rect r2(pair_r2.second);
gfx::Rect r1(pair_r1.second);
pinned_rect_ = r1;
is_swap_ = false;
if (r1.x() > r2.x()) {
is_swap_ = true;
std::swap(r1, r2);
}
if (r1.Contains(r2)) {
highlight_rect_config_ = HighlightRectsConfiguration::R1_CONTAINS_R2;
} else if (r1.right() <= r2.x()) {
if ((r1.y() <= r2.y() && r2.y() <= r1.bottom()) ||
(r1.y() <= r2.bottom() && r2.bottom() <= r1.bottom()) ||
(r2.y() <= r1.y() && r1.y() <= r2.bottom()) ||
(r2.y() <= r1.bottom() && r1.bottom() <= r2.bottom())) {
highlight_rect_config_ =
HighlightRectsConfiguration::R1_HORIZONTAL_FULL_LEFT_R2;
} else if (r1.bottom() <= r2.y()) {
highlight_rect_config_ = HighlightRectsConfiguration::R1_TOP_FULL_LEFT_R2;
} else if (r1.y() >= r2.bottom()) {
highlight_rect_config_ =
HighlightRectsConfiguration::R1_BOTTOM_FULL_LEFT_R2;
}
} else if (r1.x() <= r2.x() && r2.x() <= r1.right()) {
if (r1.bottom() <= r2.y()) {
highlight_rect_config_ =
HighlightRectsConfiguration::R1_TOP_PARTIAL_LEFT_R2;
} else if (r1.y() >= r2.bottom()) {
highlight_rect_config_ =
HighlightRectsConfiguration::R1_BOTTOM_PARTIAL_LEFT_R2;
} else if (r1.Intersects(r2)) {
highlight_rect_config_ = HighlightRectsConfiguration::R1_INTERSECTS_R2;
} else {
NOTREACHED();
}
} else {
highlight_rect_config_ = HighlightRectsConfiguration::NO_DRAW;
}
}
int DOMAgent::GetParentIdOfNodeId(int node_id) const {
DCHECK(node_id_to_ui_element_.count(node_id));
const UIElement* element = node_id_to_ui_element_.at(node_id);
if (element->parent() && element->parent() != window_element_root_.get())
return element->parent()->node_id();
return 0;
}
void DOMAgent::OnPaintLayer(const ui::PaintContext& context) {
const gfx::Rect& screen_bounds(layer_for_highlighting_->bounds());
ui::PaintRecorder recorder(context, screen_bounds.size());
gfx::Canvas* canvas = recorder.canvas();
gfx::RectF hovered_rect_f(hovered_rect_);
cc::PaintFlags flags;
flags.setStrokeWidth(1.0f);
flags.setColor(SK_ColorBLUE);
flags.setStyle(cc::PaintFlags::kStroke_Style);
constexpr SkScalar intervals[] = {1.f, 4.f};
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
if (!render_text_) {
render_text_ =
base::WrapUnique<gfx::RenderText>(gfx::RenderText::CreateInstance());
}
DrawRulers(screen_bounds, canvas, render_text_.get());
// Display guide lines if |highlight_rect_config_| is NO_DRAW.
if (highlight_rect_config_ == HighlightRectsConfiguration::NO_DRAW) {
hovered_rect_f.Inset(gfx::InsetsF(-1));
DrawRectGuideLinesOnCanvas(screen_bounds, hovered_rect_f, flags, canvas);
// Draw |hovered_rect_f| bounds.
flags.setPathEffect(nullptr);
canvas->DrawRect(hovered_rect_f, flags);
// Display size of the rectangle after mouse click.
if (show_size_on_canvas_) {
DrawSizeOfRectangle(hovered_rect_, RectSide::BOTTOM_SIDE, canvas,
render_text_.get());
}
return;
}
flags.setPathEffect(nullptr);
flags.setColor(SK_ColorBLUE);
gfx::RectF pinned_rect_f(pinned_rect_);
// Draw |pinned_rect_f| bounds in blue.
canvas->DrawRect(pinned_rect_f, flags);
// Draw |hovered_rect_f| bounds in green.
flags.setColor(SK_ColorGREEN);
canvas->DrawRect(hovered_rect_f, flags);
// Draw distances in red colour.
flags.setPathEffect(nullptr);
flags.setColor(SK_ColorRED);
// Make sure |pinned_rect_f| stays on the right or below of |hovered_rect_f|.
if (pinned_rect_.x() < hovered_rect_.x() ||
(pinned_rect_.x() == hovered_rect_.x() &&
pinned_rect_.y() < hovered_rect_.y())) {
std::swap(pinned_rect_f, hovered_rect_f);
}
switch (highlight_rect_config_) {
case HighlightRectsConfiguration::R1_CONTAINS_R2:
DrawR1ContainsR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
return;
case HighlightRectsConfiguration::R1_HORIZONTAL_FULL_LEFT_R2:
DrawR1HorizontalFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
return;
case HighlightRectsConfiguration::R1_TOP_FULL_LEFT_R2:
DrawR1TopFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
// Draw 4 guide lines along distance lines.
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
// Bottom horizontal dotted line from left to right.
canvas->DrawLine(
gfx::PointF(0.0f, hovered_rect_f.bottom()),
gfx::PointF(screen_bounds.right(), hovered_rect_f.bottom()), flags);
// Right vertical dotted line from top to bottom.
canvas->DrawLine(
gfx::PointF(hovered_rect_f.right(), 0.0f),
gfx::PointF(hovered_rect_f.right(), screen_bounds.bottom()), flags);
// Top horizontal dotted line from left to right.
canvas->DrawLine(gfx::PointF(0.0f, pinned_rect_f.y()),
gfx::PointF(screen_bounds.right(), pinned_rect_f.y()),
flags);
// Left vertical dotted line from top to bottom.
canvas->DrawLine(gfx::PointF(pinned_rect_f.x(), 0.0f),
gfx::PointF(pinned_rect_f.x(), screen_bounds.bottom()),
flags);
return;
case HighlightRectsConfiguration::R1_BOTTOM_FULL_LEFT_R2:
DrawR1BottomFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
// Draw 2 guide lines along distance lines.
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
// Top horizontal dotted line from left to right.
canvas->DrawLine(
gfx::PointF(0.0f, pinned_rect_f.bottom()),
gfx::PointF(screen_bounds.right(), pinned_rect_f.bottom()), flags);
// Left vertical dotted line from top to bottom.
canvas->DrawLine(gfx::PointF(pinned_rect_f.x(), 0.0f),
gfx::PointF(pinned_rect_f.x(), screen_bounds.bottom()),
flags);
return;
case HighlightRectsConfiguration::R1_TOP_PARTIAL_LEFT_R2:
DrawR1TopPartialLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
// Draw 1 guide line along distance lines.
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
// Top horizontal dotted line from left to right.
canvas->DrawLine(gfx::PointF(0.0f, pinned_rect_f.y()),
gfx::PointF(screen_bounds.right(), pinned_rect_f.y()),
flags);
return;
case HighlightRectsConfiguration::R1_BOTTOM_PARTIAL_LEFT_R2:
DrawR1BottomPartialLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
return;
case HighlightRectsConfiguration::R1_INTERSECTS_R2:
DrawR1IntersectsR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
// Draw 4 guide line along distance lines.
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
DrawRectGuideLinesOnCanvas(screen_bounds, hovered_rect_f, flags, canvas);
return;
default:
NOTREACHED();
return;
}
}
void DOMAgent::OnHostInitialized(aura::WindowTreeHost* host) {
root_windows_.push_back(host->window());
}
void DOMAgent::OnElementBoundsChanged(UIElement* ui_element) {
for (auto& observer : observers_)
observer.OnElementBoundsChanged(ui_element);
}
std::unique_ptr<DOM::Node> DOMAgent::BuildInitialTree() {
is_building_tree_ = true;
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
// TODO(thanhph): Root of UIElement tree shoudn't be WindowElement
// but maybe a new different element type.
window_element_root_ =
base::MakeUnique<WindowElement>(nullptr, this, nullptr);
for (aura::Window* window : root_windows()) {
UIElement* window_element =
new WindowElement(window, this, window_element_root_.get());
children->addItem(BuildTreeForUIElement(window_element));
window_element_root_->AddChild(window_element);
}
std::unique_ptr<DOM::Node> root_node = BuildNode(
"root", nullptr, std::move(children), window_element_root_->node_id());
is_building_tree_ = false;
return root_node;
}
std::unique_ptr<DOM::Node> DOMAgent::BuildTreeForUIElement(
UIElement* ui_element) {
if (ui_element->type() == UIElementType::WINDOW) {
return BuildTreeForWindow(
ui_element,
UIElement::GetBackingElement<aura::Window, WindowElement>(ui_element));
} else if (ui_element->type() == UIElementType::WIDGET) {
return BuildTreeForRootWidget(
ui_element,
UIElement::GetBackingElement<views::Widget, WidgetElement>(ui_element));
} else if (ui_element->type() == UIElementType::VIEW) {
return BuildTreeForView(
ui_element,
UIElement::GetBackingElement<views::View, ViewElement>(ui_element));
}
return nullptr;
}
std::unique_ptr<DOM::Node> DOMAgent::BuildTreeForWindow(
UIElement* window_element_root,
aura::Window* window) {
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
views::Widget* widget = GetWidgetFromWindow(window);
if (widget) {
UIElement* widget_element =
new WidgetElement(widget, this, window_element_root);
children->addItem(BuildTreeForRootWidget(widget_element, widget));
window_element_root->AddChild(widget_element);
}
for (aura::Window* child : window->children()) {
UIElement* window_element =
new WindowElement(child, this, window_element_root);
children->addItem(BuildTreeForWindow(window_element, child));
window_element_root->AddChild(window_element);
}
std::unique_ptr<DOM::Node> node =
BuildNode("Window", GetAttributes(window_element_root),
std::move(children), window_element_root->node_id());
return node;
}
std::unique_ptr<DOM::Node> DOMAgent::BuildTreeForRootWidget(
UIElement* widget_element,
views::Widget* widget) {
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
UIElement* view_element =
new ViewElement(widget->GetRootView(), this, widget_element);
children->addItem(BuildTreeForView(view_element, widget->GetRootView()));
widget_element->AddChild(view_element);
std::unique_ptr<DOM::Node> node =
BuildNode("Widget", GetAttributes(widget_element), std::move(children),
widget_element->node_id());
return node;
}
std::unique_ptr<DOM::Node> DOMAgent::BuildTreeForView(UIElement* view_element,
views::View* view) {
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
for (auto* child : view->GetChildrenInZOrder()) {
UIElement* view_element_child = new ViewElement(child, this, view_element);
children->addItem(BuildTreeForView(view_element_child, child));
view_element->AddChild(view_element_child);
}
std::unique_ptr<DOM::Node> node =
BuildNode("View", GetAttributes(view_element), std::move(children),
view_element->node_id());
return node;
}
void DOMAgent::RemoveDomNode(UIElement* ui_element) {
for (auto* child_element : ui_element->children())
RemoveDomNode(child_element);
frontend()->childNodeRemoved(ui_element->parent()->node_id(),
ui_element->node_id());
}
void DOMAgent::Reset() {
is_building_tree_ = false;
render_text_.reset();
layer_for_highlighting_.reset();
window_element_root_.reset();
node_id_to_ui_element_.clear();
observers_.Clear();
}
void DOMAgent::UpdateHighlight(
const std::pair<aura::Window*, gfx::Rect>& window_and_bounds) {
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestWindow(
window_and_bounds.first);
aura::Window* root = window_and_bounds.first->GetRootWindow();
layer_for_highlighting_->SetBounds(root->bounds());
layer_for_highlighting_->SchedulePaint(root->bounds());
if (root->layer() != layer_for_highlighting_->parent())
root->layer()->Add(layer_for_highlighting_.get());
else
root->layer()->StackAtTop(layer_for_highlighting_.get());
aura::client::ScreenPositionClient* screen_position_client =
aura::client::GetScreenPositionClient(root);
hovered_rect_ = window_and_bounds.second;
gfx::Point origin = hovered_rect_.origin();
screen_position_client->ConvertPointFromScreen(root, &origin);
hovered_rect_.set_origin(origin);
}
} // namespace ui_devtools