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

Last change on this file was 295665, checked in by Alan Bujtas, 3 years ago

REGRESSION (r294902): Content with continuation leaves decoration bits behind when removed
https://bugs.webkit.org/show_bug.cgi?id=241734
<rdar://95308322>

Reviewed by Simon Fraser.

This patch ensures that when a renderer is removed we always issue a repaint regardless of what the associated layer's repaint bit says.

  1. after r294902, repaint is not issued anymore if either the associated or an ancestor layer have the "full repaint" bit set.
  2. such layer-driven repaints happen after layout.

In some dynamic content cases, the layer may be removed before layout happens. This patch ensures that we preemptively issue such repaints.

  • LayoutTests/fast/repaint/force-repaint-when-layer-is-destroyed-expected.txt: Added.
  • LayoutTests/fast/repaint/force-repaint-when-layer-is-destroyed.html: Added.
  • Source/WebCore/rendering/RenderLayerModelObject.cpp: Force (full) repaint when the renderer is being destroyed (detached -> non-internal move).

(WebCore::RenderLayerModelObject::willBeRemovedFromTree):

  • Source/WebCore/rendering/RenderLayerModelObject.h:
  • Source/WebCore/rendering/RenderObject.cpp: move duplicated code from repaint() and repaintRectangle() to issueRepaint().

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

  • Property svn:eol-style set to native
