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