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_grid_auto_repeat_value.h"
11 #include "third_party/blink/renderer/core/css/css_grid_integer_repeat_value.h"
12 #include "third_party/blink/renderer/core/css/css_property_name.h"
13 #include "third_party/blink/renderer/core/css/css_property_names.h"
14 #include "third_party/blink/renderer/core/css/css_value.h"
15 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
16 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
17 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
18 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
19 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
20 #include "third_party/blink/renderer/core/inspector/dom_traversal_utils.h"
21 #include "third_party/blink/renderer/core/inspector/inspector_dom_agent.h"
22 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
23 #include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
24 #include "third_party/blink/renderer/core/layout/layout_box.h"
25 #include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
26 #include "third_party/blink/renderer/core/layout/layout_grid.h"
27 #include "third_party/blink/renderer/core/layout/layout_inline.h"
28 #include "third_party/blink/renderer/core/layout/layout_object.h"
29 #include "third_party/blink/renderer/core/layout/layout_view.h"
30 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
31 #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
32 #include "third_party/blink/renderer/core/page/chrome_client.h"
33 #include "third_party/blink/renderer/core/page/page.h"
34 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
35 #include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
36 #include "third_party/blink/renderer/platform/geometry/float_point.h"
37 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
38 #include "third_party/blink/renderer/platform/graphics/path.h"
39 #include "third_party/blink/renderer/platform/text/writing_mode.h"
40 #include "third_party/blink/renderer/platform/web_test_support.h"
41
42 namespace blink {
43
44 namespace {
45
46 class PathBuilder {
47 STACK_ALLOCATED();
48
49 public:
PathBuilder()50 PathBuilder() : path_(protocol::ListValue::create()) {}
51 virtual ~PathBuilder() = default;
52
Release()53 std::unique_ptr<protocol::ListValue> Release() { return std::move(path_); }
54
AppendPath(const Path & path,float scale)55 void AppendPath(const Path& path, float scale) {
56 Path transform_path(path);
57 transform_path.Transform(AffineTransform().Scale(scale));
58 transform_path.Apply(this, &PathBuilder::AppendPathElement);
59 }
60
61 protected:
TranslatePoint(const FloatPoint & point)62 virtual FloatPoint TranslatePoint(const FloatPoint& point) { return point; }
63
64 private:
AppendPathElement(void * path_builder,const PathElement * path_element)65 static void AppendPathElement(void* path_builder,
66 const PathElement* path_element) {
67 static_cast<PathBuilder*>(path_builder)->AppendPathElement(path_element);
68 }
69
70 void AppendPathElement(const PathElement*);
71 void AppendPathCommandAndPoints(const char* command,
72 const FloatPoint points[],
73 size_t length);
74
75 std::unique_ptr<protocol::ListValue> path_;
76 DISALLOW_COPY_AND_ASSIGN(PathBuilder);
77 };
78
AppendPathCommandAndPoints(const char * command,const FloatPoint points[],size_t length)79 void PathBuilder::AppendPathCommandAndPoints(const char* command,
80 const FloatPoint points[],
81 size_t length) {
82 path_->pushValue(protocol::StringValue::create(command));
83 for (size_t i = 0; i < length; i++) {
84 FloatPoint point = TranslatePoint(points[i]);
85 path_->pushValue(protocol::FundamentalValue::create(point.X()));
86 path_->pushValue(protocol::FundamentalValue::create(point.Y()));
87 }
88 }
89
AppendPathElement(const PathElement * path_element)90 void PathBuilder::AppendPathElement(const PathElement* path_element) {
91 switch (path_element->type) {
92 // The points member will contain 1 value.
93 case kPathElementMoveToPoint:
94 AppendPathCommandAndPoints("M", path_element->points, 1);
95 break;
96 // The points member will contain 1 value.
97 case kPathElementAddLineToPoint:
98 AppendPathCommandAndPoints("L", path_element->points, 1);
99 break;
100 // The points member will contain 3 values.
101 case kPathElementAddCurveToPoint:
102 AppendPathCommandAndPoints("C", path_element->points, 3);
103 break;
104 // The points member will contain 2 values.
105 case kPathElementAddQuadCurveToPoint:
106 AppendPathCommandAndPoints("Q", path_element->points, 2);
107 break;
108 // The points member will contain no values.
109 case kPathElementCloseSubpath:
110 AppendPathCommandAndPoints("Z", nullptr, 0);
111 break;
112 }
113 }
114
115 class ShapePathBuilder : public PathBuilder {
116 public:
ShapePathBuilder(LocalFrameView & view,LayoutObject & layout_object,const ShapeOutsideInfo & shape_outside_info)117 ShapePathBuilder(LocalFrameView& view,
118 LayoutObject& layout_object,
119 const ShapeOutsideInfo& shape_outside_info)
120 : view_(&view),
121 layout_object_(&layout_object),
122 shape_outside_info_(shape_outside_info) {}
123
BuildPath(LocalFrameView & view,LayoutObject & layout_object,const ShapeOutsideInfo & shape_outside_info,const Path & path,float scale)124 static std::unique_ptr<protocol::ListValue> BuildPath(
125 LocalFrameView& view,
126 LayoutObject& layout_object,
127 const ShapeOutsideInfo& shape_outside_info,
128 const Path& path,
129 float scale) {
130 ShapePathBuilder builder(view, layout_object, shape_outside_info);
131 builder.AppendPath(path, scale);
132 return builder.Release();
133 }
134
135 protected:
TranslatePoint(const FloatPoint & point)136 FloatPoint TranslatePoint(const FloatPoint& point) override {
137 PhysicalOffset layout_object_point = PhysicalOffset::FromFloatPointRound(
138 shape_outside_info_.ShapeToLayoutObjectPoint(point));
139 // TODO(pfeldman): Is this kIgnoreTransforms correct?
140 return FloatPoint(view_->FrameToViewport(
141 RoundedIntPoint(layout_object_->LocalToAbsolutePoint(
142 layout_object_point, kIgnoreTransforms))));
143 }
144
145 private:
146 LocalFrameView* view_;
147 LayoutObject* const layout_object_;
148 const ShapeOutsideInfo& shape_outside_info_;
149 };
150
BuildArrayForQuad(const FloatQuad & quad)151 std::unique_ptr<protocol::Array<double>> BuildArrayForQuad(
152 const FloatQuad& quad) {
153 return std::make_unique<std::vector<double>, std::initializer_list<double>>(
154 {quad.P1().X(), quad.P1().Y(), quad.P2().X(), quad.P2().Y(),
155 quad.P3().X(), quad.P3().Y(), quad.P4().X(), quad.P4().Y()});
156 }
157
QuadToPath(const FloatQuad & quad)158 Path QuadToPath(const FloatQuad& quad) {
159 Path quad_path;
160 quad_path.MoveTo(quad.P1());
161 quad_path.AddLineTo(quad.P2());
162 quad_path.AddLineTo(quad.P3());
163 quad_path.AddLineTo(quad.P4());
164 quad_path.CloseSubpath();
165 return quad_path;
166 }
167
RowQuadToPath(const FloatQuad & quad,bool drawEndLine)168 Path RowQuadToPath(const FloatQuad& quad, bool drawEndLine) {
169 Path quad_path;
170 quad_path.MoveTo(quad.P1());
171 quad_path.AddLineTo(quad.P2());
172 if (drawEndLine) {
173 quad_path.MoveTo(quad.P3());
174 quad_path.AddLineTo(quad.P4());
175 }
176 return quad_path;
177 }
178
ColumnQuadToPath(const FloatQuad & quad,bool drawEndLine)179 Path ColumnQuadToPath(const FloatQuad& quad, bool drawEndLine) {
180 Path quad_path;
181 quad_path.MoveTo(quad.P1());
182 quad_path.AddLineTo(quad.P4());
183 if (drawEndLine) {
184 quad_path.MoveTo(quad.P3());
185 quad_path.AddLineTo(quad.P2());
186 }
187 return quad_path;
188 }
189
FramePointToViewport(const LocalFrameView * view,FloatPoint point_in_frame)190 FloatPoint FramePointToViewport(const LocalFrameView* view,
191 FloatPoint point_in_frame) {
192 FloatPoint point_in_root_frame = view->ConvertToRootFrame(point_in_frame);
193 return view->GetPage()->GetVisualViewport().RootFrameToViewport(
194 point_in_root_frame);
195 }
196
FrameQuadToViewport(const LocalFrameView * view,FloatQuad & quad)197 void FrameQuadToViewport(const LocalFrameView* view, FloatQuad& quad) {
198 quad.SetP1(FramePointToViewport(view, quad.P1()));
199 quad.SetP2(FramePointToViewport(view, quad.P2()));
200 quad.SetP3(FramePointToViewport(view, quad.P3()));
201 quad.SetP4(FramePointToViewport(view, quad.P4()));
202 }
203
ShapeOutsideInfoForNode(Node * node,Shape::DisplayPaths * paths,FloatQuad * bounds)204 const ShapeOutsideInfo* ShapeOutsideInfoForNode(Node* node,
205 Shape::DisplayPaths* paths,
206 FloatQuad* bounds) {
207 LayoutObject* layout_object = node->GetLayoutObject();
208 if (!layout_object || !layout_object->IsBox() ||
209 !To<LayoutBox>(layout_object)->GetShapeOutsideInfo())
210 return nullptr;
211
212 LocalFrameView* containing_view = node->GetDocument().View();
213 auto* layout_box = To<LayoutBox>(layout_object);
214 const ShapeOutsideInfo* shape_outside_info =
215 layout_box->GetShapeOutsideInfo();
216
217 shape_outside_info->ComputedShape().BuildDisplayPaths(*paths);
218
219 PhysicalRect shape_bounds =
220 shape_outside_info->ComputedShapePhysicalBoundingBox();
221 *bounds = layout_box->LocalRectToAbsoluteQuad(shape_bounds);
222 FrameQuadToViewport(containing_view, *bounds);
223
224 return shape_outside_info;
225 }
226
ToHEXA(const Color & color)227 String ToHEXA(const Color& color) {
228 return String::Format("#%02X%02X%02X%02X", color.Red(), color.Green(),
229 color.Blue(), color.Alpha());
230 }
231
AppendStyleInfo(Node * node,protocol::DictionaryValue * element_info,const InspectorHighlightContrastInfo & node_contrast)232 void AppendStyleInfo(Node* node,
233 protocol::DictionaryValue* element_info,
234 const InspectorHighlightContrastInfo& node_contrast) {
235 std::unique_ptr<protocol::DictionaryValue> computed_style =
236 protocol::DictionaryValue::create();
237 CSSComputedStyleDeclaration* style =
238 MakeGarbageCollected<CSSComputedStyleDeclaration>(node, true);
239 Vector<CSSPropertyID> properties;
240
241 // For text nodes, we can show color & font properties.
242 bool has_text_children = false;
243 for (Node* child = node->firstChild(); !has_text_children && child;
244 child = child->nextSibling()) {
245 has_text_children = child->IsTextNode();
246 }
247 if (has_text_children) {
248 properties.push_back(CSSPropertyID::kColor);
249 properties.push_back(CSSPropertyID::kFontFamily);
250 properties.push_back(CSSPropertyID::kFontSize);
251 properties.push_back(CSSPropertyID::kLineHeight);
252 }
253
254 properties.push_back(CSSPropertyID::kPadding);
255 properties.push_back(CSSPropertyID::kMargin);
256 properties.push_back(CSSPropertyID::kBackgroundColor);
257
258 for (size_t i = 0; i < properties.size(); ++i) {
259 const CSSValue* value = style->GetPropertyCSSValue(properties[i]);
260 if (!value)
261 continue;
262 AtomicString name = CSSPropertyName(properties[i]).ToAtomicString();
263 if (value->IsColorValue()) {
264 Color color = static_cast<const cssvalue::CSSColorValue*>(value)->Value();
265 computed_style->setString(name, ToHEXA(color));
266 } else {
267 computed_style->setString(name, value->CssText());
268 }
269 }
270 element_info->setValue("style", std::move(computed_style));
271
272 if (!node_contrast.font_size.IsEmpty()) {
273 std::unique_ptr<protocol::DictionaryValue> contrast =
274 protocol::DictionaryValue::create();
275 contrast->setString("fontSize", node_contrast.font_size);
276 contrast->setString("fontWeight", node_contrast.font_weight);
277 contrast->setString("backgroundColor",
278 ToHEXA(node_contrast.background_color));
279 element_info->setValue("contrast", std::move(contrast));
280 }
281 }
282
BuildElementInfo(Element * element)283 std::unique_ptr<protocol::DictionaryValue> BuildElementInfo(Element* element) {
284 std::unique_ptr<protocol::DictionaryValue> element_info =
285 protocol::DictionaryValue::create();
286 Element* real_element = element;
287 auto* pseudo_element = DynamicTo<PseudoElement>(element);
288 if (pseudo_element) {
289 real_element = element->ParentOrShadowHostElement();
290 }
291 bool is_xhtml = real_element->GetDocument().IsXHTMLDocument();
292 element_info->setString(
293 "tagName", is_xhtml ? real_element->nodeName()
294 : real_element->nodeName().DeprecatedLower());
295 element_info->setString("idValue", real_element->GetIdAttribute());
296 StringBuilder class_names;
297 if (real_element->HasClass() && real_element->IsStyledElement()) {
298 HashSet<AtomicString> used_class_names;
299 const SpaceSplitString& class_names_string = real_element->ClassNames();
300 wtf_size_t class_name_count = class_names_string.size();
301 for (wtf_size_t i = 0; i < class_name_count; ++i) {
302 const AtomicString& class_name = class_names_string[i];
303 if (!used_class_names.insert(class_name).is_new_entry)
304 continue;
305 class_names.Append('.');
306 class_names.Append(class_name);
307 }
308 }
309 if (pseudo_element) {
310 if (pseudo_element->GetPseudoId() == kPseudoIdBefore)
311 class_names.Append("::before");
312 else if (pseudo_element->GetPseudoId() == kPseudoIdAfter)
313 class_names.Append("::after");
314 else if (pseudo_element->GetPseudoId() == kPseudoIdMarker)
315 class_names.Append("::marker");
316 }
317 if (!class_names.IsEmpty())
318 element_info->setString("className", class_names.ToString());
319
320 LayoutObject* layout_object = element->GetLayoutObject();
321 LocalFrameView* containing_view = element->GetDocument().View();
322 if (!layout_object || !containing_view)
323 return element_info;
324
325 // layoutObject the getBoundingClientRect() data in the tooltip
326 // to be consistent with the rulers (see http://crbug.com/262338).
327 DOMRect* bounding_box = element->getBoundingClientRect();
328 element_info->setString("nodeWidth", String::Number(bounding_box->width()));
329 element_info->setString("nodeHeight", String::Number(bounding_box->height()));
330
331 element_info->setBoolean("isKeyboardFocusable",
332 element->IsKeyboardFocusable());
333 element_info->setString("accessibleName", element->computedName());
334 element_info->setString("accessibleRole", element->computedRole());
335
336 element_info->setString("layoutObjectName", layout_object->GetName());
337
338 return element_info;
339 }
340
BuildTextNodeInfo(Text * text_node)341 std::unique_ptr<protocol::DictionaryValue> BuildTextNodeInfo(Text* text_node) {
342 std::unique_ptr<protocol::DictionaryValue> text_info =
343 protocol::DictionaryValue::create();
344 LayoutObject* layout_object = text_node->GetLayoutObject();
345 if (!layout_object || !layout_object->IsText())
346 return text_info;
347 PhysicalRect bounding_box =
348 To<LayoutText>(layout_object)->PhysicalVisualOverflowRect();
349 text_info->setString("nodeWidth", bounding_box.Width().ToString());
350 text_info->setString("nodeHeight", bounding_box.Height().ToString());
351 text_info->setString("tagName", "#text");
352 text_info->setBoolean("showAccessibilityInfo", false);
353 return text_info;
354 }
355
AppendLineStyleConfig(const std::unique_ptr<LineStyle> & line_style,std::unique_ptr<protocol::DictionaryValue> & parent_config,String line_name)356 void AppendLineStyleConfig(
357 const std::unique_ptr<LineStyle>& line_style,
358 std::unique_ptr<protocol::DictionaryValue>& parent_config,
359 String line_name) {
360 if (line_style && line_style->color != Color::kTransparent) {
361 std::unique_ptr<protocol::DictionaryValue> config =
362 protocol::DictionaryValue::create();
363 config->setString("color", line_style->color.Serialized());
364 config->setString("pattern", line_style->pattern);
365
366 parent_config->setValue(line_name, std::move(config));
367 }
368 }
369
370 std::unique_ptr<protocol::DictionaryValue>
BuildFlexContainerHighlightConfigInfo(const InspectorFlexContainerHighlightConfig & flex_config)371 BuildFlexContainerHighlightConfigInfo(
372 const InspectorFlexContainerHighlightConfig& flex_config) {
373 std::unique_ptr<protocol::DictionaryValue> flex_config_info =
374 protocol::DictionaryValue::create();
375
376 AppendLineStyleConfig(flex_config.container_border, flex_config_info,
377 "containerBorder");
378 AppendLineStyleConfig(flex_config.line_separator, flex_config_info,
379 "lineSeparator");
380 AppendLineStyleConfig(flex_config.item_separator, flex_config_info,
381 "itemSeparator");
382
383 return flex_config_info;
384 }
385
BuildGridHighlightConfigInfo(const InspectorGridHighlightConfig & grid_config)386 std::unique_ptr<protocol::DictionaryValue> BuildGridHighlightConfigInfo(
387 const InspectorGridHighlightConfig& grid_config) {
388 std::unique_ptr<protocol::DictionaryValue> grid_config_info =
389 protocol::DictionaryValue::create();
390 grid_config_info->setBoolean("gridBorderDash", grid_config.grid_border_dash);
391 grid_config_info->setBoolean("rowLineDash", grid_config.row_line_dash);
392 grid_config_info->setBoolean("columnLineDash", grid_config.column_line_dash);
393 grid_config_info->setBoolean("showGridExtensionLines",
394 grid_config.show_grid_extension_lines);
395 grid_config_info->setBoolean("showPositiveLineNumbers",
396 grid_config.show_positive_line_numbers);
397 grid_config_info->setBoolean("showNegativeLineNumbers",
398 grid_config.show_negative_line_numbers);
399 grid_config_info->setBoolean("showAreaNames", grid_config.show_area_names);
400 grid_config_info->setBoolean("showLineNames", grid_config.show_line_names);
401
402 if (grid_config.grid_color != Color::kTransparent) {
403 grid_config_info->setString("gridBorderColor",
404 grid_config.grid_color.Serialized());
405 }
406 if (grid_config.row_line_color != Color::kTransparent) {
407 grid_config_info->setString("rowLineColor",
408 grid_config.row_line_color.Serialized());
409 }
410 if (grid_config.column_line_color != Color::kTransparent) {
411 grid_config_info->setString("columnLineColor",
412 grid_config.column_line_color.Serialized());
413 }
414 if (grid_config.row_gap_color != Color::kTransparent) {
415 grid_config_info->setString("rowGapColor",
416 grid_config.row_gap_color.Serialized());
417 }
418 if (grid_config.column_gap_color != Color::kTransparent) {
419 grid_config_info->setString("columnGapColor",
420 grid_config.column_gap_color.Serialized());
421 }
422 if (grid_config.row_hatch_color != Color::kTransparent) {
423 grid_config_info->setString("rowHatchColor",
424 grid_config.row_hatch_color.Serialized());
425 }
426 if (grid_config.column_hatch_color != Color::kTransparent) {
427 grid_config_info->setString("columnHatchColor",
428 grid_config.column_hatch_color.Serialized());
429 }
430 if (grid_config.area_border_color != Color::kTransparent) {
431 grid_config_info->setString("areaBorderColor",
432 grid_config.area_border_color.Serialized());
433 }
434 if (grid_config.grid_background_color != Color::kTransparent) {
435 grid_config_info->setString("gridBackgroundColor",
436 grid_config.grid_background_color.Serialized());
437 }
438 return grid_config_info;
439 }
440
441 // Swaps |left| and |top| of an offset.
Transpose(PhysicalOffset & offset)442 PhysicalOffset Transpose(PhysicalOffset& offset) {
443 return PhysicalOffset(offset.top, offset.left);
444 }
445
GetTrackCount(const LayoutGrid * layout_grid,GridTrackSizingDirection direction)446 size_t GetTrackCount(const LayoutGrid* layout_grid,
447 GridTrackSizingDirection direction) {
448 return direction == kForRows ? layout_grid->RowPositions().size()
449 : layout_grid->ColumnPositions().size();
450 }
451
GetPositionForTrackAt(const LayoutGrid * layout_grid,size_t index,GridTrackSizingDirection direction)452 LayoutUnit GetPositionForTrackAt(const LayoutGrid* layout_grid,
453 size_t index,
454 GridTrackSizingDirection direction) {
455 if (direction == kForRows)
456 return layout_grid->RowPositions().at(index);
457
458 LayoutUnit position = layout_grid->ColumnPositions().at(index);
459 return layout_grid->StyleRef().IsLeftToRightDirection()
460 ? position
461 : layout_grid->TranslateRTLCoordinate(position);
462 }
463
GetPositionForFirstTrack(const LayoutGrid * layout_grid,GridTrackSizingDirection direction)464 LayoutUnit GetPositionForFirstTrack(const LayoutGrid* layout_grid,
465 GridTrackSizingDirection direction) {
466 return GetPositionForTrackAt(layout_grid, 0, direction);
467 }
468
GetPositionForLastTrack(const LayoutGrid * layout_grid,GridTrackSizingDirection direction)469 LayoutUnit GetPositionForLastTrack(const LayoutGrid* layout_grid,
470 GridTrackSizingDirection direction) {
471 size_t index = GetTrackCount(layout_grid, direction) - 1;
472 return GetPositionForTrackAt(layout_grid, index, direction);
473 }
474
LocalToAbsolutePoint(Node * node,PhysicalOffset local,float scale)475 PhysicalOffset LocalToAbsolutePoint(Node* node,
476 PhysicalOffset local,
477 float scale) {
478 LayoutObject* layout_object = node->GetLayoutObject();
479 LayoutGrid* layout_grid = ToLayoutGrid(layout_object);
480 FloatPoint local_in_frame = FramePointToViewport(
481 node->GetDocument().View(), FloatPoint(local.left, local.top));
482 PhysicalOffset abs_number_pos = layout_grid->LocalToAbsolutePoint(
483 PhysicalOffset::FromFloatPointRound(local_in_frame));
484 abs_number_pos.Scale(scale);
485 return abs_number_pos;
486 }
487
BuildPosition(PhysicalOffset position)488 std::unique_ptr<protocol::DictionaryValue> BuildPosition(
489 PhysicalOffset position) {
490 std::unique_ptr<protocol::DictionaryValue> result =
491 protocol::DictionaryValue::create();
492 result->setDouble("x", position.left);
493 result->setDouble("y", position.top);
494 return result;
495 }
496
BuildGridTrackSizes(Node * node,GridTrackSizingDirection direction,float scale,LayoutUnit gap,const Vector<String> * authored_values)497 std::unique_ptr<protocol::ListValue> BuildGridTrackSizes(
498 Node* node,
499 GridTrackSizingDirection direction,
500 float scale,
501 LayoutUnit gap,
502 const Vector<String>* authored_values) {
503 LayoutObject* layout_object = node->GetLayoutObject();
504 LayoutGrid* layout_grid = ToLayoutGrid(layout_object);
505 bool is_rtl = direction == kForColumns &&
506 !layout_grid->StyleRef().IsLeftToRightDirection();
507
508 std::unique_ptr<protocol::ListValue> sizes = protocol::ListValue::create();
509 size_t track_count = GetTrackCount(layout_grid, direction);
510 LayoutUnit alt_axis_pos = GetPositionForFirstTrack(
511 layout_grid, direction == kForRows ? kForColumns : kForRows);
512
513 for (size_t i = 1; i < track_count; i++) {
514 LayoutUnit current_position =
515 GetPositionForTrackAt(layout_grid, i, direction);
516 LayoutUnit prev_position =
517 GetPositionForTrackAt(layout_grid, i - 1, direction);
518 LayoutUnit gap_offset = i < track_count - 1 ? gap : LayoutUnit();
519 LayoutUnit width = current_position - prev_position - gap_offset;
520 if (is_rtl)
521 width = prev_position - current_position - gap_offset;
522 LayoutUnit main_axis_pos = prev_position + width / 2;
523 if (is_rtl)
524 main_axis_pos = prev_position - width / 2;
525 auto adjusted_size = AdjustForAbsoluteZoom::AdjustFloat(
526 width * scale, layout_grid->StyleRef());
527 PhysicalOffset track_size_pos(main_axis_pos, alt_axis_pos);
528 if (direction == kForRows)
529 track_size_pos = Transpose(track_size_pos);
530 std::unique_ptr<protocol::DictionaryValue> size_info =
531 BuildPosition(LocalToAbsolutePoint(node, track_size_pos, scale));
532 size_info->setDouble("computedSize", adjusted_size);
533 if (i - 1 < authored_values->size()) {
534 size_info->setString("authoredSize", authored_values->at(i - 1));
535 }
536 sizes->pushValue(std::move(size_info));
537 }
538
539 return sizes;
540 }
541
BuildGridPositiveLineNumberPositions(Node * node,const LayoutUnit & grid_gap,GridTrackSizingDirection direction,float scale)542 std::unique_ptr<protocol::ListValue> BuildGridPositiveLineNumberPositions(
543 Node* node,
544 const LayoutUnit& grid_gap,
545 GridTrackSizingDirection direction,
546 float scale) {
547 LayoutObject* layout_object = node->GetLayoutObject();
548 LayoutGrid* layout_grid = ToLayoutGrid(layout_object);
549 bool is_rtl = direction == kForColumns &&
550 !layout_grid->StyleRef().IsLeftToRightDirection();
551
552 std::unique_ptr<protocol::ListValue> number_positions =
553 protocol::ListValue::create();
554
555 size_t track_count = GetTrackCount(layout_grid, direction);
556 LayoutUnit alt_axis_pos = GetPositionForFirstTrack(
557 layout_grid, direction == kForRows ? kForColumns : kForRows);
558
559 // Find index of the first explicit Grid Line.
560 size_t first_explicit_index =
561 layout_grid->ExplicitGridStartForDirection(direction);
562
563 // Go line by line, calculating the offset to fall in the middle of gaps
564 // if needed.
565 for (size_t i = first_explicit_index; i < track_count; ++i) {
566 LayoutUnit gapOffset = grid_gap / 2;
567 if (is_rtl)
568 gapOffset *= -1;
569 // No need for a gap offset if there is no gap, or the first line is
570 // explicit, or this is the last line.
571 if (grid_gap == 0 || i == 0 || i == track_count - 1) {
572 gapOffset = LayoutUnit();
573 }
574 LayoutUnit offset = GetPositionForTrackAt(layout_grid, i, direction);
575 PhysicalOffset number_position(offset - gapOffset, alt_axis_pos);
576 if (direction == kForRows)
577 number_position = Transpose(number_position);
578 number_positions->pushValue(
579 BuildPosition(LocalToAbsolutePoint(node, number_position, scale)));
580 }
581
582 return number_positions;
583 }
584
BuildGridNegativeLineNumberPositions(Node * node,const LayoutUnit & grid_gap,GridTrackSizingDirection direction,float scale)585 std::unique_ptr<protocol::ListValue> BuildGridNegativeLineNumberPositions(
586 Node* node,
587 const LayoutUnit& grid_gap,
588 GridTrackSizingDirection direction,
589 float scale) {
590 LayoutObject* layout_object = node->GetLayoutObject();
591 LayoutGrid* layout_grid = ToLayoutGrid(layout_object);
592 bool is_rtl = direction == kForColumns &&
593 !layout_grid->StyleRef().IsLeftToRightDirection();
594
595 std::unique_ptr<protocol::ListValue> number_positions =
596 protocol::ListValue::create();
597
598 size_t track_count = GetTrackCount(layout_grid, direction);
599 LayoutUnit alt_axis_pos = GetPositionForLastTrack(
600 layout_grid, direction == kForRows ? kForColumns : kForRows);
601
602 // This is the number of tracks from the start of the grid, to the end of the
603 // explicit grid (including any leading implicit tracks).
604 size_t explicit_grid_end_track_count =
605 layout_grid->ExplicitGridEndForDirection(direction);
606
607 {
608 LayoutUnit first_offset = GetPositionForFirstTrack(layout_grid, direction);
609
610 // Always start negative numbers at the first line.
611 std::unique_ptr<protocol::DictionaryValue> pos =
612 protocol::DictionaryValue::create();
613 PhysicalOffset number_position(first_offset, alt_axis_pos);
614 if (direction == kForRows)
615 number_position = Transpose(number_position);
616 number_positions->pushValue(
617 BuildPosition(LocalToAbsolutePoint(node, number_position, scale)));
618 }
619
620 // Then go line by line, calculating the offset to fall in the middle of gaps
621 // if needed.
622 for (size_t i = 1; i <= explicit_grid_end_track_count; i++) {
623 LayoutUnit gapOffset = grid_gap / 2;
624 if (is_rtl)
625 gapOffset *= -1;
626 if (grid_gap == 0 ||
627 (i == explicit_grid_end_track_count && i == track_count - 1)) {
628 gapOffset = LayoutUnit();
629 }
630 LayoutUnit offset = GetPositionForTrackAt(layout_grid, i, direction);
631 PhysicalOffset number_position(offset - gapOffset, alt_axis_pos);
632 if (direction == kForRows)
633 number_position = Transpose(number_position);
634 number_positions->pushValue(
635 BuildPosition(LocalToAbsolutePoint(node, number_position, scale)));
636 }
637
638 return number_positions;
639 }
640
BuildAreaNamePaths(Node * node,float scale)641 std::unique_ptr<protocol::DictionaryValue> BuildAreaNamePaths(Node* node,
642 float scale) {
643 LayoutObject* layout_object = node->GetLayoutObject();
644 LayoutGrid* layout_grid = ToLayoutGrid(layout_object);
645 LocalFrameView* containing_view = node->GetDocument().View();
646 bool is_rtl = !layout_grid->StyleRef().IsLeftToRightDirection();
647
648 std::unique_ptr<protocol::DictionaryValue> area_paths =
649 protocol::DictionaryValue::create();
650
651 const Vector<LayoutUnit>& rows = layout_grid->RowPositions();
652 const Vector<LayoutUnit>& columns = layout_grid->ColumnPositions();
653 LayoutUnit row_gap = layout_grid->GridGap(kForRows);
654 LayoutUnit column_gap = layout_grid->GridGap(kForColumns);
655
656 NamedGridAreaMap grid_area_map = layout_grid->StyleRef().NamedGridArea();
657 for (const auto& item : grid_area_map) {
658 const GridArea& area = item.value;
659 const String& name = item.key;
660
661 LayoutUnit start_column = GetPositionForTrackAt(
662 layout_grid, area.columns.StartLine(), kForColumns);
663 LayoutUnit end_column =
664 GetPositionForTrackAt(layout_grid, area.columns.EndLine(), kForColumns);
665 LayoutUnit start_row =
666 GetPositionForTrackAt(layout_grid, area.rows.StartLine(), kForRows);
667 LayoutUnit end_row =
668 GetPositionForTrackAt(layout_grid, area.rows.EndLine(), kForRows);
669
670 // Only subtract the gap size if the end line isn't the last line in the
671 // container.
672 LayoutUnit row_gap_offset =
673 area.rows.EndLine() == rows.size() - 1 ? LayoutUnit() : row_gap;
674 LayoutUnit column_gap_offset = area.columns.EndLine() == columns.size() - 1
675 ? LayoutUnit()
676 : column_gap;
677 if (is_rtl)
678 column_gap_offset *= -1;
679
680 PhysicalOffset position(start_column, start_row);
681 PhysicalSize size(end_column - start_column - column_gap_offset,
682 end_row - start_row - row_gap_offset);
683 PhysicalRect area_rect(position, size);
684 FloatQuad area_quad = layout_grid->LocalRectToAbsoluteQuad(area_rect);
685 FrameQuadToViewport(containing_view, area_quad);
686 PathBuilder area_builder;
687 area_builder.AppendPath(QuadToPath(area_quad), scale);
688
689 area_paths->setValue(name, area_builder.Release());
690 }
691
692 return area_paths;
693 }
694
BuildGridLineNames(Node * node,GridTrackSizingDirection direction,float scale)695 std::unique_ptr<protocol::ListValue> BuildGridLineNames(
696 Node* node,
697 GridTrackSizingDirection direction,
698 float scale) {
699 LayoutObject* layout_object = node->GetLayoutObject();
700 LayoutGrid* layout_grid = ToLayoutGrid(layout_object);
701 bool is_rtl = direction == kForColumns &&
702 !layout_grid->StyleRef().IsLeftToRightDirection();
703
704 std::unique_ptr<protocol::ListValue> lines = protocol::ListValue::create();
705
706 const Vector<LayoutUnit>& tracks = direction == kForColumns
707 ? layout_grid->ColumnPositions()
708 : layout_grid->RowPositions();
709 const NamedGridLinesMap& named_lines_map =
710 direction == kForColumns ? layout_grid->StyleRef().NamedGridColumnLines()
711 : layout_grid->StyleRef().NamedGridRowLines();
712 LayoutUnit gap = layout_grid->GridGap(direction);
713 LayoutUnit alt_axis_pos = GetPositionForFirstTrack(
714 layout_grid, direction == kForRows ? kForColumns : kForRows);
715
716 for (const auto& item : named_lines_map) {
717 const String& name = item.key;
718
719 for (const size_t index : item.value) {
720 LayoutUnit track = GetPositionForTrackAt(layout_grid, index, direction);
721
722 LayoutUnit gap_offset =
723 index > 0 && index < tracks.size() - 1 ? gap / 2 : LayoutUnit();
724 if (is_rtl)
725 gap_offset *= -1;
726
727 LayoutUnit main_axis_pos = track - gap_offset;
728 PhysicalOffset line_name_pos(main_axis_pos, alt_axis_pos);
729
730 if (direction == kForRows)
731 line_name_pos = Transpose(line_name_pos);
732
733 std::unique_ptr<protocol::DictionaryValue> line =
734 BuildPosition(LocalToAbsolutePoint(node, line_name_pos, scale));
735
736 line->setString("name", name);
737
738 lines->pushValue(std::move(line));
739 }
740 }
741
742 return lines;
743 }
744
745 // Gets the rotation angle of the grid layout (clock-wise).
GetRotationAngle(LayoutGrid * layout_grid)746 int GetRotationAngle(LayoutGrid* layout_grid) {
747 // Local vector has 135deg bearing to the Y axis.
748 int local_vector_bearing = 135;
749 FloatPoint local_a(0, 0);
750 FloatPoint local_b(1, 1);
751 FloatPoint abs_a = layout_grid->LocalToAbsoluteFloatPoint(local_a);
752 FloatPoint abs_b = layout_grid->LocalToAbsoluteFloatPoint(local_b);
753 // Compute bearing of the absolute vector against the Y axis.
754 double theta = atan2(abs_b.X() - abs_a.X(), abs_a.Y() - abs_b.Y());
755 if (theta < 0.0)
756 theta += kTwoPiDouble;
757 int bearing = std::round(rad2deg(theta));
758 return bearing - local_vector_bearing;
759 }
760
GetWritingMode(const LayoutGrid * layout_grid)761 String GetWritingMode(const LayoutGrid* layout_grid) {
762 // The grid overlay uses this to flip the grid lines and labels accordingly.
763 // lr, lr-tb, rl, rl-tb, tb, and tb-rl are deprecated and not handled here.
764 // sideways-lr and sideways-rl are not supported yet and not handled here.
765 WritingMode writing_mode = layout_grid->StyleRef().GetWritingMode();
766 if (writing_mode == WritingMode::kVerticalLr) {
767 return "vertical-lr";
768 }
769 if (writing_mode == WritingMode::kVerticalRl) {
770 return "vertical-rl";
771 }
772 return "horizontal-tb";
773 }
774
775 // Gets the list of authored track size values resolving repeat() functions
776 // and skipping line names.
GetAuthoredGridTrackSizes(const CSSValue * value,size_t auto_repeat_count)777 Vector<String> GetAuthoredGridTrackSizes(const CSSValue* value,
778 size_t auto_repeat_count) {
779 Vector<String> result;
780
781 if (!value)
782 return result;
783
784 // TODO(alexrudenko): this would not handle track sizes defined using CSS
785 // variables.
786 const CSSValueList* value_list = DynamicTo<CSSValueList>(value);
787
788 if (!value_list)
789 return result;
790
791 for (auto list_value : *value_list) {
792 if (auto* grid_auto_repeat_value =
793 DynamicTo<cssvalue::CSSGridAutoRepeatValue>(list_value.Get())) {
794 Vector<String> repeated_track_sizes;
795 for (auto auto_repeat_value : To<CSSValueList>(*list_value)) {
796 if (!auto_repeat_value->IsGridLineNamesValue())
797 repeated_track_sizes.push_back(auto_repeat_value->CssText());
798 }
799 // There could be only one auto repeat value in a |value_list|, therefore,
800 // resetting auto_repeat_count to zero after inserting repeated values.
801 for (; auto_repeat_count; --auto_repeat_count)
802 result.AppendVector(repeated_track_sizes);
803 continue;
804 }
805
806 if (auto* repeated_values =
807 DynamicTo<cssvalue::CSSGridIntegerRepeatValue>(list_value.Get())) {
808 size_t repetitions = repeated_values->Repetitions();
809 for (size_t i = 0; i < repetitions; ++i) {
810 for (auto repeated_value : *repeated_values) {
811 if (repeated_value->IsGridLineNamesValue())
812 continue;
813 result.push_back(repeated_value->CssText());
814 }
815 }
816 continue;
817 }
818
819 if (list_value->IsGridLineNamesValue())
820 continue;
821
822 result.push_back(list_value->CssText());
823 }
824
825 return result;
826 }
827
IsHorizontalFlex(LayoutObject * layout_flex)828 bool IsHorizontalFlex(LayoutObject* layout_flex) {
829 return layout_flex->StyleRef().IsHorizontalWritingMode() !=
830 layout_flex->StyleRef().ResolvedIsColumnFlexDirection();
831 }
832
GetFlexLinesAndItems(LayoutBox * layout_box,bool is_horizontal)833 Vector<Vector<PhysicalRect>> GetFlexLinesAndItems(LayoutBox* layout_box,
834 bool is_horizontal) {
835 Vector<Vector<PhysicalRect>> flex_lines;
836
837 // Flex containers can't get fragmented yet, but this may change in the
838 // future.
839 for (const auto& fragment : layout_box->PhysicalFragments()) {
840 LayoutUnit progression;
841 for (const auto& child : fragment.Children()) {
842 const NGPhysicalFragment* child_fragment = child.get();
843 if (!child_fragment || child_fragment->IsOutOfFlowPositioned())
844 continue;
845
846 PhysicalSize fragment_size = child_fragment->Size();
847 PhysicalOffset fragment_offset = child.Offset();
848
849 const LayoutObject* object = child_fragment->GetLayoutObject();
850 const auto* box = To<LayoutBox>(object);
851 PhysicalRect item_rect =
852 PhysicalRect(fragment_offset.left - box->MarginLeft(),
853 fragment_offset.top - box->MarginTop(),
854 fragment_size.width + box->MarginWidth(),
855 fragment_size.height + box->MarginHeight());
856
857 LayoutUnit item_start = is_horizontal ? item_rect.X() : item_rect.Y();
858 LayoutUnit item_end = is_horizontal ? item_rect.X() + item_rect.Width()
859 : item_rect.Y() + item_rect.Height();
860
861 if (flex_lines.IsEmpty() || item_start < progression) {
862 flex_lines.emplace_back();
863 }
864
865 flex_lines.back().push_back(item_rect);
866
867 progression = item_end;
868 }
869 }
870
871 return flex_lines;
872 }
873
BuildFlexInfo(Node * node,const InspectorFlexContainerHighlightConfig & flex_container_highlight_config,float scale)874 std::unique_ptr<protocol::DictionaryValue> BuildFlexInfo(
875 Node* node,
876 const InspectorFlexContainerHighlightConfig&
877 flex_container_highlight_config,
878 float scale) {
879 LocalFrameView* containing_view = node->GetDocument().View();
880 LayoutObject* layout_object = node->GetLayoutObject();
881 auto* layout_box = To<LayoutBox>(layout_object);
882 DCHECK(layout_object);
883 bool is_horizontal = IsHorizontalFlex(layout_object);
884
885 std::unique_ptr<protocol::DictionaryValue> flex_info =
886 protocol::DictionaryValue::create();
887
888 // Create the path for the flex container
889 PathBuilder container_builder;
890 PhysicalRect content_box = layout_box->PhysicalContentBoxRect();
891 FloatQuad content_quad = layout_object->LocalRectToAbsoluteQuad(content_box);
892 FrameQuadToViewport(containing_view, content_quad);
893 container_builder.AppendPath(QuadToPath(content_quad), scale);
894
895 // Gather all flex items, sorted by flex line.
896 Vector<Vector<PhysicalRect>> flex_lines =
897 GetFlexLinesAndItems(layout_box, is_horizontal);
898
899 // Send the offset information for each item to the frontend.
900 std::unique_ptr<protocol::ListValue> lines_info =
901 protocol::ListValue::create();
902 for (auto line : flex_lines) {
903 std::unique_ptr<protocol::ListValue> items_info =
904 protocol::ListValue::create();
905 for (auto item_rect : line) {
906 FloatQuad item_margin_quad =
907 layout_object->LocalRectToAbsoluteQuad(item_rect);
908 FrameQuadToViewport(containing_view, item_margin_quad);
909 PathBuilder item_builder;
910 item_builder.AppendPath(QuadToPath(item_margin_quad), scale);
911
912 items_info->pushValue(item_builder.Release());
913 }
914 lines_info->pushValue(std::move(items_info));
915 }
916
917 flex_info->setValue("containerBorder", container_builder.Release());
918 flex_info->setArray("lines", std::move(lines_info));
919 flex_info->setBoolean("isHorizontalFlow", is_horizontal);
920 flex_info->setValue(
921 "flexContainerHighlightConfig",
922 BuildFlexContainerHighlightConfigInfo(flex_container_highlight_config));
923
924 return flex_info;
925 }
926
BuildGridInfo(Node * node,const InspectorGridHighlightConfig & grid_highlight_config,float scale,bool isPrimary)927 std::unique_ptr<protocol::DictionaryValue> BuildGridInfo(
928 Node* node,
929 const InspectorGridHighlightConfig& grid_highlight_config,
930 float scale,
931 bool isPrimary) {
932 LocalFrameView* containing_view = node->GetDocument().View();
933 LayoutObject* layout_object = node->GetLayoutObject();
934 DCHECK(layout_object);
935 LayoutGrid* layout_grid = ToLayoutGrid(layout_object);
936
937 std::unique_ptr<protocol::DictionaryValue> grid_info =
938 protocol::DictionaryValue::create();
939
940 const auto& rows = layout_grid->RowPositions();
941 const auto& columns = layout_grid->ColumnPositions();
942
943 grid_info->setInteger("rotationAngle", GetRotationAngle(layout_grid));
944
945 // The grid track information collected in this method and sent to the overlay
946 // frontend assumes that the grid layout is in a horizontal-tb writing-mode.
947 // It is the responsibility of the frontend to flip the rendering of the grid
948 // overlay based on the following writingMode value.
949 grid_info->setString("writingMode", GetWritingMode(layout_grid));
950
951 auto row_gap =
952 layout_grid->GridGap(kForRows) + layout_grid->GridItemOffset(kForRows);
953 auto column_gap = layout_grid->GridGap(kForColumns) +
954 layout_grid->GridItemOffset(kForColumns);
955
956 if (grid_highlight_config.show_track_sizes) {
957 Element* element = DynamicTo<Element>(node);
958 DCHECK(element);
959 StyleResolver& style_resolver = element->GetDocument().GetStyleResolver();
960 HeapHashMap<CSSPropertyName, Member<const CSSValue>> cascaded_values =
961 style_resolver.CascadedValuesForElement(element, kPseudoIdNone);
962 Vector<String> column_authored_values = GetAuthoredGridTrackSizes(
963 cascaded_values.at(
964 CSSPropertyName(CSSPropertyID::kGridTemplateColumns)),
965 layout_grid->AutoRepeatCountForDirection(kForColumns));
966 Vector<String> row_authored_values = GetAuthoredGridTrackSizes(
967 cascaded_values.at(CSSPropertyName(CSSPropertyID::kGridTemplateRows)),
968 layout_grid->AutoRepeatCountForDirection(kForRows));
969
970 grid_info->setValue(
971 "columnTrackSizes",
972 BuildGridTrackSizes(node, kForColumns, scale, column_gap,
973 &column_authored_values));
974 grid_info->setValue("rowTrackSizes",
975 BuildGridTrackSizes(node, kForRows, scale, row_gap,
976 &row_authored_values));
977 }
978
979 PathBuilder row_builder;
980 PathBuilder row_gap_builder;
981 LayoutUnit row_left = columns.front();
982 LayoutUnit row_width = columns.back() - columns.front();
983 for (size_t i = 1; i < rows.size(); ++i) {
984 // Rows
985 PhysicalOffset position(row_left, rows.at(i - 1));
986 PhysicalSize size(row_width, rows.at(i) - rows.at(i - 1));
987 if (i != rows.size() - 1)
988 size.height -= row_gap;
989 PhysicalRect row(position, size);
990 FloatQuad row_quad = layout_grid->LocalRectToAbsoluteQuad(row);
991 FrameQuadToViewport(containing_view, row_quad);
992 row_builder.AppendPath(
993 RowQuadToPath(row_quad, i == rows.size() - 1 || row_gap > 0), scale);
994 // Row Gaps
995 if (i != rows.size() - 1) {
996 PhysicalOffset gap_position(row_left, rows.at(i) - row_gap);
997 PhysicalSize gap_size(row_width, row_gap);
998 PhysicalRect gap(gap_position, gap_size);
999 FloatQuad gap_quad = layout_grid->LocalRectToAbsoluteQuad(gap);
1000 FrameQuadToViewport(containing_view, gap_quad);
1001 row_gap_builder.AppendPath(QuadToPath(gap_quad), scale);
1002 }
1003 }
1004 grid_info->setValue("rows", row_builder.Release());
1005 grid_info->setValue("rowGaps", row_gap_builder.Release());
1006
1007 PathBuilder column_builder;
1008 PathBuilder column_gap_builder;
1009 LayoutUnit column_top = rows.front();
1010 LayoutUnit column_height = rows.back() - rows.front();
1011 bool is_ltr = layout_grid->StyleRef().IsLeftToRightDirection();
1012 for (size_t i = 1; i < columns.size(); ++i) {
1013 PhysicalSize size(columns.at(i) - columns.at(i - 1), column_height);
1014 if (i != columns.size() - 1)
1015 size.width -= column_gap;
1016 LayoutUnit line_left =
1017 GetPositionForTrackAt(layout_grid, i - 1, kForColumns);
1018 if (!is_ltr)
1019 line_left -= size.width;
1020 PhysicalOffset position(line_left, column_top);
1021 PhysicalRect column(position, size);
1022 FloatQuad column_quad = layout_grid->LocalRectToAbsoluteQuad(column);
1023 FrameQuadToViewport(containing_view, column_quad);
1024 bool draw_end_line = is_ltr ? i == columns.size() - 1 : i == 1;
1025 column_builder.AppendPath(
1026 ColumnQuadToPath(column_quad, draw_end_line || column_gap > 0), scale);
1027 // Column Gaps
1028 if (i != columns.size() - 1) {
1029 LayoutUnit gap_left = GetPositionForTrackAt(layout_grid, i, kForColumns);
1030 if (is_ltr)
1031 gap_left -= column_gap;
1032 PhysicalOffset gap_position(gap_left, column_top);
1033 PhysicalSize gap_size(column_gap, column_height);
1034 PhysicalRect gap(gap_position, gap_size);
1035 FloatQuad gap_quad = layout_grid->LocalRectToAbsoluteQuad(gap);
1036 FrameQuadToViewport(containing_view, gap_quad);
1037 column_gap_builder.AppendPath(QuadToPath(gap_quad), scale);
1038 }
1039 }
1040 grid_info->setValue("columns", column_builder.Release());
1041 grid_info->setValue("columnGaps", column_gap_builder.Release());
1042
1043 // Positive Row and column Line positions
1044 if (grid_highlight_config.show_positive_line_numbers) {
1045 grid_info->setValue(
1046 "positiveRowLineNumberPositions",
1047 BuildGridPositiveLineNumberPositions(node, row_gap, kForRows, scale));
1048 grid_info->setValue("positiveColumnLineNumberPositions",
1049 BuildGridPositiveLineNumberPositions(
1050 node, column_gap, kForColumns, scale));
1051 }
1052
1053 // Negative Row and column Line positions
1054 if (grid_highlight_config.show_negative_line_numbers) {
1055 grid_info->setValue(
1056 "negativeRowLineNumberPositions",
1057 BuildGridNegativeLineNumberPositions(node, row_gap, kForRows, scale));
1058 grid_info->setValue("negativeColumnLineNumberPositions",
1059 BuildGridNegativeLineNumberPositions(
1060 node, column_gap, kForColumns, scale));
1061 }
1062
1063 // Area names
1064 if (grid_highlight_config.show_area_names) {
1065 grid_info->setValue("areaNames", BuildAreaNamePaths(node, scale));
1066 }
1067
1068 // line names
1069 if (grid_highlight_config.show_line_names) {
1070 grid_info->setValue("rowLineNameOffsets",
1071 BuildGridLineNames(node, kForRows, scale));
1072 grid_info->setValue("columnLineNameOffsets",
1073 BuildGridLineNames(node, kForColumns, scale));
1074 }
1075
1076 // Grid border
1077 PathBuilder grid_border_builder;
1078 PhysicalOffset grid_position(row_left, column_top);
1079 PhysicalSize grid_size(row_width, column_height);
1080 PhysicalRect grid_rect(grid_position, grid_size);
1081 FloatQuad grid_quad = layout_grid->LocalRectToAbsoluteQuad(grid_rect);
1082 FrameQuadToViewport(containing_view, grid_quad);
1083 grid_border_builder.AppendPath(QuadToPath(grid_quad), scale);
1084 grid_info->setValue("gridBorder", grid_border_builder.Release());
1085
1086 grid_info->setValue("gridHighlightConfig",
1087 BuildGridHighlightConfigInfo(grid_highlight_config));
1088
1089 grid_info->setBoolean("isPrimaryGrid", isPrimary);
1090 return grid_info;
1091 }
1092
BuildGridInfo(Node * node,const InspectorHighlightConfig & highlight_config,float scale,bool isPrimary)1093 std::unique_ptr<protocol::DictionaryValue> BuildGridInfo(
1094 Node* node,
1095 const InspectorHighlightConfig& highlight_config,
1096 float scale,
1097 bool isPrimary) {
1098 // Legacy support for highlight_config.css_grid
1099 if (highlight_config.css_grid != Color::kTransparent) {
1100 std::unique_ptr<InspectorGridHighlightConfig> grid_config =
1101 std::make_unique<InspectorGridHighlightConfig>();
1102 grid_config->row_line_color = highlight_config.css_grid;
1103 grid_config->column_line_color = highlight_config.css_grid;
1104 grid_config->row_line_dash = true;
1105 grid_config->column_line_dash = true;
1106 return BuildGridInfo(node, *grid_config, scale, isPrimary);
1107 }
1108
1109 return BuildGridInfo(node, *(highlight_config.grid_highlight_config), scale,
1110 isPrimary);
1111 }
1112
CollectQuadsRecursive(Node * node,Vector<FloatQuad> & out_quads)1113 void CollectQuadsRecursive(Node* node, Vector<FloatQuad>& out_quads) {
1114 LayoutObject* layout_object = node->GetLayoutObject();
1115 if (!layout_object)
1116 return;
1117
1118 // For inline elements, absoluteQuads will return a line box based on the
1119 // line-height and font metrics, which is technically incorrect as replaced
1120 // elements like images should use their intristic height and expand the
1121 // linebox as needed. To get an appropriate quads we descend
1122 // into the children and have them add their boxes.
1123 if (layout_object->IsLayoutInline() &&
1124 LayoutTreeBuilderTraversal::FirstChild(*node)) {
1125 for (Node* child = LayoutTreeBuilderTraversal::FirstChild(*node); child;
1126 child = LayoutTreeBuilderTraversal::NextSibling(*child))
1127 CollectQuadsRecursive(child, out_quads);
1128 } else {
1129 layout_object->AbsoluteQuads(out_quads);
1130 }
1131 }
1132
CollectQuads(Node * node,Vector<FloatQuad> & out_quads)1133 void CollectQuads(Node* node, Vector<FloatQuad>& out_quads) {
1134 CollectQuadsRecursive(node, out_quads);
1135 LocalFrameView* containing_view =
1136 node->GetLayoutObject() ? node->GetLayoutObject()->GetFrameView()
1137 : nullptr;
1138 if (containing_view) {
1139 for (FloatQuad& quad : out_quads)
1140 FrameQuadToViewport(containing_view, quad);
1141 }
1142 }
1143
RectForPhysicalRect(const PhysicalRect & rect)1144 std::unique_ptr<protocol::Array<double>> RectForPhysicalRect(
1145 const PhysicalRect& rect) {
1146 return std::make_unique<std::vector<double>, std::initializer_list<double>>(
1147 {rect.X(), rect.Y(), rect.Width(), rect.Height()});
1148 }
1149
1150 // Returns |layout_object|'s bounding box in document coordinates.
RectInRootFrame(const LayoutObject * layout_object)1151 PhysicalRect RectInRootFrame(const LayoutObject* layout_object) {
1152 LocalFrameView* local_frame_view = layout_object->GetFrameView();
1153 PhysicalRect rect_in_absolute = PhysicalRect::EnclosingRect(
1154 layout_object->AbsoluteBoundingBoxFloatRect());
1155 return local_frame_view
1156 ? local_frame_view->ConvertToRootFrame(rect_in_absolute)
1157 : rect_in_absolute;
1158 }
1159
TextFragmentRectInRootFrame(const LayoutObject * layout_object,const LayoutText::TextBoxInfo & text_box)1160 PhysicalRect TextFragmentRectInRootFrame(
1161 const LayoutObject* layout_object,
1162 const LayoutText::TextBoxInfo& text_box) {
1163 PhysicalRect absolute_coords_text_box_rect =
1164 layout_object->LocalToAbsoluteRect(
1165 layout_object->FlipForWritingMode(text_box.local_rect));
1166 LocalFrameView* local_frame_view = layout_object->GetFrameView();
1167 return local_frame_view ? local_frame_view->ConvertToRootFrame(
1168 absolute_coords_text_box_rect)
1169 : absolute_coords_text_box_rect;
1170 }
1171
1172 } // namespace
1173
InspectorHighlightConfig()1174 InspectorHighlightConfig::InspectorHighlightConfig()
1175 : show_info(false),
1176 show_styles(false),
1177 show_rulers(false),
1178 show_extension_lines(false),
1179 show_accessibility_info(true),
1180 color_format(ColorFormat::HEX) {}
1181
InspectorHighlight(float scale)1182 InspectorHighlight::InspectorHighlight(float scale)
1183 : InspectorHighlightBase(scale),
1184 show_rulers_(false),
1185 show_extension_lines_(false),
1186 show_accessibility_info_(true),
1187 color_format_(ColorFormat::HEX) {}
1188
1189 InspectorSourceOrderConfig::InspectorSourceOrderConfig() = default;
1190
1191 LineStyle::LineStyle() = default;
1192
InspectorGridHighlightConfig()1193 InspectorGridHighlightConfig::InspectorGridHighlightConfig()
1194 : show_grid_extension_lines(false),
1195 grid_border_dash(false),
1196 row_line_dash(false),
1197 column_line_dash(false),
1198 show_positive_line_numbers(false),
1199 show_negative_line_numbers(false),
1200 show_area_names(false),
1201 show_line_names(false),
1202 show_track_sizes(false) {}
1203
1204 InspectorFlexContainerHighlightConfig::InspectorFlexContainerHighlightConfig() =
1205 default;
1206
InspectorHighlightBase(float scale)1207 InspectorHighlightBase::InspectorHighlightBase(float scale)
1208 : highlight_paths_(protocol::ListValue::create()), scale_(scale) {}
1209
InspectorHighlightBase(Node * node)1210 InspectorHighlightBase::InspectorHighlightBase(Node* node)
1211 : highlight_paths_(protocol::ListValue::create()), scale_(1.f) {
1212 DCHECK(!DisplayLockUtilities::NearestLockedExclusiveAncestor(*node));
1213 LocalFrameView* frame_view = node->GetDocument().View();
1214 if (frame_view) {
1215 scale_ = 1.f / frame_view->GetChromeClient()->WindowToViewportScalar(
1216 &frame_view->GetFrame(), 1.f);
1217 }
1218 }
1219
BuildNodeQuads(Node * node,FloatQuad * content,FloatQuad * padding,FloatQuad * border,FloatQuad * margin)1220 bool InspectorHighlightBase::BuildNodeQuads(Node* node,
1221 FloatQuad* content,
1222 FloatQuad* padding,
1223 FloatQuad* border,
1224 FloatQuad* margin) {
1225 LayoutObject* layout_object = node->GetLayoutObject();
1226 if (!layout_object)
1227 return false;
1228
1229 LocalFrameView* containing_view = layout_object->GetFrameView();
1230 if (!containing_view)
1231 return false;
1232 if (!layout_object->IsBox() && !layout_object->IsLayoutInline() &&
1233 !layout_object->IsText()) {
1234 return false;
1235 }
1236
1237 PhysicalRect content_box;
1238 PhysicalRect padding_box;
1239 PhysicalRect border_box;
1240 PhysicalRect margin_box;
1241
1242 if (layout_object->IsText()) {
1243 auto* layout_text = To<LayoutText>(layout_object);
1244 PhysicalRect text_rect = layout_text->PhysicalVisualOverflowRect();
1245 content_box = text_rect;
1246 padding_box = text_rect;
1247 border_box = text_rect;
1248 margin_box = text_rect;
1249 } else if (layout_object->IsBox()) {
1250 auto* layout_box = To<LayoutBox>(layout_object);
1251 content_box = layout_box->PhysicalContentBoxRect();
1252
1253 // Include scrollbars and gutters in the padding highlight.
1254 padding_box = layout_box->PhysicalPaddingBoxRect();
1255 NGPhysicalBoxStrut scrollbars = layout_box->ComputeScrollbars();
1256 padding_box.SetX(padding_box.X() - scrollbars.left);
1257 padding_box.SetY(padding_box.Y() - scrollbars.top);
1258 padding_box.SetWidth(padding_box.Width() + scrollbars.HorizontalSum());
1259 padding_box.SetHeight(padding_box.Height() + scrollbars.VerticalSum());
1260
1261 border_box = layout_box->PhysicalBorderBoxRect();
1262
1263 margin_box = PhysicalRect(border_box.X() - layout_box->MarginLeft(),
1264 border_box.Y() - layout_box->MarginTop(),
1265 border_box.Width() + layout_box->MarginWidth(),
1266 border_box.Height() + layout_box->MarginHeight());
1267 } else {
1268 auto* layout_inline = To<LayoutInline>(layout_object);
1269
1270 // LayoutInline's bounding box includes paddings and borders, excludes
1271 // margins.
1272 border_box = layout_inline->PhysicalLinesBoundingBox();
1273 padding_box =
1274 PhysicalRect(border_box.X() + layout_inline->BorderLeft(),
1275 border_box.Y() + layout_inline->BorderTop(),
1276 border_box.Width() - layout_inline->BorderLeft() -
1277 layout_inline->BorderRight(),
1278 border_box.Height() - layout_inline->BorderTop() -
1279 layout_inline->BorderBottom());
1280 content_box =
1281 PhysicalRect(padding_box.X() + layout_inline->PaddingLeft(),
1282 padding_box.Y() + layout_inline->PaddingTop(),
1283 padding_box.Width() - layout_inline->PaddingLeft() -
1284 layout_inline->PaddingRight(),
1285 padding_box.Height() - layout_inline->PaddingTop() -
1286 layout_inline->PaddingBottom());
1287 // Ignore marginTop and marginBottom for inlines.
1288 margin_box = PhysicalRect(
1289 border_box.X() - layout_inline->MarginLeft(), border_box.Y(),
1290 border_box.Width() + layout_inline->MarginWidth(), border_box.Height());
1291 }
1292
1293 *content = layout_object->LocalRectToAbsoluteQuad(content_box);
1294 *padding = layout_object->LocalRectToAbsoluteQuad(padding_box);
1295 *border = layout_object->LocalRectToAbsoluteQuad(border_box);
1296 *margin = layout_object->LocalRectToAbsoluteQuad(margin_box);
1297
1298 FrameQuadToViewport(containing_view, *content);
1299 FrameQuadToViewport(containing_view, *padding);
1300 FrameQuadToViewport(containing_view, *border);
1301 FrameQuadToViewport(containing_view, *margin);
1302
1303 return true;
1304 }
1305
AppendQuad(const FloatQuad & quad,const Color & fill_color,const Color & outline_color,const String & name)1306 void InspectorHighlightBase::AppendQuad(const FloatQuad& quad,
1307 const Color& fill_color,
1308 const Color& outline_color,
1309 const String& name) {
1310 Path path = QuadToPath(quad);
1311 PathBuilder builder;
1312 builder.AppendPath(path, scale_);
1313 AppendPath(builder.Release(), fill_color, outline_color, name);
1314 }
1315
AppendPath(std::unique_ptr<protocol::ListValue> path,const Color & fill_color,const Color & outline_color,const String & name)1316 void InspectorHighlightBase::AppendPath(
1317 std::unique_ptr<protocol::ListValue> path,
1318 const Color& fill_color,
1319 const Color& outline_color,
1320 const String& name) {
1321 std::unique_ptr<protocol::DictionaryValue> object =
1322 protocol::DictionaryValue::create();
1323 object->setValue("path", std::move(path));
1324 object->setString("fillColor", fill_color.Serialized());
1325 if (outline_color != Color::kTransparent)
1326 object->setString("outlineColor", outline_color.Serialized());
1327 if (!name.IsEmpty())
1328 object->setString("name", name);
1329 highlight_paths_->pushValue(std::move(object));
1330 }
1331
InspectorSourceOrderHighlight(Node * node,Color outline_color,int source_order_position)1332 InspectorSourceOrderHighlight::InspectorSourceOrderHighlight(
1333 Node* node,
1334 Color outline_color,
1335 int source_order_position)
1336 : InspectorHighlightBase(node),
1337 source_order_position_(source_order_position) {
1338 FloatQuad content, padding, border, margin;
1339 if (!BuildNodeQuads(node, &content, &padding, &border, &margin))
1340 return;
1341 AppendQuad(border, Color::kTransparent, outline_color, "border");
1342 }
1343
1344 std::unique_ptr<protocol::DictionaryValue>
AsProtocolValue() const1345 InspectorSourceOrderHighlight::AsProtocolValue() const {
1346 std::unique_ptr<protocol::DictionaryValue> object =
1347 protocol::DictionaryValue::create();
1348 object->setValue("paths", highlight_paths_->clone());
1349 object->setInteger("sourceOrder", source_order_position_);
1350 return object;
1351 }
1352
1353 // static
DefaultConfig()1354 InspectorSourceOrderConfig InspectorSourceOrderHighlight::DefaultConfig() {
1355 InspectorSourceOrderConfig config;
1356 config.parent_outline_color = Color(224, 90, 183, 1);
1357 config.child_outline_color = Color(0, 120, 212, 1);
1358 return config;
1359 }
1360
InspectorHighlight(Node * node,const InspectorHighlightConfig & highlight_config,const InspectorHighlightContrastInfo & node_contrast,bool append_element_info,bool append_distance_info,bool is_locked_ancestor)1361 InspectorHighlight::InspectorHighlight(
1362 Node* node,
1363 const InspectorHighlightConfig& highlight_config,
1364 const InspectorHighlightContrastInfo& node_contrast,
1365 bool append_element_info,
1366 bool append_distance_info,
1367 bool is_locked_ancestor)
1368 : InspectorHighlightBase(node),
1369 show_rulers_(highlight_config.show_rulers),
1370 show_extension_lines_(highlight_config.show_extension_lines),
1371 show_accessibility_info_(highlight_config.show_accessibility_info),
1372 color_format_(highlight_config.color_format) {
1373 AppendPathsForShapeOutside(node, highlight_config);
1374 AppendNodeHighlight(node, highlight_config);
1375 auto* text_node = DynamicTo<Text>(node);
1376 auto* element = DynamicTo<Element>(node);
1377 if (append_element_info && element)
1378 element_info_ = BuildElementInfo(element);
1379 else if (append_element_info && text_node)
1380 element_info_ = BuildTextNodeInfo(text_node);
1381 if (element_info_ && highlight_config.show_styles)
1382 AppendStyleInfo(node, element_info_.get(), node_contrast);
1383
1384 if (element_info_ && is_locked_ancestor)
1385 element_info_->setString("isLockedAncestor", "true");
1386 if (element_info_) {
1387 element_info_->setBoolean("showAccessibilityInfo",
1388 show_accessibility_info_);
1389 }
1390 if (append_distance_info)
1391 AppendDistanceInfo(node);
1392 }
1393
1394 InspectorHighlight::~InspectorHighlight() = default;
1395
AppendDistanceInfo(Node * node)1396 void InspectorHighlight::AppendDistanceInfo(Node* node) {
1397 if (!InspectorHighlight::GetBoxModel(node, &model_, false))
1398 return;
1399 boxes_ = std::make_unique<protocol::Array<protocol::Array<double>>>();
1400 computed_style_ = protocol::DictionaryValue::create();
1401
1402 node->GetDocument().EnsurePaintLocationDataValidForNode(
1403 node, DocumentUpdateReason::kInspector);
1404 LayoutObject* layout_object = node->GetLayoutObject();
1405 if (!layout_object)
1406 return;
1407
1408 CSSComputedStyleDeclaration* style =
1409 MakeGarbageCollected<CSSComputedStyleDeclaration>(node, true);
1410 for (size_t i = 0; i < style->length(); ++i) {
1411 AtomicString name(style->item(i));
1412 const CSSValue* value = style->GetPropertyCSSValue(
1413 cssPropertyID(node->GetExecutionContext(), name));
1414 if (!value)
1415 continue;
1416 if (value->IsColorValue()) {
1417 Color color = static_cast<const cssvalue::CSSColorValue*>(value)->Value();
1418 computed_style_->setString(name, ToHEXA(color));
1419 } else {
1420 computed_style_->setString(name, value->CssText());
1421 }
1422 }
1423
1424 VisitAndCollectDistanceInfo(&(node->GetDocument()));
1425 PhysicalRect document_rect(
1426 node->GetDocument().GetLayoutView()->DocumentRect());
1427 LocalFrameView* local_frame_view = node->GetDocument().View();
1428 boxes_->emplace_back(
1429 RectForPhysicalRect(local_frame_view->ConvertToRootFrame(document_rect)));
1430 }
1431
VisitAndCollectDistanceInfo(Node * node)1432 void InspectorHighlight::VisitAndCollectDistanceInfo(Node* node) {
1433 LayoutObject* layout_object = node->GetLayoutObject();
1434 if (layout_object)
1435 AddLayoutBoxToDistanceInfo(layout_object);
1436
1437 if (auto* element = DynamicTo<Element>(node)) {
1438 if (element->GetPseudoId()) {
1439 if (layout_object)
1440 VisitAndCollectDistanceInfo(element->GetPseudoId(), layout_object);
1441 } else {
1442 for (PseudoId pseudo_id :
1443 {kPseudoIdFirstLetter, kPseudoIdBefore, kPseudoIdAfter}) {
1444 if (Node* pseudo_node = element->GetPseudoElement(pseudo_id))
1445 VisitAndCollectDistanceInfo(pseudo_node);
1446 }
1447 }
1448 }
1449
1450 if (!node->IsContainerNode())
1451 return;
1452 for (Node* child = blink::dom_traversal_utils::FirstChild(*node, false);
1453 child; child = blink::dom_traversal_utils::NextSibling(*child, false)) {
1454 VisitAndCollectDistanceInfo(child);
1455 }
1456 }
1457
VisitAndCollectDistanceInfo(PseudoId pseudo_id,LayoutObject * layout_object)1458 void InspectorHighlight::VisitAndCollectDistanceInfo(
1459 PseudoId pseudo_id,
1460 LayoutObject* layout_object) {
1461 protocol::DOM::PseudoType pseudo_type;
1462 if (pseudo_id == kPseudoIdNone)
1463 return;
1464 for (LayoutObject* child = layout_object->SlowFirstChild(); child;
1465 child = child->NextSibling()) {
1466 if (child->IsAnonymous())
1467 AddLayoutBoxToDistanceInfo(child);
1468 }
1469 }
1470
AddLayoutBoxToDistanceInfo(LayoutObject * layout_object)1471 void InspectorHighlight::AddLayoutBoxToDistanceInfo(
1472 LayoutObject* layout_object) {
1473 if (layout_object->IsText()) {
1474 auto* layout_text = To<LayoutText>(layout_object);
1475 for (const auto& text_box : layout_text->GetTextBoxInfo()) {
1476 PhysicalRect text_rect(
1477 TextFragmentRectInRootFrame(layout_object, text_box));
1478 boxes_->emplace_back(RectForPhysicalRect(text_rect));
1479 }
1480 } else {
1481 PhysicalRect rect(RectInRootFrame(layout_object));
1482 boxes_->emplace_back(RectForPhysicalRect(rect));
1483 }
1484 }
1485
AppendEventTargetQuads(Node * event_target_node,const InspectorHighlightConfig & highlight_config)1486 void InspectorHighlight::AppendEventTargetQuads(
1487 Node* event_target_node,
1488 const InspectorHighlightConfig& highlight_config) {
1489 if (event_target_node->GetLayoutObject()) {
1490 FloatQuad border, unused;
1491 if (BuildNodeQuads(event_target_node, &unused, &unused, &border, &unused))
1492 AppendQuad(border, highlight_config.event_target);
1493 }
1494 }
1495
AppendPathsForShapeOutside(Node * node,const InspectorHighlightConfig & config)1496 void InspectorHighlight::AppendPathsForShapeOutside(
1497 Node* node,
1498 const InspectorHighlightConfig& config) {
1499 Shape::DisplayPaths paths;
1500 FloatQuad bounds_quad;
1501
1502 const ShapeOutsideInfo* shape_outside_info =
1503 ShapeOutsideInfoForNode(node, &paths, &bounds_quad);
1504 if (!shape_outside_info)
1505 return;
1506
1507 if (!paths.shape.length()) {
1508 AppendQuad(bounds_quad, config.shape);
1509 return;
1510 }
1511
1512 AppendPath(ShapePathBuilder::BuildPath(
1513 *node->GetDocument().View(), *node->GetLayoutObject(),
1514 *shape_outside_info, paths.shape, scale_),
1515 config.shape, Color::kTransparent);
1516 if (paths.margin_shape.length())
1517 AppendPath(ShapePathBuilder::BuildPath(
1518 *node->GetDocument().View(), *node->GetLayoutObject(),
1519 *shape_outside_info, paths.margin_shape, scale_),
1520 config.shape_margin, Color::kTransparent);
1521 }
1522
AppendNodeHighlight(Node * node,const InspectorHighlightConfig & highlight_config)1523 void InspectorHighlight::AppendNodeHighlight(
1524 Node* node,
1525 const InspectorHighlightConfig& highlight_config) {
1526 LayoutObject* layout_object = node->GetLayoutObject();
1527 if (!layout_object)
1528 return;
1529
1530 Vector<FloatQuad> svg_quads;
1531 if (BuildSVGQuads(node, svg_quads)) {
1532 for (wtf_size_t i = 0; i < svg_quads.size(); ++i) {
1533 AppendQuad(svg_quads[i], highlight_config.content,
1534 highlight_config.content_outline);
1535 }
1536 return;
1537 }
1538
1539 FloatQuad content, padding, border, margin;
1540 if (!BuildNodeQuads(node, &content, &padding, &border, &margin))
1541 return;
1542 AppendQuad(content, highlight_config.content,
1543 highlight_config.content_outline, "content");
1544 AppendQuad(padding, highlight_config.padding, Color::kTransparent, "padding");
1545 AppendQuad(border, highlight_config.border, Color::kTransparent, "border");
1546 AppendQuad(margin, highlight_config.margin, Color::kTransparent, "margin");
1547
1548 // Don't append node's grid / flex info if it's locked since those values may
1549 // not be generated yet.
1550 if (auto* context = layout_object->GetDisplayLockContext()) {
1551 if (context->IsLocked())
1552 return;
1553 }
1554
1555 if (highlight_config.css_grid != Color::kTransparent ||
1556 highlight_config.grid_highlight_config) {
1557 grid_info_ = protocol::ListValue::create();
1558 if (layout_object->IsLayoutGrid()) {
1559 grid_info_->pushValue(
1560 BuildGridInfo(node, highlight_config, scale_, true));
1561 }
1562 }
1563
1564 if (highlight_config.flex_container_highlight_config) {
1565 flex_info_ = protocol::ListValue::create();
1566 // Some objects are flexible boxes even though display:flex is not set, we
1567 // need to avoid those.
1568 if (layout_object->StyleRef().IsDisplayFlexibleBox() &&
1569 layout_object->IsFlexibleBoxIncludingNG()) {
1570 flex_info_->pushValue(BuildFlexInfo(
1571 node, *(highlight_config.flex_container_highlight_config), scale_));
1572 }
1573 }
1574 }
1575
AsProtocolValue() const1576 std::unique_ptr<protocol::DictionaryValue> InspectorHighlight::AsProtocolValue()
1577 const {
1578 std::unique_ptr<protocol::DictionaryValue> object =
1579 protocol::DictionaryValue::create();
1580 object->setValue("paths", highlight_paths_->clone());
1581 object->setBoolean("showRulers", show_rulers_);
1582 object->setBoolean("showExtensionLines", show_extension_lines_);
1583 object->setBoolean("showAccessibilityInfo", show_accessibility_info_);
1584 switch (color_format_) {
1585 case ColorFormat::RGB:
1586 object->setString("colorFormat", "rgb");
1587 break;
1588 case ColorFormat::HSL:
1589 object->setString("colorFormat", "hsl");
1590 break;
1591 case ColorFormat::HEX:
1592 object->setString("colorFormat", "hex");
1593 break;
1594 }
1595
1596 if (model_) {
1597 std::unique_ptr<protocol::DictionaryValue> distance_info =
1598 protocol::DictionaryValue::create();
1599 distance_info->setArray(
1600 "boxes",
1601 protocol::ValueConversions<std::vector<
1602 std::unique_ptr<std::vector<double>>>>::toValue(boxes_.get()));
1603 distance_info->setArray(
1604 "content", protocol::ValueConversions<std::vector<double>>::toValue(
1605 model_->getContent()));
1606 distance_info->setArray(
1607 "padding", protocol::ValueConversions<std::vector<double>>::toValue(
1608 model_->getPadding()));
1609 distance_info->setArray(
1610 "border", protocol::ValueConversions<std::vector<double>>::toValue(
1611 model_->getBorder()));
1612 distance_info->setValue("style", computed_style_->clone());
1613 object->setValue("distanceInfo", std::move(distance_info));
1614 }
1615 if (element_info_)
1616 object->setValue("elementInfo", element_info_->clone());
1617 if (grid_info_ && grid_info_->size() > 0)
1618 object->setValue("gridInfo", grid_info_->clone());
1619 if (flex_info_ && flex_info_->size() > 0)
1620 object->setValue("flexInfo", flex_info_->clone());
1621 return object;
1622 }
1623
1624 // static
GetBoxModel(Node * node,std::unique_ptr<protocol::DOM::BoxModel> * model,bool use_absolute_zoom)1625 bool InspectorHighlight::GetBoxModel(
1626 Node* node,
1627 std::unique_ptr<protocol::DOM::BoxModel>* model,
1628 bool use_absolute_zoom) {
1629 node->GetDocument().EnsurePaintLocationDataValidForNode(
1630 node, DocumentUpdateReason::kInspector);
1631 LayoutObject* layout_object = node->GetLayoutObject();
1632 LocalFrameView* view = node->GetDocument().View();
1633 if (!layout_object || !view)
1634 return false;
1635
1636 FloatQuad content, padding, border, margin;
1637 Vector<FloatQuad> svg_quads;
1638 if (BuildSVGQuads(node, svg_quads)) {
1639 if (!svg_quads.size())
1640 return false;
1641 content = svg_quads[0];
1642 padding = svg_quads[0];
1643 border = svg_quads[0];
1644 margin = svg_quads[0];
1645 } else if (!BuildNodeQuads(node, &content, &padding, &border, &margin)) {
1646 return false;
1647 }
1648
1649 if (use_absolute_zoom) {
1650 AdjustForAbsoluteZoom::AdjustFloatQuad(content, *layout_object);
1651 AdjustForAbsoluteZoom::AdjustFloatQuad(padding, *layout_object);
1652 AdjustForAbsoluteZoom::AdjustFloatQuad(border, *layout_object);
1653 AdjustForAbsoluteZoom::AdjustFloatQuad(margin, *layout_object);
1654 }
1655
1656 float scale = 1 / view->GetPage()->GetVisualViewport().Scale();
1657 content.Scale(scale, scale);
1658 padding.Scale(scale, scale);
1659 border.Scale(scale, scale);
1660 margin.Scale(scale, scale);
1661
1662 IntRect bounding_box =
1663 view->ConvertToRootFrame(layout_object->AbsoluteBoundingBoxRect());
1664 auto* model_object = DynamicTo<LayoutBoxModelObject>(layout_object);
1665
1666 *model =
1667 protocol::DOM::BoxModel::create()
1668 .setContent(BuildArrayForQuad(content))
1669 .setPadding(BuildArrayForQuad(padding))
1670 .setBorder(BuildArrayForQuad(border))
1671 .setMargin(BuildArrayForQuad(margin))
1672 .setWidth(model_object ? AdjustForAbsoluteZoom::AdjustInt(
1673 model_object->PixelSnappedOffsetWidth(
1674 model_object->OffsetParent()),
1675 model_object)
1676 : bounding_box.Width())
1677 .setHeight(model_object ? AdjustForAbsoluteZoom::AdjustInt(
1678 model_object->PixelSnappedOffsetHeight(
1679 model_object->OffsetParent()),
1680 model_object)
1681 : bounding_box.Height())
1682 .build();
1683
1684 Shape::DisplayPaths paths;
1685 FloatQuad bounds_quad;
1686 protocol::ErrorSupport errors;
1687 if (const ShapeOutsideInfo* shape_outside_info =
1688 ShapeOutsideInfoForNode(node, &paths, &bounds_quad)) {
1689 auto shape = ShapePathBuilder::BuildPath(
1690 *view, *layout_object, *shape_outside_info, paths.shape, 1.f);
1691 auto margin_shape = ShapePathBuilder::BuildPath(
1692 *view, *layout_object, *shape_outside_info, paths.margin_shape, 1.f);
1693 (*model)->setShapeOutside(
1694 protocol::DOM::ShapeOutsideInfo::create()
1695 .setBounds(BuildArrayForQuad(bounds_quad))
1696 .setShape(protocol::ValueConversions<
1697 protocol::Array<protocol::Value>>::fromValue(shape.get(),
1698 &errors))
1699 .setMarginShape(
1700 protocol::ValueConversions<protocol::Array<protocol::Value>>::
1701 fromValue(margin_shape.get(), &errors))
1702 .build());
1703 }
1704
1705 return true;
1706 }
1707
1708 // static
BuildSVGQuads(Node * node,Vector<FloatQuad> & quads)1709 bool InspectorHighlight::BuildSVGQuads(Node* node, Vector<FloatQuad>& quads) {
1710 LayoutObject* layout_object = node->GetLayoutObject();
1711 if (!layout_object)
1712 return false;
1713 if (!layout_object->GetNode() || !layout_object->GetNode()->IsSVGElement() ||
1714 layout_object->IsSVGRoot())
1715 return false;
1716 CollectQuads(node, quads);
1717 return true;
1718 }
1719
1720 // static
GetContentQuads(Node * node,std::unique_ptr<protocol::Array<protocol::Array<double>>> * result)1721 bool InspectorHighlight::GetContentQuads(
1722 Node* node,
1723 std::unique_ptr<protocol::Array<protocol::Array<double>>>* result) {
1724 LayoutObject* layout_object = node->GetLayoutObject();
1725 LocalFrameView* view = node->GetDocument().View();
1726 if (!layout_object || !view)
1727 return false;
1728 Vector<FloatQuad> quads;
1729 CollectQuads(node, quads);
1730 float scale = 1 / view->GetPage()->GetVisualViewport().Scale();
1731 for (FloatQuad& quad : quads) {
1732 AdjustForAbsoluteZoom::AdjustFloatQuad(quad, *layout_object);
1733 quad.Scale(scale, scale);
1734 }
1735
1736 result->reset(new protocol::Array<protocol::Array<double>>());
1737 for (FloatQuad& quad : quads)
1738 (*result)->emplace_back(BuildArrayForQuad(quad));
1739 return true;
1740 }
1741
InspectorGridHighlight(Node * node,const InspectorGridHighlightConfig & config)1742 std::unique_ptr<protocol::DictionaryValue> InspectorGridHighlight(
1743 Node* node,
1744 const InspectorGridHighlightConfig& config) {
1745 if (DisplayLockUtilities::NearestLockedExclusiveAncestor(*node)) {
1746 // Skip if node is part of display locked tree.
1747 return nullptr;
1748 }
1749
1750 LocalFrameView* frame_view = node->GetDocument().View();
1751 if (!frame_view)
1752 return nullptr;
1753
1754 float scale = 1.f / frame_view->GetChromeClient()->WindowToViewportScalar(
1755 &frame_view->GetFrame(), 1.f);
1756 LayoutObject* layout_object = node->GetLayoutObject();
1757 if (!layout_object || !layout_object->IsLayoutGrid())
1758 return nullptr;
1759
1760 std::unique_ptr<protocol::DictionaryValue> grid_info =
1761 BuildGridInfo(node, config, scale, true);
1762 return grid_info;
1763 }
1764
1765 // static
DefaultConfig()1766 InspectorHighlightConfig InspectorHighlight::DefaultConfig() {
1767 InspectorHighlightConfig config;
1768 config.content = Color(255, 0, 0, 0);
1769 config.content_outline = Color(128, 0, 0, 0);
1770 config.padding = Color(0, 255, 0, 0);
1771 config.border = Color(0, 0, 255, 0);
1772 config.margin = Color(255, 255, 255, 0);
1773 config.event_target = Color(128, 128, 128, 0);
1774 config.shape = Color(0, 0, 0, 0);
1775 config.shape_margin = Color(128, 128, 128, 0);
1776 config.show_info = true;
1777 config.show_styles = false;
1778 config.show_rulers = true;
1779 config.show_extension_lines = true;
1780 config.css_grid = Color::kTransparent;
1781 config.color_format = ColorFormat::HEX;
1782 config.grid_highlight_config = std::make_unique<InspectorGridHighlightConfig>(
1783 InspectorHighlight::DefaultGridConfig());
1784 config.flex_container_highlight_config =
1785 std::make_unique<InspectorFlexContainerHighlightConfig>(
1786 InspectorHighlight::DefaultFlexContainerConfig());
1787 return config;
1788 }
1789
1790 // static
DefaultGridConfig()1791 InspectorGridHighlightConfig InspectorHighlight::DefaultGridConfig() {
1792 InspectorGridHighlightConfig config;
1793 config.grid_color = Color(255, 0, 0, 0);
1794 config.row_line_color = Color(128, 0, 0, 0);
1795 config.column_line_color = Color(128, 0, 0, 0);
1796 config.row_gap_color = Color(0, 255, 0, 0);
1797 config.column_gap_color = Color(0, 0, 255, 0);
1798 config.row_hatch_color = Color(255, 255, 255, 0);
1799 config.column_hatch_color = Color(128, 128, 128, 0);
1800 config.area_border_color = Color(255, 0, 0, 0);
1801 config.grid_background_color = Color(255, 0, 0, 0);
1802 config.show_grid_extension_lines = true;
1803 config.show_positive_line_numbers = true;
1804 config.show_negative_line_numbers = true;
1805 config.show_area_names = true;
1806 config.show_line_names = true;
1807 config.grid_border_dash = false;
1808 config.row_line_dash = true;
1809 config.column_line_dash = true;
1810 config.show_track_sizes = true;
1811 return config;
1812 }
1813
1814 // static
1815 InspectorFlexContainerHighlightConfig
DefaultFlexContainerConfig()1816 InspectorHighlight::DefaultFlexContainerConfig() {
1817 InspectorFlexContainerHighlightConfig config;
1818 config.container_border =
1819 std::make_unique<LineStyle>(InspectorHighlight::DefaultLineStyle());
1820 config.line_separator =
1821 std::make_unique<LineStyle>(InspectorHighlight::DefaultLineStyle());
1822 config.item_separator =
1823 std::make_unique<LineStyle>(InspectorHighlight::DefaultLineStyle());
1824 return config;
1825 }
1826
1827 // static
DefaultLineStyle()1828 LineStyle InspectorHighlight::DefaultLineStyle() {
1829 LineStyle style;
1830 style.color = Color(255, 0, 0, 0);
1831 style.pattern = "solid";
1832 return style;
1833 }
1834
1835 } // namespace blink
1836