source: webkit/trunk/Source/WebCore/rendering/RenderTreeAsText.cpp

Last change on this file was 295683, checked in by [email protected], 3 years ago

[LBSE] Add support for <path>
https://bugs.webkit.org/show_bug.cgi?id=240864

Patch by Nikolas Zimmermann <[email protected]> on 2022-06-21
Reviewed by Rob Buis.

Rename RenderSVGPath -> LegacyRenderSVGPath and adapt callees.

Re-introduce RenderSVGPath for LBSE, and activate
renderer creation for <path>, if LBSE is enabled at run-time.

This allows a 22 more SVG 1.1 testcases to pass with LBSE.

  • LayoutTests/platform/mac-monterey-wk2-lbse-text/TestExpectations:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-04-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-05-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-06-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-07-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-08-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-09-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-10-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-11-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-12-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-28-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-30-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-34-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-36-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-37-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-44-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/animate-elem-83-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/coords-viewattr-01-b-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/extend-namespace-01-f-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/filters-offset-01-b-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/fonts-elem-01-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/fonts-elem-02-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/fonts-elem-03-b-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/fonts-elem-04-b-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/fonts-elem-07-b-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/linking-a-05-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/metadata-example-01-b-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/painting-fill-03-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/painting-marker-01-f-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/painting-marker-02-f-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/painting-marker-03-f-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/painting-stroke-03-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/painting-stroke-04-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/painting-stroke-07-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-01-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-02-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-03-f-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-04-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-05-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-06-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-07-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-08-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-09-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-10-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-12-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-13-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-14-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/paths-data-15-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/render-elems-01-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/render-elems-02-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/render-elems-03-t-expected.txt:
  • LayoutTests/platform/mac-monterey-wk2-lbse-text/svg/W3C-SVG-1.1/struct-frag-06-t-expected.txt:
  • Source/WebCore/Sources.txt:
  • Source/WebCore/WebCore.xcodeproj/project.pbxproj:
  • Source/WebCore/accessibility/AccessibilitySVGElement.cpp:

(WebCore::AccessibilitySVGElement::determineAccessibilityRole):

  • Source/WebCore/rendering/RenderObject.h:

(WebCore::RenderObject::isLegacySVGPath const):
(WebCore::RenderObject::isSVGPathOrLegacySVGPath const):

  • Source/WebCore/rendering/RenderTreeAsText.cpp:
  • Source/WebCore/rendering/svg/LegacyRenderSVGPath.cpp: Copied from Source/WebCore/rendering/svg/RenderSVGPath.cpp.

(WebCore::LegacyRenderSVGPath::LegacyRenderSVGPath):
(WebCore::LegacyRenderSVGPath::updateShapeFromElement):
(WebCore::LegacyRenderSVGPath::calculateUpdatedStrokeBoundingBox const):
(WebCore::useStrokeStyleToFill):
(WebCore::LegacyRenderSVGPath::strokeShape const):
(WebCore::LegacyRenderSVGPath::shapeDependentStrokeContains):
(WebCore::LegacyRenderSVGPath::shouldStrokeZeroLengthSubpath const):
(WebCore::LegacyRenderSVGPath::zeroLengthLinecapPath const):
(WebCore::LegacyRenderSVGPath::zeroLengthSubpathRect const):
(WebCore::LegacyRenderSVGPath::updateZeroLengthSubpaths):
(WebCore::LegacyRenderSVGPath::isRenderingDisabled const):

  • Source/WebCore/rendering/svg/LegacyRenderSVGPath.h: Copied from Source/WebCore/rendering/svg/RenderSVGPath.h.
  • Source/WebCore/rendering/svg/LegacyRenderSVGShape.h:
  • Source/WebCore/rendering/svg/RenderSVGHiddenContainer.cpp:
  • Source/WebCore/rendering/svg/RenderSVGPath.cpp:

(WebCore::RenderSVGPath::RenderSVGPath):
(WebCore::RenderSVGPath::updateShapeFromElement):
(WebCore::RenderSVGPath::strokeShape const):
(WebCore::RenderSVGPath::shapeDependentStrokeContains):

  • Source/WebCore/rendering/svg/RenderSVGPath.h:
  • Source/WebCore/rendering/svg/RenderSVGShape.h:
  • Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp:
  • Source/WebCore/svg/SVGElement.cpp:

(WebCore::createSVGLayerAwareElementSet):

  • Source/WebCore/svg/SVGGraphicsElement.cpp:

(WebCore::SVGGraphicsElement::createElementRenderer):

  • Source/WebCore/svg/SVGPathElement.cpp:

(WebCore::SVGPathElement::svgAttributeChanged):
(WebCore::SVGPathElement::getBBox):
(WebCore::SVGPathElement::createElementRenderer):

  • Source/WebCore/svg/SVGPolyElement.cpp:

(WebCore::SVGPolyElement::svgAttributeChanged):
(WebCore::SVGPolyElement::approximateMemoryCost const):

Canonical link: https://commits.webkit.org/251688@main

  • Property svn:eol-style set to native
