1 // Copyright 2015 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 "third_party/blink/renderer/core/inspector/inspector_highlight.h"
6 
7 #include "base/macros.h"
8 #include "third_party/blink/renderer/core/css/css_color_value.h"
9 #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
10 #include "third_party/blink/renderer/core/css/css_property_names.h"
11 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
12 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
13 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
14 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
15 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
16 #include "third_party/blink/renderer/core/inspector/inspector_dom_agent.h"
17 #include "third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h"
18 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
19 #include "third_party/blink/renderer/core/layout/layout_box.h"
20 #include "third_party/blink/renderer/core/layout/layout_grid.h"
21 #include "third_party/blink/renderer/core/layout/layout_inline.h"
22 #include "third_party/blink/renderer/core/layout/layout_object.h"
23 #include "third_party/blink/renderer/core/layout/layout_view.h"
24 #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
25 #include "third_party/blink/renderer/core/page/chrome_client.h"
26 #include "third_party/blink/renderer/core/page/page.h"
27 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
28 #include "third_party/blink/renderer/platform/graphics/path.h"
29 #include "third_party/blink/renderer/platform/web_test_support.h"
30 
31 namespace blink {
32 
33 namespace {
34 
35 class PathBuilder {
36   STACK_ALLOCATED();
37 
38  public:
PathBuilder()39   PathBuilder() : path_(protocol::ListValue::create()) {}
40   virtual ~PathBuilder() = default;
41 
Release()42   std::unique_ptr<protocol::ListValue> Release() { return std::move(path_); }
43 
AppendPath(const Path & path,float scale)44   void AppendPath(const Path& path, float scale) {
45     Path transform_path(path);
46     transform_path.Transform(AffineTransform().Scale(scale));
47     transform_path.Apply(this, &PathBuilder::AppendPathElement);
48   }
49 
50  protected:
TranslatePoint(const FloatPoint & point)51   virtual FloatPoint TranslatePoint(const FloatPoint& point) { return point; }
52 
53  private:
AppendPathElement(void * path_builder,const PathElement * path_element)54   static void AppendPathElement(void* path_builder,
55                                 const PathElement* path_element) {
56     static_cast<PathBuilder*>(path_builder)->AppendPathElement(path_element);
57   }
58 
59   void AppendPathElement(const PathElement*);
60   void AppendPathCommandAndPoints(const char* command,
61                                   const FloatPoint points[],
62                                   size_t length);
63 
64   std::unique_ptr<protocol::ListValue> path_;
65   DISALLOW_COPY_AND_ASSIGN(PathBuilder);
66 };
67 
AppendPathCommandAndPoints(const char * command,const FloatPoint points[],size_t length)68 void PathBuilder::AppendPathCommandAndPoints(const char* command,
69                                              const FloatPoint points[],
70                                              size_t length) {
71   path_->pushValue(protocol::StringValue::create(command));
72   for (size_t i = 0; i < length; i++) {
73     FloatPoint point = TranslatePoint(points[i]);
74     path_->pushValue(protocol::FundamentalValue::create(point.X()));
75     path_->pushValue(protocol::FundamentalValue::create(point.Y()));
76   }
77 }
78 
AppendPathElement(const PathElement * path_element)79 void PathBuilder::AppendPathElement(const PathElement* path_element) {
80   switch (path_element->type) {
81     // The points member will contain 1 value.
82     case kPathElementMoveToPoint:
83       AppendPathCommandAndPoints("M", path_element->points, 1);
84       break;
85     // The points member will contain 1 value.
86     case kPathElementAddLineToPoint:
87       AppendPathCommandAndPoints("L", path_element->points, 1);
88       break;
89     // The points member will contain 3 values.
90     case kPathElementAddCurveToPoint:
91       AppendPathCommandAndPoints("C", path_element->points, 3);
92       break;
93     // The points member will contain 2 values.
94     case kPathElementAddQuadCurveToPoint:
95       AppendPathCommandAndPoints("Q", path_element->points, 2);
96       break;
97     // The points member will contain no values.
98     case kPathElementCloseSubpath:
99       AppendPathCommandAndPoints("Z", nullptr, 0);
100       break;
101   }
102 }
103 
104 class ShapePathBuilder : public PathBuilder {
105  public:
ShapePathBuilder(LocalFrameView & view,LayoutObject & layout_object,const ShapeOutsideInfo & shape_outside_info)106   ShapePathBuilder(LocalFrameView& view,
107                    LayoutObject& layout_object,
108                    const ShapeOutsideInfo& shape_outside_info)
109       : view_(&view),
110         layout_object_(layout_object),
111         shape_outside_info_(shape_outside_info) {}
112 
BuildPath(LocalFrameView & view,LayoutObject & layout_object,const ShapeOutsideInfo & shape_outside_info,const Path & path,float scale)113   static std::unique_ptr<protocol::ListValue> BuildPath(
114       LocalFrameView& view,
115       LayoutObject& layout_object,
116       const ShapeOutsideInfo& shape_outside_info,
117       const Path& path,
118       float scale) {
119     ShapePathBuilder builder(view, layout_object, shape_outside_info);
120     builder.AppendPath(path, scale);
121     return builder.Release();
122   }
123 
124  protected:
TranslatePoint(const FloatPoint & point)125   FloatPoint TranslatePoint(const FloatPoint& point) override {
126     PhysicalOffset layout_object_point = PhysicalOffset::FromFloatPointRound(
127         shape_outside_info_.ShapeToLayoutObjectPoint(point));
128     // TODO(pfeldman): Is this kIgnoreTransforms correct?
129     return FloatPoint(view_->FrameToViewport(
130         RoundedIntPoint(layout_object_.LocalToAbsolutePoint(
131             layout_object_point, kIgnoreTransforms))));
132   }
133 
134  private:
135   LocalFrameView* view_;
136   LayoutObject& layout_object_;
137   const ShapeOutsideInfo& shape_outside_info_;
138 };
139 
BuildArrayForQuad(const FloatQuad & quad)140 std::unique_ptr<protocol::Array<double>> BuildArrayForQuad(
141     const FloatQuad& quad) {
142   return std::make_unique<std::vector<double>, std::initializer_list<double>>(
143       {quad.P1().X(), quad.P1().Y(), quad.P2().X(), quad.P2().Y(),
144        quad.P3().X(), quad.P3().Y(), quad.P4().X(), quad.P4().Y()});
145 }
146 
QuadToPath(const FloatQuad & quad)147 Path QuadToPath(const FloatQuad& quad) {
148   Path quad_path;
149   quad_path.MoveTo(quad.P1());
150   quad_path.AddLineTo(quad.P2());
151   quad_path.AddLineTo(quad.P3());
152   quad_path.AddLineTo(quad.P4());
153   quad_path.CloseSubpath();
154   return quad_path;
155 }
156 
FramePointToViewport(const LocalFrameView * view,FloatPoint point_in_frame)157 FloatPoint FramePointToViewport(const LocalFrameView* view,
158                                 FloatPoint point_in_frame) {
159   FloatPoint point_in_root_frame = view->ConvertToRootFrame(point_in_frame);
160   return view->GetPage()->GetVisualViewport().RootFrameToViewport(
161       point_in_root_frame);
162 }
163 
FrameQuadToViewport(const LocalFrameView * view,FloatQuad & quad)164 void FrameQuadToViewport(const LocalFrameView* view, FloatQuad& quad) {
165   quad.SetP1(FramePointToViewport(view, quad.P1()));
166   quad.SetP2(FramePointToViewport(view, quad.P2()));
167   quad.SetP3(FramePointToViewport(view, quad.P3()));
168   quad.SetP4(FramePointToViewport(view, quad.P4()));
169 }
170 
ShapeOutsideInfoForNode(Node * node,Shape::DisplayPaths * paths,FloatQuad * bounds)171 const ShapeOutsideInfo* ShapeOutsideInfoForNode(Node* node,
172                                                 Shape::DisplayPaths* paths,
173                                                 FloatQuad* bounds) {
174   LayoutObject* layout_object = node->GetLayoutObject();
175   if (!layout_object || !layout_object->IsBox() ||
176       !ToLayoutBox(layout_object)->GetShapeOutsideInfo())
177     return nullptr;
178 
179   LocalFrameView* containing_view = node->GetDocument().View();
180   LayoutBox* layout_box = ToLayoutBox(layout_object);
181   const ShapeOutsideInfo* shape_outside_info =
182       layout_box->GetShapeOutsideInfo();
183 
184   shape_outside_info->ComputedShape().BuildDisplayPaths(*paths);
185 
186   PhysicalRect shape_bounds =
187       shape_outside_info->ComputedShapePhysicalBoundingBox();
188   *bounds = layout_box->LocalRectToAbsoluteQuad(shape_bounds);
189   FrameQuadToViewport(containing_view, *bounds);
190 
191   return shape_outside_info;
192 }
193 
ToHEXA(const Color & color)194 String ToHEXA(const Color& color) {
195   return String::Format("#%02X%02X%02X%02X", color.Red(), color.Green(),
196                         color.Blue(), color.Alpha());
197 }
198 
AppendStyleInfo(Node * node,protocol::DictionaryValue * element_info,const InspectorHighlightContrastInfo & node_contrast)199 void AppendStyleInfo(Node* node,
200                      protocol::DictionaryValue* element_info,
201                      const InspectorHighlightContrastInfo& node_contrast) {
202   std::unique_ptr<protocol::DictionaryValue> computed_style =
203       protocol::DictionaryValue::create();
204   CSSComputedStyleDeclaration* style =
205       MakeGarbageCollected<CSSComputedStyleDeclaration>(node, true);
206   Vector<CSSPropertyID> properties;
207 
208   // For text nodes, we can show color & font properties.
209   bool has_text_children = false;
210   for (Node* child = node->firstChild(); !has_text_children && child;
211        child = child->nextSibling()) {
212     has_text_children = child->IsTextNode();
213   }
214   if (has_text_children) {
215     properties.push_back(CSSPropertyID::kColor);
216     properties.push_back(CSSPropertyID::kFontFamily);
217     properties.push_back(CSSPropertyID::kFontSize);
218     properties.push_back(CSSPropertyID::kLineHeight);
219   }
220 
221   properties.push_back(CSSPropertyID::kPadding);
222   properties.push_back(CSSPropertyID::kMargin);
223   properties.push_back(CSSPropertyID::kBackgroundColor);
224 
225   for (size_t i = 0; i < properties.size(); ++i) {
226     const CSSValue* value = style->GetPropertyCSSValue(properties[i]);
227     if (!value)
228       continue;
229     AtomicString name = CSSPropertyName(properties[i]).ToAtomicString();
230     if (value->IsColorValue()) {
231       Color color = static_cast<const cssvalue::CSSColorValue*>(value)->Value();
232       computed_style->setString(name, ToHEXA(color));
233     } else {
234       computed_style->setString(name, value->CssText());
235     }
236   }
237   element_info->setValue("style", std::move(computed_style));
238 
239   if (!node_contrast.font_size.IsEmpty()) {
240     std::unique_ptr<protocol::DictionaryValue> contrast =
241         protocol::DictionaryValue::create();
242     contrast->setString("fontSize", node_contrast.font_size);
243     contrast->setString("fontWeight", node_contrast.font_weight);
244     contrast->setString("backgroundColor",
245                         ToHEXA(node_contrast.background_color));
246     element_info->setValue("contrast", std::move(contrast));
247   }
248 }
249 
BuildElementInfo(Element * element)250 std::unique_ptr<protocol::DictionaryValue> BuildElementInfo(Element* element) {
251   std::unique_ptr<protocol::DictionaryValue> element_info =
252       protocol::DictionaryValue::create();
253   Element* real_element = element;
254   auto* pseudo_element = DynamicTo<PseudoElement>(element);
255   if (pseudo_element) {
256     real_element = element->ParentOrShadowHostElement();
257   }
258   bool is_xhtml = real_element->GetDocument().IsXHTMLDocument();
259   element_info->setString(
260       "tagName", is_xhtml ? real_element->nodeName()
261                           : real_element->nodeName().DeprecatedLower());
262   element_info->setString("idValue", real_element->GetIdAttribute());
263   StringBuilder class_names;
264   if (real_element->HasClass() && real_element->IsStyledElement()) {
265     HashSet<AtomicString> used_class_names;
266     const SpaceSplitString& class_names_string = real_element->ClassNames();
267     wtf_size_t class_name_count = class_names_string.size();
268     for (wtf_size_t i = 0; i < class_name_count; ++i) {
269       const AtomicString& class_name = class_names_string[i];
270       if (!used_class_names.insert(class_name).is_new_entry)
271         continue;
272       class_names.Append('.');
273       class_names.Append(class_name);
274     }
275   }
276   if (pseudo_element) {
277     if (pseudo_element->GetPseudoId() == kPseudoIdBefore)
278       class_names.Append("::before");
279     else if (pseudo_element->GetPseudoId() == kPseudoIdAfter)
280       class_names.Append("::after");
281     else if (pseudo_element->GetPseudoId() == kPseudoIdMarker)
282       class_names.Append("::marker");
283   }
284   if (!class_names.IsEmpty())
285     element_info->setString("className", class_names.ToString());
286 
287   LayoutObject* layout_object = element->GetLayoutObject();
288   LocalFrameView* containing_view = element->GetDocument().View();
289   if (!layout_object || !containing_view)
290     return element_info;
291 
292   // if (auto* context = element->GetDisplayLockContext()) {
293   //  if (context->IsLocked()) {
294   //    // If it's a locked element, use the values from the locked frame rect.
295   //    // TODO(vmpstr): Verify that these values are correct here.
296   //    element_info->setString(
297   //        "nodeWidth",
298   //        String::Number(context->GetLockedContentLogicalWidth().ToDouble()));
299   //    element_info->setString(
300   //        "nodeHeight",
301   //        String::Number(context->GetLockedContentLogicalHeight().ToDouble()));
302   //  }
303   //  return element_info;
304   //}
305 
306   // layoutObject the getBoundingClientRect() data in the tooltip
307   // to be consistent with the rulers (see http://crbug.com/262338).
308   DOMRect* bounding_box = element->getBoundingClientRect();
309   element_info->setString("nodeWidth", String::Number(bounding_box->width()));
310   element_info->setString("nodeHeight", String::Number(bounding_box->height()));
311   return element_info;
312 }
313 
BuildTextNodeInfo(Text * text_node)314 std::unique_ptr<protocol::DictionaryValue> BuildTextNodeInfo(Text* text_node) {
315   std::unique_ptr<protocol::DictionaryValue> text_info =
316       protocol::DictionaryValue::create();
317   LayoutObject* layout_object = text_node->GetLayoutObject();
318   if (!layout_object || !layout_object->IsText())
319     return text_info;
320   PhysicalRect bounding_box =
321       ToLayoutText(layout_object)->PhysicalVisualOverflowRect();
322   text_info->setString("nodeWidth", bounding_box.Width().ToString());
323   text_info->setString("nodeHeight", bounding_box.Height().ToString());
324   text_info->setString("tagName", "#text");
325   return text_info;
326 }
327 
BuildGridInfo(LocalFrameView * containing_view,LayoutGrid * layout_grid,Color color,float scale,bool isPrimary)328 std::unique_ptr<protocol::DictionaryValue> BuildGridInfo(
329     LocalFrameView* containing_view,
330     LayoutGrid* layout_grid,
331     Color color,
332     float scale,
333     bool isPrimary) {
334   std::unique_ptr<protocol::DictionaryValue> grid_info =
335       protocol::DictionaryValue::create();
336 
337   const auto& rows = layout_grid->RowPositions();
338   const auto& columns = layout_grid->ColumnPositions();
339 
340   PathBuilder cell_builder;
341   auto row_gap =
342       layout_grid->GridGap(kForRows) + layout_grid->GridItemOffset(kForRows);
343   auto column_gap = layout_grid->GridGap(kForColumns) +
344                     layout_grid->GridItemOffset(kForColumns);
345 
346   for (size_t i = 1; i < rows.size(); ++i) {
347     for (size_t j = 1; j < columns.size(); ++j) {
348       PhysicalOffset position(columns.at(j - 1), rows.at(i - 1));
349       PhysicalSize size(columns.at(j) - columns.at(j - 1),
350                         rows.at(i) - rows.at(i - 1));
351       if (i != rows.size() - 1)
352         size.height -= row_gap;
353       if (j != columns.size() - 1)
354         size.width -= column_gap;
355       PhysicalRect cell(position, size);
356       FloatQuad cell_quad = layout_grid->LocalRectToAbsoluteQuad(cell);
357       FrameQuadToViewport(containing_view, cell_quad);
358       cell_builder.AppendPath(QuadToPath(cell_quad), scale);
359     }
360   }
361   grid_info->setValue("cells", cell_builder.Release());
362   grid_info->setString("color", color.Serialized());
363   grid_info->setBoolean("isPrimaryGrid", isPrimary);
364   return grid_info;
365 }
366 
CollectQuadsRecursive(Node * node,Vector<FloatQuad> & out_quads)367 void CollectQuadsRecursive(Node* node, Vector<FloatQuad>& out_quads) {
368   LayoutObject* layout_object = node->GetLayoutObject();
369   if (!layout_object)
370     return;
371 
372   // For inline elements, absoluteQuads will return a line box based on the
373   // line-height and font metrics, which is technically incorrect as replaced
374   // elements like images should use their intristic height and expand the
375   // linebox  as needed. To get an appropriate quads we descend
376   // into the children and have them add their boxes.
377   if (layout_object->IsLayoutInline() &&
378       LayoutTreeBuilderTraversal::FirstChild(*node)) {
379     for (Node* child = LayoutTreeBuilderTraversal::FirstChild(*node); child;
380          child = LayoutTreeBuilderTraversal::NextSibling(*child))
381       CollectQuadsRecursive(child, out_quads);
382   } else {
383     layout_object->AbsoluteQuads(out_quads);
384   }
385 }
386 
CollectQuads(Node * node,Vector<FloatQuad> & out_quads)387 void CollectQuads(Node* node, Vector<FloatQuad>& out_quads) {
388   CollectQuadsRecursive(node, out_quads);
389   LocalFrameView* containing_view =
390       node->GetLayoutObject() ? node->GetLayoutObject()->GetFrameView()
391                               : nullptr;
392   if (containing_view) {
393     for (FloatQuad& quad : out_quads)
394       FrameQuadToViewport(containing_view, quad);
395   }
396 }
397 
RectForPhysicalRect(const PhysicalRect & rect)398 std::unique_ptr<protocol::Array<double>> RectForPhysicalRect(
399     const PhysicalRect& rect) {
400   return std::make_unique<std::vector<double>, std::initializer_list<double>>(
401       {rect.X(), rect.Y(), rect.Width(), rect.Height()});
402 }
403 
404 // Returns |layout_object|'s bounding box in document coordinates.
RectInRootFrame(const LayoutObject * layout_object)405 PhysicalRect RectInRootFrame(const LayoutObject* layout_object) {
406   LocalFrameView* local_frame_view = layout_object->GetFrameView();
407   PhysicalRect rect_in_absolute = PhysicalRect::EnclosingRect(
408       layout_object->AbsoluteBoundingBoxFloatRect());
409   return local_frame_view
410              ? local_frame_view->ConvertToRootFrame(rect_in_absolute)
411              : rect_in_absolute;
412 }
413 
TextFragmentRectInRootFrame(const LayoutObject * layout_object,const LayoutText::TextBoxInfo & text_box)414 PhysicalRect TextFragmentRectInRootFrame(
415     const LayoutObject* layout_object,
416     const LayoutText::TextBoxInfo& text_box) {
417   PhysicalRect absolute_coords_text_box_rect =
418       layout_object->LocalToAbsoluteRect(
419           layout_object->FlipForWritingMode(text_box.local_rect));
420   LocalFrameView* local_frame_view = layout_object->GetFrameView();
421   return local_frame_view ? local_frame_view->ConvertToRootFrame(
422                                 absolute_coords_text_box_rect)
423                           : absolute_coords_text_box_rect;
424 }
425 
426 }  // namespace
427 
InspectorHighlight(float scale)428 InspectorHighlight::InspectorHighlight(float scale)
429     : highlight_paths_(protocol::ListValue::create()),
430       show_rulers_(false),
431       show_extension_lines_(false),
432       scale_(scale) {}
433 
InspectorHighlightConfig()434 InspectorHighlightConfig::InspectorHighlightConfig()
435     : show_info(false),
436       show_styles(false),
437       show_rulers(false),
438       show_extension_lines(false) {}
439 
InspectorHighlight(Node * node,const InspectorHighlightConfig & highlight_config,const InspectorHighlightContrastInfo & node_contrast,bool append_element_info,bool append_distance_info,bool is_locked_ancestor)440 InspectorHighlight::InspectorHighlight(
441     Node* node,
442     const InspectorHighlightConfig& highlight_config,
443     const InspectorHighlightContrastInfo& node_contrast,
444     bool append_element_info,
445     bool append_distance_info,
446     bool is_locked_ancestor)
447     : highlight_paths_(protocol::ListValue::create()),
448       show_rulers_(highlight_config.show_rulers),
449       show_extension_lines_(highlight_config.show_extension_lines),
450       scale_(1.f) {
451   DCHECK(!DisplayLockUtilities::NearestLockedExclusiveAncestor(*node));
452   LocalFrameView* frame_view = node->GetDocument().View();
453   if (frame_view) {
454     scale_ = 1.f / frame_view->GetChromeClient()->WindowToViewportScalar(
455                        &frame_view->GetFrame(), 1.f);
456   }
457   AppendPathsForShapeOutside(node, highlight_config);
458   AppendNodeHighlight(node, highlight_config);
459   auto* text_node = DynamicTo<Text>(node);
460   auto* element = DynamicTo<Element>(node);
461   if (append_element_info && element)
462     element_info_ = BuildElementInfo(element);
463   else if (append_element_info && text_node)
464     element_info_ = BuildTextNodeInfo(text_node);
465   if (element_info_ && highlight_config.show_styles)
466     AppendStyleInfo(node, element_info_.get(), node_contrast);
467 
468   if (element_info_ && is_locked_ancestor)
469     element_info_->setString("isLockedAncestor", "true");
470   if (append_distance_info)
471     AppendDistanceInfo(node);
472 }
473 
474 InspectorHighlight::~InspectorHighlight() = default;
475 
AppendDistanceInfo(Node * node)476 void InspectorHighlight::AppendDistanceInfo(Node* node) {
477   if (!InspectorHighlight::GetBoxModel(node, &model_, false))
478     return;
479   boxes_ = std::make_unique<protocol::Array<protocol::Array<double>>>();
480   computed_style_ = protocol::DictionaryValue::create();
481 
482   node->GetDocument().EnsurePaintLocationDataValidForNode(
483       node, DocumentUpdateReason::kInspector);
484   LayoutObject* layout_object = node->GetLayoutObject();
485   if (!layout_object)
486     return;
487 
488   CSSComputedStyleDeclaration* style =
489       MakeGarbageCollected<CSSComputedStyleDeclaration>(node, true);
490   for (size_t i = 0; i < style->length(); ++i) {
491     AtomicString name(style->item(i));
492     const CSSValue* value = style->GetPropertyCSSValue(
493         cssPropertyID(node->GetExecutionContext(), name));
494     if (!value)
495       continue;
496     if (value->IsColorValue()) {
497       Color color = static_cast<const cssvalue::CSSColorValue*>(value)->Value();
498       String hex_color =
499           String::Format("#%02X%02X%02X%02X", color.Red(), color.Green(),
500                          color.Blue(), color.Alpha());
501       computed_style_->setString(name, hex_color);
502     } else {
503       computed_style_->setString(name, value->CssText());
504     }
505   }
506 
507   VisitAndCollectDistanceInfo(&(node->GetDocument()));
508   PhysicalRect document_rect(
509       node->GetDocument().GetLayoutView()->DocumentRect());
510   LocalFrameView* local_frame_view = node->GetDocument().View();
511   boxes_->emplace_back(
512       RectForPhysicalRect(local_frame_view->ConvertToRootFrame(document_rect)));
513 }
514 
VisitAndCollectDistanceInfo(Node * node)515 void InspectorHighlight::VisitAndCollectDistanceInfo(Node* node) {
516   LayoutObject* layout_object = node->GetLayoutObject();
517   if (layout_object)
518     AddLayoutBoxToDistanceInfo(layout_object);
519 
520   if (auto* element = DynamicTo<Element>(node)) {
521     if (element->GetPseudoId()) {
522       if (layout_object)
523         VisitAndCollectDistanceInfo(element->GetPseudoId(), layout_object);
524     } else {
525       for (PseudoId pseudo_id :
526            {kPseudoIdFirstLetter, kPseudoIdBefore, kPseudoIdAfter}) {
527         if (Node* pseudo_node = element->GetPseudoElement(pseudo_id))
528           VisitAndCollectDistanceInfo(pseudo_node);
529       }
530     }
531   }
532 
533   if (!node->IsContainerNode())
534     return;
535   Node* first_child = InspectorDOMSnapshotAgent::FirstChild(*node, false);
536   for (Node* child = first_child; child;
537        child = InspectorDOMSnapshotAgent::NextSibling(*child, false))
538     VisitAndCollectDistanceInfo(child);
539 }
540 
VisitAndCollectDistanceInfo(PseudoId pseudo_id,LayoutObject * layout_object)541 void InspectorHighlight::VisitAndCollectDistanceInfo(
542     PseudoId pseudo_id,
543     LayoutObject* layout_object) {
544   protocol::DOM::PseudoType pseudo_type;
545   if (pseudo_id == kPseudoIdNone)
546     return;
547   for (LayoutObject* child = layout_object->SlowFirstChild(); child;
548        child = child->NextSibling()) {
549     if (child->IsAnonymous())
550       AddLayoutBoxToDistanceInfo(child);
551   }
552 }
553 
AddLayoutBoxToDistanceInfo(LayoutObject * layout_object)554 void InspectorHighlight::AddLayoutBoxToDistanceInfo(
555     LayoutObject* layout_object) {
556   if (layout_object->IsText()) {
557     LayoutText* layout_text = ToLayoutText(layout_object);
558     for (const auto& text_box : layout_text->GetTextBoxInfo()) {
559       PhysicalRect text_rect(
560           TextFragmentRectInRootFrame(layout_object, text_box));
561       boxes_->emplace_back(RectForPhysicalRect(text_rect));
562     }
563   } else {
564     PhysicalRect rect(RectInRootFrame(layout_object));
565     boxes_->emplace_back(RectForPhysicalRect(rect));
566   }
567 }
568 
AppendQuad(const FloatQuad & quad,const Color & fill_color,const Color & outline_color,const String & name)569 void InspectorHighlight::AppendQuad(const FloatQuad& quad,
570                                     const Color& fill_color,
571                                     const Color& outline_color,
572                                     const String& name) {
573   Path path = QuadToPath(quad);
574   PathBuilder builder;
575   builder.AppendPath(path, scale_);
576   AppendPath(builder.Release(), fill_color, outline_color, name);
577 }
578 
AppendPath(std::unique_ptr<protocol::ListValue> path,const Color & fill_color,const Color & outline_color,const String & name)579 void InspectorHighlight::AppendPath(std::unique_ptr<protocol::ListValue> path,
580                                     const Color& fill_color,
581                                     const Color& outline_color,
582                                     const String& name) {
583   std::unique_ptr<protocol::DictionaryValue> object =
584       protocol::DictionaryValue::create();
585   object->setValue("path", std::move(path));
586   object->setString("fillColor", fill_color.Serialized());
587   if (outline_color != Color::kTransparent)
588     object->setString("outlineColor", outline_color.Serialized());
589   if (!name.IsEmpty())
590     object->setString("name", name);
591   highlight_paths_->pushValue(std::move(object));
592 }
593 
AppendEventTargetQuads(Node * event_target_node,const InspectorHighlightConfig & highlight_config)594 void InspectorHighlight::AppendEventTargetQuads(
595     Node* event_target_node,
596     const InspectorHighlightConfig& highlight_config) {
597   if (event_target_node->GetLayoutObject()) {
598     FloatQuad border, unused;
599     if (BuildNodeQuads(event_target_node, &unused, &unused, &border, &unused))
600       AppendQuad(border, highlight_config.event_target);
601   }
602 }
603 
AppendPathsForShapeOutside(Node * node,const InspectorHighlightConfig & config)604 void InspectorHighlight::AppendPathsForShapeOutside(
605     Node* node,
606     const InspectorHighlightConfig& config) {
607   Shape::DisplayPaths paths;
608   FloatQuad bounds_quad;
609 
610   const ShapeOutsideInfo* shape_outside_info =
611       ShapeOutsideInfoForNode(node, &paths, &bounds_quad);
612   if (!shape_outside_info)
613     return;
614 
615   if (!paths.shape.length()) {
616     AppendQuad(bounds_quad, config.shape);
617     return;
618   }
619 
620   AppendPath(ShapePathBuilder::BuildPath(
621                  *node->GetDocument().View(), *node->GetLayoutObject(),
622                  *shape_outside_info, paths.shape, scale_),
623              config.shape, Color::kTransparent);
624   if (paths.margin_shape.length())
625     AppendPath(ShapePathBuilder::BuildPath(
626                    *node->GetDocument().View(), *node->GetLayoutObject(),
627                    *shape_outside_info, paths.margin_shape, scale_),
628                config.shape_margin, Color::kTransparent);
629 }
630 
AppendNodeHighlight(Node * node,const InspectorHighlightConfig & highlight_config)631 void InspectorHighlight::AppendNodeHighlight(
632     Node* node,
633     const InspectorHighlightConfig& highlight_config) {
634   LayoutObject* layout_object = node->GetLayoutObject();
635   if (!layout_object)
636     return;
637 
638   Vector<FloatQuad> svg_quads;
639   if (BuildSVGQuads(node, svg_quads)) {
640     for (wtf_size_t i = 0; i < svg_quads.size(); ++i) {
641       AppendQuad(svg_quads[i], highlight_config.content,
642                  highlight_config.content_outline);
643     }
644     return;
645   }
646 
647   FloatQuad content, padding, border, margin;
648   if (!BuildNodeQuads(node, &content, &padding, &border, &margin))
649     return;
650   AppendQuad(content, highlight_config.content,
651              highlight_config.content_outline, "content");
652   AppendQuad(padding, highlight_config.padding, Color::kTransparent, "padding");
653   AppendQuad(border, highlight_config.border, Color::kTransparent, "border");
654   AppendQuad(margin, highlight_config.margin, Color::kTransparent, "margin");
655 
656   if (highlight_config.css_grid == Color::kTransparent)
657     return;
658   grid_info_ = protocol::ListValue::create();
659   if (layout_object->IsLayoutGrid()) {
660     grid_info_->pushValue(
661         BuildGridInfo(node->GetDocument().View(), ToLayoutGrid(layout_object),
662                       highlight_config.css_grid, scale_, true));
663   }
664   LayoutObject* parent = layout_object->Parent();
665   if (!parent || !parent->IsLayoutGrid())
666     return;
667   if (!BuildNodeQuads(parent->GetNode(), &content, &padding, &border, &margin))
668     return;
669   grid_info_->pushValue(
670       BuildGridInfo(node->GetDocument().View(), ToLayoutGrid(parent),
671                     highlight_config.css_grid, scale_, false));
672 }
673 
AsProtocolValue() const674 std::unique_ptr<protocol::DictionaryValue> InspectorHighlight::AsProtocolValue()
675     const {
676   std::unique_ptr<protocol::DictionaryValue> object =
677       protocol::DictionaryValue::create();
678   object->setValue("paths", highlight_paths_->clone());
679   object->setBoolean("showRulers", show_rulers_);
680   object->setBoolean("showExtensionLines", show_extension_lines_);
681   if (model_) {
682     std::unique_ptr<protocol::DictionaryValue> distance_info =
683         protocol::DictionaryValue::create();
684     distance_info->setArray(
685         "boxes",
686         protocol::ValueConversions<std::vector<
687             std::unique_ptr<std::vector<double>>>>::toValue(boxes_.get()));
688     distance_info->setArray(
689         "content", protocol::ValueConversions<std::vector<double>>::toValue(
690                        model_->getContent()));
691     distance_info->setArray(
692         "padding", protocol::ValueConversions<std::vector<double>>::toValue(
693                        model_->getPadding()));
694     distance_info->setArray(
695         "border", protocol::ValueConversions<std::vector<double>>::toValue(
696                       model_->getBorder()));
697     distance_info->setValue("style", computed_style_->clone());
698     object->setValue("distanceInfo", std::move(distance_info));
699   }
700   if (element_info_)
701     object->setValue("elementInfo", element_info_->clone());
702   if (grid_info_ && grid_info_->size() > 0)
703     object->setValue("gridInfo", grid_info_->clone());
704   return object;
705 }
706 
707 // static
GetBoxModel(Node * node,std::unique_ptr<protocol::DOM::BoxModel> * model,bool use_absolute_zoom)708 bool InspectorHighlight::GetBoxModel(
709     Node* node,
710     std::unique_ptr<protocol::DOM::BoxModel>* model,
711     bool use_absolute_zoom) {
712   node->GetDocument().EnsurePaintLocationDataValidForNode(
713       node, DocumentUpdateReason::kInspector);
714   LayoutObject* layout_object = node->GetLayoutObject();
715   LocalFrameView* view = node->GetDocument().View();
716   if (!layout_object || !view)
717     return false;
718 
719   FloatQuad content, padding, border, margin;
720   Vector<FloatQuad> svg_quads;
721   if (BuildSVGQuads(node, svg_quads)) {
722     if (!svg_quads.size())
723       return false;
724     content = svg_quads[0];
725     padding = svg_quads[0];
726     border = svg_quads[0];
727     margin = svg_quads[0];
728   } else if (!BuildNodeQuads(node, &content, &padding, &border, &margin)) {
729     return false;
730   }
731 
732   if (use_absolute_zoom) {
733     AdjustForAbsoluteZoom::AdjustFloatQuad(content, *layout_object);
734     AdjustForAbsoluteZoom::AdjustFloatQuad(padding, *layout_object);
735     AdjustForAbsoluteZoom::AdjustFloatQuad(border, *layout_object);
736     AdjustForAbsoluteZoom::AdjustFloatQuad(margin, *layout_object);
737   }
738 
739   float scale = 1 / view->GetPage()->GetVisualViewport().Scale();
740   content.Scale(scale, scale);
741   padding.Scale(scale, scale);
742   border.Scale(scale, scale);
743   margin.Scale(scale, scale);
744 
745   IntRect bounding_box =
746       view->ConvertToRootFrame(layout_object->AbsoluteBoundingBoxRect());
747   LayoutBoxModelObject* model_object =
748       layout_object->IsBoxModelObject() ? ToLayoutBoxModelObject(layout_object)
749                                         : nullptr;
750 
751   *model =
752       protocol::DOM::BoxModel::create()
753           .setContent(BuildArrayForQuad(content))
754           .setPadding(BuildArrayForQuad(padding))
755           .setBorder(BuildArrayForQuad(border))
756           .setMargin(BuildArrayForQuad(margin))
757           .setWidth(model_object ? AdjustForAbsoluteZoom::AdjustInt(
758                                        model_object->PixelSnappedOffsetWidth(
759                                            model_object->OffsetParent()),
760                                        model_object)
761                                  : bounding_box.Width())
762           .setHeight(model_object ? AdjustForAbsoluteZoom::AdjustInt(
763                                         model_object->PixelSnappedOffsetHeight(
764                                             model_object->OffsetParent()),
765                                         model_object)
766                                   : bounding_box.Height())
767           .build();
768 
769   Shape::DisplayPaths paths;
770   FloatQuad bounds_quad;
771   protocol::ErrorSupport errors;
772   if (const ShapeOutsideInfo* shape_outside_info =
773           ShapeOutsideInfoForNode(node, &paths, &bounds_quad)) {
774     auto shape = ShapePathBuilder::BuildPath(
775         *view, *layout_object, *shape_outside_info, paths.shape, 1.f);
776     auto margin_shape = ShapePathBuilder::BuildPath(
777         *view, *layout_object, *shape_outside_info, paths.margin_shape, 1.f);
778     (*model)->setShapeOutside(
779         protocol::DOM::ShapeOutsideInfo::create()
780             .setBounds(BuildArrayForQuad(bounds_quad))
781             .setShape(protocol::ValueConversions<
782                       protocol::Array<protocol::Value>>::fromValue(shape.get(),
783                                                                    &errors))
784             .setMarginShape(
785                 protocol::ValueConversions<protocol::Array<protocol::Value>>::
786                     fromValue(margin_shape.get(), &errors))
787             .build());
788   }
789 
790   return true;
791 }
792 
793 // static
BuildSVGQuads(Node * node,Vector<FloatQuad> & quads)794 bool InspectorHighlight::BuildSVGQuads(Node* node, Vector<FloatQuad>& quads) {
795   LayoutObject* layout_object = node->GetLayoutObject();
796   if (!layout_object)
797     return false;
798   if (!layout_object->GetNode() || !layout_object->GetNode()->IsSVGElement() ||
799       layout_object->IsSVGRoot())
800     return false;
801   CollectQuads(node, quads);
802   return true;
803 }
804 
805 // static
GetContentQuads(Node * node,std::unique_ptr<protocol::Array<protocol::Array<double>>> * result)806 bool InspectorHighlight::GetContentQuads(
807     Node* node,
808     std::unique_ptr<protocol::Array<protocol::Array<double>>>* result) {
809   LayoutObject* layout_object = node->GetLayoutObject();
810   LocalFrameView* view = node->GetDocument().View();
811   if (!layout_object || !view)
812     return false;
813   Vector<FloatQuad> quads;
814   CollectQuads(node, quads);
815   float scale = 1 / view->GetPage()->GetVisualViewport().Scale();
816   for (FloatQuad& quad : quads) {
817     AdjustForAbsoluteZoom::AdjustFloatQuad(quad, *layout_object);
818     quad.Scale(scale, scale);
819   }
820 
821   result->reset(new protocol::Array<protocol::Array<double>>());
822   for (FloatQuad& quad : quads)
823     (*result)->emplace_back(BuildArrayForQuad(quad));
824   return true;
825 }
826 
BuildNodeQuads(Node * node,FloatQuad * content,FloatQuad * padding,FloatQuad * border,FloatQuad * margin)827 bool InspectorHighlight::BuildNodeQuads(Node* node,
828                                         FloatQuad* content,
829                                         FloatQuad* padding,
830                                         FloatQuad* border,
831                                         FloatQuad* margin) {
832   LayoutObject* layout_object = node->GetLayoutObject();
833   if (!layout_object)
834     return false;
835 
836   LocalFrameView* containing_view = layout_object->GetFrameView();
837   if (!containing_view)
838     return false;
839   if (!layout_object->IsBox() && !layout_object->IsLayoutInline() &&
840       !layout_object->IsText())
841     return false;
842 
843   PhysicalRect content_box;
844   PhysicalRect padding_box;
845   PhysicalRect border_box;
846   PhysicalRect margin_box;
847 
848   if (layout_object->IsText()) {
849     LayoutText* layout_text = ToLayoutText(layout_object);
850     PhysicalRect text_rect = layout_text->PhysicalVisualOverflowRect();
851     content_box = text_rect;
852     padding_box = text_rect;
853     border_box = text_rect;
854     margin_box = text_rect;
855   } else if (layout_object->IsBox()) {
856     LayoutBox* layout_box = ToLayoutBox(layout_object);
857 
858     // LayoutBox returns the "pure" content area box, exclusive of the
859     // scrollbars (if present), which also count towards the content area in
860     // CSS.
861     const int vertical_scrollbar_width = layout_box->VerticalScrollbarWidth();
862     const int horizontal_scrollbar_height =
863         layout_box->HorizontalScrollbarHeight();
864     content_box = layout_box->PhysicalContentBoxRect();
865     content_box.SetWidth(content_box.Width() + vertical_scrollbar_width);
866     content_box.SetHeight(content_box.Height() + horizontal_scrollbar_height);
867 
868     padding_box = layout_box->PhysicalPaddingBoxRect();
869     padding_box.SetWidth(padding_box.Width() + vertical_scrollbar_width);
870     padding_box.SetHeight(padding_box.Height() + horizontal_scrollbar_height);
871 
872     border_box = layout_box->PhysicalBorderBoxRect();
873 
874     margin_box = PhysicalRect(border_box.X() - layout_box->MarginLeft(),
875                               border_box.Y() - layout_box->MarginTop(),
876                               border_box.Width() + layout_box->MarginWidth(),
877                               border_box.Height() + layout_box->MarginHeight());
878   } else {
879     LayoutInline* layout_inline = ToLayoutInline(layout_object);
880 
881     // LayoutInline's bounding box includes paddings and borders, excludes
882     // margins.
883     border_box = layout_inline->PhysicalLinesBoundingBox();
884     padding_box =
885         PhysicalRect(border_box.X() + layout_inline->BorderLeft(),
886                      border_box.Y() + layout_inline->BorderTop(),
887                      border_box.Width() - layout_inline->BorderLeft() -
888                          layout_inline->BorderRight(),
889                      border_box.Height() - layout_inline->BorderTop() -
890                          layout_inline->BorderBottom());
891     content_box =
892         PhysicalRect(padding_box.X() + layout_inline->PaddingLeft(),
893                      padding_box.Y() + layout_inline->PaddingTop(),
894                      padding_box.Width() - layout_inline->PaddingLeft() -
895                          layout_inline->PaddingRight(),
896                      padding_box.Height() - layout_inline->PaddingTop() -
897                          layout_inline->PaddingBottom());
898     // Ignore marginTop and marginBottom for inlines.
899     margin_box = PhysicalRect(
900         border_box.X() - layout_inline->MarginLeft(), border_box.Y(),
901         border_box.Width() + layout_inline->MarginWidth(), border_box.Height());
902   }
903 
904   *content = layout_object->LocalRectToAbsoluteQuad(content_box);
905   *padding = layout_object->LocalRectToAbsoluteQuad(padding_box);
906   *border = layout_object->LocalRectToAbsoluteQuad(border_box);
907   *margin = layout_object->LocalRectToAbsoluteQuad(margin_box);
908 
909   FrameQuadToViewport(containing_view, *content);
910   FrameQuadToViewport(containing_view, *padding);
911   FrameQuadToViewport(containing_view, *border);
912   FrameQuadToViewport(containing_view, *margin);
913 
914   return true;
915 }
916 
917 // static
DefaultConfig()918 InspectorHighlightConfig InspectorHighlight::DefaultConfig() {
919   InspectorHighlightConfig config;
920   config.content = Color(255, 0, 0, 0);
921   config.content_outline = Color(128, 0, 0, 0);
922   config.padding = Color(0, 255, 0, 0);
923   config.border = Color(0, 0, 255, 0);
924   config.margin = Color(255, 255, 255, 0);
925   config.event_target = Color(128, 128, 128, 0);
926   config.shape = Color(0, 0, 0, 0);
927   config.shape_margin = Color(128, 128, 128, 0);
928   config.show_info = true;
929   config.show_styles = false;
930   config.show_rulers = true;
931   config.show_extension_lines = true;
932   config.css_grid = Color(128, 128, 128, 0);
933   return config;
934 }
935 
936 }  // namespace blink
937