1 /*
2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "third_party/blink/renderer/core/layout/layout_tree_as_text.h"
27 
28 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
29 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
30 #include "third_party/blink/renderer/core/dom/document.h"
31 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
32 #include "third_party/blink/renderer/core/editing/frame_selection.h"
33 #include "third_party/blink/renderer/core/editing/visible_selection.h"
34 #include "third_party/blink/renderer/core/frame/local_frame.h"
35 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
36 #include "third_party/blink/renderer/core/frame/settings.h"
37 #include "third_party/blink/renderer/core/html/html_element.h"
38 #include "third_party/blink/renderer/core/html_names.h"
39 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
40 #include "third_party/blink/renderer/core/layout/layout_details_marker.h"
41 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
42 #include "third_party/blink/renderer/core/layout/layout_file_upload_control.h"
43 #include "third_party/blink/renderer/core/layout/layout_inline.h"
44 #include "third_party/blink/renderer/core/layout/layout_list_item.h"
45 #include "third_party/blink/renderer/core/layout/layout_list_marker.h"
46 #include "third_party/blink/renderer/core/layout/layout_table_cell.h"
47 #include "third_party/blink/renderer/core/layout/layout_view.h"
48 #include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
49 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
50 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
51 #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment.h"
52 #include "third_party/blink/renderer/core/layout/ng/list/list_marker.h"
53 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
54 #include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
55 #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline.h"
56 #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h"
57 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
58 #include "third_party/blink/renderer/core/layout/svg/layout_svg_shape.h"
59 #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
60 #include "third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.h"
61 #include "third_party/blink/renderer/core/page/print_context.h"
62 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
63 #include "third_party/blink/renderer/core/paint/paint_layer.h"
64 #include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
65 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
66 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
67 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
68 #include "third_party/blink/renderer/platform/wtf/vector.h"
69 
70 namespace blink {
71 
PrintBorderStyle(WTF::TextStream & ts,const EBorderStyle border_style)72 static void PrintBorderStyle(WTF::TextStream& ts,
73                              const EBorderStyle border_style) {
74   switch (border_style) {
75     case EBorderStyle::kNone:
76       ts << "none";
77       break;
78     case EBorderStyle::kHidden:
79       ts << "hidden";
80       break;
81     case EBorderStyle::kInset:
82       ts << "inset";
83       break;
84     case EBorderStyle::kGroove:
85       ts << "groove";
86       break;
87     case EBorderStyle::kRidge:
88       ts << "ridge";
89       break;
90     case EBorderStyle::kOutset:
91       ts << "outset";
92       break;
93     case EBorderStyle::kDotted:
94       ts << "dotted";
95       break;
96     case EBorderStyle::kDashed:
97       ts << "dashed";
98       break;
99     case EBorderStyle::kSolid:
100       ts << "solid";
101       break;
102     case EBorderStyle::kDouble:
103       ts << "double";
104       break;
105   }
106 
107   ts << " ";
108 }
109 
GetTagName(Node * n)110 static String GetTagName(Node* n) {
111   if (n->IsDocumentNode())
112     return "";
113   if (n->getNodeType() == Node::kCommentNode)
114     return "COMMENT";
115   return n->nodeName();
116 }
117 
QuoteAndEscapeNonPrintables(const String & s)118 String QuoteAndEscapeNonPrintables(const String& s) {
119   StringBuilder result;
120   result.Append('"');
121   for (unsigned i = 0; i != s.length(); ++i) {
122     UChar c = s[i];
123     if (c == '\\') {
124       result.Append('\\');
125       result.Append('\\');
126     } else if (c == '"') {
127       result.Append('\\');
128       result.Append('"');
129     } else if (c == '\n' || c == kNoBreakSpaceCharacter) {
130       result.Append(' ');
131     } else {
132       if (c >= 0x20 && c < 0x7F) {
133         result.Append(c);
134       } else {
135         result.AppendFormat("\\x{%X}", c);
136       }
137     }
138   }
139   result.Append('"');
140   return result.ToString();
141 }
142 
operator <<(WTF::TextStream & ts,const Color & c)143 WTF::TextStream& operator<<(WTF::TextStream& ts, const Color& c) {
144   return ts << c.NameForLayoutTreeAsText();
145 }
146 
WriteLayoutObject(WTF::TextStream & ts,const LayoutObject & o,LayoutAsTextBehavior behavior)147 void LayoutTreeAsText::WriteLayoutObject(WTF::TextStream& ts,
148                                          const LayoutObject& o,
149                                          LayoutAsTextBehavior behavior) {
150   ts << o.DecoratedName();
151 
152   if (behavior & kLayoutAsTextShowAddresses)
153     ts << " " << static_cast<const void*>(&o);
154 
155   if (o.Style() && o.StyleRef().ZIndex())
156     ts << " zI: " << o.StyleRef().ZIndex();
157 
158   if (o.GetNode()) {
159     String tag_name = GetTagName(o.GetNode());
160     if (!tag_name.IsEmpty())
161       ts << " {" << tag_name << "}";
162   }
163 
164   PhysicalRect rect = o.DebugRect();
165   ts << " " << rect;
166 
167   if (!(o.IsText() && !o.IsBR())) {
168     if (o.IsFileUploadControl())
169       ts << " "
170          << QuoteAndEscapeNonPrintables(
171                 ToLayoutFileUploadControl(&o)->FileTextValue());
172 
173     if (o.Parent()) {
174       Color color = o.ResolveColor(GetCSSPropertyColor());
175       if (o.Parent()->ResolveColor(GetCSSPropertyColor()) != color)
176         ts << " [color=" << color << "]";
177 
178       // Do not dump invalid or transparent backgrounds, since that is the
179       // default.
180       Color background_color = o.ResolveColor(GetCSSPropertyBackgroundColor());
181       if (o.Parent()->ResolveColor(GetCSSPropertyBackgroundColor()) !=
182               background_color &&
183           background_color.Rgb())
184         ts << " [bgcolor=" << background_color << "]";
185 
186       Color text_fill_color =
187           o.ResolveColor(GetCSSPropertyWebkitTextFillColor());
188       if (o.Parent()->ResolveColor(GetCSSPropertyWebkitTextFillColor()) !=
189               text_fill_color &&
190           text_fill_color != color && text_fill_color.Rgb())
191         ts << " [textFillColor=" << text_fill_color << "]";
192 
193       Color text_stroke_color =
194           o.ResolveColor(GetCSSPropertyWebkitTextStrokeColor());
195       if (o.Parent()->ResolveColor(GetCSSPropertyWebkitTextStrokeColor()) !=
196               text_stroke_color &&
197           text_stroke_color != color && text_stroke_color.Rgb())
198         ts << " [textStrokeColor=" << text_stroke_color << "]";
199 
200       if (o.Parent()->StyleRef().TextStrokeWidth() !=
201               o.StyleRef().TextStrokeWidth() &&
202           o.StyleRef().TextStrokeWidth() > 0)
203         ts << " [textStrokeWidth=" << o.StyleRef().TextStrokeWidth() << "]";
204     }
205 
206     if (!o.IsBoxModelObject())
207       return;
208 
209     const LayoutBoxModelObject& box = ToLayoutBoxModelObject(o);
210     if (box.BorderTop() || box.BorderRight() || box.BorderBottom() ||
211         box.BorderLeft()) {
212       ts << " [border:";
213 
214       BorderValue prev_border = o.StyleRef().BorderTop();
215       if (!box.BorderTop()) {
216         ts << " none";
217       } else {
218         ts << " (" << box.BorderTop() << "px ";
219         PrintBorderStyle(ts, o.StyleRef().BorderTopStyle());
220         ts << o.ResolveColor(GetCSSPropertyBorderTopColor()) << ")";
221       }
222 
223       if (!o.StyleRef().BorderRightEquals(prev_border)) {
224         prev_border = o.StyleRef().BorderRight();
225         if (!box.BorderRight()) {
226           ts << " none";
227         } else {
228           ts << " (" << box.BorderRight() << "px ";
229           PrintBorderStyle(ts, o.StyleRef().BorderRightStyle());
230           ts << o.ResolveColor(GetCSSPropertyBorderRightColor()) << ")";
231         }
232       }
233 
234       if (!o.StyleRef().BorderBottomEquals(prev_border)) {
235         prev_border = box.StyleRef().BorderBottom();
236         if (!box.BorderBottom()) {
237           ts << " none";
238         } else {
239           ts << " (" << box.BorderBottom() << "px ";
240           PrintBorderStyle(ts, o.StyleRef().BorderBottomStyle());
241           ts << o.ResolveColor(GetCSSPropertyBorderBottomColor()) << ")";
242         }
243       }
244 
245       if (!o.StyleRef().BorderLeftEquals(prev_border)) {
246         prev_border = o.StyleRef().BorderLeft();
247         if (!box.BorderLeft()) {
248           ts << " none";
249         } else {
250           ts << " (" << box.BorderLeft() << "px ";
251           PrintBorderStyle(ts, o.StyleRef().BorderLeftStyle());
252           ts << o.ResolveColor(GetCSSPropertyBorderLeftColor()) << ")";
253         }
254       }
255 
256       ts << "]";
257     }
258   }
259 
260   if (o.IsTableCell()) {
261     const LayoutNGTableCellInterface& c =
262         ToInterface<LayoutNGTableCellInterface>(o);
263     ts << " [r=" << c.RowIndex() << " c=" << c.AbsoluteColumnIndex()
264        << " rs=" << c.ResolvedRowSpan() << " cs=" << c.ColSpan() << "]";
265   }
266 
267   if (o.IsDetailsMarker()) {
268     ts << ": ";
269     switch (ToLayoutDetailsMarker(&o)->GetOrientation()) {
270       case LayoutDetailsMarker::kLeft:
271         ts << "left";
272         break;
273       case LayoutDetailsMarker::kRight:
274         ts << "right";
275         break;
276       case LayoutDetailsMarker::kUp:
277         ts << "up";
278         break;
279       case LayoutDetailsMarker::kDown:
280         ts << "down";
281         break;
282     }
283   }
284 
285   if (o.IsListMarker()) {
286     String text = ToLayoutListMarker(o).GetText();
287     if (!text.IsEmpty()) {
288       if (text.length() != 1) {
289         text = QuoteAndEscapeNonPrintables(text);
290       } else {
291         switch (text[0]) {
292           case kBulletCharacter:
293             text = "bullet";
294             break;
295           case kBlackSquareCharacter:
296             text = "black square";
297             break;
298           case kWhiteBulletCharacter:
299             text = "white bullet";
300             break;
301           default:
302             text = QuoteAndEscapeNonPrintables(text);
303         }
304       }
305       ts << ": " << text;
306     }
307   }
308 
309   if (behavior & kLayoutAsTextShowIDAndClass) {
310     if (auto* element = DynamicTo<Element>(o.GetNode())) {
311       if (element->HasID())
312         ts << " id=\"" + element->GetIdAttribute() + "\"";
313 
314       if (element->HasClass()) {
315         ts << " class=\"";
316         for (wtf_size_t i = 0; i < element->ClassNames().size(); ++i) {
317           if (i > 0)
318             ts << " ";
319           ts << element->ClassNames()[i];
320         }
321         ts << "\"";
322       }
323     }
324   }
325 
326   if (behavior & kLayoutAsTextShowLayoutState) {
327     bool needs_layout = o.SelfNeedsLayout() ||
328                         o.NeedsPositionedMovementLayout() ||
329                         o.PosChildNeedsLayout() || o.NormalChildNeedsLayout();
330     if (needs_layout)
331       ts << " (needs layout:";
332 
333     bool have_previous = false;
334     if (o.SelfNeedsLayout()) {
335       ts << " self";
336       have_previous = true;
337     }
338 
339     if (o.NeedsPositionedMovementLayout()) {
340       if (have_previous)
341         ts << ",";
342       have_previous = true;
343       ts << " positioned movement";
344     }
345 
346     if (o.NormalChildNeedsLayout()) {
347       if (have_previous)
348         ts << ",";
349       have_previous = true;
350       ts << " child";
351     }
352 
353     if (o.PosChildNeedsLayout()) {
354       if (have_previous)
355         ts << ",";
356       ts << " positioned child";
357     }
358 
359     if (needs_layout)
360       ts << ")";
361   }
362 
363   if (o.LayoutBlockedByDisplayLock(DisplayLockLifecycleTarget::kChildren))
364     ts << " (display-locked)";
365 }
366 
WriteInlineBox(WTF::TextStream & ts,const InlineBox & box,int indent)367 static void WriteInlineBox(WTF::TextStream& ts,
368                            const InlineBox& box,
369                            int indent) {
370   WriteIndent(ts, indent);
371   ts << "+ ";
372   ts << box.BoxName() << " {" << box.GetLineLayoutItem().DebugName() << "}"
373      << " pos=(" << box.X() << "," << box.Y() << ")"
374      << " size=(" << box.Width() << "," << box.Height() << ")"
375      << " baseline=" << box.BaselinePosition(kAlphabeticBaseline) << "/"
376      << box.BaselinePosition(kIdeographicBaseline);
377 }
378 
WriteInlineTextBox(WTF::TextStream & ts,const InlineTextBox & text_box,int indent)379 static void WriteInlineTextBox(WTF::TextStream& ts,
380                                const InlineTextBox& text_box,
381                                int indent) {
382   WriteInlineBox(ts, text_box, indent);
383   String value = text_box.GetText();
384   value.Replace('\\', "\\\\");
385   value.Replace('\n', "\\n");
386   value.Replace('"', "\\\"");
387   ts << " range=(" << text_box.Start() << ","
388      << (text_box.Start() + text_box.Len()) << ")"
389      << " \"" << value << "\"";
390 }
391 
WriteInlineFlowBox(WTF::TextStream & ts,const InlineFlowBox & root_box,int indent)392 static void WriteInlineFlowBox(WTF::TextStream& ts,
393                                const InlineFlowBox& root_box,
394                                int indent) {
395   WriteInlineBox(ts, root_box, indent);
396   ts << "\n";
397   for (const InlineBox* box = root_box.FirstChild(); box;
398        box = box->NextOnLine()) {
399     if (box->IsInlineFlowBox()) {
400       WriteInlineFlowBox(ts, static_cast<const InlineFlowBox&>(*box),
401                          indent + 1);
402       continue;
403     }
404     if (box->IsInlineTextBox())
405       WriteInlineTextBox(ts, static_cast<const InlineTextBox&>(*box),
406                          indent + 1);
407     else
408       WriteInlineBox(ts, *box, indent + 1);
409     ts << "\n";
410   }
411 }
412 
WriteLineBoxTree(WTF::TextStream & ts,const LayoutBlockFlow & o,int indent)413 void LayoutTreeAsText::WriteLineBoxTree(WTF::TextStream& ts,
414                                         const LayoutBlockFlow& o,
415                                         int indent) {
416   for (const InlineFlowBox* root_box : o.LineBoxes()) {
417     WriteInlineFlowBox(ts, *root_box, indent);
418   }
419 }
420 
WriteTextRun(WTF::TextStream & ts,const LayoutText & o,const InlineTextBox & run)421 static void WriteTextRun(WTF::TextStream& ts,
422                          const LayoutText& o,
423                          const InlineTextBox& run) {
424   // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth,
425   // although this makes it harder to detect any changes caused by the
426   // conversion to floating point. :(
427   int x = run.X().ToInt();
428   int y = run.Y().ToInt();
429   int logical_width = (run.X() + run.LogicalWidth()).Ceil() - x;
430 
431   // FIXME: Table cell adjustment is temporary until results can be updated.
432   if (o.ContainingBlock()->IsTableCell()) {
433     y -= ToInterface<LayoutNGTableCellInterface>(o.ContainingBlock())
434              ->IntrinsicPaddingBefore();
435   }
436 
437   ts << "text run at (" << x << "," << y << ") width " << logical_width;
438   if (!run.IsLeftToRightDirection() || run.DirOverride()) {
439     ts << (!run.IsLeftToRightDirection() ? " RTL" : " LTR");
440     if (run.DirOverride())
441       ts << " override";
442   }
443   ts << ": "
444      << QuoteAndEscapeNonPrintables(
445             String(o.GetText()).Substring(run.Start(), run.Len()));
446   if (run.HasHyphen()) {
447     ts << " + hyphen string "
448        << QuoteAndEscapeNonPrintables(o.StyleRef().HyphenString());
449   }
450   ts << "\n";
451 }
452 
WriteTextFragment(WTF::TextStream & ts,const LayoutObject * layout_object,PhysicalRect rect,const ComputedStyle & style,StringView text,LayoutUnit inline_size)453 static void WriteTextFragment(WTF::TextStream& ts,
454                               const LayoutObject* layout_object,
455                               PhysicalRect rect,
456                               const ComputedStyle& style,
457                               StringView text,
458                               LayoutUnit inline_size) {
459   // TODO(layout-dev): Dump physical coordinates when removing the legacy inline
460   // layout code.
461   PhysicalOffset offset_to_container_box = rect.offset;
462   if (UNLIKELY(style.IsFlippedBlocksWritingMode())) {
463     if (layout_object) {
464       const LayoutBlock* containing_block = layout_object->ContainingBlock();
465       LayoutRect layout_rect = containing_block->FlipForWritingMode(rect);
466       offset_to_container_box.left = layout_rect.X();
467     }
468   }
469 
470   // See WriteTextRun() for why we convert to int.
471   int x = offset_to_container_box.left.ToInt();
472   int y = offset_to_container_box.top.ToInt();
473   int logical_width = (offset_to_container_box.left + inline_size).Ceil() - x;
474   ts << "text run at (" << x << "," << y << ") width " << logical_width;
475   ts << ": " << QuoteAndEscapeNonPrintables(text.ToString());
476   ts << "\n";
477 }
478 
WriteTextFragment(WTF::TextStream & ts,const NGInlineCursor & cursor)479 static void WriteTextFragment(WTF::TextStream& ts,
480                               const NGInlineCursor& cursor) {
481   if (const NGPaintFragment* const paint_fragment =
482           cursor.CurrentPaintFragment()) {
483     const auto* physical_text_fragment =
484         DynamicTo<NGPhysicalTextFragment>(paint_fragment->PhysicalFragment());
485     if (!physical_text_fragment)
486       return;
487     const NGTextFragment fragment(paint_fragment->Style().GetWritingMode(),
488                                   *physical_text_fragment);
489     WriteTextFragment(ts, paint_fragment->GetLayoutObject(),
490                       paint_fragment->RectInContainerBlock(),
491                       paint_fragment->Style(), physical_text_fragment->Text(),
492                       fragment.InlineSize());
493     return;
494   }
495   DCHECK(cursor.CurrentItem());
496   const NGFragmentItem& item = *cursor.CurrentItem();
497   DCHECK(item.Type() == NGFragmentItem::kText ||
498          item.Type() == NGFragmentItem::kGeneratedText);
499   const LayoutUnit inline_size =
500       item.IsHorizontal() ? item.Size().width : item.Size().height;
501   WriteTextFragment(ts, item.GetLayoutObject(), item.RectInContainerBlock(),
502                     item.Style(), item.Text(cursor.Items()), inline_size);
503 }
504 
WritePaintProperties(WTF::TextStream & ts,const LayoutObject & o,int indent)505 static void WritePaintProperties(WTF::TextStream& ts,
506                                  const LayoutObject& o,
507                                  int indent) {
508   bool has_fragments = o.FirstFragment().NextFragment();
509   if (has_fragments) {
510     WriteIndent(ts, indent);
511     ts << "fragments:\n";
512   }
513   int fragment_index = 0;
514   for (const auto *fragment = &o.FirstFragment(); fragment;
515        fragment = fragment->NextFragment(), ++fragment_index) {
516     WriteIndent(ts, indent);
517     if (has_fragments)
518       ts << " " << fragment_index << ":";
519     ts << " paint_offset=(" << fragment->PaintOffset().ToString()
520        << ") visual_rect=(" << fragment->VisualRect().ToString() << ")";
521     if (fragment->HasLocalBorderBoxProperties()) {
522       // To know where they point into the paint property tree, you can dump
523       // the tree using ShowAllPropertyTrees(frame_view).
524       ts << " state=(" << fragment->LocalBorderBoxProperties().ToString()
525          << ")";
526     }
527     ts << "\n";
528   }
529 }
530 
Write(WTF::TextStream & ts,const LayoutObject & o,int indent,LayoutAsTextBehavior behavior)531 void Write(WTF::TextStream& ts,
532            const LayoutObject& o,
533            int indent,
534            LayoutAsTextBehavior behavior) {
535   if (o.IsSVGShape()) {
536     Write(ts, ToLayoutSVGShape(o), indent);
537     return;
538   }
539   if (o.IsSVGResourceContainer()) {
540     WriteSVGResourceContainer(ts, o, indent);
541     return;
542   }
543   if (o.IsSVGContainer()) {
544     WriteSVGContainer(ts, o, indent);
545     return;
546   }
547   if (o.IsSVGRoot()) {
548     Write(ts, ToLayoutSVGRoot(o), indent);
549     return;
550   }
551   if (o.IsSVGText()) {
552     WriteSVGText(ts, ToLayoutSVGText(o), indent);
553     return;
554   }
555   if (o.IsSVGInline()) {
556     WriteSVGInline(ts, ToLayoutSVGInline(o), indent);
557     return;
558   }
559   if (o.IsSVGInlineText()) {
560     WriteSVGInlineText(ts, ToLayoutSVGInlineText(o), indent);
561     return;
562   }
563   if (o.IsSVGImage()) {
564     WriteSVGImage(ts, ToLayoutSVGImage(o), indent);
565     return;
566   }
567 
568   WriteIndent(ts, indent);
569 
570   LayoutTreeAsText::WriteLayoutObject(ts, o, behavior);
571   ts << "\n";
572 
573   if (behavior & kLayoutAsTextShowPaintProperties) {
574     WritePaintProperties(ts, o, indent + 1);
575   }
576 
577   auto* layout_block_flow = DynamicTo<LayoutBlockFlow>(o);
578   if ((behavior & kLayoutAsTextShowLineTrees) && layout_block_flow) {
579     LayoutTreeAsText::WriteLineBoxTree(ts, *layout_block_flow, indent + 1);
580   }
581 
582   if (o.IsText() && !o.IsBR()) {
583     const LayoutText& text = ToLayoutText(o);
584     if (const LayoutBlockFlow* block_flow = text.ContainingNGBlockFlow()) {
585       NGInlineCursor cursor(*block_flow);
586       cursor.MoveTo(text);
587       for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
588         WriteIndent(ts, indent + 1);
589         WriteTextFragment(ts, cursor);
590       }
591     } else {
592       for (InlineTextBox* box : text.TextBoxes()) {
593         WriteIndent(ts, indent + 1);
594         WriteTextRun(ts, text, *box);
595       }
596     }
597   }
598 
599   if (!o.LayoutBlockedByDisplayLock(DisplayLockLifecycleTarget::kChildren)) {
600     for (LayoutObject* child = o.SlowFirstChild(); child;
601          child = child->NextSibling()) {
602       if (child->HasLayer())
603         continue;
604       Write(ts, *child, indent + 1, behavior);
605     }
606   }
607 
608   if (o.IsLayoutEmbeddedContent()) {
609     FrameView* frame_view = ToLayoutEmbeddedContent(o).ChildFrameView();
610     if (auto* local_frame_view = DynamicTo<LocalFrameView>(frame_view)) {
611       if (auto* layout_view = local_frame_view->GetLayoutView()) {
612         layout_view->GetDocument().UpdateStyleAndLayout(
613             DocumentUpdateReason::kTest);
614         if (auto* layer = layout_view->Layer()) {
615           LayoutTreeAsText::WriteLayers(ts, layer, layer, indent + 1, behavior);
616         }
617       }
618     }
619   }
620 }
621 
622 enum LayerPaintPhase {
623   kLayerPaintPhaseAll = 0,
624   kLayerPaintPhaseBackground = -1,
625   kLayerPaintPhaseForeground = 1
626 };
627 
Write(WTF::TextStream & ts,PaintLayer & layer,const PhysicalRect & layer_bounds,const PhysicalRect & background_clip_rect,const PhysicalRect & clip_rect,LayerPaintPhase paint_phase=kLayerPaintPhaseAll,int indent=0,LayoutAsTextBehavior behavior=kLayoutAsTextBehaviorNormal,const PaintLayer * marked_layer=nullptr)628 static void Write(WTF::TextStream& ts,
629                   PaintLayer& layer,
630                   const PhysicalRect& layer_bounds,
631                   const PhysicalRect& background_clip_rect,
632                   const PhysicalRect& clip_rect,
633                   LayerPaintPhase paint_phase = kLayerPaintPhaseAll,
634                   int indent = 0,
635                   LayoutAsTextBehavior behavior = kLayoutAsTextBehaviorNormal,
636                   const PaintLayer* marked_layer = nullptr) {
637   IntRect adjusted_layout_bounds = PixelSnappedIntRect(layer_bounds);
638   IntRect adjusted_background_clip_rect =
639       PixelSnappedIntRect(background_clip_rect);
640   IntRect adjusted_clip_rect = PixelSnappedIntRect(clip_rect);
641 
642   if (marked_layer)
643     ts << (marked_layer == &layer ? "*" : " ");
644 
645   WriteIndent(ts, indent);
646 
647   if (layer.GetLayoutObject().StyleRef().Visibility() == EVisibility::kHidden)
648     ts << "hidden ";
649 
650   ts << "layer ";
651 
652   if (behavior & kLayoutAsTextShowAddresses)
653     ts << static_cast<const void*>(&layer) << " ";
654 
655   ts << adjusted_layout_bounds;
656 
657   if (!adjusted_layout_bounds.IsEmpty()) {
658     if (!adjusted_background_clip_rect.Contains(adjusted_layout_bounds))
659       ts << " backgroundClip " << adjusted_background_clip_rect;
660     if (!adjusted_clip_rect.Contains(adjusted_layout_bounds))
661       ts << " clip " << adjusted_clip_rect;
662   }
663   if (layer.IsTransparent())
664     ts << " transparent";
665 
666   if (layer.GetLayoutObject().HasOverflowClip()) {
667     PaintLayerScrollableArea* scrollable_area = layer.GetScrollableArea();
668     ScrollOffset adjusted_scroll_offset =
669         scrollable_area->GetScrollOffset() +
670         ToFloatSize(FloatPoint(scrollable_area->ScrollOrigin()));
671     if (adjusted_scroll_offset.Width())
672       ts << " scrollX " << adjusted_scroll_offset.Width();
673     if (adjusted_scroll_offset.Height())
674       ts << " scrollY " << adjusted_scroll_offset.Height();
675     if (layer.GetLayoutBox() &&
676         layer.GetLayoutBox()->PixelSnappedClientWidth() !=
677             layer.GetLayoutBox()->PixelSnappedScrollWidth())
678       ts << " scrollWidth " << layer.GetLayoutBox()->PixelSnappedScrollWidth();
679     if (layer.GetLayoutBox() &&
680         layer.GetLayoutBox()->PixelSnappedClientHeight() !=
681             layer.GetLayoutBox()->PixelSnappedScrollHeight())
682       ts << " scrollHeight "
683          << layer.GetLayoutBox()->PixelSnappedScrollHeight();
684   }
685 
686   if (paint_phase == kLayerPaintPhaseBackground)
687     ts << " layerType: background only";
688   else if (paint_phase == kLayerPaintPhaseForeground)
689     ts << " layerType: foreground only";
690 
691   if (layer.GetLayoutObject().StyleRef().HasBlendMode()) {
692     ts << " blendMode: "
693        << CompositeOperatorName(
694               kCompositeSourceOver,
695               layer.GetLayoutObject().StyleRef().GetBlendMode());
696   }
697 
698   if (behavior & kLayoutAsTextShowCompositedLayers) {
699     if (layer.HasCompositedLayerMapping()) {
700       ts << " (composited, bounds="
701          << layer.GetCompositedLayerMapping()->CompositedBounds()
702          << ", drawsContent="
703          << layer.GetCompositedLayerMapping()
704                 ->MainGraphicsLayer()
705                 ->DrawsContent()
706          << (layer.ShouldIsolateCompositedDescendants()
707                  ? ", isolatesCompositedBlending"
708                  : "")
709          << ")";
710     }
711   }
712 
713   if ((behavior & kLayoutAsTextShowPaintProperties) && layer.SelfNeedsRepaint())
714     ts << " needsRepaint";
715 
716   ts << "\n";
717 
718   if (paint_phase != kLayerPaintPhaseBackground)
719     Write(ts, layer.GetLayoutObject(), indent + 1, behavior);
720 }
721 
ChildLayers(const PaintLayer * layer,PaintLayerIteration which_children)722 static Vector<PaintLayer*> ChildLayers(const PaintLayer* layer,
723                                        PaintLayerIteration which_children) {
724   Vector<PaintLayer*> vector;
725   PaintLayerPaintOrderIterator it(*layer, which_children);
726   while (PaintLayer* child = it.Next())
727     vector.push_back(child);
728   return vector;
729 }
730 
WriteLayers(WTF::TextStream & ts,const PaintLayer * root_layer,PaintLayer * layer,int indent,LayoutAsTextBehavior behavior,const PaintLayer * marked_layer)731 void LayoutTreeAsText::WriteLayers(WTF::TextStream& ts,
732                                    const PaintLayer* root_layer,
733                                    PaintLayer* layer,
734                                    int indent,
735                                    LayoutAsTextBehavior behavior,
736                                    const PaintLayer* marked_layer) {
737   // Calculate the clip rects we should use.
738   PhysicalRect layer_bounds;
739   ClipRect damage_rect, clip_rect_to_apply;
740   if (layer->GetLayoutObject().FirstFragment().HasLocalBorderBoxProperties()) {
741     layer->Clipper(PaintLayer::GeometryMapperOption::kUseGeometryMapper)
742         .CalculateRects(
743             ClipRectsContext(root_layer,
744                              &root_layer->GetLayoutObject().FirstFragment(),
745                              kUncachedClipRects),
746             &layer->GetLayoutObject().FirstFragment(), nullptr, layer_bounds,
747             damage_rect, clip_rect_to_apply);
748   } else {
749     layer->Clipper(PaintLayer::GeometryMapperOption::kDoNotUseGeometryMapper)
750         .CalculateRects(
751             ClipRectsContext(root_layer, nullptr, kUncachedClipRects), nullptr,
752             nullptr, layer_bounds, damage_rect, clip_rect_to_apply);
753   }
754 
755   PhysicalOffset offset_from_root;
756   layer->ConvertToLayerCoords(root_layer, offset_from_root);
757   bool should_paint =
758       (behavior & kLayoutAsTextShowAllLayers)
759           ? true
760           : layer->IntersectsDamageRect(layer_bounds, damage_rect.Rect(),
761                                         offset_from_root);
762 
763   if (layer->GetLayoutObject().IsLayoutEmbeddedContent() &&
764       ToLayoutEmbeddedContent(layer->GetLayoutObject()).IsThrottledFrameView())
765     should_paint = false;
766 
767 #if DCHECK_IS_ON()
768   if (layer->NeedsPositionUpdate()) {
769     WriteIndent(ts, indent);
770     ts << " NEEDS POSITION UPDATE\n";
771   }
772 #endif
773 
774   const auto& neg_list = ChildLayers(layer, kNegativeZOrderChildren);
775   bool paints_background_separately = !neg_list.IsEmpty();
776   if (should_paint && paints_background_separately) {
777     Write(ts, *layer, layer_bounds, damage_rect.Rect(),
778           clip_rect_to_apply.Rect(), kLayerPaintPhaseBackground, indent,
779           behavior, marked_layer);
780   }
781 
782   if (!neg_list.IsEmpty()) {
783     int curr_indent = indent;
784     if (behavior & kLayoutAsTextShowLayerNesting) {
785       WriteIndent(ts, indent);
786       ts << " negative z-order list(" << neg_list.size() << ")\n";
787       ++curr_indent;
788     }
789     for (auto* layer : neg_list)
790       WriteLayers(ts, root_layer, layer, curr_indent, behavior, marked_layer);
791   }
792 
793   if (should_paint) {
794     Write(ts, *layer, layer_bounds, damage_rect.Rect(),
795           clip_rect_to_apply.Rect(),
796           paints_background_separately ? kLayerPaintPhaseForeground
797                                        : kLayerPaintPhaseAll,
798           indent, behavior, marked_layer);
799   }
800 
801   const auto& normal_flow_list = ChildLayers(layer, kNormalFlowChildren);
802   if (!normal_flow_list.IsEmpty()) {
803     int curr_indent = indent;
804     if (behavior & kLayoutAsTextShowLayerNesting) {
805       WriteIndent(ts, indent);
806       ts << " normal flow list(" << normal_flow_list.size() << ")\n";
807       ++curr_indent;
808     }
809     for (auto* layer : normal_flow_list)
810       WriteLayers(ts, root_layer, layer, curr_indent, behavior, marked_layer);
811   }
812 
813   const auto& pos_list = ChildLayers(layer, kPositiveZOrderChildren);
814   if (!pos_list.IsEmpty()) {
815     int curr_indent = indent;
816     if (behavior & kLayoutAsTextShowLayerNesting) {
817       WriteIndent(ts, indent);
818       ts << " positive z-order list(" << pos_list.size() << ")\n";
819       ++curr_indent;
820     }
821     for (auto* layer : pos_list)
822       WriteLayers(ts, root_layer, layer, curr_indent, behavior, marked_layer);
823   }
824 }
825 
NodePosition(Node * node)826 static String NodePosition(Node* node) {
827   StringBuilder result;
828 
829   Element* body = node->GetDocument().body();
830   Node* parent;
831   for (Node* n = node; n; n = parent) {
832     parent = n->ParentOrShadowHostNode();
833     if (n != node)
834       result.Append(" of ");
835     if (parent) {
836       if (body && n == body) {
837         // We don't care what offset body may be in the document.
838         result.Append("body");
839         break;
840       }
841       if (n->IsShadowRoot()) {
842         result.Append('{');
843         result.Append(GetTagName(n));
844         result.Append('}');
845       } else {
846         result.Append("child ");
847         result.AppendNumber(n->NodeIndex());
848         result.Append(" {");
849         result.Append(GetTagName(n));
850         result.Append('}');
851       }
852     } else {
853       result.Append("document");
854     }
855   }
856 
857   return result.ToString();
858 }
859 
WriteSelection(WTF::TextStream & ts,const LayoutObject * o)860 static void WriteSelection(WTF::TextStream& ts, const LayoutObject* o) {
861   Document* doc = DynamicTo<Document>(o->GetNode());
862   if (!doc)
863     return;
864 
865   LocalFrame* frame = doc->GetFrame();
866   if (!frame)
867     return;
868 
869   const VisibleSelection& selection =
870       frame->Selection().ComputeVisibleSelectionInDOMTree();
871   if (selection.IsCaret()) {
872     ts << "caret: position " << selection.Start().ComputeEditingOffset()
873        << " of " << NodePosition(selection.Start().AnchorNode());
874     if (selection.Affinity() == TextAffinity::kUpstream)
875       ts << " (upstream affinity)";
876     ts << "\n";
877   } else if (selection.IsRange()) {
878     ts << "selection start: position "
879        << selection.Start().ComputeEditingOffset() << " of "
880        << NodePosition(selection.Start().AnchorNode()) << "\n"
881        << "selection end:   position " << selection.End().ComputeEditingOffset()
882        << " of " << NodePosition(selection.End().AnchorNode()) << "\n";
883   }
884 }
885 
ExternalRepresentation(LayoutBox * layout_object,LayoutAsTextBehavior behavior,const PaintLayer * marked_layer=nullptr)886 static String ExternalRepresentation(LayoutBox* layout_object,
887                                      LayoutAsTextBehavior behavior,
888                                      const PaintLayer* marked_layer = nullptr) {
889   WTF::TextStream ts;
890   if (!layout_object->HasLayer())
891     return ts.Release();
892 
893   PaintLayer* layer = layout_object->Layer();
894   LayoutTreeAsText::WriteLayers(ts, layer, layer, 0, behavior, marked_layer);
895   WriteSelection(ts, layout_object);
896   return ts.Release();
897 }
898 
ExternalRepresentation(LocalFrame * frame,LayoutAsTextBehavior behavior,const PaintLayer * marked_layer)899 String ExternalRepresentation(LocalFrame* frame,
900                               LayoutAsTextBehavior behavior,
901                               const PaintLayer* marked_layer) {
902   if (!(behavior & kLayoutAsTextDontUpdateLayout)) {
903     bool success = frame->View()->UpdateAllLifecyclePhasesExceptPaint(
904         DocumentUpdateReason::kTest);
905     DCHECK(success);
906   };
907 
908   LayoutObject* layout_object = frame->ContentLayoutObject();
909   if (!layout_object || !layout_object->IsBox())
910     return String();
911   LayoutBox* layout_box = ToLayoutBox(layout_object);
912 
913   PrintContext print_context(frame, /*use_printing_layout=*/true);
914   bool is_text_printing_mode = !!(behavior & kLayoutAsTextPrintingMode);
915   if (is_text_printing_mode) {
916     print_context.BeginPrintMode(layout_box->ClientWidth(),
917                                  layout_box->ClientHeight());
918 
919     // The lifecycle needs to be run again after changing printing mode,
920     // to account for any style updates due to media query change.
921     if (!(behavior & kLayoutAsTextDontUpdateLayout))
922       frame->View()->UpdateLifecyclePhasesForPrinting();
923   }
924 
925   String representation = ExternalRepresentation(ToLayoutBox(layout_object),
926                                                  behavior, marked_layer);
927   if (is_text_printing_mode)
928     print_context.EndPrintMode();
929   return representation;
930 }
931 
ExternalRepresentation(Element * element,LayoutAsTextBehavior behavior)932 String ExternalRepresentation(Element* element, LayoutAsTextBehavior behavior) {
933   // Doesn't support printing mode.
934   DCHECK(!(behavior & kLayoutAsTextPrintingMode));
935   if (!(behavior & kLayoutAsTextDontUpdateLayout)) {
936     element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
937   }
938 
939   LayoutObject* layout_object = element->GetLayoutObject();
940   if (!layout_object || !layout_object->IsBox())
941     return String();
942 
943   return ExternalRepresentation(ToLayoutBox(layout_object),
944                                 behavior | kLayoutAsTextShowAllLayers);
945 }
946 
WriteCounterValuesFromChildren(WTF::TextStream & stream,LayoutObject * parent,bool & is_first_counter)947 static void WriteCounterValuesFromChildren(WTF::TextStream& stream,
948                                            LayoutObject* parent,
949                                            bool& is_first_counter) {
950   for (LayoutObject* child = parent->SlowFirstChild(); child;
951        child = child->NextSibling()) {
952     if (child->IsCounter()) {
953       if (!is_first_counter)
954         stream << " ";
955       is_first_counter = false;
956       String str(ToLayoutText(child)->GetText());
957       stream << str;
958     }
959   }
960 }
961 
CounterValueForElement(Element * element)962 String CounterValueForElement(Element* element) {
963   element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
964   WTF::TextStream stream;
965   bool is_first_counter = true;
966   // The counter LayoutObjects should be children of ::marker, ::before or
967   // ::after pseudo-elements.
968   if (LayoutObject* marker =
969           element->PseudoElementLayoutObject(kPseudoIdMarker))
970     WriteCounterValuesFromChildren(stream, marker, is_first_counter);
971   if (LayoutObject* before =
972           element->PseudoElementLayoutObject(kPseudoIdBefore))
973     WriteCounterValuesFromChildren(stream, before, is_first_counter);
974   if (LayoutObject* after = element->PseudoElementLayoutObject(kPseudoIdAfter))
975     WriteCounterValuesFromChildren(stream, after, is_first_counter);
976   return stream.Release();
977 }
978 
MarkerTextForListItem(Element * element)979 String MarkerTextForListItem(Element* element) {
980   element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
981 
982   LayoutObject* layout_object = element->GetLayoutObject();
983   if (layout_object) {
984     if (layout_object->IsListItem())
985       return ToLayoutListItem(layout_object)->MarkerText();
986     if (layout_object->IsLayoutNGListItem()) {
987       if (LayoutObject* marker = ToLayoutNGListItem(layout_object)->Marker())
988         return ListMarker::Get(marker)->MarkerTextWithoutSuffix(*marker);
989     }
990   }
991   return String();
992 }
993 
994 }  // namespace blink
995