1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/ui_devtools/views/overlay_agent_views.h"
6
7 #include "base/strings/utf_string_conversions.h"
8 #include "build/build_config.h"
9 #include "components/ui_devtools/ui_element.h"
10 #include "components/ui_devtools/views/view_element.h"
11 #include "components/ui_devtools/views/widget_element.h"
12 #include "third_party/skia/include/core/SkColor.h"
13 #include "third_party/skia/include/effects/SkDashPathEffect.h"
14 #include "ui/compositor/paint_recorder.h"
15 #include "ui/events/event.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/geometry/rect_conversions.h"
18 #include "ui/gfx/render_text.h"
19 #include "ui/views/background.h"
20 #include "ui/views/border.h"
21
22 #if defined(USE_AURA)
23 #include "ui/aura/window.h"
24 #include "ui/wm/core/window_util.h"
25 #endif
26
27 namespace ui_devtools {
28
29 namespace {
30
DrawRulerText(const base::string16 & utf16_text,const gfx::Point & p,gfx::Canvas * canvas,gfx::RenderText * render_text_)31 void DrawRulerText(const base::string16& utf16_text,
32 const gfx::Point& p,
33 gfx::Canvas* canvas,
34 gfx::RenderText* render_text_) {
35 render_text_->SetText(utf16_text);
36 render_text_->SetColor(SK_ColorRED);
37 const gfx::Rect text_rect(gfx::Rect(p, render_text_->GetStringSize()));
38 canvas->FillRect(text_rect, SK_ColorWHITE, SkBlendMode::kColor);
39 render_text_->SetDisplayRect(text_rect);
40 render_text_->Draw(canvas);
41 }
42
DrawRulers(const gfx::Rect & screen_bounds,gfx::Canvas * canvas,gfx::RenderText * render_text_)43 void DrawRulers(const gfx::Rect& screen_bounds,
44 gfx::Canvas* canvas,
45 gfx::RenderText* render_text_) {
46 // Top horizontal ruler from left to right.
47 canvas->Draw1pxLine(gfx::PointF(0.0f, 0.0f),
48 gfx::PointF(screen_bounds.right(), 0.0f),
49 SK_ColorMAGENTA);
50
51 // Left vertical ruler from top to bottom.
52 canvas->Draw1pxLine(gfx::PointF(0.0f, 0.0f),
53 gfx::PointF(0.0f, screen_bounds.bottom()),
54 SK_ColorMAGENTA);
55
56 int short_stroke = 5;
57 int long_stroke = 10;
58 int gap_between_strokes = 4;
59 int gap_between_long_stroke = 100;
60
61 // Draw top horizontal ruler.
62 for (int x = gap_between_strokes; x < screen_bounds.right();
63 x += gap_between_strokes) {
64 if (x % gap_between_long_stroke == 0) {
65 canvas->Draw1pxLine(gfx::PointF(x, 0.0f), gfx::PointF(x, long_stroke),
66 SK_ColorMAGENTA);
67 // Draw ruler marks.
68 base::string16 utf16_text = base::UTF8ToUTF16(std::to_string(x));
69 DrawRulerText(utf16_text, gfx::Point(x + 2, long_stroke), canvas,
70 render_text_);
71
72 } else {
73 canvas->Draw1pxLine(gfx::PointF(x, 0.0f), gfx::PointF(x, short_stroke),
74 SK_ColorMAGENTA);
75 }
76 }
77
78 // Draw left vertical ruler.
79 for (int y = 0; y < screen_bounds.bottom(); y += gap_between_strokes) {
80 if (y % gap_between_long_stroke == 0) {
81 canvas->Draw1pxLine(gfx::PointF(0.0f, y), gfx::PointF(long_stroke, y),
82 SK_ColorMAGENTA);
83 // Draw ruler marks.
84 base::string16 utf16_text = base::UTF8ToUTF16(std::to_string(y));
85 DrawRulerText(utf16_text, gfx::Point(short_stroke + 1, y + 2), canvas,
86 render_text_);
87 } else {
88 canvas->Draw1pxLine(gfx::PointF(0.0f, y), gfx::PointF(short_stroke, y),
89 SK_ColorMAGENTA);
90 }
91 }
92 }
93
94 // Draw width() x height() of a rectangle if not empty. Otherwise, draw either
95 // width() or height() if any of them is not empty.
DrawSizeOfRectangle(const gfx::Rect & hovered_rect,const RectSide drawing_side,gfx::Canvas * canvas,gfx::RenderText * render_text_)96 void DrawSizeOfRectangle(const gfx::Rect& hovered_rect,
97 const RectSide drawing_side,
98 gfx::Canvas* canvas,
99 gfx::RenderText* render_text_) {
100 base::string16 utf16_text;
101 const std::string unit = "dp";
102
103 if (!hovered_rect.IsEmpty()) {
104 utf16_text = base::UTF8ToUTF16(hovered_rect.size().ToString() + unit);
105 } else if (hovered_rect.height()) {
106 // Draw only height() if height() is not empty.
107 utf16_text =
108 base::UTF8ToUTF16(std::to_string(hovered_rect.height()) + unit);
109 } else if (hovered_rect.width()) {
110 // Draw only width() if width() is not empty.
111 utf16_text = base::UTF8ToUTF16(std::to_string(hovered_rect.width()) + unit);
112 } else {
113 // If both width() and height() are empty, canvas won't draw size.
114 return;
115 }
116 render_text_->SetText(utf16_text);
117 render_text_->SetColor(SK_ColorRED);
118
119 const gfx::Size& text_size = render_text_->GetStringSize();
120 gfx::Rect text_rect;
121 if (drawing_side == RectSide::LEFT_SIDE) {
122 const gfx::Point text_left_side(
123 hovered_rect.x() + 1,
124 hovered_rect.height() / 2 - text_size.height() / 2 + hovered_rect.y());
125 text_rect = gfx::Rect(text_left_side,
126 gfx::Size(text_size.width(), text_size.height()));
127 } else if (drawing_side == RectSide::RIGHT_SIDE) {
128 const gfx::Point text_right_side(
129 hovered_rect.right() - 1 - text_size.width(),
130 hovered_rect.height() / 2 - text_size.height() / 2 + hovered_rect.y());
131 text_rect = gfx::Rect(text_right_side,
132 gfx::Size(text_size.width(), text_size.height()));
133 } else if (drawing_side == RectSide::TOP_SIDE) {
134 const gfx::Point text_top_side(
135 hovered_rect.x() + hovered_rect.width() / 2 - text_size.width() / 2,
136 hovered_rect.y() + 1);
137 text_rect = gfx::Rect(text_top_side,
138 gfx::Size(text_size.width(), text_size.height()));
139 } else if (drawing_side == RectSide::BOTTOM_SIDE) {
140 const gfx::Point text_top_side(
141 hovered_rect.x() + hovered_rect.width() / 2 - text_size.width() / 2,
142 hovered_rect.bottom() - 1 - text_size.height());
143 text_rect = gfx::Rect(text_top_side,
144 gfx::Size(text_size.width(), text_size.height()));
145 }
146 canvas->FillRect(text_rect, SK_ColorWHITE, SkBlendMode::kColor);
147 render_text_->SetDisplayRect(text_rect);
148 render_text_->Draw(canvas);
149 }
150
DrawRectGuideLinesOnCanvas(const gfx::Rect & screen_bounds,const gfx::RectF & rect_f,cc::PaintFlags flags,gfx::Canvas * canvas)151 void DrawRectGuideLinesOnCanvas(const gfx::Rect& screen_bounds,
152 const gfx::RectF& rect_f,
153 cc::PaintFlags flags,
154 gfx::Canvas* canvas) {
155 // Top horizontal dotted line from left to right.
156 canvas->DrawLine(gfx::PointF(0.0f, rect_f.y()),
157 gfx::PointF(screen_bounds.right(), rect_f.y()), flags);
158
159 // Bottom horizontal dotted line from left to right.
160 canvas->DrawLine(gfx::PointF(0.0f, rect_f.bottom()),
161 gfx::PointF(screen_bounds.right(), rect_f.bottom()), flags);
162
163 // Left vertical dotted line from top to bottom.
164 canvas->DrawLine(gfx::PointF(rect_f.x(), 0.0f),
165 gfx::PointF(rect_f.x(), screen_bounds.bottom()), flags);
166
167 // Right vertical dotted line from top to bottom.
168 canvas->DrawLine(gfx::PointF(rect_f.right(), 0.0f),
169 gfx::PointF(rect_f.right(), screen_bounds.bottom()), flags);
170 }
171
DrawSizeWithAnyBounds(float x1,float y1,float x2,float y2,RectSide side,gfx::Canvas * canvas,gfx::RenderText * render_text)172 void DrawSizeWithAnyBounds(float x1,
173 float y1,
174 float x2,
175 float y2,
176 RectSide side,
177 gfx::Canvas* canvas,
178 gfx::RenderText* render_text) {
179 if (x2 > x1 || y2 > y1) {
180 DrawSizeOfRectangle(gfx::Rect(x1, y1, x2 - x1, y2 - y1), side, canvas,
181 render_text);
182 } else {
183 DrawSizeOfRectangle(gfx::Rect(x2, y2, x1 - x2, y1 - y2), side, canvas,
184 render_text);
185 }
186 }
187
DrawR1ContainsR2(const gfx::RectF & pinned_rect_f,const gfx::RectF & hovered_rect_f,const cc::PaintFlags & flags,gfx::Canvas * canvas,gfx::RenderText * render_text)188 void DrawR1ContainsR2(const gfx::RectF& pinned_rect_f,
189 const gfx::RectF& hovered_rect_f,
190 const cc::PaintFlags& flags,
191 gfx::Canvas* canvas,
192 gfx::RenderText* render_text) {
193 // Horizontal left distance line.
194 float x1 = pinned_rect_f.x();
195 float y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
196 float x2 = hovered_rect_f.x();
197 float y2 = y1;
198 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
199 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
200 render_text);
201
202 // Horizontal right distance line.
203 x1 = hovered_rect_f.right();
204 y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
205 x2 = pinned_rect_f.right();
206 y2 = y1;
207 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
208 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
209 render_text);
210
211 // Vertical top distance line.
212 x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
213 y1 = pinned_rect_f.y();
214 x2 = x1;
215 y2 = hovered_rect_f.y();
216 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
217 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
218 render_text);
219
220 // Vertical bottom distance line.
221 x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
222 y1 = hovered_rect_f.bottom();
223 x2 = x1;
224 y2 = pinned_rect_f.bottom();
225 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
226 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
227 render_text);
228 }
229
DrawR1HorizontalFullLeftR2(const gfx::RectF & pinned_rect_f,const gfx::RectF & hovered_rect_f,const cc::PaintFlags & flags,gfx::Canvas * canvas,gfx::RenderText * render_text)230 void DrawR1HorizontalFullLeftR2(const gfx::RectF& pinned_rect_f,
231 const gfx::RectF& hovered_rect_f,
232 const cc::PaintFlags& flags,
233 gfx::Canvas* canvas,
234 gfx::RenderText* render_text) {
235 // Horizontal left distance line.
236 float x1 = hovered_rect_f.right();
237 float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
238 float x2 = pinned_rect_f.x();
239 float y2 = y1;
240 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
241 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
242 render_text);
243 }
244
DrawR1TopFullLeftR2(const gfx::RectF & pinned_rect_f,const gfx::RectF & hovered_rect_f,const cc::PaintFlags & flags,gfx::Canvas * canvas_,gfx::RenderText * render_text)245 void DrawR1TopFullLeftR2(const gfx::RectF& pinned_rect_f,
246 const gfx::RectF& hovered_rect_f,
247 const cc::PaintFlags& flags,
248 gfx::Canvas* canvas_,
249 gfx::RenderText* render_text) {
250 float x1 = hovered_rect_f.x() + hovered_rect_f.width();
251 float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
252 float x2 = pinned_rect_f.x();
253 float y2 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
254
255 // Horizontal left dotted line.
256 canvas_->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
257 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas_,
258 render_text);
259 x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
260 y1 = hovered_rect_f.y() + hovered_rect_f.height();
261 x2 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
262 y2 = pinned_rect_f.y();
263
264 // Vertical left dotted line.
265 canvas_->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
266 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas_,
267 render_text);
268 }
269
DrawR1BottomFullLeftR2(const gfx::RectF & pinned_rect_f,const gfx::RectF & hovered_rect_f,const cc::PaintFlags & flags,gfx::Canvas * canvas,gfx::RenderText * render_text)270 void DrawR1BottomFullLeftR2(const gfx::RectF& pinned_rect_f,
271 const gfx::RectF& hovered_rect_f,
272 const cc::PaintFlags& flags,
273 gfx::Canvas* canvas,
274 gfx::RenderText* render_text) {
275 float x1 = hovered_rect_f.right();
276 float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
277 float x2 = pinned_rect_f.x();
278 float y2 = y1;
279
280 // Horizontal left distance line.
281 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
282 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
283 render_text);
284
285 x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
286 y1 = pinned_rect_f.bottom();
287 x2 = x1;
288 y2 = hovered_rect_f.y();
289
290 // Vertical left distance line.
291 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
292 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
293 render_text);
294 }
295
DrawR1TopPartialLeftR2(const gfx::RectF & pinned_rect_f,const gfx::RectF & hovered_rect_f,const cc::PaintFlags & flags,gfx::Canvas * canvas,gfx::RenderText * render_text)296 void DrawR1TopPartialLeftR2(const gfx::RectF& pinned_rect_f,
297 const gfx::RectF& hovered_rect_f,
298 const cc::PaintFlags& flags,
299 gfx::Canvas* canvas,
300 gfx::RenderText* render_text) {
301 float x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
302 float y1 = hovered_rect_f.bottom();
303 float x2 = x1;
304 float y2 = pinned_rect_f.y();
305
306 // Vertical left dotted line.
307 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
308 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
309 render_text);
310 }
311
DrawR1BottomPartialLeftR2(const gfx::RectF & pinned_rect_f,const gfx::RectF & hovered_rect_f,const cc::PaintFlags & flags,gfx::Canvas * canvas,gfx::RenderText * render_text)312 void DrawR1BottomPartialLeftR2(const gfx::RectF& pinned_rect_f,
313 const gfx::RectF& hovered_rect_f,
314 const cc::PaintFlags& flags,
315 gfx::Canvas* canvas,
316 gfx::RenderText* render_text) {
317 float x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
318 float y1 = pinned_rect_f.bottom();
319 float x2 = x1;
320 float y2 = hovered_rect_f.y();
321
322 // Vertical left dotted line.
323 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
324 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
325 render_text);
326 }
327
DrawR1IntersectsR2(const gfx::RectF & pinned_rect_f,const gfx::RectF & hovered_rect_f,const cc::PaintFlags & flags,gfx::Canvas * canvas,gfx::RenderText * render_text)328 void DrawR1IntersectsR2(const gfx::RectF& pinned_rect_f,
329 const gfx::RectF& hovered_rect_f,
330 const cc::PaintFlags& flags,
331 gfx::Canvas* canvas,
332 gfx::RenderText* render_text) {
333 // Vertical dotted line for the top side of the pinned rectangle
334 float x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
335 float y1 = pinned_rect_f.y();
336 float x2 = x1;
337 float y2 = hovered_rect_f.y();
338 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
339 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
340 render_text);
341
342 // Vertical dotted line for the bottom side of the pinned rectangle
343 x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
344 y1 = pinned_rect_f.bottom();
345 x2 = x1;
346 y2 = hovered_rect_f.bottom();
347 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
348 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
349 render_text);
350
351 // Horizontal dotted line for the left side of the pinned rectangle
352 x1 = pinned_rect_f.x();
353 y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
354 x2 = hovered_rect_f.x();
355 y2 = y1;
356 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
357 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
358 render_text);
359
360 // Horizontal dotted line for the right side of the pinned rectangle
361 x1 = pinned_rect_f.right();
362 y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
363 x2 = hovered_rect_f.right();
364 y2 = y1;
365 canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
366 DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
367 render_text);
368 }
369
370 } // namespace
371
OverlayAgentViews(DOMAgent * dom_agent)372 OverlayAgentViews::OverlayAgentViews(DOMAgent* dom_agent)
373 : OverlayAgent(dom_agent),
374 show_size_on_canvas_(false),
375 highlight_rect_config_(HighlightRectsConfiguration::NO_DRAW) {}
376
~OverlayAgentViews()377 OverlayAgentViews::~OverlayAgentViews() {}
378
SetPinnedNodeId(int node_id)379 void OverlayAgentViews::SetPinnedNodeId(int node_id) {
380 pinned_id_ = node_id;
381 frontend()->nodeHighlightRequested(pinned_id_);
382 HighlightNode(pinned_id_, true /* show_size */);
383 }
384
setInspectMode(const protocol::String & in_mode,protocol::Maybe<protocol::Overlay::HighlightConfig> in_highlightConfig)385 protocol::Response OverlayAgentViews::setInspectMode(
386 const protocol::String& in_mode,
387 protocol::Maybe<protocol::Overlay::HighlightConfig> in_highlightConfig) {
388 pinned_id_ = 0;
389 if (in_mode.compare("searchForNode") == 0) {
390 InstallPreTargetHandler();
391 } else if (in_mode.compare("none") == 0) {
392 RemovePreTargetHandler();
393 }
394 return protocol::Response::Success();
395 }
396
highlightNode(std::unique_ptr<protocol::Overlay::HighlightConfig> highlight_config,protocol::Maybe<int> node_id)397 protocol::Response OverlayAgentViews::highlightNode(
398 std::unique_ptr<protocol::Overlay::HighlightConfig> highlight_config,
399 protocol::Maybe<int> node_id) {
400 return HighlightNode(node_id.fromJust());
401 }
402
hideHighlight()403 protocol::Response OverlayAgentViews::hideHighlight() {
404 if (layer_for_highlighting_ && layer_for_highlighting_->visible())
405 layer_for_highlighting_->SetVisible(false);
406 return protocol::Response::Success();
407 }
408
ShowDistancesInHighlightOverlay(int pinned_id,int element_id)409 void OverlayAgentViews::ShowDistancesInHighlightOverlay(int pinned_id,
410 int element_id) {
411 UIElement* element_r1 = dom_agent()->GetElementFromNodeId(pinned_id);
412 UIElement* element_r2 = dom_agent()->GetElementFromNodeId(element_id);
413 if (!element_r1 || !element_r2)
414 return;
415
416 const std::pair<gfx::NativeWindow, gfx::Rect> pair_r2(
417 element_r2->GetNodeWindowAndScreenBounds());
418 const std::pair<gfx::NativeWindow, gfx::Rect> pair_r1(
419 element_r1->GetNodeWindowAndScreenBounds());
420 #if defined(OS_MACOSX)
421 // TODO(lgrey): Explain this
422 if (pair_r1.first != pair_r2.first) {
423 pinned_id_ = 0;
424 return;
425 }
426 #endif
427 gfx::Rect r2(pair_r2.second);
428 gfx::Rect r1(pair_r1.second);
429 pinned_rect_ = r1;
430
431 is_swap_ = false;
432 if (r1.x() > r2.x()) {
433 is_swap_ = true;
434 std::swap(r1, r2);
435 }
436 if (r1.Contains(r2)) {
437 highlight_rect_config_ = HighlightRectsConfiguration::R1_CONTAINS_R2;
438 } else if (r1.right() <= r2.x()) {
439 if ((r1.y() <= r2.y() && r2.y() <= r1.bottom()) ||
440 (r1.y() <= r2.bottom() && r2.bottom() <= r1.bottom()) ||
441 (r2.y() <= r1.y() && r1.y() <= r2.bottom()) ||
442 (r2.y() <= r1.bottom() && r1.bottom() <= r2.bottom())) {
443 highlight_rect_config_ =
444 HighlightRectsConfiguration::R1_HORIZONTAL_FULL_LEFT_R2;
445 } else if (r1.bottom() <= r2.y()) {
446 highlight_rect_config_ = HighlightRectsConfiguration::R1_TOP_FULL_LEFT_R2;
447 } else if (r1.y() >= r2.bottom()) {
448 highlight_rect_config_ =
449 HighlightRectsConfiguration::R1_BOTTOM_FULL_LEFT_R2;
450 }
451 } else if (r1.x() <= r2.x() && r2.x() <= r1.right()) {
452 if (r1.bottom() <= r2.y()) {
453 highlight_rect_config_ =
454 HighlightRectsConfiguration::R1_TOP_PARTIAL_LEFT_R2;
455 } else if (r1.y() >= r2.bottom()) {
456 highlight_rect_config_ =
457 HighlightRectsConfiguration::R1_BOTTOM_PARTIAL_LEFT_R2;
458 } else if (r1.Intersects(r2)) {
459 highlight_rect_config_ = HighlightRectsConfiguration::R1_INTERSECTS_R2;
460 } else {
461 NOTREACHED();
462 }
463 } else {
464 highlight_rect_config_ = HighlightRectsConfiguration::NO_DRAW;
465 }
466 }
467
HighlightNode(int node_id,bool show_size)468 protocol::Response OverlayAgentViews::HighlightNode(int node_id,
469 bool show_size) {
470 UIElement* element = dom_agent()->GetElementFromNodeId(node_id);
471 if (!element)
472 return protocol::Response::ServerError("No node found with that id");
473
474 if (element->type() == UIElementType::ROOT)
475 return protocol::Response::ServerError("Cannot highlight root node.");
476
477 if (!layer_for_highlighting_) {
478 layer_for_highlighting_.reset(new ui::Layer(ui::LayerType::LAYER_TEXTURED));
479 layer_for_highlighting_->SetName("HighlightingLayer");
480 layer_for_highlighting_->set_delegate(this);
481 layer_for_highlighting_->SetFillsBoundsOpaquely(false);
482 }
483
484 highlight_rect_config_ = HighlightRectsConfiguration::NO_DRAW;
485 show_size_on_canvas_ = show_size;
486 layer_for_highlighting_->SetVisible(
487 UpdateHighlight(element->GetNodeWindowAndScreenBounds()));
488 return protocol::Response::Success();
489 }
490
OnMouseEvent(ui::MouseEvent * event)491 void OverlayAgentViews::OnMouseEvent(ui::MouseEvent* event) {
492 // Make sure the element tree has been populated before processing
493 // mouse events.
494 if (!dom_agent()->element_root())
495 return;
496
497 // Show parent of the pinned element with id |pinned_id_| when mouse scrolls
498 // up. If parent exists, highlight and re-pin parent element.
499 if (event->type() == ui::ET_MOUSEWHEEL && pinned_id_) {
500 const ui::MouseWheelEvent* mouse_event =
501 static_cast<ui::MouseWheelEvent*>(event);
502 DCHECK(mouse_event);
503 if (mouse_event->y_offset() > 0) {
504 const int parent_node_id = dom_agent()->GetParentIdOfNodeId(pinned_id_);
505 if (parent_node_id)
506 SetPinnedNodeId(parent_node_id);
507 event->SetHandled();
508 } else if (mouse_event->y_offset() < 0) {
509 // TODO(thanhph): discuss behaviours when mouse scrolls down.
510 }
511 return;
512 }
513
514 // Find node id of element whose bounds contain the mouse pointer location.
515 int element_id = FindElementIdTargetedByPoint(event);
516 if (!element_id)
517 return;
518
519 #if defined(USE_AURA)
520 aura::Window* target = static_cast<aura::Window*>(event->target());
521 bool active_window = ::wm::IsActiveWindow(
522 target->GetRootWindow()->GetEventHandlerForPoint(event->root_location()));
523 #else
524 bool active_window = true;
525 #endif
526 if (pinned_id_ == element_id && active_window) {
527 event->SetHandled();
528 return;
529 }
530
531 // Pin the hover element on click.
532 if (event->type() == ui::ET_MOUSE_PRESSED) {
533 if (active_window)
534 event->SetHandled();
535 SetPinnedNodeId(element_id);
536 } else if (pinned_id_) {
537 // If hovering with a pinned element, then show distances between the pinned
538 // element and the hover element.
539 HighlightNode(element_id, false /* show_size */);
540 ShowDistancesInHighlightOverlay(pinned_id_, element_id);
541 } else {
542 // Display only guidelines if hovering without a pinned element.
543 frontend()->nodeHighlightRequested(element_id);
544 HighlightNode(element_id, false /* show_size */);
545 }
546 }
547
OnKeyEvent(ui::KeyEvent * event)548 void OverlayAgentViews::OnKeyEvent(ui::KeyEvent* event) {
549 if (!dom_agent()->element_root())
550 return;
551
552 // Exit inspect mode by pressing ESC key.
553 if (event->key_code() == ui::KeyboardCode::VKEY_ESCAPE) {
554 RemovePreTargetHandler();
555 if (pinned_id_) {
556 frontend()->inspectNodeRequested(pinned_id_);
557 HighlightNode(pinned_id_, true /* show_size */);
558 }
559 // Unpin element.
560 pinned_id_ = 0;
561 }
562 }
563
OnPaintLayer(const ui::PaintContext & context)564 void OverlayAgentViews::OnPaintLayer(const ui::PaintContext& context) {
565 const gfx::Rect& screen_bounds(layer_for_highlighting_->bounds());
566 ui::PaintRecorder recorder(context, screen_bounds.size());
567 gfx::Canvas* canvas = recorder.canvas();
568 // Convert the hovered rect from screen coordinates to layer coordinates.
569 gfx::RectF hovered_rect_f(hovered_rect_);
570 hovered_rect_f.Offset(-layer_for_highlighting_screen_offset_);
571
572 cc::PaintFlags flags;
573 flags.setStrokeWidth(1.0f);
574 flags.setColor(SK_ColorBLUE);
575 flags.setStyle(cc::PaintFlags::kStroke_Style);
576
577 constexpr SkScalar intervals[] = {1.f, 4.f};
578 flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
579
580 if (!render_text_)
581 render_text_ = gfx::RenderText::CreateRenderText();
582 DrawRulers(screen_bounds, canvas, render_text_.get());
583
584 // Display guide lines if |highlight_rect_config_| is NO_DRAW.
585 if (highlight_rect_config_ == HighlightRectsConfiguration::NO_DRAW) {
586 hovered_rect_f.Inset(gfx::InsetsF(-1));
587 DrawRectGuideLinesOnCanvas(screen_bounds, hovered_rect_f, flags, canvas);
588 // Draw |hovered_rect_f| bounds.
589 flags.setPathEffect(nullptr);
590 canvas->DrawRect(hovered_rect_f, flags);
591
592 // Display size of the rectangle after mouse click.
593 if (show_size_on_canvas_) {
594 DrawSizeOfRectangle(gfx::ToNearestRect(hovered_rect_f),
595 RectSide::BOTTOM_SIDE, canvas, render_text_.get());
596 }
597 return;
598 }
599 flags.setPathEffect(nullptr);
600 flags.setColor(SK_ColorBLUE);
601
602 // Convert the pinned rect from screen coordinates to layer coordinates.
603 gfx::RectF pinned_rect_f(pinned_rect_);
604 pinned_rect_f.Offset(-layer_for_highlighting_screen_offset_);
605
606 // Draw |pinned_rect_f| bounds in blue.
607 canvas->DrawRect(pinned_rect_f, flags);
608
609 // Draw |hovered_rect_f| bounds in green.
610 flags.setColor(SK_ColorGREEN);
611 canvas->DrawRect(hovered_rect_f, flags);
612
613 // Draw distances in red colour.
614 flags.setPathEffect(nullptr);
615 flags.setColor(SK_ColorRED);
616
617 // Make sure |pinned_rect_f| stays on the right or below of |hovered_rect_f|.
618 if (pinned_rect_f.x() < hovered_rect_f.x() ||
619 (pinned_rect_f.x() == hovered_rect_f.x() &&
620 pinned_rect_f.y() < hovered_rect_f.y())) {
621 std::swap(pinned_rect_f, hovered_rect_f);
622 }
623
624 switch (highlight_rect_config_) {
625 case HighlightRectsConfiguration::R1_CONTAINS_R2:
626 DrawR1ContainsR2(pinned_rect_f, hovered_rect_f, flags, canvas,
627 render_text_.get());
628 return;
629 case HighlightRectsConfiguration::R1_HORIZONTAL_FULL_LEFT_R2:
630 DrawR1HorizontalFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
631 render_text_.get());
632 return;
633 case HighlightRectsConfiguration::R1_TOP_FULL_LEFT_R2:
634 DrawR1TopFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
635 render_text_.get());
636
637 // Draw 4 guide lines along distance lines.
638 flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
639
640 // Bottom horizontal dotted line from left to right.
641 canvas->DrawLine(
642 gfx::PointF(0.0f, hovered_rect_f.bottom()),
643 gfx::PointF(screen_bounds.right(), hovered_rect_f.bottom()), flags);
644
645 // Right vertical dotted line from top to bottom.
646 canvas->DrawLine(
647 gfx::PointF(hovered_rect_f.right(), 0.0f),
648 gfx::PointF(hovered_rect_f.right(), screen_bounds.bottom()), flags);
649
650 // Top horizontal dotted line from left to right.
651 canvas->DrawLine(gfx::PointF(0.0f, pinned_rect_f.y()),
652 gfx::PointF(screen_bounds.right(), pinned_rect_f.y()),
653 flags);
654
655 // Left vertical dotted line from top to bottom.
656 canvas->DrawLine(gfx::PointF(pinned_rect_f.x(), 0.0f),
657 gfx::PointF(pinned_rect_f.x(), screen_bounds.bottom()),
658 flags);
659 return;
660 case HighlightRectsConfiguration::R1_BOTTOM_FULL_LEFT_R2:
661 DrawR1BottomFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
662 render_text_.get());
663
664 // Draw 2 guide lines along distance lines.
665 flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
666
667 // Top horizontal dotted line from left to right.
668 canvas->DrawLine(
669 gfx::PointF(0.0f, pinned_rect_f.bottom()),
670 gfx::PointF(screen_bounds.right(), pinned_rect_f.bottom()), flags);
671
672 // Left vertical dotted line from top to bottom.
673 canvas->DrawLine(gfx::PointF(pinned_rect_f.x(), 0.0f),
674 gfx::PointF(pinned_rect_f.x(), screen_bounds.bottom()),
675 flags);
676 return;
677 case HighlightRectsConfiguration::R1_TOP_PARTIAL_LEFT_R2:
678 DrawR1TopPartialLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
679 render_text_.get());
680
681 // Draw 1 guide line along distance lines.
682 flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
683
684 // Top horizontal dotted line from left to right.
685 canvas->DrawLine(gfx::PointF(0.0f, pinned_rect_f.y()),
686 gfx::PointF(screen_bounds.right(), pinned_rect_f.y()),
687 flags);
688 return;
689 case HighlightRectsConfiguration::R1_BOTTOM_PARTIAL_LEFT_R2:
690 DrawR1BottomPartialLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
691 render_text_.get());
692 return;
693 case HighlightRectsConfiguration::R1_INTERSECTS_R2:
694 DrawR1IntersectsR2(pinned_rect_f, hovered_rect_f, flags, canvas,
695 render_text_.get());
696 // Draw 4 guide line along distance lines.
697 flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
698
699 DrawRectGuideLinesOnCanvas(screen_bounds, hovered_rect_f, flags, canvas);
700 return;
701 default:
702 NOTREACHED();
703 return;
704 }
705 }
706
UpdateHighlight(const std::pair<gfx::NativeWindow,gfx::Rect> & window_and_bounds)707 bool OverlayAgentViews::UpdateHighlight(
708 const std::pair<gfx::NativeWindow, gfx::Rect>& window_and_bounds) {
709 if (window_and_bounds.second.IsEmpty()) {
710 hovered_rect_.SetRect(0, 0, 0, 0);
711 return false;
712 }
713 ui::Layer* root_layer = nullptr;
714 #if defined(OS_MACOSX)
715 views::Widget* widget =
716 views::Widget::GetWidgetForNativeWindow(window_and_bounds.first);
717 root_layer = widget->GetLayer();
718 layer_for_highlighting_screen_offset_ =
719 widget->GetContentsView()->GetBoundsInScreen().OffsetFromOrigin();
720 #else
721 gfx::NativeWindow root = window_and_bounds.first->GetRootWindow();
722 root_layer = root->layer();
723 layer_for_highlighting_screen_offset_ =
724 root->GetBoundsInScreen().OffsetFromOrigin();
725 #endif // defined(OS_MACOSX)
726 DCHECK(root_layer);
727
728 layer_for_highlighting_->SetBounds(root_layer->bounds());
729 layer_for_highlighting_->SchedulePaint(root_layer->bounds());
730
731 if (root_layer != layer_for_highlighting_->parent())
732 root_layer->Add(layer_for_highlighting_.get());
733 else
734 root_layer->StackAtTop(layer_for_highlighting_.get());
735
736 hovered_rect_ = window_and_bounds.second;
737 return true;
738 }
739
740 } // namespace ui_devtools
741