File size: 98.2 KB
Line 
1/*
2 * Copyright (C) 1999 Lars Knoll ([email protected])
3 * (C) 1999 Antti Koivisto ([email protected])
4 * (C) 2000 Dirk Mueller ([email protected])
5 * (C) 2004 Allan Sandfeld Jensen ([email protected])
6 * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
7 * Copyright (C) 2009 Google Inc. All rights reserved.
8 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 *
25 */
26
27#include "config.h"
28#include "RenderObject.h"
29
30#include "AXObjectCache.h"
31#include "DocumentInlines.h"
32#include "Editing.h"
33#include "ElementAncestorIterator.h"
34#include "FloatQuad.h"
35#include "Frame.h"
36#include "FrameSelection.h"
37#include "FrameView.h"
38#include "GeometryUtilities.h"
39#include "GraphicsContext.h"
40#include "HTMLBRElement.h"
41#include "HTMLNames.h"
42#include "HTMLTableCellElement.h"
43#include "HTMLTableElement.h"
44#include "HitTestResult.h"
45#include "LayoutIntegrationLineLayout.h"
46#include "LegacyRenderSVGModelObject.h"
47#include "LegacyRenderSVGRoot.h"
48#include "LogicalSelectionOffsetCaches.h"
49#include "Page.h"
50#include "PseudoElement.h"
51#include "ReferencedSVGResources.h"
52#include "RenderChildIterator.h"
53#include "RenderCounter.h"
54#include "RenderFragmentedFlow.h"
55#include "RenderGeometryMap.h"
56#include "RenderInline.h"
57#include "RenderIterator.h"
58#include "RenderLayer.h"
59#include "RenderLayerBacking.h"
60#include "RenderLayerCompositor.h"
61#include "RenderLineBreak.h"
62#include "RenderMultiColumnFlow.h"
63#include "RenderMultiColumnSet.h"
64#include "RenderRuby.h"
65#include "RenderSVGBlock.h"
66#include "RenderSVGInline.h"
67#include "RenderSVGResourceContainer.h"
68#include "RenderScrollbarPart.h"
69#include "RenderTableRow.h"
70#include "RenderTheme.h"
71#include "RenderTreeBuilder.h"
72#include "RenderView.h"
73#include "RenderWidget.h"
74#include "SVGRenderSupport.h"
75#include "StyleResolver.h"
76#include "TransformState.h"
77#include <algorithm>
78#include <stdio.h>
79#include <wtf/HexNumber.h>
80#include <wtf/IsoMallocInlines.h>
81#include <wtf/RefCountedLeakCounter.h>
82#include <wtf/text/TextStream.h>
83
84#if PLATFORM(IOS_FAMILY)
85#include "SelectionGeometry.h"
86#endif
87
88namespace WebCore {
89
90using namespace HTMLNames;
91
92WTF_MAKE_ISO_ALLOCATED_IMPL(RenderObject);
93
94#if ASSERT_ENABLED
95
96RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(const RenderObject& renderObject, bool isForbidden)
97 : m_renderObject(renderObject)
98 , m_preexistingForbidden(m_renderObject.isSetNeedsLayoutForbidden())
99{
100 m_renderObject.setNeedsLayoutIsForbidden(isForbidden);
101}
102
103RenderObject::SetLayoutNeededForbiddenScope::~SetLayoutNeededForbiddenScope()
104{
105 m_renderObject.setNeedsLayoutIsForbidden(m_preexistingForbidden);
106}
107
108#endif
109
110struct SameSizeAsRenderObject {
111 virtual ~SameSizeAsRenderObject() = default; // Allocate vtable pointer.
112#if ASSERT_ENABLED
113 bool weakPtrFactorWasConstructedOnMainThread;
114 HashSet<void*> cachedResourceClientAssociatedResources;
115#endif
116 void* pointers[5];
117#if ASSERT_ENABLED
118 unsigned m_debugBitfields : 2;
119#endif
120 unsigned m_bitfields;
121};
122
123static_assert(sizeof(RenderObject) == sizeof(SameSizeAsRenderObject), "RenderObject should stay small");
124
125DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, renderObjectCounter, ("RenderObject"));
126
127void RenderObjectDeleter::operator() (RenderObject* renderer) const
128{
129 renderer->destroy();
130}
131
132RenderObject::RenderObject(Node& node)
133 : CachedImageClient()
134 , m_node(node)
135 , m_parent(nullptr)
136 , m_previous(nullptr)
137 , m_next(nullptr)
138#if ASSERT_ENABLED
139 , m_hasAXObject(false)
140 , m_setNeedsLayoutForbidden(false)
141#endif
142 , m_bitfields(node)
143{
144 if (RenderView* renderView = node.document().renderView())
145 renderView->didCreateRenderer();
146#ifndef NDEBUG
147 renderObjectCounter.increment();
148#endif
149}
150
151RenderObject::~RenderObject()
152{
153 view().didDestroyRenderer();
154 ASSERT(!m_hasAXObject);
155#ifndef NDEBUG
156 renderObjectCounter.decrement();
157#endif
158 ASSERT(!hasRareData());
159}
160
161RenderTheme& RenderObject::theme() const
162{
163 return RenderTheme::singleton();
164}
165
166bool RenderObject::isDescendantOf(const RenderObject* ancestor) const
167{
168 for (const RenderObject* renderer = this; renderer; renderer = renderer->m_parent) {
169 if (renderer == ancestor)
170 return true;
171 }
172 return false;
173}
174
175RenderElement* RenderObject::firstNonAnonymousAncestor() const
176{
177 auto* ancestor = parent();
178 while (ancestor && ancestor->isAnonymous())
179 ancestor = ancestor->parent();
180 return ancestor;
181}
182
183bool RenderObject::isLegend() const
184{
185 return node() && node()->hasTagName(legendTag);
186}
187
188
189bool RenderObject::isFieldset() const
190{
191 return node() && node()->hasTagName(fieldsetTag);
192}
193
194bool RenderObject::isHTMLMarquee() const
195{
196 return node() && node()->renderer() == this && node()->hasTagName(marqueeTag);
197}
198
199bool RenderObject::isBlockContainer() const
200{
201 auto display = style().display();
202 return (display == DisplayType::Block
203 || display == DisplayType::InlineBlock
204 || display == DisplayType::FlowRoot
205 || display == DisplayType::ListItem
206 || display == DisplayType::TableCell
207 || display == DisplayType::TableCaption) && !isRenderReplaced();
208}
209
210void RenderObject::setFragmentedFlowStateIncludingDescendants(FragmentedFlowState state, const RenderElement* fragmentedFlowRoot)
211{
212 setFragmentedFlowState(state);
213
214 if (!is<RenderElement>(*this))
215 return;
216
217 for (auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*this))) {
218 // If the child is a fragmentation context it already updated the descendants flag accordingly.
219 if (child.isRenderFragmentedFlow())
220 continue;
221 if (fragmentedFlowRoot && child.isOutOfFlowPositioned()) {
222 // Fragmented status propagation stops at out-of-flow boundary.
223 auto isInsideMulticolumnFlow = [&] {
224 auto* containingBlock = child.containingBlock();
225 if (!containingBlock) {
226 ASSERT_NOT_REACHED();
227 return false;
228 }
229 // It's ok to only check the first level containing block (as opposed to the containing block chain) as setFragmentedFlowStateIncludingDescendants is top to down.
230 return containingBlock->isDescendantOf(fragmentedFlowRoot);
231 };
232 if (!isInsideMulticolumnFlow())
233 continue;
234 }
235 ASSERT(state != child.fragmentedFlowState());
236 child.setFragmentedFlowStateIncludingDescendants(state, fragmentedFlowRoot);
237 }
238}
239
240RenderObject::FragmentedFlowState RenderObject::computedFragmentedFlowState(const RenderObject& renderer)
241{
242 if (!renderer.parent())
243 return renderer.fragmentedFlowState();
244
245 if (is<RenderMultiColumnFlow>(renderer)) {
246 // Multicolumn flows do not inherit the flow state.
247 return InsideInFragmentedFlow;
248 }
249
250 auto inheritedFlowState = RenderObject::NotInsideFragmentedFlow;
251 if (is<RenderText>(renderer))
252 inheritedFlowState = renderer.parent()->fragmentedFlowState();
253 else if (is<RenderSVGBlock>(renderer) || is<RenderSVGInline>(renderer) || is<LegacyRenderSVGModelObject>(renderer)) {
254 // containingBlock() skips svg boundary (SVG root is a RenderReplaced).
255 if (auto* svgRoot = SVGRenderSupport::findTreeRootObject(downcast<RenderElement>(renderer)))
256 inheritedFlowState = svgRoot->fragmentedFlowState();
257 } else if (auto* container = renderer.container())
258 inheritedFlowState = container->fragmentedFlowState();
259 else {
260 // Splitting lines or doing continuation, so just keep the current state.
261 inheritedFlowState = renderer.fragmentedFlowState();
262 }
263 return inheritedFlowState;
264}
265
266void RenderObject::initializeFragmentedFlowStateOnInsertion()
267{
268 ASSERT(parent());
269
270 // A RenderFragmentedFlow is always considered to be inside itself, so it never has to change its state in response to parent changes.
271 if (isRenderFragmentedFlow())
272 return;
273
274 auto computedState = computedFragmentedFlowState(*this);
275 if (fragmentedFlowState() == computedState)
276 return;
277
278 auto* enclosingFragmentedFlow = locateEnclosingFragmentedFlow();
279 setFragmentedFlowStateIncludingDescendants(computedState, enclosingFragmentedFlow ? enclosingFragmentedFlow->parent() : nullptr);
280}
281
282void RenderObject::resetFragmentedFlowStateOnRemoval()
283{
284 if (fragmentedFlowState() == NotInsideFragmentedFlow)
285 return;
286
287 if (!renderTreeBeingDestroyed() && is<RenderElement>(*this)) {
288 downcast<RenderElement>(*this).removeFromRenderFragmentedFlow();
289 return;
290 }
291
292 // A RenderFragmentedFlow is always considered to be inside itself, so it never has to change its state in response to parent changes.
293 if (isRenderFragmentedFlow())
294 return;
295
296 auto* enclosingFragmentedFlow = this->enclosingFragmentedFlow();
297 setFragmentedFlowStateIncludingDescendants(NotInsideFragmentedFlow, enclosingFragmentedFlow ? enclosingFragmentedFlow->parent() : nullptr);
298}
299
300void RenderObject::setParent(RenderElement* parent)
301{
302 m_parent = parent;
303}
304
305RenderObject* RenderObject::nextInPreOrder() const
306{
307 if (RenderObject* o = firstChildSlow())
308 return o;
309
310 return nextInPreOrderAfterChildren();
311}
312
313RenderObject* RenderObject::nextInPreOrderAfterChildren() const
314{
315 RenderObject* o;
316 if (!(o = nextSibling())) {
317 o = parent();
318 while (o && !o->nextSibling())
319 o = o->parent();
320 if (o)
321 o = o->nextSibling();
322 }
323
324 return o;
325}
326
327RenderObject* RenderObject::nextInPreOrder(const RenderObject* stayWithin) const
328{
329 if (RenderObject* o = firstChildSlow())
330 return o;
331
332 return nextInPreOrderAfterChildren(stayWithin);
333}
334
335RenderObject* RenderObject::nextInPreOrderAfterChildren(const RenderObject* stayWithin) const
336{
337 if (this == stayWithin)
338 return nullptr;
339
340 const RenderObject* current = this;
341 RenderObject* next;
342 while (!(next = current->nextSibling())) {
343 current = current->parent();
344 if (!current || current == stayWithin)
345 return nullptr;
346 }
347 return next;
348}
349
350RenderObject* RenderObject::previousInPreOrder() const
351{
352 if (RenderObject* o = previousSibling()) {
353 while (RenderObject* last = o->lastChildSlow())
354 o = last;
355 return o;
356 }
357
358 return parent();
359}
360
361RenderObject* RenderObject::previousInPreOrder(const RenderObject* stayWithin) const
362{
363 if (this == stayWithin)
364 return nullptr;
365
366 return previousInPreOrder();
367}
368
369RenderObject* RenderObject::childAt(unsigned index) const
370{
371 RenderObject* child = firstChildSlow();
372 for (unsigned i = 0; child && i < index; i++)
373 child = child->nextSibling();
374 return child;
375}
376
377RenderObject* RenderObject::firstLeafChild() const
378{
379 RenderObject* r = firstChildSlow();
380 while (r) {
381 RenderObject* n = nullptr;
382 n = r->firstChildSlow();
383 if (!n)
384 break;
385 r = n;
386 }
387 return r;
388}
389
390RenderObject* RenderObject::lastLeafChild() const
391{
392 RenderObject* r = lastChildSlow();
393 while (r) {
394 RenderObject* n = nullptr;
395 n = r->lastChildSlow();
396 if (!n)
397 break;
398 r = n;
399 }
400 return r;
401}
402
403#if ENABLE(TEXT_AUTOSIZING)
404
405// Non-recursive version of the DFS search.
406RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightTypeTraverseNextInclusionFunction inclusionFunction, int& currentDepth, int& newFixedDepth) const
407{
408 BlockContentHeightType overflowType;
409
410 // Check for suitable children.
411 for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) {
412 overflowType = inclusionFunction(*child);
413 if (overflowType != FixedHeight) {
414 currentDepth++;
415 if (overflowType == OverflowHeight)
416 newFixedDepth = currentDepth;
417 ASSERT(!stayWithin || child->isDescendantOf(stayWithin));
418 return child;
419 }
420 }
421
422 if (this == stayWithin)
423 return nullptr;
424
425 // Now we traverse other nodes if they exist, otherwise
426 // we go to the parent node and try doing the same.
427 const RenderObject* n = this;
428 while (n) {
429 while (n && !n->nextSibling() && (!stayWithin || n->parent() != stayWithin)) {
430 n = n->parent();
431 currentDepth--;
432 }
433 if (!n)
434 return nullptr;
435 for (RenderObject* sibling = n->nextSibling(); sibling; sibling = sibling->nextSibling()) {
436 overflowType = inclusionFunction(*sibling);
437 if (overflowType != FixedHeight) {
438 if (overflowType == OverflowHeight)
439 newFixedDepth = currentDepth;
440 ASSERT(!stayWithin || !n->nextSibling() || n->nextSibling()->isDescendantOf(stayWithin));
441 return sibling;
442 }
443 }
444 if (!stayWithin || n->parent() != stayWithin) {
445 n = n->parent();
446 currentDepth--;
447 } else
448 return nullptr;
449 }
450 return nullptr;
451}
452
453#endif // ENABLE(TEXT_AUTOSIZING)
454
455RenderLayer* RenderObject::enclosingLayer() const
456{
457 for (auto& renderer : lineageOfType<RenderLayerModelObject>(*this)) {
458 if (renderer.hasLayer())
459 return renderer.layer();
460 }
461 return nullptr;
462}
463
464bool RenderObject::scrollRectToVisible(const LayoutRect& absoluteRect, bool insideFixed, const ScrollRectToVisibleOptions& options)
465{
466 if (options.revealMode == SelectionRevealMode::DoNotReveal)
467 return false;
468
469 RenderLayer* enclosingLayer = this->enclosingLayer();
470 if (!enclosingLayer)
471 return false;
472
473 enclosingLayer->scrollRectToVisible(absoluteRect, insideFixed, options);
474 return true;
475}
476
477RenderBox& RenderObject::enclosingBox() const
478{
479 return *lineageOfType<RenderBox>(const_cast<RenderObject&>(*this)).first();
480}
481
482RenderBoxModelObject& RenderObject::enclosingBoxModelObject() const
483{
484 return *lineageOfType<RenderBoxModelObject>(const_cast<RenderObject&>(*this)).first();
485}
486
487RenderBox* RenderObject::enclosingScrollableContainerForSnapping() const
488{
489 // Walk up the container chain to find the scrollable container that contains
490 // this RenderObject. The important thing here is that `container()` respects
491 // the containing block chain for positioned elements. This is important because
492 // scrollable overflow does not establish a new containing block for children.
493 for (auto* candidate = container(); candidate; candidate = candidate->container()) {
494 // Currently the RenderView can look like it has scrollable overflow, but we never
495 // want to return this as our container. Instead we should use the root element.
496 if (candidate->isRenderView())
497 break;
498 if (candidate->hasPotentiallyScrollableOverflow())
499 return downcast<RenderBox>(candidate);
500 }
501
502 // If we reach the root, then the root element is the scrolling container.
503 return document().documentElement() ? document().documentElement()->renderBox() : nullptr;
504}
505
506static inline bool objectIsRelayoutBoundary(const RenderElement* object)
507{
508 // FIXME: In future it may be possible to broaden these conditions in order to improve performance.
509 if (object->isRenderView())
510 return true;
511
512 if (object->isTextControl())
513 return true;
514
515 if (object->shouldApplyLayoutContainment() && object->shouldApplySizeContainment())
516 return true;
517
518 if (object->isSVGRootOrLegacySVGRoot())
519 return true;
520
521 if (!object->hasNonVisibleOverflow())
522 return false;
523
524#if ENABLE(LAYER_BASED_SVG_ENGINE)
525 if (object->document().settings().layerBasedSVGEngineEnabled() && object->isSVGLayerAwareRenderer())
526 return false;
527#endif
528
529 if (object->style().width().isIntrinsicOrAuto() || object->style().height().isIntrinsicOrAuto() || object->style().height().isPercentOrCalculated())
530 return false;
531
532 // Table parts can't be relayout roots since the table is responsible for layouting all the parts.
533 if (object->isTablePart())
534 return false;
535
536 return true;
537}
538
539void RenderObject::clearNeedsLayout()
540{
541 m_bitfields.setNeedsLayout(false);
542 setEverHadLayout(true);
543 setPosChildNeedsLayoutBit(false);
544 setNeedsSimplifiedNormalFlowLayoutBit(false);
545 setNormalChildNeedsLayoutBit(false);
546 setNeedsPositionedMovementLayoutBit(false);
547 if (is<RenderElement>(*this))
548 downcast<RenderElement>(*this).setAncestorLineBoxDirty(false);
549#if ASSERT_ENABLED
550 checkBlockPositionedObjectsNeedLayout();
551#endif
552}
553
554static void scheduleRelayoutForSubtree(RenderElement& renderer)
555{
556 if (is<RenderView>(renderer)) {
557 downcast<RenderView>(renderer).frameView().layoutContext().scheduleLayout();
558 return;
559 }
560
561 if (renderer.isRooted())
562 renderer.view().frameView().layoutContext().scheduleSubtreeLayout(renderer);
563}
564
565void RenderObject::markContainingBlocksForLayout(ScheduleRelayout scheduleRelayout, RenderElement* newRoot)
566{
567 ASSERT(scheduleRelayout == ScheduleRelayout::No || !newRoot);
568 ASSERT(!isSetNeedsLayoutForbidden());
569
570 auto ancestor = container();
571
572 bool simplifiedNormalFlowLayout = needsSimplifiedNormalFlowLayout() && !selfNeedsLayout() && !normalChildNeedsLayout();
573 bool hasOutOfFlowPosition = !isText() && style().hasOutOfFlowPosition();
574
575 while (ancestor) {
576 // FIXME: Remove this once we remove the special cases for counters, quotes and mathml calling setNeedsLayout during preferred width computation.
577 SetLayoutNeededForbiddenScope layoutForbiddenScope(*ancestor, isSetNeedsLayoutForbidden());
578
579 // Don't mark the outermost object of an unrooted subtree. That object will be
580 // marked when the subtree is added to the document.
581 auto container = ancestor->container();
582 if (!container && !ancestor->isRenderView())
583 return;
584 if (hasOutOfFlowPosition) {
585 bool willSkipRelativelyPositionedInlines = !ancestor->isRenderBlock() || ancestor->isAnonymousBlock();
586 // Skip relatively positioned inlines and anonymous blocks to get to the enclosing RenderBlock.
587 while (ancestor && (!ancestor->isRenderBlock() || ancestor->isAnonymousBlock()))
588 ancestor = ancestor->container();
589 if (!ancestor || ancestor->posChildNeedsLayout())
590 return;
591 if (willSkipRelativelyPositionedInlines)
592 container = ancestor->container();
593 ancestor->setPosChildNeedsLayoutBit(true);
594 simplifiedNormalFlowLayout = true;
595 } else if (simplifiedNormalFlowLayout) {
596 if (ancestor->needsSimplifiedNormalFlowLayout())
597 return;
598 ancestor->setNeedsSimplifiedNormalFlowLayoutBit(true);
599 } else {
600 if (ancestor->normalChildNeedsLayout())
601 return;
602 ancestor->setNormalChildNeedsLayoutBit(true);
603 }
604 ASSERT(!ancestor->isSetNeedsLayoutForbidden());
605
606 if (ancestor == newRoot)
607 return;
608
609 if (scheduleRelayout == ScheduleRelayout::Yes && objectIsRelayoutBoundary(ancestor))
610 break;
611
612 hasOutOfFlowPosition = ancestor->style().hasOutOfFlowPosition();
613 ancestor = container;
614 }
615
616 if (scheduleRelayout == ScheduleRelayout::Yes && ancestor)
617 scheduleRelayoutForSubtree(*ancestor);
618}
619
620#if ASSERT_ENABLED
621void RenderObject::checkBlockPositionedObjectsNeedLayout()
622{
623 ASSERT(!needsLayout());
624
625 if (is<RenderBlock>(*this))
626 downcast<RenderBlock>(*this).checkPositionedObjectsNeedLayout();
627}
628#endif // ASSERT_ENABLED
629
630void RenderObject::setPreferredLogicalWidthsDirty(bool shouldBeDirty, MarkingBehavior markParents)
631{
632 bool alreadyDirty = preferredLogicalWidthsDirty();
633 m_bitfields.setPreferredLogicalWidthsDirty(shouldBeDirty);
634 if (shouldBeDirty && !alreadyDirty && markParents == MarkContainingBlockChain && (isText() || !style().hasOutOfFlowPosition()))
635 invalidateContainerPreferredLogicalWidths();
636}
637
638void RenderObject::invalidateContainerPreferredLogicalWidths()
639{
640 // In order to avoid pathological behavior when inlines are deeply nested, we do include them
641 // in the chain that we mark dirty (even though they're kind of irrelevant).
642 auto o = isTableCell() ? containingBlock() : container();
643 while (o && !o->preferredLogicalWidthsDirty()) {
644 // Don't invalidate the outermost object of an unrooted subtree. That object will be
645 // invalidated when the subtree is added to the document.
646 auto container = o->isTableCell() ? o->containingBlock() : o->container();
647 if (!container && !o->isRenderView())
648 break;
649
650 o->m_bitfields.setPreferredLogicalWidthsDirty(true);
651 if (o->style().hasOutOfFlowPosition())
652 // A positioned object has no effect on the min/max width of its containing block ever.
653 // We can optimize this case and not go up any further.
654 break;
655 o = container;
656 }
657}
658
659void RenderObject::setLayerNeedsFullRepaint()
660{
661 ASSERT(hasLayer());
662 downcast<RenderLayerModelObject>(*this).layer()->setRepaintStatus(NeedsFullRepaint);
663}
664
665void RenderObject::setLayerNeedsFullRepaintForPositionedMovementLayout()
666{
667 ASSERT(hasLayer());
668 downcast<RenderLayerModelObject>(*this).layer()->setRepaintStatus(NeedsFullRepaintForPositionedMovementLayout);
669}
670
671static inline RenderBlock* nearestNonAnonymousContainingBlockIncludingSelf(RenderElement* renderer)
672{
673 while (renderer && (!is<RenderBlock>(*renderer) || renderer->isAnonymousBlock()))
674 renderer = renderer->containingBlock();
675 return downcast<RenderBlock>(renderer);
676}
677
678RenderBlock* RenderObject::containingBlockForPositionType(PositionType positionType, const RenderObject& renderer)
679{
680 if (positionType == PositionType::Static || positionType == PositionType::Relative || positionType == PositionType::Sticky) {
681 auto containingBlockForObjectInFlow = [&] {
682 auto* ancestor = renderer.parent();
683 while (ancestor && ((ancestor->isInline() && !ancestor->isReplacedOrInlineBlock()) || !ancestor->isRenderBlock()))
684 ancestor = ancestor->parent();
685 return downcast<RenderBlock>(ancestor);
686 };
687 return containingBlockForObjectInFlow();
688 }
689
690 if (positionType == PositionType::Absolute) {
691 auto containingBlockForAbsolutePosition = [&] {
692 if (is<RenderInline>(renderer) && renderer.style().position() == PositionType::Relative) {
693 // A relatively positioned RenderInline forwards its absolute positioned descendants to
694 // its nearest non-anonymous containing block (to avoid having positioned objects list in RenderInlines).
695 return nearestNonAnonymousContainingBlockIncludingSelf(renderer.parent());
696 }
697 auto* ancestor = renderer.parent();
698 while (ancestor && !ancestor->canContainAbsolutelyPositionedObjects())
699 ancestor = ancestor->parent();
700 // Make sure we only return non-anonymous RenderBlock as containing block.
701 return nearestNonAnonymousContainingBlockIncludingSelf(ancestor);
702 };
703 return containingBlockForAbsolutePosition();
704 }
705
706 if (positionType == PositionType::Fixed) {
707 auto containingBlockForFixedPosition = [&] {
708 auto* ancestor = renderer.parent();
709 while (ancestor && !ancestor->canContainFixedPositionObjects())
710 ancestor = ancestor->parent();
711 return nearestNonAnonymousContainingBlockIncludingSelf(ancestor);
712 };
713 return containingBlockForFixedPosition();
714 }
715
716 ASSERT_NOT_REACHED();
717 return nullptr;
718}
719
720RenderBlock* RenderObject::containingBlock() const
721{
722 if (is<RenderText>(*this))
723 return containingBlockForPositionType(PositionType::Static, *this);
724
725 auto containingBlockForRenderer = [](const auto& renderer) -> RenderBlock* {
726 if (isInTopLayerOrBackdrop(renderer.style(), renderer.element()))
727 return &renderer.view();
728 return containingBlockForPositionType(renderer.style().position(), renderer);
729 };
730
731 if (!parent() && is<RenderScrollbarPart>(*this)) {
732 if (auto* scrollbarPart = downcast<RenderScrollbarPart>(*this).rendererOwningScrollbar())
733 return containingBlockForRenderer(*scrollbarPart);
734 return nullptr;
735 }
736 return containingBlockForRenderer(downcast<RenderElement>(*this));
737}
738
739void RenderObject::addPDFURLRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
740{
741 Vector<LayoutRect> focusRingRects;
742 addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer);
743 LayoutRect urlRect = unionRect(focusRingRects);
744
745 if (urlRect.isEmpty())
746 return;
747 Node* node = this->node();
748 if (!is<Element>(node) || !node->isLink())
749 return;
750 Element& element = downcast<Element>(*node);
751 const AtomString& href = element.getAttribute(hrefAttr);
752 if (href.isNull())
753 return;
754
755 if (paintInfo.context().supportsInternalLinks()) {
756 String outAnchorName;
757 Element* linkTarget = element.findAnchorElementForLink(outAnchorName);
758 if (linkTarget) {
759 paintInfo.context().setDestinationForRect(outAnchorName, urlRect);
760 return;
761 }
762 }
763
764 paintInfo.context().setURLForRect(element.document().completeURL(href), urlRect);
765
766}
767
768#if PLATFORM(IOS_FAMILY)
769// This function is similar in spirit to RenderText::absoluteRectsForRange, but returns rectangles
770// which are annotated with additional state which helps iOS draw selections in its unique way.
771// No annotations are added in this class.
772// FIXME: Move to RenderText with absoluteRectsForRange()?
773void RenderObject::collectSelectionGeometries(Vector<SelectionGeometry>& geometries, unsigned start, unsigned end)
774{
775 Vector<FloatQuad> quads;
776
777 if (!firstChildSlow()) {
778 // FIXME: WebKit's position for an empty span after a BR is incorrect, so we can't trust
779 // quads for them. We don't need selection geometries for those anyway though, since they
780 // are just empty containers. See <https://bugs.webkit.org/show_bug.cgi?id=49358>.
781 RenderObject* previous = previousSibling();
782 Node* node = this->node();
783 if (!previous || !previous->isBR() || !node || !node->isContainerNode() || !isInline()) {
784 // For inline elements we don't use absoluteQuads, since it takes into account continuations and leads to wrong results.
785 absoluteQuadsForSelection(quads);
786 }
787 } else {
788 unsigned offset = start;
789 for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset)
790 child->absoluteQuads(quads);
791 }
792
793 for (auto& quad : quads)
794 geometries.append(SelectionGeometry(quad, HTMLElement::selectionRenderingBehavior(node()), isHorizontalWritingMode(), view().pageNumberForBlockProgressionOffset(quad.enclosingBoundingBox().x())));
795}
796#endif
797
798IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms, bool* wasFixed) const
799{
800 if (useTransforms) {
801 Vector<FloatQuad> quads;
802 absoluteQuads(quads, wasFixed);
803 return enclosingIntRect(unitedBoundingBoxes(quads));
804 }
805
806 FloatPoint absPos = localToAbsolute(FloatPoint(), { } /* ignore transforms */, wasFixed);
807 Vector<IntRect> rects;
808 absoluteRects(rects, flooredLayoutPoint(absPos));
809
810 size_t n = rects.size();
811 if (!n)
812 return IntRect();
813
814 LayoutRect result = rects[0];
815 for (size_t i = 1; i < n; ++i)
816 result.unite(rects[i]);
817 return snappedIntRect(result);
818}
819
820void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads)
821{
822 Vector<LayoutRect> rects;
823 // FIXME: addFocusRingRects() needs to be passed this transform-unaware
824 // localToAbsolute() offset here because RenderInline::addFocusRingRects()
825 // implicitly assumes that. This doesn't work correctly with transformed
826 // descendants.
827 FloatPoint absolutePoint = localToAbsolute();
828 addFocusRingRects(rects, flooredLayoutPoint(absolutePoint));
829 float deviceScaleFactor = document().deviceScaleFactor();
830 for (auto rect : rects) {
831 rect.moveBy(LayoutPoint(-absolutePoint));
832 quads.append(localToAbsoluteQuad(FloatQuad(snapRectToDevicePixels(rect, deviceScaleFactor))));
833 }
834}
835
836void RenderObject::addAbsoluteRectForLayer(LayoutRect& result)
837{
838 if (hasLayer())
839 result.unite(absoluteBoundingBoxRectIgnoringTransforms());
840
841 if (!is<RenderElement>(*this))
842 return;
843
844 for (auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*this)))
845 child.addAbsoluteRectForLayer(result);
846}
847
848// FIXME: change this to use the subtreePaint terminology
849LayoutRect RenderObject::paintingRootRect(LayoutRect& topLevelRect)
850{
851 LayoutRect result = absoluteBoundingBoxRectIgnoringTransforms();
852 topLevelRect = result;
853 if (is<RenderElement>(*this)) {
854 for (auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*this)))
855 child.addAbsoluteRectForLayer(result);
856 }
857 return result;
858}
859
860RenderObject::RepaintContainerStatus RenderObject::containerForRepaint() const
861{
862 RenderLayerModelObject* repaintContainer = nullptr;
863 auto fullRepaintAlreadyScheduled = false;
864
865 if (view().usesCompositing()) {
866 if (auto* parentLayer = enclosingLayer()) {
867 auto compLayerStatus = parentLayer->enclosingCompositingLayerForRepaint();
868 if (compLayerStatus.layer) {
869 repaintContainer = &compLayerStatus.layer->renderer();
870 fullRepaintAlreadyScheduled = compLayerStatus.fullRepaintAlreadyScheduled;
871 }
872 }
873 }
874 if (view().hasSoftwareFilters()) {
875 if (auto* parentLayer = enclosingLayer()) {
876 auto* enclosingFilterLayer = parentLayer->enclosingFilterLayer();
877 if (enclosingFilterLayer) {
878 fullRepaintAlreadyScheduled = parentLayer->needsFullRepaint();
879 return { fullRepaintAlreadyScheduled, &enclosingFilterLayer->renderer() };
880 }
881 }
882 }
883
884 // If we have a flow thread, then we need to do individual repaints within the RenderFragmentContainers instead.
885 // Return the flow thread as a repaint container in order to create a chokepoint that allows us to change
886 // repainting to do individual region repaints.
887 RenderFragmentedFlow* parentRenderFragmentedFlow = enclosingFragmentedFlow();
888 if (parentRenderFragmentedFlow) {
889 // If we have already found a repaint container then we will repaint into that container only if it is part of the same
890 // flow thread. Otherwise we will need to catch the repaint call and send it to the flow thread.
891 RenderFragmentedFlow* repaintContainerFragmentedFlow = repaintContainer ? repaintContainer->enclosingFragmentedFlow() : nullptr;
892 if (!repaintContainerFragmentedFlow || repaintContainerFragmentedFlow != parentRenderFragmentedFlow)
893 repaintContainer = parentRenderFragmentedFlow;
894 }
895 return { fullRepaintAlreadyScheduled, repaintContainer };
896}
897
898void RenderObject::propagateRepaintToParentWithOutlineAutoIfNeeded(const RenderLayerModelObject& repaintContainer, const LayoutRect& repaintRect) const
899{
900 if (!hasOutlineAutoAncestor())
901 return;
902
903 // FIXME: We should really propagate only when the child renderer sticks out.
904 bool repaintRectNeedsConverting = false;
905 // Issue repaint on the renderer with outline: auto.
906 for (const auto* renderer = this; renderer; renderer = renderer->parent()) {
907 bool rendererHasOutlineAutoAncestor = renderer->hasOutlineAutoAncestor();
908 ASSERT(rendererHasOutlineAutoAncestor
909 || renderer->outlineStyleForRepaint().outlineStyleIsAuto() == OutlineIsAuto::On
910 || (is<RenderBoxModelObject>(*renderer) && downcast<RenderBoxModelObject>(*renderer).isContinuation()));
911 if (renderer == &repaintContainer && rendererHasOutlineAutoAncestor)
912 repaintRectNeedsConverting = true;
913 if (rendererHasOutlineAutoAncestor)
914 continue;
915 // Issue repaint on the correct repaint container.
916 LayoutRect adjustedRepaintRect = repaintRect;
917 adjustedRepaintRect.inflate(renderer->outlineStyleForRepaint().outlineSize());
918 if (!repaintRectNeedsConverting)
919 repaintContainer.repaintRectangle(adjustedRepaintRect);
920 else if (is<RenderLayerModelObject>(renderer)) {
921 const auto& rendererWithOutline = downcast<RenderLayerModelObject>(*renderer);
922 adjustedRepaintRect = LayoutRect(repaintContainer.localToContainerQuad(FloatRect(adjustedRepaintRect), &rendererWithOutline).boundingBox());
923 rendererWithOutline.repaintRectangle(adjustedRepaintRect);
924 }
925 return;
926 }
927 ASSERT_NOT_REACHED();
928}
929
930void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintContainer, const LayoutRect& r, bool shouldClipToLayer) const
931{
932 if (r.isEmpty())
933 return;
934
935 if (!repaintContainer)
936 repaintContainer = &view();
937
938 if (is<RenderFragmentedFlow>(*repaintContainer)) {
939 downcast<RenderFragmentedFlow>(*repaintContainer).repaintRectangleInFragments(r);
940 return;
941 }
942
943 propagateRepaintToParentWithOutlineAutoIfNeeded(*repaintContainer, r);
944
945 if (repaintContainer->hasFilter() && repaintContainer->layer() && repaintContainer->layer()->requiresFullLayerImageForFilters()) {
946 repaintContainer->layer()->setFilterBackendNeedsRepaintingInRect(r);
947 return;
948 }
949
950 if (repaintContainer->isRenderView()) {
951 RenderView& view = this->view();
952 ASSERT(repaintContainer == &view);
953 bool viewHasCompositedLayer = view.isComposited();
954 if (!viewHasCompositedLayer || view.layer()->backing()->paintsIntoWindow()) {
955 LayoutRect rect = r;
956 if (viewHasCompositedLayer && view.layer()->transform())
957 rect = LayoutRect(view.layer()->transform()->mapRect(snapRectToDevicePixels(rect, document().deviceScaleFactor())));
958 view.repaintViewRectangle(rect);
959 return;
960 }
961 }
962
963 if (view().usesCompositing()) {
964 ASSERT(repaintContainer->isComposited());
965 repaintContainer->layer()->setBackingNeedsRepaintInRect(r, shouldClipToLayer ? GraphicsLayer::ClipToLayer : GraphicsLayer::DoNotClipToLayer);
966 }
967}
968
969static inline bool fullRepaintIsScheduled(const RenderObject& renderer)
970{
971 if (!renderer.view().usesCompositing() && !renderer.document().ownerElement())
972 return false;
973 for (auto* ancestorLayer = renderer.enclosingLayer(); ancestorLayer; ancestorLayer = ancestorLayer->paintOrderParent()) {
974 if (ancestorLayer->needsFullRepaint())
975 return true;
976 }
977 return false;
978}
979
980void RenderObject::issueRepaint(std::optional<LayoutRect> partialRepaintRect, ClipRepaintToLayer clipRepaintToLayer, ForceRepaint forceRepaint) const
981{
982 auto repaintContainer = containerForRepaint();
983 if (!repaintContainer.renderer)
984 repaintContainer = { fullRepaintIsScheduled(*this), &view() };
985
986 if (repaintContainer.fullRepaintIsScheduled && forceRepaint == ForceRepaint::No)
987 return;
988
989 auto repaintRect = partialRepaintRect ? computeRectForRepaint(*partialRepaintRect, repaintContainer.renderer) : clippedOverflowRectForRepaint(repaintContainer.renderer);
990 repaintUsingContainer(repaintContainer.renderer, repaintRect, clipRepaintToLayer == ClipRepaintToLayer::Yes);
991}
992
993void RenderObject::repaint() const
994{
995 // Don't repaint if we're unrooted (note that view() still returns the view when unrooted)
996 if (!isRooted() || view().printing())
997 return;
998 issueRepaint();
999}
1000
1001void RenderObject::repaintRectangle(const LayoutRect& repaintRect, bool shouldClipToLayer) const
1002{
1003 // Don't repaint if we're unrooted (note that view() still returns the view when unrooted)
1004 if (!isRooted() || view().printing())
1005 return;
1006 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
1007 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
1008 auto dirtyRect = repaintRect;
1009 dirtyRect.move(view().frameView().layoutContext().layoutDelta());
1010 issueRepaint(dirtyRect, shouldClipToLayer ? ClipRepaintToLayer::Yes : ClipRepaintToLayer::No);
1011}
1012
1013void RenderObject::repaintSlowRepaintObject() const
1014{
1015 // Don't repaint if we're unrooted (note that view() still returns the view when unrooted)
1016 if (!isRooted())
1017 return;
1018
1019 const RenderView& view = this->view();
1020 if (view.printing())
1021 return;
1022
1023 auto* repaintContainer = containerForRepaint().renderer;
1024
1025 bool shouldClipToLayer = true;
1026 IntRect repaintRect;
1027 // If this is the root background, we need to check if there is an extended background rect. If
1028 // there is, then we should not allow painting to clip to the layer size.
1029 if (isDocumentElementRenderer() || isBody()) {
1030 shouldClipToLayer = !view.frameView().hasExtendedBackgroundRectForPainting();
1031 repaintRect = snappedIntRect(view.backgroundRect());
1032 } else
1033 repaintRect = snappedIntRect(clippedOverflowRectForRepaint(repaintContainer));
1034
1035 repaintUsingContainer(repaintContainer, repaintRect, shouldClipToLayer);
1036}
1037
1038IntRect RenderObject::pixelSnappedAbsoluteClippedOverflowRect() const
1039{
1040 return snappedIntRect(absoluteClippedOverflowRectForRepaint());
1041}
1042
1043LayoutRect RenderObject::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
1044{
1045 LayoutRect r(clippedOverflowRectForRepaint(repaintContainer));
1046 r.inflate(outlineWidth);
1047 return r;
1048}
1049
1050LayoutRect RenderObject::clippedOverflowRect(const RenderLayerModelObject*, VisibleRectContext) const
1051{
1052 ASSERT_NOT_REACHED();
1053 return LayoutRect();
1054}
1055
1056LayoutRect RenderObject::computeRect(const LayoutRect& rect, const RenderLayerModelObject* repaintContainer, VisibleRectContext context) const
1057{
1058 return *computeVisibleRectInContainer(rect, repaintContainer, context);
1059}
1060
1061FloatRect RenderObject::computeFloatRectForRepaint(const FloatRect& rect, const RenderLayerModelObject* repaintContainer) const
1062{
1063 return *computeFloatVisibleRectInContainer(rect, repaintContainer, visibleRectContextForRepaint());
1064}
1065
1066std::optional<LayoutRect> RenderObject::computeVisibleRectInContainer(const LayoutRect& rect, const RenderLayerModelObject* container, VisibleRectContext context) const
1067{
1068 if (container == this)
1069 return rect;
1070
1071 auto* parent = this->parent();
1072 if (!parent)
1073 return rect;
1074
1075 LayoutRect adjustedRect = rect;
1076 if (parent->hasNonVisibleOverflow()) {
1077 bool isEmpty = !downcast<RenderBox>(*parent).applyCachedClipAndScrollPosition(adjustedRect, container, context);
1078 if (isEmpty) {
1079 if (context.options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection))
1080 return std::nullopt;
1081 return adjustedRect;
1082 }
1083 }
1084 return parent->computeVisibleRectInContainer(adjustedRect, container, context);
1085}
1086
1087std::optional<FloatRect> RenderObject::computeFloatVisibleRectInContainer(const FloatRect&, const RenderLayerModelObject*, VisibleRectContext) const
1088{
1089 ASSERT_NOT_REACHED();
1090 return FloatRect();
1091}
1092
1093#if ENABLE(TREE_DEBUGGING)
1094
1095static void outputRenderTreeLegend(TextStream& stream)
1096{
1097 stream.nextLine();
1098 stream << "(B)lock/(I)nline/I(N)line-block, (A)bsolute/Fi(X)ed/(R)elative/Stic(K)y, (F)loating, (O)verflow clip, Anon(Y)mous, (G)enerated, has(L)ayer, hasLayer(S)crollableArea, (C)omposited, (+)Dirty style, (+)Dirty layout";
1099 stream.nextLine();
1100}
1101
1102void RenderObject::showNodeTreeForThis() const
1103{
1104 if (!node())
1105 return;
1106 node()->showTreeForThis();
1107}
1108
1109void RenderObject::showRenderTreeForThis() const
1110{
1111 const WebCore::RenderObject* root = this;
1112 while (root->parent())
1113 root = root->parent();
1114 TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect);
1115 outputRenderTreeLegend(stream);
1116 root->outputRenderSubTreeAndMark(stream, this, 1);
1117 WTFLogAlways("%s", stream.release().utf8().data());
1118}
1119
1120void RenderObject::showLineTreeForThis() const
1121{
1122 if (!is<RenderBlockFlow>(*this))
1123 return;
1124 TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect);
1125 outputRenderTreeLegend(stream);
1126 outputRenderObject(stream, false, 1);
1127 downcast<RenderBlockFlow>(*this).outputLineTreeAndMark(stream, nullptr, 2);
1128 WTFLogAlways("%s", stream.release().utf8().data());
1129}
1130
1131static const RenderFragmentedFlow* enclosingFragmentedFlowFromRenderer(const RenderObject* renderer)
1132{
1133 if (!renderer)
1134 return nullptr;
1135
1136 if (renderer->fragmentedFlowState() == RenderObject::NotInsideFragmentedFlow)
1137 return nullptr;
1138
1139 if (is<RenderBlock>(*renderer))
1140 return downcast<RenderBlock>(*renderer).cachedEnclosingFragmentedFlow();
1141
1142 return nullptr;
1143}
1144
1145void RenderObject::outputRegionsInformation(TextStream& stream) const
1146{
1147 if (is<RenderFragmentedFlow>(*this)) {
1148 const auto& fragmentedFlow = downcast<RenderFragmentedFlow>(*this);
1149 auto fragmentContainers = fragmentedFlow.renderFragmentContainerList();
1150
1151 stream << " [fragment containers ";
1152 bool first = true;
1153 for (const auto* fragment : fragmentContainers) {
1154 if (!first)
1155 stream << ", ";
1156 first = false;
1157 stream << fragment;
1158 }
1159 stream << "]";
1160 }
1161
1162 const RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlowFromRenderer(this);
1163
1164 if (!fragmentedFlow) {
1165 // Only the boxes have region range information.
1166 // Try to get the flow thread containing block information
1167 // from the containing block of this box.
1168 if (is<RenderBox>(*this))
1169 fragmentedFlow = enclosingFragmentedFlowFromRenderer(containingBlock());
1170 }
1171
1172 if (!fragmentedFlow || !is<RenderBox>(*this))
1173 return;
1174
1175 RenderFragmentContainer* startContainer = nullptr;
1176 RenderFragmentContainer* endContainer = nullptr;
1177 fragmentedFlow->getFragmentRangeForBox(downcast<RenderBox>(this), startContainer, endContainer);
1178 stream << " [spans fragment containers in flow " << fragmentedFlow << " from " << startContainer << " to " << endContainer << "]";
1179}
1180
1181void RenderObject::outputRenderObject(TextStream& stream, bool mark, int depth) const
1182{
1183 if (isInlineBlockOrInlineTable())
1184 stream << "N";
1185 else if (isInline())
1186 stream << "I";
1187 else
1188 stream << "B";
1189
1190 if (isPositioned()) {
1191 if (isRelativelyPositioned())
1192 stream << "R";
1193 else if (isStickilyPositioned())
1194 stream << "K";
1195 else if (isOutOfFlowPositioned()) {
1196 if (isAbsolutelyPositioned())
1197 stream << "A";
1198 else
1199 stream << "X";
1200 }
1201 } else
1202 stream << "-";
1203
1204 if (isFloating())
1205 stream << "F";
1206 else
1207 stream << "-";
1208
1209 if (hasNonVisibleOverflow())
1210 stream << "O";
1211 else
1212 stream << "-";
1213
1214 if (isAnonymous())
1215 stream << "Y";
1216 else
1217 stream << "-";
1218
1219 if (isPseudoElement() || isAnonymous())
1220 stream << "G";
1221 else
1222 stream << "-";
1223
1224 if (hasLayer()) {
1225 stream << "L";
1226 if (downcast<RenderLayerModelObject>(*this).layer()->scrollableArea())
1227 stream << "S";
1228 else
1229 stream << "-";
1230 } else
1231 stream << "--";
1232
1233 if (isComposited())
1234 stream << "C";
1235 else
1236 stream << "-";
1237
1238 stream << " ";
1239
1240 if (node() && node()->needsStyleRecalc())
1241 stream << "+";
1242 else
1243 stream << "-";
1244
1245 if (needsLayout())
1246 stream << "+";
1247 else
1248 stream << "-";
1249
1250 int printedCharacters = 0;
1251 if (mark) {
1252 stream << "*";
1253 ++printedCharacters;
1254 }
1255
1256 while (++printedCharacters <= depth * 2)
1257 stream << " ";
1258
1259 if (node())
1260 stream << node()->nodeName().utf8().data() << " ";
1261
1262 ASCIILiteral name = renderName();
1263 StringView nameView { name };
1264 // FIXME: Renderer's name should not include property value listing.
1265 int pos = nameView.find('(');
1266 if (pos > 0)
1267 stream << nameView.left(pos - 1);
1268 else
1269 stream << nameView;
1270
1271 if (is<RenderBox>(*this)) {
1272 auto& renderBox = downcast<RenderBox>(*this);
1273 FloatRect boxRect = renderBox.frameRect();
1274 if (renderBox.isInFlowPositioned())
1275 boxRect.move(renderBox.offsetForInFlowPosition());
1276 stream << " " << boxRect;
1277 } else if (is<RenderInline>(*this) && isInFlowPositioned()) {
1278 FloatSize inlineOffset = downcast<RenderInline>(*this).offsetForInFlowPosition();
1279 stream << " (" << inlineOffset.width() << ", " << inlineOffset.height() << ")";
1280 }
1281
1282 stream << " renderer->(" << this << ")";
1283 if (node()) {
1284 stream << " node->(" << node() << ")";
1285 if (node()->isTextNode()) {
1286 String value = node()->nodeValue();
1287 stream << " length->(" << value.length() << ")";
1288
1289 value = makeStringByReplacingAll(value, '\\', "\\\\"_s);
1290 value = makeStringByReplacingAll(value, '\n', "\\n"_s);
1291
1292 const int maxPrintedLength = 80;
1293 if (value.length() > maxPrintedLength) {
1294 auto substring = StringView(value).left(maxPrintedLength);
1295 stream << " \"" << substring.utf8().data() << "\"...";
1296 } else
1297 stream << " \"" << value.utf8().data() << "\"";
1298 }
1299 }
1300 if (is<RenderBoxModelObject>(*this)) {
1301 auto& renderer = downcast<RenderBoxModelObject>(*this);
1302 if (renderer.continuation())
1303 stream << " continuation->(" << renderer.continuation() << ")";
1304 }
1305
1306 if (is<RenderBox>(*this)) {
1307 const auto& box = downcast<RenderBox>(*this);
1308 if (box.hasRenderOverflow()) {
1309 auto layoutOverflow = box.layoutOverflowRect();
1310 stream << " (layout overflow " << layoutOverflow.x() << "," << layoutOverflow.y() << " " << layoutOverflow.width() << "x" << layoutOverflow.height() << ")";
1311
1312 if (box.hasVisualOverflow()) {
1313 auto visualOverflow = box.visualOverflowRect();
1314 stream << " (visual overflow " << visualOverflow.x() << "," << visualOverflow.y() << " " << visualOverflow.width() << "x" << visualOverflow.height() << ")";
1315 }
1316 }
1317 }
1318
1319 if (is<RenderMultiColumnSet>(*this)) {
1320 const auto& multicolSet = downcast<RenderMultiColumnSet>(*this);
1321 stream << " (column count " << multicolSet.computedColumnCount() << ", size " << multicolSet.computedColumnWidth() << "x" << multicolSet.computedColumnHeight() << ", gap " << multicolSet.columnGap() << ")";
1322 }
1323
1324 outputRegionsInformation(stream);
1325
1326 if (needsLayout()) {
1327 stream << " layout->";
1328 if (selfNeedsLayout())
1329 stream << "[self]";
1330 if (normalChildNeedsLayout())
1331 stream << "[normal child]";
1332 if (posChildNeedsLayout())
1333 stream << "[positioned child]";
1334 if (needsSimplifiedNormalFlowLayout())
1335 stream << "[simplified]";
1336 if (needsPositionedMovementLayout())
1337 stream << "[positioned movement]";
1338 }
1339 stream.nextLine();
1340}
1341
1342void RenderObject::outputRenderSubTreeAndMark(TextStream& stream, const RenderObject* markedObject, int depth) const
1343{
1344 outputRenderObject(stream, markedObject == this, depth);
1345
1346 if (is<RenderBlockFlow>(*this))
1347 downcast<RenderBlockFlow>(*this).outputFloatingObjects(stream, depth + 1);
1348
1349 if (is<RenderBlockFlow>(*this))
1350 downcast<RenderBlockFlow>(*this).outputLineTreeAndMark(stream, nullptr, depth + 1);
1351
1352 for (auto* child = firstChildSlow(); child; child = child->nextSibling())
1353 child->outputRenderSubTreeAndMark(stream, markedObject, depth + 1);
1354}
1355
1356#endif // NDEBUG
1357
1358FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const
1359{
1360 TransformState transformState(TransformState::ApplyTransformDirection, localPoint);
1361 mapLocalToContainer(nullptr, transformState, mode | ApplyContainerFlip, wasFixed);
1362 transformState.flatten();
1363
1364 return transformState.lastPlanarPoint();
1365}
1366
1367FloatPoint RenderObject::absoluteToLocal(const FloatPoint& containerPoint, OptionSet<MapCoordinatesMode> mode) const
1368{
1369 TransformState transformState(TransformState::UnapplyInverseTransformDirection, containerPoint);
1370 mapAbsoluteToLocalPoint(mode, transformState);
1371 transformState.flatten();
1372
1373 return transformState.lastPlanarPoint();
1374}
1375
1376FloatQuad RenderObject::absoluteToLocalQuad(const FloatQuad& quad, OptionSet<MapCoordinatesMode> mode) const
1377{
1378 TransformState transformState(TransformState::UnapplyInverseTransformDirection, quad.boundingBox().center(), quad);
1379 mapAbsoluteToLocalPoint(mode, transformState);
1380 transformState.flatten();
1381 return transformState.lastPlanarQuad();
1382}
1383
1384void RenderObject::mapLocalToContainer(const RenderLayerModelObject* ancestorContainer, TransformState& transformState, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const
1385{
1386 if (ancestorContainer == this)
1387 return;
1388
1389 auto* parent = this->parent();
1390 if (!parent)
1391 return;
1392
1393 // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called.
1394 LayoutPoint centerPoint(transformState.mappedPoint());
1395 if (mode.contains(ApplyContainerFlip) && is<RenderBox>(*parent)) {
1396 if (parent->style().isFlippedBlocksWritingMode())
1397 transformState.move(downcast<RenderBox>(parent)->flipForWritingMode(LayoutPoint(transformState.mappedPoint())) - centerPoint);
1398 mode.remove(ApplyContainerFlip);
1399 }
1400
1401 if (is<RenderBox>(*parent))
1402 transformState.move(-toLayoutSize(downcast<RenderBox>(*parent).scrollPosition()));
1403
1404 parent->mapLocalToContainer(ancestorContainer, transformState, mode, wasFixed);
1405}
1406
1407const RenderObject* RenderObject::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
1408{
1409 ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != this);
1410
1411 auto* container = parent();
1412 if (!container)
1413 return nullptr;
1414
1415 // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called.
1416 LayoutSize offset;
1417 if (is<RenderBox>(*container))
1418 offset = -toLayoutSize(downcast<RenderBox>(*container).scrollPosition());
1419
1420 geometryMap.push(this, offset, false);
1421
1422 return container;
1423}
1424
1425void RenderObject::mapAbsoluteToLocalPoint(OptionSet<MapCoordinatesMode> mode, TransformState& transformState) const
1426{
1427 if (auto* parent = this->parent()) {
1428 parent->mapAbsoluteToLocalPoint(mode, transformState);
1429 if (is<RenderBox>(*parent))
1430 transformState.move(toLayoutSize(downcast<RenderBox>(*parent).scrollPosition()));
1431 }
1432}
1433
1434bool RenderObject::shouldUseTransformFromContainer(const RenderObject* containerObject) const
1435{
1436#if ENABLE(3D_TRANSFORMS)
1437 return hasTransform() || (containerObject && containerObject->style().hasPerspective());
1438#else
1439 UNUSED_PARAM(containerObject);
1440 return hasTransform();
1441#endif
1442}
1443
1444void RenderObject::getTransformFromContainer(const RenderObject* containerObject, const LayoutSize& offsetInContainer, TransformationMatrix& transform) const
1445{
1446 transform.makeIdentity();
1447 transform.translate(offsetInContainer.width(), offsetInContainer.height());
1448 RenderLayer* layer;
1449 if (hasLayer() && (layer = downcast<RenderLayerModelObject>(*this).layer()) && layer->transform())
1450 transform.multiply(layer->currentTransform());
1451
1452#if ENABLE(3D_TRANSFORMS)
1453 if (containerObject && containerObject->hasLayer() && containerObject->style().hasPerspective()) {
1454 // Perpsective on the container affects us, so we have to factor it in here.
1455 ASSERT(containerObject->hasLayer());
1456 FloatPoint perspectiveOrigin = downcast<RenderLayerModelObject>(*containerObject).layer()->perspectiveOrigin();
1457
1458 TransformationMatrix perspectiveMatrix;
1459 perspectiveMatrix.applyPerspective(containerObject->style().usedPerspective(*this));
1460
1461 transform.translateRight3d(-perspectiveOrigin.x(), -perspectiveOrigin.y(), 0);
1462 transform = perspectiveMatrix * transform;
1463 transform.translateRight3d(perspectiveOrigin.x(), perspectiveOrigin.y(), 0);
1464 }
1465#else
1466 UNUSED_PARAM(containerObject);
1467#endif
1468}
1469
1470FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, const RenderLayerModelObject* container, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const
1471{
1472 // Track the point at the center of the quad's bounding box. As mapLocalToContainer() calls offsetFromContainer(),
1473 // it will use that point as the reference point to decide which column's transform to apply in multiple-column blocks.
1474 TransformState transformState(TransformState::ApplyTransformDirection, localQuad.boundingBox().center(), localQuad);
1475 mapLocalToContainer(container, transformState, mode | ApplyContainerFlip, wasFixed);
1476 transformState.flatten();
1477
1478 return transformState.lastPlanarQuad();
1479}
1480
1481FloatPoint RenderObject::localToContainerPoint(const FloatPoint& localPoint, const RenderLayerModelObject* container, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const
1482{
1483 TransformState transformState(TransformState::ApplyTransformDirection, localPoint);
1484 mapLocalToContainer(container, transformState, mode | ApplyContainerFlip, wasFixed);
1485 transformState.flatten();
1486
1487 return transformState.lastPlanarPoint();
1488}
1489
1490LayoutSize RenderObject::offsetFromContainer(RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const
1491{
1492 ASSERT(&container == this->container());
1493
1494 LayoutSize offset;
1495 if (is<RenderBox>(container))
1496 offset -= toLayoutSize(downcast<RenderBox>(container).scrollPosition());
1497
1498 if (offsetDependsOnPoint)
1499 *offsetDependsOnPoint = is<RenderFragmentedFlow>(container);
1500
1501 return offset;
1502}
1503
1504LayoutSize RenderObject::offsetFromAncestorContainer(RenderElement& container) const
1505{
1506 LayoutSize offset;
1507 LayoutPoint referencePoint;
1508 const RenderObject* currContainer = this;
1509 do {
1510 RenderElement* nextContainer = currContainer->container();
1511 ASSERT(nextContainer); // This means we reached the top without finding container.
1512 if (!nextContainer)
1513 break;
1514 ASSERT(!currContainer->hasTransform());
1515 LayoutSize currentOffset = currContainer->offsetFromContainer(*nextContainer, referencePoint);
1516 offset += currentOffset;
1517 referencePoint.move(currentOffset);
1518 currContainer = nextContainer;
1519 } while (currContainer != &container);
1520
1521 return offset;
1522}
1523
1524HostWindow* RenderObject::hostWindow() const
1525{
1526 return view().frameView().root() ? view().frameView().root()->hostWindow() : nullptr;
1527}
1528
1529bool RenderObject::isRooted() const
1530{
1531 return isDescendantOf(&view());
1532}
1533
1534static inline RenderElement* containerForElement(const RenderObject& renderer, const RenderLayerModelObject* repaintContainer, bool* repaintContainerSkipped)
1535{
1536 // This method is extremely similar to containingBlock(), but with a few notable
1537 // exceptions.
1538 // (1) For normal flow elements, it just returns the parent.
1539 // (2) For absolute positioned elements, it will return a relative positioned inline, while
1540 // containingBlock() skips to the non-anonymous containing block.
1541 // This does mean that computePositionedLogicalWidth and computePositionedLogicalHeight have to use container().
1542 if (!is<RenderElement>(renderer))
1543 return renderer.parent();
1544 if (isInTopLayerOrBackdrop(renderer.style(), downcast<RenderElement>(renderer).element())) {
1545 auto updateRepaintContainerSkippedFlagIfApplicable = [&] {
1546 if (!repaintContainerSkipped)
1547 return;
1548 *repaintContainerSkipped = false;
1549 if (repaintContainer == &renderer.view())
1550 return;
1551 for (auto& ancestor : ancestorsOfType<RenderElement>(renderer)) {
1552 if (repaintContainer == &ancestor) {
1553 *repaintContainerSkipped = true;
1554 break;
1555 }
1556 }
1557 };
1558 updateRepaintContainerSkippedFlagIfApplicable();
1559 return &renderer.view();
1560 }
1561 auto position = renderer.style().position();
1562 if (position == PositionType::Static || position == PositionType::Relative || position == PositionType::Sticky)
1563 return renderer.parent();
1564 auto* parent = renderer.parent();
1565 for (; parent && (position == PositionType::Absolute ? !parent->canContainAbsolutelyPositionedObjects() : !parent->canContainFixedPositionObjects()); parent = parent->parent()) {
1566 if (repaintContainerSkipped && repaintContainer == parent)
1567 *repaintContainerSkipped = true;
1568 }
1569 return parent;
1570}
1571
1572RenderElement* RenderObject::container() const
1573{
1574 return containerForElement(*this, nullptr, nullptr);
1575}
1576
1577RenderElement* RenderObject::container(const RenderLayerModelObject* repaintContainer, bool& repaintContainerSkipped) const
1578{
1579 repaintContainerSkipped = false;
1580 return containerForElement(*this, repaintContainer, &repaintContainerSkipped);
1581}
1582
1583bool RenderObject::isSelectionBorder() const
1584{
1585 HighlightState st = selectionState();
1586 return st == HighlightState::Start
1587 || st == HighlightState::End
1588 || st == HighlightState::Both
1589 || view().selection().start() == this
1590 || view().selection().end() == this;
1591}
1592
1593void RenderObject::willBeDestroyed()
1594{
1595 ASSERT(!m_parent);
1596 ASSERT(renderTreeBeingDestroyed() || !is<RenderElement>(*this) || !view().frameView().hasSlowRepaintObject(downcast<RenderElement>(*this)));
1597
1598 if (AXObjectCache* cache = document().existingAXObjectCache())
1599 cache->remove(this);
1600
1601 if (auto* node = this->node()) {
1602 // FIXME: Continuations should be anonymous.
1603 ASSERT(!node->renderer() || node->renderer() == this || (is<RenderElement>(*this) && downcast<RenderElement>(*this).isContinuation()));
1604 if (node->renderer() == this)
1605 node->setRenderer(nullptr);
1606 }
1607
1608 removeRareData();
1609}
1610
1611void RenderObject::insertedIntoTree(IsInternalMove)
1612{
1613#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
1614 if (auto* container = LayoutIntegration::LineLayout::blockContainer(*this))
1615 container->invalidateLineLayoutPath();
1616#endif
1617
1618 // FIXME: We should ASSERT(isRooted()) here but generated content makes some out-of-order insertion.
1619 if (!isFloating() && parent()->childrenInline())
1620 parent()->dirtyLinesFromChangedChild(*this);
1621}
1622
1623void RenderObject::willBeRemovedFromTree(IsInternalMove)
1624{
1625#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
1626 if (auto* container = LayoutIntegration::LineLayout::blockContainer(*this))
1627 container->invalidateLineLayoutPath();
1628#endif
1629
1630 // FIXME: We should ASSERT(isRooted()) but we have some out-of-order removals which would need to be fixed first.
1631 // Update cached boundaries in SVG renderers, if a child is removed.
1632 parent()->setNeedsBoundariesUpdate();
1633}
1634
1635void RenderObject::destroy()
1636{
1637 RELEASE_ASSERT(!m_parent);
1638 RELEASE_ASSERT(!m_next);
1639 RELEASE_ASSERT(!m_previous);
1640 RELEASE_ASSERT(!m_bitfields.beingDestroyed());
1641
1642 m_bitfields.setBeingDestroyed(true);
1643
1644#if PLATFORM(IOS_FAMILY)
1645 if (hasLayer())
1646 downcast<RenderBoxModelObject>(*this).layer()->willBeDestroyed();
1647#endif
1648
1649 willBeDestroyed();
1650
1651 if (is<RenderWidget>(*this)) {
1652 downcast<RenderWidget>(*this).deref();
1653 return;
1654 }
1655 delete this;
1656}
1657
1658Position RenderObject::positionForPoint(const LayoutPoint& point)
1659{
1660 // FIXME: This should just create a Position object instead (webkit.org/b/168566).
1661 return positionForPoint(point, nullptr).deepEquivalent();
1662}
1663
1664VisiblePosition RenderObject::positionForPoint(const LayoutPoint&, const RenderFragmentContainer*)
1665{
1666 return createVisiblePosition(caretMinOffset(), Affinity::Downstream);
1667}
1668
1669bool RenderObject::isComposited() const
1670{
1671 return hasLayer() && downcast<RenderLayerModelObject>(*this).layer()->isComposited();
1672}
1673
1674bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestFilter hitTestFilter)
1675{
1676 bool inside = false;
1677 if (hitTestFilter != HitTestSelf) {
1678 // First test the foreground layer (lines and inlines).
1679 inside = nodeAtPoint(request, result, locationInContainer, accumulatedOffset, HitTestForeground);
1680
1681 // Test floats next.
1682 if (!inside)
1683 inside = nodeAtPoint(request, result, locationInContainer, accumulatedOffset, HitTestFloat);
1684
1685 // Finally test to see if the mouse is in the background (within a child block's background).
1686 if (!inside)
1687 inside = nodeAtPoint(request, result, locationInContainer, accumulatedOffset, HitTestChildBlockBackgrounds);
1688 }
1689
1690 // See if the mouse is inside us but not any of our descendants
1691 if (hitTestFilter != HitTestDescendants && !inside)
1692 inside = nodeAtPoint(request, result, locationInContainer, accumulatedOffset, HitTestBlockBackground);
1693
1694 return inside;
1695}
1696
1697Node* RenderObject::nodeForHitTest() const
1698{
1699 auto* node = this->node();
1700 // If we hit the anonymous renderers inside generated content we should
1701 // actually hit the generated content so walk up to the PseudoElement.
1702 if (!node && parent() && parent()->isBeforeOrAfterContent()) {
1703 for (auto* renderer = parent(); renderer && !node; renderer = renderer->parent())
1704 node = renderer->element();
1705 }
1706 return node;
1707}
1708
1709void RenderObject::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
1710{
1711 if (result.innerNode())
1712 return;
1713
1714 if (auto* node = nodeForHitTest()) {
1715 result.setInnerNode(node);
1716 if (!result.innerNonSharedNode())
1717 result.setInnerNonSharedNode(node);
1718 result.setLocalPoint(point);
1719 }
1720}
1721
1722bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& /*locationInContainer*/, const LayoutPoint& /*accumulatedOffset*/, HitTestAction)
1723{
1724 return false;
1725}
1726
1727int RenderObject::innerLineHeight() const
1728{
1729 return style().computedLineHeight();
1730}
1731
1732int RenderObject::caretMinOffset() const
1733{
1734 return 0;
1735}
1736
1737int RenderObject::caretMaxOffset() const
1738{
1739 if (isReplacedOrInlineBlock())
1740 return node() ? std::max(1U, node()->countChildNodes()) : 1;
1741 if (isHR())
1742 return 1;
1743 return 0;
1744}
1745
1746int RenderObject::previousOffset(int current) const
1747{
1748 return current - 1;
1749}
1750
1751int RenderObject::previousOffsetForBackwardDeletion(int current) const
1752{
1753 return current - 1;
1754}
1755
1756int RenderObject::nextOffset(int current) const
1757{
1758 return current + 1;
1759}
1760
1761void RenderObject::adjustRectForOutlineAndShadow(LayoutRect& rect) const
1762{
1763 LayoutUnit outlineSize { outlineStyleForRepaint().outlineSize() };
1764 if (const ShadowData* boxShadow = style().boxShadow()) {
1765 boxShadow->adjustRectForShadow(rect, outlineSize);
1766 return;
1767 }
1768 rect.inflate(outlineSize);
1769}
1770
1771void RenderObject::imageChanged(CachedImage* image, const IntRect* rect)
1772{
1773 imageChanged(static_cast<WrappedImagePtr>(image), rect);
1774}
1775
1776RenderBoxModelObject* RenderObject::offsetParent() const
1777{
1778 // If any of the following holds true return null and stop this algorithm:
1779 // A is the root element.
1780 // A is the HTML body element.
1781 // The computed value of the position property for element A is fixed.
1782 if (isDocumentElementRenderer() || isBody() || isFixedPositioned())
1783 return nullptr;
1784
1785 // If A is an area HTML element which has a map HTML element somewhere in the ancestor
1786 // chain return the nearest ancestor map HTML element and stop this algorithm.
1787 // FIXME: Implement!
1788
1789 // Return the nearest ancestor element of A for which at least one of the following is
1790 // true and stop this algorithm if such an ancestor is found:
1791 // * The element is a containing block of absolutely-positioned descendants (regardless
1792 // of whether there are any absolutely-positioned descendants).
1793 // * It is the HTML body element.
1794 // * The computed value of the position property of A is static and the ancestor
1795 // is one of the following HTML elements: td, th, or table.
1796 // * Our own extension: if there is a difference in the effective zoom
1797
1798 bool skipTables = isPositioned();
1799 float currZoom = style().effectiveZoom();
1800 auto current = parent();
1801 while (current && (!current->element() || (!current->canContainAbsolutelyPositionedObjects() && !current->isBody()))) {
1802 Element* element = current->element();
1803 if (!skipTables && element && (is<HTMLTableElement>(*element) || is<HTMLTableCellElement>(*element)))
1804 break;
1805
1806 float newZoom = current->style().effectiveZoom();
1807 if (currZoom != newZoom)
1808 break;
1809 currZoom = newZoom;
1810 current = current->parent();
1811 }
1812
1813 return dynamicDowncast<RenderBoxModelObject>(current);
1814}
1815
1816VisiblePosition RenderObject::createVisiblePosition(int offset, Affinity affinity) const
1817{
1818 // If this is a non-anonymous renderer in an editable area, then it's simple.
1819 if (Node* node = nonPseudoNode()) {
1820 if (!node->hasEditableStyle()) {
1821 // If it can be found, we prefer a visually equivalent position that is editable.
1822 Position position = makeDeprecatedLegacyPosition(node, offset);
1823 Position candidate = position.downstream(CanCrossEditingBoundary);
1824 if (candidate.deprecatedNode()->hasEditableStyle())
1825 return VisiblePosition(candidate, affinity);
1826 candidate = position.upstream(CanCrossEditingBoundary);
1827 if (candidate.deprecatedNode()->hasEditableStyle())
1828 return VisiblePosition(candidate, affinity);
1829 }
1830 // FIXME: Eliminate legacy editing positions
1831 return VisiblePosition(makeDeprecatedLegacyPosition(node, offset), affinity);
1832 }
1833
1834 // We don't want to cross the boundary between editable and non-editable
1835 // regions of the document, but that is either impossible or at least
1836 // extremely unlikely in any normal case because we stop as soon as we
1837 // find a single non-anonymous renderer.
1838
1839 // Find a nearby non-anonymous renderer.
1840 const RenderObject* child = this;
1841 while (const auto parent = child->parent()) {
1842 // Find non-anonymous content after.
1843 const RenderObject* renderer = child;
1844 while ((renderer = renderer->nextInPreOrder(parent))) {
1845 if (Node* node = renderer->nonPseudoNode())
1846 return firstPositionInOrBeforeNode(node);
1847 }
1848
1849 // Find non-anonymous content before.
1850 renderer = child;
1851 while ((renderer = renderer->previousInPreOrder())) {
1852 if (renderer == parent)
1853 break;
1854 if (Node* node = renderer->nonPseudoNode())
1855 return lastPositionInOrAfterNode(node);
1856 }
1857
1858 // Use the parent itself unless it too is anonymous.
1859 if (Element* element = parent->nonPseudoElement())
1860 return firstPositionInOrBeforeNode(element);
1861
1862 // Repeat at the next level up.
1863 child = parent;
1864 }
1865
1866 // Everything was anonymous. Give up.
1867 return VisiblePosition();
1868}
1869
1870VisiblePosition RenderObject::createVisiblePosition(const Position& position) const
1871{
1872 if (position.isNotNull())
1873 return VisiblePosition(position);
1874
1875 ASSERT(!node());
1876 return createVisiblePosition(0, Affinity::Downstream);
1877}
1878
1879CursorDirective RenderObject::getCursor(const LayoutPoint&, Cursor&) const
1880{
1881 return SetCursorBasedOnStyle;
1882}
1883
1884bool RenderObject::useDarkAppearance() const
1885{
1886 return document().useDarkAppearance(&style());
1887}
1888
1889OptionSet<StyleColorOptions> RenderObject::styleColorOptions() const
1890{
1891 return document().styleColorOptions(&style());
1892}
1893
1894void RenderObject::setSelectionState(HighlightState state)
1895{
1896 m_bitfields.setSelectionState(state);
1897}
1898
1899bool RenderObject::canUpdateSelectionOnRootLineBoxes()
1900{
1901 if (needsLayout())
1902 return false;
1903
1904 RenderBlock* containingBlock = this->containingBlock();
1905 return containingBlock ? !containingBlock->needsLayout() : true;
1906}
1907
1908// We only create "generated" child renderers like one for first-letter if:
1909// - the firstLetterBlock can have children in the DOM and
1910// - the block doesn't have any special assumption on its text children.
1911// This correctly prevents form controls from having such renderers.
1912bool RenderObject::canHaveGeneratedChildren() const
1913{
1914 return canHaveChildren();
1915}
1916
1917Node* RenderObject::generatingPseudoHostElement() const
1918{
1919 return downcast<PseudoElement>(*node()).hostElement();
1920}
1921
1922void RenderObject::setNeedsBoundariesUpdate()
1923{
1924 if (auto renderer = parent())
1925 renderer->setNeedsBoundariesUpdate();
1926}
1927
1928FloatRect RenderObject::objectBoundingBox() const
1929{
1930 ASSERT_NOT_REACHED();
1931 return FloatRect();
1932}
1933
1934FloatRect RenderObject::strokeBoundingBox() const
1935{
1936 ASSERT_NOT_REACHED();
1937 return FloatRect();
1938}
1939
1940// Returns the smallest rectangle enclosing all of the painted content
1941// respecting clipping, masking, filters, opacity, stroke-width and markers
1942FloatRect RenderObject::repaintRectInLocalCoordinates() const
1943{
1944 ASSERT_NOT_REACHED();
1945 return FloatRect();
1946}
1947
1948AffineTransform RenderObject::localTransform() const
1949{
1950 static const AffineTransform identity;
1951 return identity;
1952}
1953
1954const AffineTransform& RenderObject::localToParentTransform() const
1955{
1956 static const AffineTransform identity;
1957 return identity;
1958}
1959
1960bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint&, HitTestAction)
1961{
1962 ASSERT_NOT_REACHED();
1963 return false;
1964}
1965
1966RenderFragmentedFlow* RenderObject::locateEnclosingFragmentedFlow() const
1967{
1968 RenderBlock* containingBlock = this->containingBlock();
1969 return containingBlock ? containingBlock->enclosingFragmentedFlow() : nullptr;
1970}
1971
1972void RenderObject::calculateBorderStyleColor(const BorderStyle& style, const BoxSide& side, Color& color)
1973{
1974 ASSERT(style == BorderStyle::Inset || style == BorderStyle::Outset);
1975
1976 // This values were derived empirically.
1977 constexpr float baseDarkColorLuminance { 0.014443844f }; // Luminance of SRGBA<uint8_t> { 32, 32, 32 }
1978 constexpr float baseLightColorLuminance { 0.83077f }; // Luminance of SRGBA<uint8_t> { 235, 235, 235 }
1979
1980 enum Operation { Darken, Lighten };
1981
1982 Operation operation = (side == BoxSide::Top || side == BoxSide::Left) == (style == BorderStyle::Inset) ? Darken : Lighten;
1983
1984 // Here we will darken the border decoration color when needed. This will yield a similar behavior as in FF.
1985 if (operation == Darken) {
1986 if (color.luminance() > baseDarkColorLuminance)
1987 color = color.darkened();
1988 } else {
1989 if (color.luminance() < baseLightColorLuminance)
1990 color = color.lightened();
1991 }
1992}
1993
1994void RenderObject::setHasReflection(bool hasReflection)
1995{
1996 if (hasReflection || hasRareData())
1997 ensureRareData().setHasReflection(hasReflection);
1998}
1999
2000void RenderObject::setIsRenderFragmentedFlow(bool isFragmentedFlow)
2001{
2002 if (isFragmentedFlow || hasRareData())
2003 ensureRareData().setIsRenderFragmentedFlow(isFragmentedFlow);
2004}
2005
2006void RenderObject::setHasOutlineAutoAncestor(bool hasOutlineAutoAncestor)
2007{
2008 if (hasOutlineAutoAncestor || hasRareData())
2009 ensureRareData().setHasOutlineAutoAncestor(hasOutlineAutoAncestor);
2010}
2011
2012#if ENABLE(LAYER_BASED_SVG_ENGINE)
2013void RenderObject::setHasSVGTransform(bool hasSVGTransform)
2014{
2015 if (hasSVGTransform || hasRareData())
2016 ensureRareData().setHasSVGTransform(hasSVGTransform);
2017}
2018#endif
2019
2020void RenderObject::setPaintContainmentApplies(bool paintContainmentApplies)
2021{
2022 if (paintContainmentApplies || hasRareData())
2023 ensureRareData().setPaintContainmentApplies(paintContainmentApplies);
2024}
2025
2026RenderObject::RareDataMap& RenderObject::rareDataMap()
2027{
2028 static NeverDestroyed<RareDataMap> map;
2029 return map;
2030}
2031
2032const RenderObject::RenderObjectRareData& RenderObject::rareData() const
2033{
2034 ASSERT(hasRareData());
2035 return *rareDataMap().get(this);
2036}
2037
2038RenderObject::RenderObjectRareData& RenderObject::ensureRareData()
2039{
2040 setHasRareData(true);
2041 return *rareDataMap().ensure(this, [] { return makeUnique<RenderObjectRareData>(); }).iterator->value;
2042}
2043
2044void RenderObject::removeRareData()
2045{
2046 rareDataMap().remove(this);
2047 setHasRareData(false);
2048}
2049
2050RenderObject::RenderObjectRareData::RenderObjectRareData()
2051 : m_hasReflection(false)
2052 , m_isRenderFragmentedFlow(false)
2053 , m_hasOutlineAutoAncestor(false)
2054 , m_paintContainmentApplies(false)
2055#if ENABLE(LAYER_BASED_SVG_ENGINE)
2056 , m_hasSVGTransform(false)
2057#endif
2058{
2059}
2060
2061RenderObject::RenderObjectRareData::~RenderObjectRareData() = default;
2062
2063bool RenderObject::hasNonEmptyVisibleRectRespectingParentFrames() const
2064{
2065 auto enclosingFrameRenderer = [] (const RenderObject& renderer) {
2066 auto* ownerElement = renderer.document().ownerElement();
2067 return ownerElement ? ownerElement->renderer() : nullptr;
2068 };
2069
2070 auto hasEmptyVisibleRect = [] (const RenderObject& renderer) {
2071 VisibleRectContext context { false, false, { VisibleRectContextOption::UseEdgeInclusiveIntersection, VisibleRectContextOption::ApplyCompositedClips }};
2072 auto& box = renderer.enclosingBoxModelObject();
2073 auto clippedBounds = box.computeVisibleRectInContainer(box.borderBoundingBox(), &box.view(), context);
2074 return !clippedBounds || clippedBounds->isEmpty();
2075 };
2076
2077 for (auto* renderer = this; renderer; renderer = enclosingFrameRenderer(*renderer)) {
2078 if (hasEmptyVisibleRect(*renderer))
2079 return true;
2080 }
2081
2082 return false;
2083}
2084
2085Vector<FloatQuad> RenderObject::absoluteTextQuads(const SimpleRange& range, OptionSet<RenderObject::BoundingRectBehavior> behavior)
2086{
2087 Vector<FloatQuad> quads;
2088 for (auto& node : intersectingNodes(range)) {
2089 auto renderer = node.renderer();
2090 if (renderer && renderer->isBR())
2091 downcast<RenderLineBreak>(*renderer).absoluteQuads(quads);
2092 else if (is<RenderText>(renderer)) {
2093 auto offsetRange = characterDataOffsetRange(range, downcast<CharacterData>(node));
2094 quads.appendVector(downcast<RenderText>(*renderer).absoluteQuadsForRange(offsetRange.start, offsetRange.end, behavior.contains(BoundingRectBehavior::UseSelectionHeight)));
2095 }
2096 }
2097 return quads;
2098}
2099
2100static Vector<FloatRect> absoluteRectsForRangeInText(const SimpleRange& range, Text& node, OptionSet<RenderObject::BoundingRectBehavior> behavior)
2101{
2102 auto renderer = node.renderer();
2103 if (!renderer)
2104 return { };
2105
2106 auto offsetRange = characterDataOffsetRange(range, node);
2107 auto textQuads = renderer->absoluteQuadsForRange(offsetRange.start, offsetRange.end, behavior.contains(RenderObject::BoundingRectBehavior::UseSelectionHeight), behavior.contains(RenderObject::BoundingRectBehavior::IgnoreEmptyTextSelections));
2108
2109 if (behavior.contains(RenderObject::BoundingRectBehavior::RespectClipping)) {
2110 auto absoluteClippedOverflowRect = renderer->absoluteClippedOverflowRectForRepaint();
2111 Vector<FloatRect> clippedRects;
2112 clippedRects.reserveInitialCapacity(textQuads.size());
2113 for (auto& quad : textQuads) {
2114 auto clippedRect = intersection(quad.boundingBox(), absoluteClippedOverflowRect);
2115 if (!clippedRect.isEmpty())
2116 clippedRects.uncheckedAppend(clippedRect);
2117 }
2118 return clippedRects;
2119 }
2120
2121 return boundingBoxes(textQuads);
2122}
2123
2124// FIXME: This should return Vector<FloatRect> like the other similar functions.
2125// FIXME: Find a way to share with absoluteTextQuads rather than repeating so much of the logic from that function.
2126Vector<IntRect> RenderObject::absoluteTextRects(const SimpleRange& range, OptionSet<BoundingRectBehavior> behavior)
2127{
2128 ASSERT(!behavior.contains(BoundingRectBehavior::UseVisibleBounds));
2129 ASSERT(!behavior.contains(BoundingRectBehavior::IgnoreTinyRects));
2130 Vector<IntRect> rects;
2131 for (auto& node : intersectingNodes(range)) {
2132 auto renderer = node.renderer();
2133 if (renderer && renderer->isBR())
2134 downcast<RenderLineBreak>(*renderer).absoluteRects(rects, flooredLayoutPoint(renderer->localToAbsolute()));
2135 else if (is<Text>(node)) {
2136 for (auto& rect : absoluteRectsForRangeInText(range, downcast<Text>(node), behavior))
2137 rects.append(enclosingIntRect(rect));
2138 }
2139 }
2140 return rects;
2141}
2142
2143static RefPtr<Node> nodeBefore(const BoundaryPoint& point)
2144{
2145 if (point.offset) {
2146 if (auto node = point.container->traverseToChildAt(point.offset - 1))
2147 return node;
2148 }
2149 return point.container.ptr();
2150}
2151
2152enum class CoordinateSpace { Client, Absolute };
2153
2154static Vector<FloatRect> borderAndTextRects(const SimpleRange& range, CoordinateSpace space, OptionSet<RenderObject::BoundingRectBehavior> behavior)
2155{
2156 Vector<FloatRect> rects;
2157
2158 range.start.document().updateLayoutIgnorePendingStylesheets();
2159
2160 bool useVisibleBounds = behavior.contains(RenderObject::BoundingRectBehavior::UseVisibleBounds);
2161
2162 HashSet<Element*> selectedElementsSet;
2163 for (auto& node : intersectingNodesWithDeprecatedZeroOffsetStartQuirk(range)) {
2164 if (is<Element>(node))
2165 selectedElementsSet.add(&downcast<Element>(node));
2166 }
2167
2168 // Don't include elements at the end of the range that are only partially selected.
2169 // FIXME: What about the start of the range? The asymmetry here does not make sense. Seems likely this logic is not quite right in other respects, too.
2170 if (auto lastNode = nodeBefore(range.end)) {
2171 for (auto& ancestor : ancestorsOfType<Element>(*lastNode))
2172 selectedElementsSet.remove(&ancestor);
2173 }
2174
2175 constexpr OptionSet<RenderObject::VisibleRectContextOption> visibleRectOptions = {
2176 RenderObject::VisibleRectContextOption::UseEdgeInclusiveIntersection,
2177 RenderObject::VisibleRectContextOption::ApplyCompositedClips,
2178 RenderObject::VisibleRectContextOption::ApplyCompositedContainerScrolls
2179 };
2180
2181 for (auto& node : intersectingNodesWithDeprecatedZeroOffsetStartQuirk(range)) {
2182 if (is<Element>(node) && selectedElementsSet.contains(&downcast<Element>(node)) && (useVisibleBounds || !node.parentElement() || !selectedElementsSet.contains(node.parentElement()))) {
2183 if (auto renderer = downcast<Element>(node).renderBoxModelObject()) {
2184 if (useVisibleBounds) {
2185 auto localBounds = renderer->borderBoundingBox();
2186 auto rootClippedBounds = renderer->computeVisibleRectInContainer(localBounds, &renderer->view(), { false, false, visibleRectOptions });
2187 if (!rootClippedBounds)
2188 continue;
2189 auto snappedBounds = snapRectToDevicePixels(*rootClippedBounds, node.document().deviceScaleFactor());
2190 if (space == CoordinateSpace::Client)
2191 node.document().convertAbsoluteToClientRect(snappedBounds, renderer->style());
2192 rects.append(snappedBounds);
2193 continue;
2194 }
2195
2196 Vector<FloatQuad> elementQuads;
2197 renderer->absoluteQuads(elementQuads);
2198 if (space == CoordinateSpace::Client)
2199 node.document().convertAbsoluteToClientQuads(elementQuads, renderer->style());
2200 rects.appendVector(boundingBoxes(elementQuads));
2201 }
2202 } else if (is<Text>(node)) {
2203 if (auto renderer = downcast<Text>(node).renderer()) {
2204 auto clippedRects = absoluteRectsForRangeInText(range, downcast<Text>(node), behavior);
2205 if (space == CoordinateSpace::Client)
2206 node.document().convertAbsoluteToClientRects(clippedRects, renderer->style());
2207 rects.appendVector(clippedRects);
2208 }
2209 }
2210 }
2211
2212 if (behavior.contains(RenderObject::BoundingRectBehavior::IgnoreTinyRects)) {
2213 rects.removeAllMatching([&] (const FloatRect& rect) -> bool {
2214 return rect.area() <= 1;
2215 });
2216 }
2217
2218 return rects;
2219}
2220
2221Vector<FloatRect> RenderObject::absoluteBorderAndTextRects(const SimpleRange& range, OptionSet<BoundingRectBehavior> behavior)
2222{
2223 return borderAndTextRects(range, CoordinateSpace::Absolute, behavior);
2224}
2225
2226Vector<FloatRect> RenderObject::clientBorderAndTextRects(const SimpleRange& range)
2227{
2228 return borderAndTextRects(range, CoordinateSpace::Client, { });
2229}
2230
2231#if PLATFORM(IOS_FAMILY)
2232
2233static bool intervalsSufficientlyOverlap(int startA, int endA, int startB, int endB)
2234{
2235 if (endA <= startA || endB <= startB)
2236 return false;
2237
2238 const float sufficientOverlap = .75;
2239
2240 int lengthA = endA - startA;
2241 int lengthB = endB - startB;
2242
2243 int maxStart = std::max(startA, startB);
2244 int minEnd = std::min(endA, endB);
2245
2246 if (maxStart > minEnd)
2247 return false;
2248
2249 return minEnd - maxStart >= sufficientOverlap * std::min(lengthA, lengthB);
2250}
2251
2252static inline void adjustLineHeightOfSelectionGeometries(Vector<SelectionGeometry>& geometries, size_t numberOfGeometries, int lineNumber, int lineTop, int lineHeight)
2253{
2254 ASSERT(geometries.size() >= numberOfGeometries);
2255 for (size_t i = numberOfGeometries; i; ) {
2256 --i;
2257 if (geometries[i].lineNumber())
2258 break;
2259 if (geometries[i].behavior() == SelectionRenderingBehavior::UseIndividualQuads)
2260 continue;
2261 geometries[i].setLineNumber(lineNumber);
2262 geometries[i].setLogicalTop(lineTop);
2263 geometries[i].setLogicalHeight(lineHeight);
2264 }
2265}
2266
2267static SelectionGeometry coalesceSelectionGeometries(const SelectionGeometry& original, const SelectionGeometry& previous)
2268{
2269 SelectionGeometry result({ unionRect(previous.rect(), original.rect()) }, SelectionRenderingBehavior::CoalesceBoundingRects, original.isHorizontal(), original.pageNumber());
2270 result.setDirection(original.containsStart() || original.containsEnd() ? original.direction() : previous.direction());
2271 result.setContainsStart(previous.containsStart() || original.containsStart());
2272 result.setContainsEnd(previous.containsEnd() || original.containsEnd());
2273 result.setIsFirstOnLine(previous.isFirstOnLine() || original.isFirstOnLine());
2274 result.setIsLastOnLine(previous.isLastOnLine() || original.isLastOnLine());
2275 return result;
2276}
2277
2278Vector<SelectionGeometry> RenderObject::collectSelectionGeometriesWithoutUnionInteriorLines(const SimpleRange& range)
2279{
2280 return collectSelectionGeometriesInternal(range).geometries;
2281}
2282
2283auto RenderObject::collectSelectionGeometriesInternal(const SimpleRange& range) -> SelectionGeometries
2284{
2285 Vector<SelectionGeometry> geometries;
2286 Vector<SelectionGeometry> newGeometries;
2287 bool hasFlippedWritingMode = range.start.container->renderer() && range.start.container->renderer()->style().isFlippedBlocksWritingMode();
2288 bool containsDifferentWritingModes = false;
2289 for (auto& node : intersectingNodesWithDeprecatedZeroOffsetStartQuirk(range)) {
2290 auto renderer = node.renderer();
2291 // Only ask leaf render objects for their line box rects.
2292 if (renderer && !renderer->firstChildSlow() && renderer->style().effectiveUserSelect() != UserSelect::None) {
2293 bool isStartNode = renderer->node() == range.start.container.ptr();
2294 bool isEndNode = renderer->node() == range.end.container.ptr();
2295 if (hasFlippedWritingMode != renderer->style().isFlippedBlocksWritingMode())
2296 containsDifferentWritingModes = true;
2297 // FIXME: Sending 0 for the startOffset is a weird way of telling the renderer that the selection
2298 // doesn't start inside it, since we'll also send 0 if the selection *does* start in it, at offset 0.
2299 //
2300 // FIXME: Selection endpoints aren't always inside leaves, and we only build SelectionGeometries for leaves,
2301 // so we can't accurately determine which SelectionGeometries contain the selection start and end using
2302 // only the offsets of the start and end. We need to pass the whole Range.
2303 int beginSelectionOffset = isStartNode ? range.start.offset : 0;
2304 int endSelectionOffset = isEndNode ? range.end.offset : std::numeric_limits<int>::max();
2305 renderer->collectSelectionGeometries(newGeometries, beginSelectionOffset, endSelectionOffset);
2306 for (auto& selectionGeometry : newGeometries) {
2307 if (selectionGeometry.containsStart() && !isStartNode)
2308 selectionGeometry.setContainsStart(false);
2309 if (selectionGeometry.containsEnd() && !isEndNode)
2310 selectionGeometry.setContainsEnd(false);
2311 if (selectionGeometry.logicalWidth() || selectionGeometry.logicalHeight())
2312 geometries.append(selectionGeometry);
2313 }
2314 newGeometries.shrink(0);
2315 }
2316 }
2317
2318 // The range could span nodes with different writing modes.
2319 // If this is the case, we use the writing mode of the common ancestor.
2320 if (containsDifferentWritingModes) {
2321 if (auto ancestor = commonInclusiveAncestor<ComposedTree>(range))
2322 hasFlippedWritingMode = ancestor->renderer()->style().isFlippedBlocksWritingMode();
2323 }
2324
2325 auto numberOfGeometries = geometries.size();
2326
2327 // If the selection ends in a BR, then add the line break bit to the last rect we have.
2328 // This will cause its selection rect to extend to the end of the line.
2329 if (numberOfGeometries) {
2330 // Only set the line break bit if the end of the range actually
2331 // extends all the way to include the <br>. VisiblePosition helps to
2332 // figure this out.
2333 if (is<HTMLBRElement>(VisiblePosition(makeContainerOffsetPosition(range.end)).deepEquivalent().firstNode()))
2334 geometries.last().setIsLineBreak(true);
2335 }
2336
2337 int lineTop = std::numeric_limits<int>::max();
2338 int lineBottom = std::numeric_limits<int>::min();
2339 int lastLineTop = lineTop;
2340 int lastLineBottom = lineBottom;
2341 int lineNumber = 0;
2342
2343 for (size_t i = 0; i < numberOfGeometries; ++i) {
2344 int currentRectTop = geometries[i].logicalTop();
2345 int currentRectBottom = currentRectTop + geometries[i].logicalHeight();
2346
2347 // We don't want to count the ruby text as a separate line.
2348 if (intervalsSufficientlyOverlap(currentRectTop, currentRectBottom, lineTop, lineBottom) || (i && geometries[i].isRubyText())) {
2349 // Grow the current line bounds.
2350 lineTop = std::min(lineTop, currentRectTop);
2351 lineBottom = std::max(lineBottom, currentRectBottom);
2352 // Avoid overlap with the previous line.
2353 if (!hasFlippedWritingMode)
2354 lineTop = std::max(lastLineBottom, lineTop);
2355 else
2356 lineBottom = std::min(lastLineTop, lineBottom);
2357 } else {
2358 adjustLineHeightOfSelectionGeometries(geometries, i, lineNumber, lineTop, lineBottom - lineTop);
2359 if (!hasFlippedWritingMode) {
2360 lastLineTop = lineTop;
2361 if (currentRectBottom >= lastLineTop) {
2362 lastLineBottom = lineBottom;
2363 lineTop = lastLineBottom;
2364 } else {
2365 lineTop = currentRectTop;
2366 lastLineBottom = std::numeric_limits<int>::min();
2367 }
2368 lineBottom = currentRectBottom;
2369 } else {
2370 lastLineBottom = lineBottom;
2371 if (currentRectTop <= lastLineBottom && i && geometries[i].pageNumber() == geometries[i - 1].pageNumber()) {
2372 lastLineTop = lineTop;
2373 lineBottom = lastLineTop;
2374 } else {
2375 lastLineTop = std::numeric_limits<int>::max();
2376 lineBottom = currentRectBottom;
2377 }
2378 lineTop = currentRectTop;
2379 }
2380 ++lineNumber;
2381 }
2382 }
2383
2384 // Adjust line height.
2385 adjustLineHeightOfSelectionGeometries(geometries, numberOfGeometries, lineNumber, lineTop, lineBottom - lineTop);
2386
2387 // When using SelectionRenderingBehavior::CoalesceBoundingRects, sort the rectangles and make sure there are no gaps.
2388 // The rectangles could be unsorted when there is ruby text and we could have gaps on the line when adjacent elements
2389 // on the line have a different orientation.
2390 //
2391 // Note that for selection geometries with SelectionRenderingBehavior::UseIndividualQuads, we avoid sorting in order to
2392 // preserve the fact that the resulting geometries correspond to the order in which the quads are discovered during DOM
2393 // traversal. This allows us to efficiently coalesce adjacent selection quads.
2394 size_t firstRectWithCurrentLineNumber = 0;
2395 for (size_t currentRect = 1; currentRect < numberOfGeometries; ++currentRect) {
2396 if (geometries[currentRect].lineNumber() != geometries[currentRect - 1].lineNumber()) {
2397 firstRectWithCurrentLineNumber = currentRect;
2398 continue;
2399 }
2400 if (geometries[currentRect].logicalLeft() >= geometries[currentRect - 1].logicalLeft())
2401 continue;
2402
2403 if (geometries[currentRect].behavior() != SelectionRenderingBehavior::CoalesceBoundingRects)
2404 continue;
2405
2406 auto selectionRect = geometries[currentRect];
2407 size_t i;
2408 for (i = currentRect; i > firstRectWithCurrentLineNumber && selectionRect.logicalLeft() < geometries[i - 1].logicalLeft(); --i)
2409 geometries[i] = geometries[i - 1];
2410 geometries[i] = selectionRect;
2411 }
2412
2413 for (size_t j = 1; j < numberOfGeometries; ++j) {
2414 if (geometries[j].lineNumber() != geometries[j - 1].lineNumber())
2415 continue;
2416 if (geometries[j].behavior() == SelectionRenderingBehavior::UseIndividualQuads)
2417 continue;
2418 auto& previousRect = geometries[j - 1];
2419 bool previousRectMayNotReachRightEdge = (previousRect.direction() == TextDirection::LTR && previousRect.containsEnd()) || (previousRect.direction() == TextDirection::RTL && previousRect.containsStart());
2420 if (previousRectMayNotReachRightEdge)
2421 continue;
2422 int adjustedWidth = geometries[j].logicalLeft() - previousRect.logicalLeft();
2423 if (adjustedWidth > previousRect.logicalWidth())
2424 previousRect.setLogicalWidth(adjustedWidth);
2425 }
2426
2427 int maxLineNumber = lineNumber;
2428
2429 // Extend rects out to edges as needed.
2430 for (size_t i = 0; i < numberOfGeometries; ++i) {
2431 auto& selectionGeometry = geometries[i];
2432 if (!selectionGeometry.isLineBreak() && selectionGeometry.lineNumber() >= maxLineNumber)
2433 continue;
2434 if (selectionGeometry.behavior() == SelectionRenderingBehavior::UseIndividualQuads)
2435 continue;
2436 if (selectionGeometry.direction() == TextDirection::RTL && selectionGeometry.isFirstOnLine()) {
2437 selectionGeometry.setLogicalWidth(selectionGeometry.logicalWidth() + selectionGeometry.logicalLeft() - selectionGeometry.minX());
2438 selectionGeometry.setLogicalLeft(selectionGeometry.minX());
2439 } else if (selectionGeometry.direction() == TextDirection::LTR && selectionGeometry.isLastOnLine())
2440 selectionGeometry.setLogicalWidth(selectionGeometry.maxX() - selectionGeometry.logicalLeft());
2441 }
2442
2443 return { WTFMove(geometries), maxLineNumber };
2444}
2445
2446static bool coalesceSelectionGeometryWithAdjacentQuadsIfPossible(SelectionGeometry& current, const SelectionGeometry& next)
2447{
2448 auto nextQuad = next.quad();
2449 if (nextQuad.isEmpty())
2450 return true;
2451
2452 auto areCloseEnoughToCoalesce = [](const FloatPoint& first, const FloatPoint& second) {
2453 constexpr float maxDistanceBetweenBoundaryPoints = 2;
2454 return (first - second).diagonalLengthSquared() <= maxDistanceBetweenBoundaryPoints * maxDistanceBetweenBoundaryPoints;
2455 };
2456
2457 auto currentQuad = current.quad();
2458 if (!areCloseEnoughToCoalesce(currentQuad.p2(), nextQuad.p1()) || !areCloseEnoughToCoalesce(currentQuad.p3(), nextQuad.p4()))
2459 return false;
2460
2461 if (std::abs(rotatedBoundingRectWithMinimumAngleOfRotation(currentQuad).angleInRadians - rotatedBoundingRectWithMinimumAngleOfRotation(nextQuad).angleInRadians) > radiansPerDegreeFloat)
2462 return false;
2463
2464 currentQuad.setP2(nextQuad.p2());
2465 currentQuad.setP3(nextQuad.p3());
2466 current.setQuad(currentQuad);
2467 current.setDirection(current.containsStart() || current.containsEnd() ? current.direction() : next.direction());
2468 current.setContainsStart(current.containsStart() || next.containsStart());
2469 current.setContainsEnd(current.containsEnd() || next.containsEnd());
2470 current.setIsFirstOnLine(current.isFirstOnLine() || next.isFirstOnLine());
2471 current.setIsLastOnLine(current.isLastOnLine() || next.isLastOnLine());
2472 return true;
2473}
2474
2475Vector<SelectionGeometry> RenderObject::collectSelectionGeometries(const SimpleRange& range)
2476{
2477 auto result = RenderObject::collectSelectionGeometriesInternal(range);
2478 auto numberOfGeometries = result.geometries.size();
2479
2480 // Union all the rectangles on interior lines (i.e. not first or last).
2481 // On first and last lines, just avoid having overlaps by merging intersecting rectangles.
2482 Vector<SelectionGeometry> coalescedGeometries;
2483 IntRect interiorUnionRect;
2484 for (size_t i = 0; i < numberOfGeometries; ++i) {
2485 auto& currentGeometry = result.geometries[i];
2486 if (currentGeometry.behavior() == SelectionRenderingBehavior::UseIndividualQuads) {
2487 if (currentGeometry.quad().isEmpty())
2488 continue;
2489
2490 if (coalescedGeometries.isEmpty() || !coalesceSelectionGeometryWithAdjacentQuadsIfPossible(coalescedGeometries.last(), currentGeometry))
2491 coalescedGeometries.append(currentGeometry);
2492 continue;
2493 }
2494
2495 if (currentGeometry.lineNumber() == 1) {
2496 ASSERT(interiorUnionRect.isEmpty());
2497 if (!coalescedGeometries.isEmpty()) {
2498 auto& previousRect = coalescedGeometries.last();
2499 if (previousRect.rect().intersects(currentGeometry.rect())) {
2500 previousRect = coalesceSelectionGeometries(currentGeometry, previousRect);
2501 continue;
2502 }
2503 }
2504 // Couldn't merge with previous rect, so just appending.
2505 coalescedGeometries.append(currentGeometry);
2506 } else if (currentGeometry.lineNumber() < result.maxLineNumber) {
2507 if (interiorUnionRect.isEmpty()) {
2508 // Start collecting interior rects.
2509 interiorUnionRect = currentGeometry.rect();
2510 } else if (interiorUnionRect.intersects(currentGeometry.rect())
2511 || interiorUnionRect.maxX() == currentGeometry.rect().x()
2512 || interiorUnionRect.maxY() == currentGeometry.rect().y()
2513 || interiorUnionRect.x() == currentGeometry.rect().maxX()
2514 || interiorUnionRect.y() == currentGeometry.rect().maxY()) {
2515 // Only union the lines that are attached.
2516 // For iBooks, the interior lines may cross multiple horizontal pages.
2517 interiorUnionRect.unite(currentGeometry.rect());
2518 } else {
2519 coalescedGeometries.append(SelectionGeometry({ interiorUnionRect }, SelectionRenderingBehavior::CoalesceBoundingRects, currentGeometry.isHorizontal(), currentGeometry.pageNumber()));
2520 interiorUnionRect = currentGeometry.rect();
2521 }
2522 } else {
2523 // Processing last line.
2524 if (!interiorUnionRect.isEmpty()) {
2525 coalescedGeometries.append(SelectionGeometry({ interiorUnionRect }, SelectionRenderingBehavior::CoalesceBoundingRects, currentGeometry.isHorizontal(), currentGeometry.pageNumber()));
2526 interiorUnionRect = IntRect();
2527 }
2528
2529 ASSERT(!coalescedGeometries.isEmpty());
2530 auto& previousGeometry = coalescedGeometries.last();
2531 if (previousGeometry.logicalTop() == currentGeometry.logicalTop() && previousGeometry.rect().intersects(currentGeometry.rect())) {
2532 // previousRect is also on the last line, and intersects the current one.
2533 previousGeometry = coalesceSelectionGeometries(currentGeometry, previousGeometry);
2534 continue;
2535 }
2536 // Couldn't merge with previous rect, so just appending.
2537 coalescedGeometries.append(currentGeometry);
2538 }
2539 }
2540
2541 return coalescedGeometries;
2542}
2543
2544#endif
2545
2546String RenderObject::description() const
2547{
2548 StringBuilder builder;
2549
2550 builder.append(renderName(), ' ');
2551 if (node())
2552 builder.append(' ', node()->description());
2553
2554 return builder.toString();
2555}
2556
2557String RenderObject::debugDescription() const
2558{
2559 StringBuilder builder;
2560
2561 builder.append(renderName(), " 0x"_s, hex(reinterpret_cast<uintptr_t>(this), Lowercase));
2562 if (node())
2563 builder.append(' ', node()->debugDescription());
2564
2565 return builder.toString();
2566}
2567
2568TextStream& operator<<(TextStream& ts, const RenderObject& renderer)
2569{
2570 ts << renderer.debugDescription();
2571 return ts;
2572}
2573
2574#if ENABLE(TREE_DEBUGGING)
2575
2576void printRenderTreeForLiveDocuments()
2577{
2578 for (const auto* document : Document::allDocuments()) {
2579 if (!document->renderView())
2580 continue;
2581 if (document->frame() && document->frame()->isMainFrame())
2582 fprintf(stderr, "----------------------main frame--------------------------\n");
2583 fprintf(stderr, "%s", document->url().string().utf8().data());
2584 showRenderTree(document->renderView());
2585 }
2586}
2587
2588void printLayerTreeForLiveDocuments()
2589{
2590 for (const auto* document : Document::allDocuments()) {
2591 if (!document->renderView())
2592 continue;
2593 if (document->frame() && document->frame()->isMainFrame())
2594 fprintf(stderr, "----------------------main frame--------------------------\n");
2595 fprintf(stderr, "%s", document->url().string().utf8().data());
2596 showLayerTree(document->renderView());
2597 }
2598}
2599
2600void printGraphicsLayerTreeForLiveDocuments()
2601{
2602 for (const auto* document : Document::allDocuments()) {
2603 if (!document->renderView())
2604 continue;
2605 if (document->frame() && document->frame()->isMainFrame()) {
2606 WTFLogAlways("Graphics layer tree for root document %p %s", document, document->url().string().utf8().data());
2607 showGraphicsLayerTreeForCompositor(document->renderView()->compositor());
2608 }
2609 }
2610}
2611
2612#endif // ENABLE(TREE_DEBUGGING)
2613
2614} // namespace WebCore
2615
2616#if ENABLE(TREE_DEBUGGING)
2617
2618void showNodeTree(const WebCore::RenderObject* object)
2619{
2620 if (!object)
2621 return;
2622 object->showNodeTreeForThis();
2623}
2624
2625void showLineTree(const WebCore::RenderObject* object)
2626{
2627 if (!object)
2628 return;
2629 object->showLineTreeForThis();
2630}
2631
2632void showRenderTree(const WebCore::RenderObject* object)
2633{
2634 if (!object)
2635 return;
2636 object->showRenderTreeForThis();
2637}
2638
2639#endif
Note: See TracBrowser for help on using the repository browser.