File size: 38.3 KB
Line 
1/*
2 * Copyright (C) 2004-2017 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 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 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 "config.h"
27#include "RenderTreeAsText.h"
28
29#include "ClipRect.h"
30#include "ColorSerialization.h"
31#include "Document.h"
32#include "ElementInlines.h"
33#include "Frame.h"
34#include "FrameSelection.h"
35#include "FrameView.h"
36#include "HTMLElement.h"
37#include "HTMLNames.h"
38#include "HTMLSpanElement.h"
39#include "InlineIteratorTextBox.h"
40#include "LegacyInlineTextBox.h"
41#include "LegacyRenderSVGContainer.h"
42#include "LegacyRenderSVGRoot.h"
43#include "LegacyRenderSVGShape.h"
44#include "Logging.h"
45#include "PrintContext.h"
46#include "PseudoElement.h"
47#include "RenderBlockFlow.h"
48#include "RenderCounter.h"
49#include "RenderDetailsMarker.h"
50#include "RenderFileUploadControl.h"
51#include "RenderFragmentContainer.h"
52#include "RenderInline.h"
53#include "RenderIterator.h"
54#include "RenderLayer.h"
55#include "RenderLayerBacking.h"
56#include "RenderLayerScrollableArea.h"
57#include "RenderLineBreak.h"
58#include "RenderListItem.h"
59#include "RenderListMarker.h"
60#include "RenderQuote.h"
61#include "RenderRuby.h"
62#include "RenderSVGContainer.h"
63#include "RenderSVGGradientStop.h"
64#include "RenderSVGImage.h"
65#include "RenderSVGInlineText.h"
66#include "RenderSVGResourceContainer.h"
67#include "RenderSVGRoot.h"
68#include "RenderSVGShape.h"
69#include "RenderSVGText.h"
70#include "RenderTableCell.h"
71#include "RenderView.h"
72#include "RenderWidget.h"
73#include "SVGRenderTreeAsText.h"
74#include "ShadowRoot.h"
75#include "StyleProperties.h"
76#include <wtf/HexNumber.h>
77#include <wtf/Vector.h>
78#include <wtf/text/TextStream.h>
79#include <wtf/unicode/CharacterNames.h>
80
81#if PLATFORM(MAC)
82#include "ScrollbarThemeMac.h"
83#endif
84
85namespace WebCore {
86
87using namespace HTMLNames;
88
89static void writeLayers(TextStream&, const RenderLayer& rootLayer, RenderLayer&, const LayoutRect& paintDirtyRect, OptionSet<RenderAsTextFlag>);
90
91static void printBorderStyle(TextStream& ts, const BorderStyle borderStyle)
92{
93 switch (borderStyle) {
94 case BorderStyle::None:
95 ts << "none";
96 break;
97 case BorderStyle::Hidden:
98 ts << "hidden";
99 break;
100 case BorderStyle::Inset:
101 ts << "inset";
102 break;
103 case BorderStyle::Groove:
104 ts << "groove";
105 break;
106 case BorderStyle::Ridge:
107 ts << "ridge";
108 break;
109 case BorderStyle::Outset:
110 ts << "outset";
111 break;
112 case BorderStyle::Dotted:
113 ts << "dotted";
114 break;
115 case BorderStyle::Dashed:
116 ts << "dashed";
117 break;
118 case BorderStyle::Solid:
119 ts << "solid";
120 break;
121 case BorderStyle::Double:
122 ts << "double";
123 break;
124 }
125
126 ts << " ";
127}
128
129static String getTagName(Node* n)
130{
131 if (n->isDocumentNode())
132 return ""_s;
133 if (n->nodeType() == Node::COMMENT_NODE)
134 return "COMMENT"_s;
135 return n->nodeName();
136}
137
138static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
139{
140 if (!is<HTMLSpanElement>(node))
141 return false;
142
143 const HTMLElement& element = downcast<HTMLSpanElement>(*node);
144 if (element.getAttribute(classAttr) != "Apple-style-span"_s)
145 return false;
146
147 if (!node->hasChildNodes())
148 return true;
149
150 const StyleProperties* inlineStyleDecl = element.inlineStyle();
151 return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
152}
153
154String quoteAndEscapeNonPrintables(StringView s)
155{
156 StringBuilder result;
157 result.append('"');
158 for (unsigned i = 0; i != s.length(); ++i) {
159 UChar c = s[i];
160 if (c == '\\') {
161 result.append("\\\\");
162 } else if (c == '"') {
163 result.append("\\\"");
164 } else if (c == '\n' || c == noBreakSpace)
165 result.append(' ');
166 else {
167 if (c >= 0x20 && c < 0x7F)
168 result.append(c);
169 else
170 result.append("\\x{", hex(c), '}');
171 }
172 }
173 result.append('"');
174 return result.toString();
175}
176
177static inline bool isRenderInlineEmpty(const RenderInline& inlineRenderer)
178{
179 if (isEmptyInline(inlineRenderer))
180 return true;
181
182 for (auto& child : childrenOfType<RenderObject>(inlineRenderer)) {
183 if (child.isFloatingOrOutOfFlowPositioned())
184 continue;
185 auto isChildEmpty = false;
186 if (is<RenderInline>(child))
187 isChildEmpty = isRenderInlineEmpty(downcast<RenderInline>(child));
188 else if (is<RenderText>(child))
189 isChildEmpty = !downcast<RenderText>(child).linesBoundingBox().height();
190 if (!isChildEmpty)
191 return false;
192 }
193 return true;
194}
195
196static inline bool hasNonEmptySibling(const RenderInline& inlineRenderer)
197{
198 auto* parent = inlineRenderer.parent();
199 if (!parent)
200 return false;
201
202 for (auto& sibling : childrenOfType<RenderObject>(*parent)) {
203 if (&sibling == &inlineRenderer || sibling.isFloatingOrOutOfFlowPositioned())
204 continue;
205 if (!is<RenderInline>(sibling))
206 return true;
207 auto& siblingRendererInline = downcast<RenderInline>(sibling);
208 if (siblingRendererInline.mayAffectLayout() || !isRenderInlineEmpty(siblingRendererInline))
209 return true;
210 }
211 return false;
212}
213
214void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, OptionSet<RenderAsTextFlag> behavior)
215{
216 ts << o.renderName().characters();
217
218 if (behavior.contains(RenderAsTextFlag::ShowAddresses))
219 ts << " " << &o;
220
221 if (o.style().usedZIndex()) // FIXME: This should use !hasAutoUsedZIndex().
222 ts << " zI: " << o.style().usedZIndex();
223
224 if (o.node()) {
225 String tagName = getTagName(o.node());
226 // FIXME: Temporary hack to make tests pass by simulating the old generated content output.
227 if (o.isPseudoElement() || (o.parent() && o.parent()->isPseudoElement()))
228 tagName = emptyAtom();
229 if (!tagName.isEmpty()) {
230 ts << " {" << tagName << "}";
231 // flag empty or unstyled AppleStyleSpan because we never
232 // want to leave them in the DOM
233 if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
234 ts << " *empty or unstyled AppleStyleSpan*";
235 }
236 }
237
238 RenderBlock* cb = o.containingBlock();
239 bool adjustForTableCells = cb ? cb->isTableCell() : false;
240
241 LayoutRect r;
242 if (is<RenderText>(o)) {
243 // FIXME: Would be better to dump the bounding box x and y rather than the first run's x and y, but that would involve updating
244 // many test results.
245 const RenderText& text = downcast<RenderText>(o);
246 r = IntRect(text.firstRunLocation(), text.linesBoundingBox().size());
247 if (!InlineIterator::firstTextBoxFor(text))
248 adjustForTableCells = false;
249 } else if (o.isBR()) {
250 const RenderLineBreak& br = downcast<RenderLineBreak>(o);
251 IntRect linesBox = br.linesBoundingBox();
252 r = IntRect(linesBox.x(), linesBox.y(), linesBox.width(), linesBox.height());
253 if (!br.inlineBoxWrapper() && !InlineIterator::boxFor(br))
254 adjustForTableCells = false;
255 } else if (is<RenderInline>(o)) {
256 const RenderInline& inlineFlow = downcast<RenderInline>(o);
257 // FIXME: Would be better not to just dump 0, 0 as the x and y here.
258 auto width = inlineFlow.linesBoundingBox().width();
259 auto inlineHeight = [&] {
260 // Let's match legacy line layout's RenderInline behavior and report 0 height when the inline box is "empty".
261 // FIXME: Remove and rebaseline when LFC inline boxes are enabled (see webkit.org/b/220722)
262 auto height = inlineFlow.linesBoundingBox().height();
263 if (width)
264 return height;
265 if (is<RenderQuote>(inlineFlow) || is<RenderRubyAsInline>(inlineFlow))
266 return height;
267 if (inlineFlow.marginStart() || inlineFlow.marginEnd())
268 return height;
269 // This is mostly pre/post continuation content. Also see webkit.org/b/220735
270 if (hasNonEmptySibling(inlineFlow))
271 return height;
272 if (isRenderInlineEmpty(inlineFlow))
273 return 0;
274 return height;
275 };
276 r = IntRect(0, 0, width, inlineHeight());
277 adjustForTableCells = false;
278 } else if (is<RenderTableCell>(o)) {
279 // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like
280 // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
281 // captured by the results.
282 const RenderTableCell& cell = downcast<RenderTableCell>(o);
283 r = LayoutRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
284 } else if (is<RenderBox>(o))
285 r = downcast<RenderBox>(o).frameRect();
286
287 // FIXME: Temporary in order to ensure compatibility with existing layout test results.
288 if (adjustForTableCells)
289 r.move(0_lu, -downcast<RenderTableCell>(*o.containingBlock()).intrinsicPaddingBefore());
290
291 // FIXME: Convert layout test results to report sub-pixel values, in the meantime using enclosingIntRect
292 // for consistency with old results.
293 ts << " " << enclosingIntRect(r);
294
295 if (!is<RenderText>(o)) {
296 if (is<RenderFileUploadControl>(o))
297 ts << " " << quoteAndEscapeNonPrintables(downcast<RenderFileUploadControl>(o).fileTextValue());
298
299 if (o.parent()) {
300 Color color = o.style().visitedDependentColor(CSSPropertyColor);
301 if (!equalIgnoringSemanticColor(o.parent()->style().visitedDependentColor(CSSPropertyColor), color))
302 ts << " [color=" << serializationForRenderTreeAsText(color) << "]";
303
304 // Do not dump invalid or transparent backgrounds, since that is the default.
305 Color backgroundColor = o.style().visitedDependentColor(CSSPropertyBackgroundColor);
306 if (!equalIgnoringSemanticColor(o.parent()->style().visitedDependentColor(CSSPropertyBackgroundColor), backgroundColor)
307 && backgroundColor != Color::transparentBlack)
308 ts << " [bgcolor=" << serializationForRenderTreeAsText(backgroundColor) << "]";
309
310 Color textFillColor = o.style().visitedDependentColor(CSSPropertyWebkitTextFillColor);
311 if (!equalIgnoringSemanticColor(o.parent()->style().visitedDependentColor(CSSPropertyWebkitTextFillColor), textFillColor)
312 && textFillColor != color && textFillColor != Color::transparentBlack)
313 ts << " [textFillColor=" << serializationForRenderTreeAsText(textFillColor) << "]";
314
315 Color textStrokeColor = o.style().visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
316 if (!equalIgnoringSemanticColor(o.parent()->style().visitedDependentColor(CSSPropertyWebkitTextStrokeColor), textStrokeColor)
317 && textStrokeColor != color && textStrokeColor != Color::transparentBlack)
318 ts << " [textStrokeColor=" << serializationForRenderTreeAsText(textStrokeColor) << "]";
319
320 if (o.parent()->style().textStrokeWidth() != o.style().textStrokeWidth() && o.style().textStrokeWidth() > 0)
321 ts << " [textStrokeWidth=" << o.style().textStrokeWidth() << "]";
322 }
323
324 if (!is<RenderBoxModelObject>(o) || is<RenderLineBreak>(o))
325 return;
326
327 const RenderBoxModelObject& box = downcast<RenderBoxModelObject>(o);
328 LayoutUnit borderTop = box.borderTop();
329 LayoutUnit borderRight = box.borderRight();
330 LayoutUnit borderBottom = box.borderBottom();
331 LayoutUnit borderLeft = box.borderLeft();
332 bool overridden = o.style().borderImage().overridesBorderWidths();
333 if (box.isFieldset()) {
334 const auto& block = downcast<RenderBlock>(box);
335 if (o.style().writingMode() == WritingMode::TopToBottom)
336 borderTop -= block.intrinsicBorderForFieldset();
337 else if (o.style().writingMode() == WritingMode::BottomToTop)
338 borderBottom -= block.intrinsicBorderForFieldset();
339 else if (o.style().writingMode() == WritingMode::LeftToRight)
340 borderLeft -= block.intrinsicBorderForFieldset();
341 else if (o.style().writingMode() == WritingMode::RightToLeft)
342 borderRight -= block.intrinsicBorderForFieldset();
343
344 }
345 if (borderTop || borderRight || borderBottom || borderLeft) {
346 ts << " [border:";
347
348 BorderValue prevBorder = o.style().borderTop();
349 if (!borderTop)
350 ts << " none";
351 else {
352 ts << " (" << borderTop << "px ";
353 printBorderStyle(ts, o.style().borderTopStyle());
354 auto color = o.style().borderTopColor();
355 if (!color.isValid())
356 color = o.style().color();
357 ts << serializationForRenderTreeAsText(color) << ")";
358 }
359
360 if (o.style().borderRight() != prevBorder || (overridden && borderRight != borderTop)) {
361 prevBorder = o.style().borderRight();
362 if (!borderRight)
363 ts << " none";
364 else {
365 ts << " (" << borderRight << "px ";
366 printBorderStyle(ts, o.style().borderRightStyle());
367 auto color = o.style().borderRightColor();
368 if (!color.isValid())
369 color = o.style().color();
370 ts << serializationForRenderTreeAsText(color) << ")";
371 }
372 }
373
374 if (o.style().borderBottom() != prevBorder || (overridden && borderBottom != borderRight)) {
375 prevBorder = box.style().borderBottom();
376 if (!borderBottom)
377 ts << " none";
378 else {
379 ts << " (" << borderBottom << "px ";
380 printBorderStyle(ts, o.style().borderBottomStyle());
381 auto color = o.style().borderBottomColor();
382 if (!color.isValid())
383 color = o.style().color();
384 ts << serializationForRenderTreeAsText(color) << ")";
385 }
386 }
387
388 if (o.style().borderLeft() != prevBorder || (overridden && borderLeft != borderBottom)) {
389 prevBorder = o.style().borderLeft();
390 if (!borderLeft)
391 ts << " none";
392 else {
393 ts << " (" << borderLeft << "px ";
394 printBorderStyle(ts, o.style().borderLeftStyle());
395 auto color = o.style().borderLeftColor();
396 if (!color.isValid())
397 color = o.style().color();
398 ts << serializationForRenderTreeAsText(color) << ")";
399 }
400 }
401
402 ts << "]";
403 }
404
405#if ENABLE(MATHML)
406 // We want to show any layout padding, both CSS padding and intrinsic padding, so we can't just check o.style().hasPadding().
407 if (o.isRenderMathMLBlock() && (box.paddingTop() || box.paddingRight() || box.paddingBottom() || box.paddingLeft())) {
408 ts << " [";
409 LayoutUnit cssTop = box.computedCSSPaddingTop();
410 LayoutUnit cssRight = box.computedCSSPaddingRight();
411 LayoutUnit cssBottom = box.computedCSSPaddingBottom();
412 LayoutUnit cssLeft = box.computedCSSPaddingLeft();
413 if (box.paddingTop() != cssTop || box.paddingRight() != cssRight || box.paddingBottom() != cssBottom || box.paddingLeft() != cssLeft) {
414 ts << "intrinsic ";
415 if (cssTop || cssRight || cssBottom || cssLeft)
416 ts << "+ CSS ";
417 }
418 ts << "padding: " << roundToInt(box.paddingTop()) << " " << roundToInt(box.paddingRight()) << " " << roundToInt(box.paddingBottom()) << " " << roundToInt(box.paddingLeft()) << "]";
419 }
420#endif
421 }
422
423 if (is<RenderTableCell>(o)) {
424 const RenderTableCell& c = downcast<RenderTableCell>(o);
425 ts << " [r=" << c.rowIndex() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
426 }
427
428 if (is<RenderDetailsMarker>(o)) {
429 ts << ": ";
430 switch (downcast<RenderDetailsMarker>(o).orientation()) {
431 case RenderDetailsMarker::Left:
432 ts << "left";
433 break;
434 case RenderDetailsMarker::Right:
435 ts << "right";
436 break;
437 case RenderDetailsMarker::Up:
438 ts << "up";
439 break;
440 case RenderDetailsMarker::Down:
441 ts << "down";
442 break;
443 }
444 }
445
446 if (is<RenderListMarker>(o)) {
447 String text = downcast<RenderListMarker>(o).textWithoutSuffix().toString();
448 if (!text.isEmpty()) {
449 if (text.length() != 1)
450 text = quoteAndEscapeNonPrintables(text);
451 else {
452 switch (text[0]) {
453 case bullet:
454 text = "bullet"_s;
455 break;
456 case blackSquare:
457 text = "black square"_s;
458 break;
459 case whiteBullet:
460 text = "white bullet"_s;
461 break;
462 default:
463 text = quoteAndEscapeNonPrintables(text);
464 }
465 }
466 ts << ": " << text;
467 }
468 }
469
470 writeDebugInfo(ts, o, behavior);
471}
472
473void writeDebugInfo(TextStream& ts, const RenderObject& object, OptionSet<RenderAsTextFlag> behavior)
474{
475 if (behavior.contains(RenderAsTextFlag::ShowIDAndClass)) {
476 if (auto* element = dynamicDowncast<Element>(object.node())) {
477 if (element->hasID())
478 ts << " id=\"" << element->getIdAttribute() << "\"";
479
480 if (element->hasClass()) {
481 ts << " class=\"";
482 for (size_t i = 0; i < element->classNames().size(); ++i) {
483 if (i > 0)
484 ts << " ";
485 ts << element->classNames()[i];
486 }
487 ts << "\"";
488 }
489 }
490 }
491
492 if (behavior.contains(RenderAsTextFlag::ShowLayoutState)) {
493 bool needsLayout = object.selfNeedsLayout() || object.needsPositionedMovementLayout() || object.posChildNeedsLayout() || object.normalChildNeedsLayout();
494 if (needsLayout)
495 ts << " (needs layout:";
496
497 bool havePrevious = false;
498 if (object.selfNeedsLayout()) {
499 ts << " self";
500 havePrevious = true;
501 }
502
503 if (object.needsPositionedMovementLayout()) {
504 if (havePrevious)
505 ts << ",";
506 havePrevious = true;
507 ts << " positioned movement";
508 }
509
510 if (object.normalChildNeedsLayout()) {
511 if (havePrevious)
512 ts << ",";
513 havePrevious = true;
514 ts << " child";
515 }
516
517 if (object.posChildNeedsLayout()) {
518 if (havePrevious)
519 ts << ",";
520 ts << " positioned child";
521 }
522
523 if (needsLayout)
524 ts << ")";
525 }
526
527 if (behavior.contains(RenderAsTextFlag::ShowOverflow) && is<RenderBox>(object)) {
528 const auto& box = downcast<RenderBox>(object);
529 if (box.hasRenderOverflow()) {
530 LayoutRect layoutOverflow = box.layoutOverflowRect();
531 ts << " (layout overflow " << layoutOverflow.x().toInt() << "," << layoutOverflow.y().toInt() << " " << layoutOverflow.width().toInt() << "x" << layoutOverflow.height().toInt() << ")";
532
533 if (box.hasVisualOverflow()) {
534 LayoutRect visualOverflow = box.visualOverflowRect();
535 ts << " (visual overflow " << visualOverflow.x().toInt() << "," << visualOverflow.y().toInt() << " " << visualOverflow.width().toInt() << "x" << visualOverflow.height().toInt() << ")";
536 }
537 }
538 }
539}
540
541void write(TextStream& ts, const RenderObject& o, OptionSet<RenderAsTextFlag> behavior)
542{
543 auto writeTextRun = [&](auto& textRenderer, auto& textRun)
544 {
545 auto rect = textRun.visualRectIgnoringBlockDirection();
546 int x = rect.x();
547 int y = rect.y();
548 // FIXME: Use non-logical width. webkit.org/b/206809.
549 int logicalWidth = ceilf(rect.x() + (textRun.isHorizontal() ? rect.width() : rect.height())) - x;
550 // FIXME: Table cell adjustment is temporary until results can be updated.
551 if (is<RenderTableCell>(*o.containingBlock()))
552 y -= floorToInt(downcast<RenderTableCell>(*o.containingBlock()).intrinsicPaddingBefore());
553
554 ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
555 if (!textRun.isLeftToRightDirection())
556 ts << " RTL";
557 ts << ": "
558 << quoteAndEscapeNonPrintables(textRun.text());
559 if (textRun.hasHyphen())
560 ts << " + hyphen string " << quoteAndEscapeNonPrintables(textRenderer.style().hyphenString().string());
561 ts << "\n";
562 };
563
564#if ENABLE(LAYER_BASED_SVG_ENGINE)
565 if (is<RenderSVGShape>(o)) {
566 write(ts, downcast<RenderSVGShape>(o), behavior);
567 return;
568 }
569#endif
570 if (is<LegacyRenderSVGShape>(o)) {
571 write(ts, downcast<LegacyRenderSVGShape>(o), behavior);
572 return;
573 }
574 if (is<RenderSVGGradientStop>(o)) {
575 writeSVGGradientStop(ts, downcast<RenderSVGGradientStop>(o), behavior);
576 return;
577 }
578 if (is<RenderSVGResourceContainer>(o)) {
579 writeSVGResourceContainer(ts, downcast<RenderSVGResourceContainer>(o), behavior);
580 return;
581 }
582#if ENABLE(LAYER_BASED_SVG_ENGINE)
583 if (is<RenderSVGContainer>(o)) {
584 writeSVGContainer(ts, downcast<RenderSVGContainer>(o), behavior);
585 return;
586 }
587#endif
588 if (is<LegacyRenderSVGContainer>(o)) {
589 writeSVGContainer(ts, downcast<LegacyRenderSVGContainer>(o), behavior);
590 return;
591 }
592#if ENABLE(LAYER_BASED_SVG_ENGINE)
593 if (is<RenderSVGRoot>(o)) {
594 write(ts, downcast<RenderSVGRoot>(o), behavior);
595 return;
596 }
597#endif
598 if (is<LegacyRenderSVGRoot>(o)) {
599 write(ts, downcast<LegacyRenderSVGRoot>(o), behavior);
600 return;
601 }
602 if (is<RenderSVGText>(o)) {
603 writeSVGText(ts, downcast<RenderSVGText>(o), behavior);
604 return;
605 }
606 if (is<RenderSVGInlineText>(o)) {
607 writeSVGInlineText(ts, downcast<RenderSVGInlineText>(o), behavior);
608 return;
609 }
610 if (is<RenderSVGImage>(o)) {
611 writeSVGImage(ts, downcast<RenderSVGImage>(o), behavior);
612 return;
613 }
614
615 ts << indent;
616
617 RenderTreeAsText::writeRenderObject(ts, o, behavior);
618 ts << "\n";
619
620 TextStream::IndentScope indentScope(ts);
621
622 if (is<RenderText>(o)) {
623 auto& text = downcast<RenderText>(o);
624 for (auto& run : InlineIterator::textBoxesFor(text)) {
625 ts << indent;
626 writeTextRun(text, run);
627 }
628 } else {
629 for (auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(o))) {
630 if (child.hasLayer())
631 continue;
632 write(ts, child, behavior);
633 }
634 }
635
636 if (is<RenderWidget>(o)) {
637 Widget* widget = downcast<RenderWidget>(o).widget();
638 if (is<FrameView>(widget)) {
639 FrameView& view = downcast<FrameView>(*widget);
640 if (RenderView* root = view.frame().contentRenderer()) {
641 if (!(behavior.contains(RenderAsTextFlag::DontUpdateLayout)))
642 view.layoutContext().layout();
643 if (RenderLayer* layer = root->layer())
644 writeLayers(ts, *layer, *layer, layer->rect(), behavior);
645 }
646 }
647 }
648}
649
650enum LayerPaintPhase {
651 LayerPaintPhaseAll = 0,
652 LayerPaintPhaseBackground = -1,
653 LayerPaintPhaseForeground = 1
654};
655
656static void writeLayer(TextStream& ts, const RenderLayer& layer, const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect,
657 LayerPaintPhase paintPhase = LayerPaintPhaseAll, OptionSet<RenderAsTextFlag> behavior = { })
658{
659 IntRect adjustedLayoutBounds = snappedIntRect(layerBounds);
660 IntRect adjustedBackgroundClipRect = snappedIntRect(backgroundClipRect);
661 IntRect adjustedClipRect = snappedIntRect(clipRect);
662
663 ts << indent << "layer ";
664
665 if (behavior.contains(RenderAsTextFlag::ShowAddresses)) {
666 ts << &layer << " ";
667 if (auto* scrollableArea = layer.scrollableArea())
668 ts << "scrollableArea " << scrollableArea << " ";
669 }
670
671 ts << adjustedLayoutBounds;
672
673 if (!adjustedLayoutBounds.isEmpty()) {
674 if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
675 ts << " backgroundClip " << adjustedBackgroundClipRect;
676 if (!adjustedClipRect.contains(adjustedLayoutBounds))
677 ts << " clip " << adjustedClipRect;
678 }
679
680 if (layer.renderer().hasNonVisibleOverflow()) {
681 if (auto* scrollableArea = layer.scrollableArea()) {
682 if (scrollableArea->scrollOffset().x())
683 ts << " scrollX " << scrollableArea->scrollOffset().x();
684 if (scrollableArea->scrollOffset().y())
685 ts << " scrollY " << scrollableArea->scrollOffset().y();
686 if (layer.renderBox() && roundToInt(layer.renderBox()->clientWidth()) != scrollableArea->scrollWidth())
687 ts << " scrollWidth " << scrollableArea->scrollWidth();
688 if (layer.renderBox() && roundToInt(layer.renderBox()->clientHeight()) != scrollableArea->scrollHeight())
689 ts << " scrollHeight " << scrollableArea->scrollHeight();
690 }
691#if PLATFORM(MAC)
692 ScrollbarTheme& scrollbarTheme = ScrollbarTheme::theme();
693 if (!scrollbarTheme.isMockTheme() && layer.scrollableArea() && layer.scrollableArea()->hasVerticalScrollbar()) {
694 ScrollbarThemeMac& macTheme = *static_cast<ScrollbarThemeMac*>(&scrollbarTheme);
695 if (macTheme.isLayoutDirectionRTL(*layer.scrollableArea()->verticalScrollbar()))
696 ts << " scrollbarHasRTLLayoutDirection";
697 }
698#endif
699 }
700
701 if (paintPhase == LayerPaintPhaseBackground)
702 ts << " layerType: background only";
703 else if (paintPhase == LayerPaintPhaseForeground)
704 ts << " layerType: foreground only";
705
706 if (behavior.contains(RenderAsTextFlag::ShowCompositedLayers)) {
707 if (layer.isComposited()) {
708 ts << " (composited " << layer.compositor().reasonsForCompositing(layer)
709 << ", bounds=" << layer.backing()->compositedBounds()
710 << ", drawsContent=" << layer.backing()->graphicsLayer()->drawsContent()
711 << ", paints into ancestor=" << layer.backing()->paintsIntoCompositedAncestor() << ")";
712 } else if (layer.paintsIntoProvidedBacking())
713 ts << " (shared backing of " << layer.backingProviderLayer() << ")";
714 }
715
716#if ENABLE(CSS_COMPOSITING)
717 if (layer.isolatesBlending())
718 ts << " isolatesBlending";
719 if (layer.hasBlendMode())
720 ts << " blendMode: " << compositeOperatorName(CompositeOperator::SourceOver, layer.blendMode());
721#endif
722
723 ts << "\n";
724}
725
726static void writeLayerRenderers(TextStream& ts, const RenderLayer& layer, LayerPaintPhase paintPhase, OptionSet<RenderAsTextFlag> behavior)
727{
728 if (paintPhase != LayerPaintPhaseBackground) {
729 TextStream::IndentScope indentScope(ts);
730 write(ts, layer.renderer(), behavior);
731 }
732}
733
734static LayoutSize maxLayoutOverflow(const RenderBox* box)
735{
736 LayoutRect overflowRect = box->layoutOverflowRect();
737 return LayoutSize(overflowRect.maxX(), overflowRect.maxY());
738}
739
740static void writeLayers(TextStream& ts, const RenderLayer& rootLayer, RenderLayer& layer, const LayoutRect& paintRect, OptionSet<RenderAsTextFlag> behavior)
741{
742 // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
743 LayoutRect paintDirtyRect(paintRect);
744 if (&rootLayer == &layer) {
745 paintDirtyRect.setWidth(std::max<LayoutUnit>(paintDirtyRect.width(), rootLayer.renderBox()->layoutOverflowRect().maxX()));
746 paintDirtyRect.setHeight(std::max<LayoutUnit>(paintDirtyRect.height(), rootLayer.renderBox()->layoutOverflowRect().maxY()));
747 layer.setSize(layer.size().expandedTo(snappedIntSize(maxLayoutOverflow(layer.renderBox()), LayoutPoint(0, 0))));
748 }
749
750 // Calculate the clip rects we should use.
751 LayoutRect layerBounds;
752 ClipRect damageRect;
753 ClipRect clipRectToApply;
754 LayoutSize offsetFromRoot = layer.offsetFromAncestor(&rootLayer);
755 layer.calculateRects(RenderLayer::ClipRectsContext(&rootLayer, TemporaryClipRects), paintDirtyRect, layerBounds, damageRect, clipRectToApply, offsetFromRoot);
756
757 // Ensure our lists are up-to-date.
758 layer.updateLayerListsIfNeeded();
759 layer.updateDescendantDependentFlags();
760
761 bool shouldPaint = (behavior.contains(RenderAsTextFlag::ShowAllLayers)) ? true : layer.intersectsDamageRect(layerBounds, damageRect.rect(), &rootLayer, layer.offsetFromAncestor(&rootLayer));
762 auto negativeZOrderLayers = layer.negativeZOrderLayers();
763 bool paintsBackgroundSeparately = negativeZOrderLayers.size() > 0;
764 if (shouldPaint && paintsBackgroundSeparately) {
765 writeLayer(ts, layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), LayerPaintPhaseBackground, behavior);
766 writeLayerRenderers(ts, layer, LayerPaintPhaseBackground, behavior);
767 }
768
769 if (negativeZOrderLayers.size()) {
770 if (behavior.contains(RenderAsTextFlag::ShowLayerNesting)) {
771 ts << indent << " negative z-order list (" << negativeZOrderLayers.size() << ")\n";
772 ts.increaseIndent();
773 }
774
775 for (auto* currLayer : negativeZOrderLayers)
776 writeLayers(ts, rootLayer, *currLayer, paintDirtyRect, behavior);
777
778 if (behavior.contains(RenderAsTextFlag::ShowLayerNesting))
779 ts.decreaseIndent();
780 }
781
782 if (shouldPaint) {
783 writeLayer(ts, layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, behavior);
784
785 if (behavior.contains(RenderAsTextFlag::ShowLayerFragments)) {
786 LayerFragments layerFragments;
787 layer.collectFragments(layerFragments, &rootLayer, paintDirtyRect, RenderLayer::PaginationInclusionMode::ExcludeCompositedPaginatedLayers, TemporaryClipRects, IgnoreOverlayScrollbarSize, RespectOverflowClip, offsetFromRoot);
788
789 if (layerFragments.size() > 1) {
790 TextStream::IndentScope indentScope(ts, 2);
791 for (unsigned i = 0; i < layerFragments.size(); ++i) {
792 const auto& fragment = layerFragments[i];
793 ts << indent << " fragment " << i << ": bounds in layer " << fragment.layerBounds << " fragment bounds " << fragment.boundingBox << "\n";
794 }
795 }
796 }
797
798 writeLayerRenderers(ts, layer, paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, behavior);
799 }
800
801 auto normalFlowLayers = layer.normalFlowLayers();
802 if (normalFlowLayers.size()) {
803 if (behavior.contains(RenderAsTextFlag::ShowLayerNesting)) {
804 ts << indent << " normal flow list (" << normalFlowLayers.size() << ")\n";
805 ts.increaseIndent();
806 }
807
808 for (auto* currLayer : normalFlowLayers)
809 writeLayers(ts, rootLayer, *currLayer, paintDirtyRect, behavior);
810
811 if (behavior.contains(RenderAsTextFlag::ShowLayerNesting))
812 ts.decreaseIndent();
813 }
814
815 auto positiveZOrderLayers = layer.positiveZOrderLayers();
816 if (positiveZOrderLayers.size()) {
817 size_t layerCount = positiveZOrderLayers.size();
818
819 if (layerCount) {
820 if (behavior.contains(RenderAsTextFlag::ShowLayerNesting)) {
821 ts << indent << " positive z-order list (" << layerCount << ")\n";
822 ts.increaseIndent();
823 }
824
825 for (auto* currLayer : positiveZOrderLayers)
826 writeLayers(ts, rootLayer, *currLayer, paintDirtyRect, behavior);
827
828 if (behavior.contains(RenderAsTextFlag::ShowLayerNesting))
829 ts.decreaseIndent();
830 }
831 }
832}
833
834static String nodePosition(Node* node)
835{
836 StringBuilder result;
837
838 auto* body = node->document().bodyOrFrameset();
839 Node* parent;
840 for (Node* n = node; n; n = parent) {
841 parent = n->parentOrShadowHostNode();
842 if (n != node)
843 result.append(" of ");
844 if (parent) {
845 if (body && n == body) {
846 // We don't care what offset body may be in the document.
847 result.append("body");
848 break;
849 }
850 if (n->isShadowRoot())
851 result.append('{', getTagName(n), '}');
852 else
853 result.append("child ", n->computeNodeIndex(), " {", getTagName(n), '}');
854 } else
855 result.append("document");
856 }
857
858 return result.toString();
859}
860
861static void writeSelection(TextStream& ts, const RenderBox& renderer)
862{
863 if (!renderer.isRenderView())
864 return;
865
866 Frame* frame = renderer.document().frame();
867 if (!frame)
868 return;
869
870 VisibleSelection selection = frame->selection().selection();
871 if (selection.isCaret()) {
872 ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode());
873 if (selection.affinity() == Affinity::Upstream)
874 ts << " (upstream affinity)";
875 ts << "\n";
876 } else if (selection.isRange())
877 ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n"
878 << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n";
879}
880
881static String externalRepresentation(RenderBox& renderer, OptionSet<RenderAsTextFlag> behavior)
882{
883 TextStream ts(TextStream::LineMode::MultipleLine, { TextStream::Formatting::SVGStyleRect, TextStream::Formatting::LayoutUnitsAsIntegers });
884 if (!renderer.hasLayer())
885 return ts.release();
886
887 LOG(Layout, "externalRepresentation: dumping layer tree");
888
889 RenderLayer& layer = *renderer.layer();
890 writeLayers(ts, layer, layer, layer.rect(), behavior);
891 writeSelection(ts, renderer);
892 return ts.release();
893}
894
895static void updateLayoutIgnoringPendingStylesheetsIncludingSubframes(Document& document)
896{
897 document.updateLayoutIgnorePendingStylesheets();
898 auto* frame = document.frame();
899 for (auto* subframe = frame; subframe; subframe = subframe->tree().traverseNext(frame)) {
900 if (auto* document = subframe->document())
901 document->updateLayoutIgnorePendingStylesheets();
902 }
903}
904
905String externalRepresentation(Frame* frame, OptionSet<RenderAsTextFlag> behavior)
906{
907 ASSERT(frame);
908 ASSERT(frame->document());
909
910 if (!(behavior.contains(RenderAsTextFlag::DontUpdateLayout)))
911 updateLayoutIgnoringPendingStylesheetsIncludingSubframes(*frame->document());
912
913 auto* renderer = frame->contentRenderer();
914 if (!renderer)
915 return String();
916
917 PrintContext printContext(frame);
918 if (behavior.contains(RenderAsTextFlag::PrintingMode))
919 printContext.begin(renderer->width());
920
921 return externalRepresentation(*renderer, behavior);
922}
923
924String externalRepresentation(Element* element, OptionSet<RenderAsTextFlag> behavior)
925{
926 ASSERT(element);
927
928 // This function doesn't support printing mode.
929 ASSERT(!(behavior.contains(RenderAsTextFlag::PrintingMode)));
930
931 if (!(behavior.contains(RenderAsTextFlag::DontUpdateLayout)))
932 updateLayoutIgnoringPendingStylesheetsIncludingSubframes(element->document());
933
934 auto* renderer = element->renderer();
935 if (!is<RenderBox>(renderer))
936 return String();
937
938 return externalRepresentation(downcast<RenderBox>(*renderer), behavior | RenderAsTextFlag::ShowAllLayers);
939}
940
941static void writeCounterValuesFromChildren(TextStream& stream, const RenderElement* parent, bool& isFirstCounter)
942{
943 if (!parent)
944 return;
945 for (auto& counter : childrenOfType<RenderCounter>(*parent)) {
946 if (!isFirstCounter)
947 stream << " ";
948 isFirstCounter = false;
949 String str(counter.text());
950 stream << str;
951 }
952}
953
954String counterValueForElement(Element* element)
955{
956 // Make sure the element is not freed during the layout.
957 RefPtr<Element> elementRef(element);
958 element->document().updateLayout();
959 TextStream stream(TextStream::LineMode::MultipleLine, { TextStream::Formatting::SVGStyleRect, TextStream::Formatting::LayoutUnitsAsIntegers });
960 bool isFirstCounter = true;
961 // The counter renderers should be children of :before or :after pseudo-elements.
962 if (PseudoElement* before = element->beforePseudoElement())
963 writeCounterValuesFromChildren(stream, before->renderer(), isFirstCounter);
964 if (PseudoElement* after = element->afterPseudoElement())
965 writeCounterValuesFromChildren(stream, after->renderer(), isFirstCounter);
966 return stream.release();
967}
968
969String markerTextForListItem(Element* element)
970{
971 // Make sure the element is not freed during the layout.
972 RefPtr<Element> elementRef(element);
973 element->document().updateLayout();
974
975 RenderElement* renderer = element->renderer();
976 if (!is<RenderListItem>(renderer))
977 return String();
978
979 return downcast<RenderListItem>(*renderer).markerTextWithoutSuffix().toString();
980}
981
982} // namespace WebCore
Note: See TracBrowser for help on using the repository browser.