1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2007 David Smith (catfish.man@gmail.com)
5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25#include "RenderBlock.h"
26
27#include "AXObjectCache.h"
28#include "Document.h"
29#include "Editor.h"
30#include "Element.h"
31#include "EventRegion.h"
32#include "FloatQuad.h"
33#include "Frame.h"
34#include "FrameSelection.h"
35#include "FrameView.h"
36#include "GraphicsContext.h"
37#include "HTMLNames.h"
38#include "HitTestLocation.h"
39#include "HitTestResult.h"
40#include "InlineElementBox.h"
41#include "InlineIterator.h"
42#include "InlineTextBox.h"
43#include "LayoutRepainter.h"
44#include "LogicalSelectionOffsetCaches.h"
45#include "OverflowEvent.h"
46#include "Page.h"
47#include "PaintInfo.h"
48#include "RenderBlockFlow.h"
49#include "RenderBoxFragmentInfo.h"
50#include "RenderButton.h"
51#include "RenderChildIterator.h"
52#include "RenderCombineText.h"
53#include "RenderDeprecatedFlexibleBox.h"
54#include "RenderFlexibleBox.h"
55#include "RenderFragmentContainer.h"
56#include "RenderInline.h"
57#include "RenderIterator.h"
58#include "RenderLayer.h"
59#include "RenderLayoutState.h"
60#include "RenderListMarker.h"
61#include "RenderMenuList.h"
62#include "RenderSVGResourceClipper.h"
63#include "RenderSVGRoot.h"
64#include "RenderTableCell.h"
65#include "RenderTextFragment.h"
66#include "RenderTheme.h"
67#include "RenderTreeBuilder.h"
68#include "RenderTreePosition.h"
69#include "RenderView.h"
70#include "SVGSVGElement.h"
71#include "Settings.h"
72#include "ShadowRoot.h"
73#include "ShapeOutsideInfo.h"
74#include "TransformState.h"
75#include <wtf/IsoMallocInlines.h>
76#include <wtf/NeverDestroyed.h>
77#include <wtf/Optional.h>
78#include <wtf/SetForScope.h>
79#include <wtf/StackStats.h>
80
81namespace WebCore {
82
83using namespace HTMLNames;
84using namespace WTF::Unicode;
85
86WTF_MAKE_ISO_ALLOCATED_IMPL(RenderBlock);
87
88struct SameSizeAsRenderBlock : public RenderBox {
89};
90
91COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small);
92
93typedef HashMap<const RenderBlock*, std::unique_ptr<TrackedRendererListHashSet>> TrackedDescendantsMap;
94typedef HashMap<const RenderBox*, std::unique_ptr<HashSet<const RenderBlock*>>> TrackedContainerMap;
95
96static TrackedDescendantsMap* percentHeightDescendantsMap;
97static TrackedContainerMap* percentHeightContainerMap;
98
99static void insertIntoTrackedRendererMaps(const RenderBlock& container, RenderBox& descendant)
100{
101 if (!percentHeightDescendantsMap) {
102 percentHeightDescendantsMap = new TrackedDescendantsMap;
103 percentHeightContainerMap = new TrackedContainerMap;
104 }
105
106 auto& descendantSet = percentHeightDescendantsMap->ensure(&container, [] {
107 return std::make_unique<TrackedRendererListHashSet>();
108 }).iterator->value;
109
110 bool added = descendantSet->add(&descendant).isNewEntry;
111 if (!added) {
112 ASSERT(percentHeightContainerMap->get(&descendant));
113 ASSERT(percentHeightContainerMap->get(&descendant)->contains(&container));
114 return;
115 }
116
117 auto& containerSet = percentHeightContainerMap->ensure(&descendant, [] {
118 return std::make_unique<HashSet<const RenderBlock*>>();
119 }).iterator->value;
120
121 ASSERT(!containerSet->contains(&container));
122 containerSet->add(&container);
123}
124
125static void removeFromTrackedRendererMaps(RenderBox& descendant)
126{
127 if (!percentHeightDescendantsMap)
128 return;
129
130 std::unique_ptr<HashSet<const RenderBlock*>> containerSet = percentHeightContainerMap->take(&descendant);
131 if (!containerSet)
132 return;
133
134 for (auto* container : *containerSet) {
135 // FIXME: Disabling this assert temporarily until we fix the layout
136 // bugs associated with positioned objects not properly cleared from
137 // their ancestor chain before being moved. See webkit bug 93766.
138 // ASSERT(descendant->isDescendantOf(container));
139 auto descendantsMapIterator = percentHeightDescendantsMap->find(container);
140 ASSERT(descendantsMapIterator != percentHeightDescendantsMap->end());
141 if (descendantsMapIterator == percentHeightDescendantsMap->end())
142 continue;
143 auto& descendantSet = descendantsMapIterator->value;
144 ASSERT(descendantSet->contains(&descendant));
145 descendantSet->remove(&descendant);
146 if (descendantSet->isEmpty())
147 percentHeightDescendantsMap->remove(descendantsMapIterator);
148 }
149}
150
151class PositionedDescendantsMap {
152public:
153 enum class MoveDescendantToEnd { No, Yes };
154 void addDescendant(const RenderBlock& containingBlock, RenderBox& positionedDescendant, MoveDescendantToEnd moveDescendantToEnd)
155 {
156 // Protect against double insert where a descendant would end up with multiple containing blocks.
157 auto* previousContainingBlock = m_containerMap.get(&positionedDescendant);
158 if (previousContainingBlock && previousContainingBlock != &containingBlock) {
159 if (auto* descendants = m_descendantsMap.get(previousContainingBlock))
160 descendants->remove(&positionedDescendant);
161 }
162
163 auto& descendants = m_descendantsMap.ensure(&containingBlock, [] {
164 return std::make_unique<TrackedRendererListHashSet>();
165 }).iterator->value;
166
167 bool isNewEntry = moveDescendantToEnd == MoveDescendantToEnd::Yes ? descendants->appendOrMoveToLast(&positionedDescendant).isNewEntry
168 : descendants->add(&positionedDescendant).isNewEntry;
169 if (!isNewEntry) {
170 ASSERT(m_containerMap.contains(&positionedDescendant));
171 return;
172 }
173 m_containerMap.set(&positionedDescendant, &containingBlock);
174 }
175
176 void removeDescendant(const RenderBox& positionedDescendant)
177 {
178 auto* containingBlock = m_containerMap.take(&positionedDescendant);
179 if (!containingBlock)
180 return;
181
182 auto descendantsIterator = m_descendantsMap.find(containingBlock);
183 ASSERT(descendantsIterator != m_descendantsMap.end());
184 if (descendantsIterator == m_descendantsMap.end())
185 return;
186
187 auto& descendants = descendantsIterator->value;
188 ASSERT(descendants->contains(const_cast<RenderBox*>(&positionedDescendant)));
189
190 descendants->remove(const_cast<RenderBox*>(&positionedDescendant));
191 if (descendants->isEmpty())
192 m_descendantsMap.remove(descendantsIterator);
193 }
194
195 void removeContainingBlock(const RenderBlock& containingBlock)
196 {
197 auto descendants = m_descendantsMap.take(&containingBlock);
198 if (!descendants)
199 return;
200
201 for (auto* renderer : *descendants)
202 m_containerMap.remove(renderer);
203 }
204
205 TrackedRendererListHashSet* positionedRenderers(const RenderBlock& containingBlock) const
206 {
207 return m_descendantsMap.get(&containingBlock);
208 }
209
210private:
211 using DescendantsMap = HashMap<const RenderBlock*, std::unique_ptr<TrackedRendererListHashSet>>;
212 using ContainerMap = HashMap<const RenderBox*, const RenderBlock*>;
213
214 DescendantsMap m_descendantsMap;
215 ContainerMap m_containerMap;
216};
217
218static PositionedDescendantsMap& positionedDescendantsMap()
219{
220 static NeverDestroyed<PositionedDescendantsMap> mapForPositionedDescendants;
221 return mapForPositionedDescendants;
222}
223
224typedef HashMap<RenderBlock*, std::unique_ptr<ListHashSet<RenderInline*>>> ContinuationOutlineTableMap;
225
226struct UpdateScrollInfoAfterLayoutTransaction {
227 UpdateScrollInfoAfterLayoutTransaction(const RenderView& view)
228 : nestedCount(0)
229 , view(&view)
230 {
231 }
232
233 int nestedCount;
234 const RenderView* view;
235 HashSet<RenderBlock*> blocks;
236};
237
238typedef Vector<UpdateScrollInfoAfterLayoutTransaction> DelayedUpdateScrollInfoStack;
239static std::unique_ptr<DelayedUpdateScrollInfoStack>& updateScrollInfoAfterLayoutTransactionStack()
240{
241 static NeverDestroyed<std::unique_ptr<DelayedUpdateScrollInfoStack>> delayedUpdatedScrollInfoStack;
242 return delayedUpdatedScrollInfoStack;
243}
244
245// Allocated only when some of these fields have non-default values
246
247struct RenderBlockRareData {
248 WTF_MAKE_NONCOPYABLE(RenderBlockRareData); WTF_MAKE_FAST_ALLOCATED;
249public:
250 RenderBlockRareData()
251 {
252 }
253
254 LayoutUnit m_paginationStrut;
255 LayoutUnit m_pageLogicalOffset;
256 LayoutUnit m_intrinsicBorderForFieldset;
257
258 Optional<WeakPtr<RenderFragmentedFlow>> m_enclosingFragmentedFlow;
259};
260
261typedef HashMap<const RenderBlock*, std::unique_ptr<RenderBlockRareData>> RenderBlockRareDataMap;
262static RenderBlockRareDataMap* gRareDataMap;
263
264// This class helps dispatching the 'overflow' event on layout change. overflow can be set on RenderBoxes, yet the existing code
265// only works on RenderBlocks. If this change, this class should be shared with other RenderBoxes.
266class OverflowEventDispatcher {
267 WTF_MAKE_NONCOPYABLE(OverflowEventDispatcher);
268public:
269 OverflowEventDispatcher(const RenderBlock* block)
270 : m_block(block)
271 , m_hadHorizontalLayoutOverflow(false)
272 , m_hadVerticalLayoutOverflow(false)
273 {
274 m_shouldDispatchEvent = !m_block->isAnonymous() && m_block->hasOverflowClip() && m_block->document().hasListenerType(Document::OVERFLOWCHANGED_LISTENER);
275 if (m_shouldDispatchEvent) {
276 m_hadHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow();
277 m_hadVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow();
278 }
279 }
280
281 ~OverflowEventDispatcher()
282 {
283 if (!m_shouldDispatchEvent)
284 return;
285
286 bool hasHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow();
287 bool hasVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow();
288
289 bool horizontalLayoutOverflowChanged = hasHorizontalLayoutOverflow != m_hadHorizontalLayoutOverflow;
290 bool verticalLayoutOverflowChanged = hasVerticalLayoutOverflow != m_hadVerticalLayoutOverflow;
291 if (!horizontalLayoutOverflowChanged && !verticalLayoutOverflowChanged)
292 return;
293
294 Ref<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow);
295 overflowEvent->setTarget(m_block->element());
296 m_block->document().enqueueOverflowEvent(WTFMove(overflowEvent));
297 }
298
299private:
300 const RenderBlock* m_block;
301 bool m_shouldDispatchEvent;
302 bool m_hadHorizontalLayoutOverflow;
303 bool m_hadVerticalLayoutOverflow;
304};
305
306RenderBlock::RenderBlock(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
307 : RenderBox(element, WTFMove(style), baseTypeFlags | RenderBlockFlag)
308{
309}
310
311RenderBlock::RenderBlock(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
312 : RenderBox(document, WTFMove(style), baseTypeFlags | RenderBlockFlag)
313{
314}
315
316static void removeBlockFromPercentageDescendantAndContainerMaps(RenderBlock* block)
317{
318 if (!percentHeightDescendantsMap)
319 return;
320 std::unique_ptr<TrackedRendererListHashSet> descendantSet = percentHeightDescendantsMap->take(block);
321 if (!descendantSet)
322 return;
323
324 for (auto* descendant : *descendantSet) {
325 auto it = percentHeightContainerMap->find(descendant);
326 ASSERT(it != percentHeightContainerMap->end());
327 if (it == percentHeightContainerMap->end())
328 continue;
329 auto* containerSet = it->value.get();
330 ASSERT(containerSet->contains(block));
331 containerSet->remove(block);
332 if (containerSet->isEmpty())
333 percentHeightContainerMap->remove(it);
334 }
335}
336
337RenderBlock::~RenderBlock()
338{
339 // Blocks can be added to gRareDataMap during willBeDestroyed(), so this code can't move there.
340 if (gRareDataMap)
341 gRareDataMap->remove(this);
342
343 // Do not add any more code here. Add it to willBeDestroyed() instead.
344}
345
346// Note that this is not called for RenderBlockFlows.
347void RenderBlock::willBeDestroyed()
348{
349 if (!renderTreeBeingDestroyed()) {
350 if (parent())
351 parent()->dirtyLinesFromChangedChild(*this);
352 }
353
354 blockWillBeDestroyed();
355
356 RenderBox::willBeDestroyed();
357}
358
359void RenderBlock::blockWillBeDestroyed()
360{
361 removeFromUpdateScrollInfoAfterLayoutTransaction();
362
363 removeBlockFromPercentageDescendantAndContainerMaps(this);
364 positionedDescendantsMap().removeContainingBlock(*this);
365}
366
367bool RenderBlock::hasRareData() const
368{
369 return gRareDataMap ? gRareDataMap->contains(this) : false;
370}
371
372void RenderBlock::removePositionedObjectsIfNeeded(const RenderStyle& oldStyle, const RenderStyle& newStyle)
373{
374 bool hadTransform = oldStyle.hasTransformRelatedProperty();
375 bool willHaveTransform = newStyle.hasTransformRelatedProperty();
376 if (oldStyle.position() == newStyle.position() && hadTransform == willHaveTransform)
377 return;
378
379 // We are no longer the containing block for fixed descendants.
380 if (hadTransform && !willHaveTransform) {
381 // Our positioned descendants will be inserted into a new containing block's positioned objects list during the next layout.
382 removePositionedObjects(nullptr, NewContainingBlock);
383 return;
384 }
385
386 // We are no longer the containing block for absolute positioned descendants.
387 if (newStyle.position() == PositionType::Static && !willHaveTransform) {
388 // Our positioned descendants will be inserted into a new containing block's positioned objects list during the next layout.
389 removePositionedObjects(nullptr, NewContainingBlock);
390 return;
391 }
392
393 // We are a new containing block.
394 if (oldStyle.position() == PositionType::Static && !hadTransform) {
395 // Remove our absolutely positioned descendants from their current containing block.
396 // They will be inserted into our positioned objects list during layout.
397 auto* containingBlock = parent();
398 while (containingBlock && !is<RenderView>(*containingBlock)
399 && (containingBlock->style().position() == PositionType::Static || (containingBlock->isInline() && !containingBlock->isReplaced()))) {
400 if (containingBlock->style().position() == PositionType::Relative && containingBlock->isInline() && !containingBlock->isReplaced()) {
401 containingBlock = containingBlock->containingBlock();
402 break;
403 }
404 containingBlock = containingBlock->parent();
405 }
406 if (containingBlock && is<RenderBlock>(*containingBlock))
407 downcast<RenderBlock>(*containingBlock).removePositionedObjects(this, NewContainingBlock);
408 }
409}
410
411void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
412{
413 const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr;
414 setReplaced(newStyle.isDisplayInlineType());
415 if (oldStyle)
416 removePositionedObjectsIfNeeded(*oldStyle, newStyle);
417 RenderBox::styleWillChange(diff, newStyle);
418}
419
420static bool borderOrPaddingLogicalWidthChanged(const RenderStyle& oldStyle, const RenderStyle& newStyle)
421{
422 if (newStyle.isHorizontalWritingMode()) {
423 return oldStyle.borderLeftWidth() != newStyle.borderLeftWidth()
424 || oldStyle.borderRightWidth() != newStyle.borderRightWidth()
425 || oldStyle.paddingLeft() != newStyle.paddingLeft()
426 || oldStyle.paddingRight() != newStyle.paddingRight();
427 }
428
429 return oldStyle.borderTopWidth() != newStyle.borderTopWidth()
430 || oldStyle.borderBottomWidth() != newStyle.borderBottomWidth()
431 || oldStyle.paddingTop() != newStyle.paddingTop()
432 || oldStyle.paddingBottom() != newStyle.paddingBottom();
433}
434
435void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
436{
437 bool hadTransform = hasTransform();
438 RenderBox::styleDidChange(diff, oldStyle);
439
440 if (hadTransform != hasTransform())
441 adjustFragmentedFlowStateOnContainingBlockChangeIfNeeded();
442
443 propagateStyleToAnonymousChildren(PropagateToBlockChildrenOnly);
444
445 // It's possible for our border/padding to change, but for the overall logical width of the block to
446 // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true.
447 setShouldForceRelayoutChildren(oldStyle && diff == StyleDifference::Layout && needsLayout() && borderOrPaddingLogicalWidthChanged(*oldStyle, style()));
448}
449
450RenderPtr<RenderBlock> RenderBlock::clone() const
451{
452 RenderPtr<RenderBlock> cloneBlock;
453 if (isAnonymousBlock()) {
454 cloneBlock = RenderPtr<RenderBlock>(createAnonymousBlock());
455 cloneBlock->setChildrenInline(childrenInline());
456 } else {
457 RenderTreePosition insertionPosition(*parent());
458 cloneBlock = static_pointer_cast<RenderBlock>(element()->createElementRenderer(RenderStyle::clone(style()), insertionPosition));
459 cloneBlock->initializeStyle();
460
461 // This takes care of setting the right value of childrenInline in case
462 // generated content is added to cloneBlock and 'this' does not have
463 // generated content added yet.
464 cloneBlock->setChildrenInline(cloneBlock->firstChild() ? cloneBlock->firstChild()->isInline() : childrenInline());
465 }
466 cloneBlock->setFragmentedFlowState(fragmentedFlowState());
467 return cloneBlock;
468}
469
470void RenderBlock::deleteLines()
471{
472 if (AXObjectCache* cache = document().existingAXObjectCache())
473 cache->deferRecomputeIsIgnored(element());
474}
475
476bool RenderBlock::childrenPreventSelfCollapsing() const
477{
478 // Whether or not we collapse is dependent on whether all our normal flow children
479 // are also self-collapsing.
480 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
481 if (child->isFloatingOrOutOfFlowPositioned())
482 continue;
483 if (!child->isSelfCollapsingBlock())
484 return true;
485 }
486 return false;
487}
488
489bool RenderBlock::isSelfCollapsingBlock() const
490{
491 // We are not self-collapsing if we
492 // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
493 // (b) are a table,
494 // (c) have border/padding,
495 // (d) have a min-height
496 // (e) have specified that one of our margins can't collapse using a CSS extension
497 if (logicalHeight() > 0
498 || isTable() || borderAndPaddingLogicalHeight()
499 || style().logicalMinHeight().isPositive()
500 || style().marginBeforeCollapse() == MarginCollapse::Separate || style().marginAfterCollapse() == MarginCollapse::Separate)
501 return false;
502
503 Length logicalHeightLength = style().logicalHeight();
504 bool hasAutoHeight = logicalHeightLength.isAuto();
505 if (logicalHeightLength.isPercentOrCalculated() && !document().inQuirksMode()) {
506 hasAutoHeight = true;
507 for (RenderBlock* cb = containingBlock(); cb && !is<RenderView>(*cb); cb = cb->containingBlock()) {
508 if (cb->style().logicalHeight().isFixed() || cb->isTableCell())
509 hasAutoHeight = false;
510 }
511 }
512
513 // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
514 // on whether we have content that is all self-collapsing or not.
515 if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercentOrCalculated()) && logicalHeightLength.isZero()))
516 return !childrenPreventSelfCollapsing();
517
518 return false;
519}
520
521static inline UpdateScrollInfoAfterLayoutTransaction* currentUpdateScrollInfoAfterLayoutTransaction()
522{
523 if (!updateScrollInfoAfterLayoutTransactionStack())
524 return nullptr;
525 return &updateScrollInfoAfterLayoutTransactionStack()->last();
526}
527
528void RenderBlock::beginUpdateScrollInfoAfterLayoutTransaction()
529{
530 if (!updateScrollInfoAfterLayoutTransactionStack())
531 updateScrollInfoAfterLayoutTransactionStack() = std::make_unique<DelayedUpdateScrollInfoStack>();
532 if (updateScrollInfoAfterLayoutTransactionStack()->isEmpty() || currentUpdateScrollInfoAfterLayoutTransaction()->view != &view())
533 updateScrollInfoAfterLayoutTransactionStack()->append(UpdateScrollInfoAfterLayoutTransaction(view()));
534 ++currentUpdateScrollInfoAfterLayoutTransaction()->nestedCount;
535}
536
537void RenderBlock::endAndCommitUpdateScrollInfoAfterLayoutTransaction()
538{
539 UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction();
540 ASSERT(transaction);
541 ASSERT(transaction->view == &view());
542 if (--transaction->nestedCount)
543 return;
544
545 // Calling RenderLayer::updateScrollInfoAfterLayout() may cause its associated block to layout again and
546 // updates its scroll info (i.e. call RenderBlock::updateScrollInfoAfterLayout()). We remove |transaction|
547 // from the transaction stack to ensure that all subsequent calls to RenderBlock::updateScrollInfoAfterLayout()
548 // are dispatched immediately. That is, to ensure that such subsequent calls aren't added to |transaction|
549 // while we are processing it.
550 auto blocksToUpdate = copyToVector(transaction->blocks);
551 updateScrollInfoAfterLayoutTransactionStack()->removeLast();
552 if (updateScrollInfoAfterLayoutTransactionStack()->isEmpty())
553 updateScrollInfoAfterLayoutTransactionStack() = nullptr;
554
555 for (auto* block : blocksToUpdate) {
556 ASSERT(block->hasOverflowClip());
557 block->layer()->updateScrollInfoAfterLayout();
558 block->clearLayoutOverflow();
559 }
560}
561
562void RenderBlock::removeFromUpdateScrollInfoAfterLayoutTransaction()
563{
564 if (UNLIKELY(updateScrollInfoAfterLayoutTransactionStack().get() != 0)) {
565 UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction();
566 ASSERT(transaction);
567 if (transaction->view == &view())
568 transaction->blocks.remove(this);
569 }
570}
571
572void RenderBlock::updateScrollInfoAfterLayout()
573{
574 if (!hasOverflowClip())
575 return;
576
577 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937
578 // Workaround for now. We cannot delay the scroll info for overflow
579 // for items with opposite writing directions, as the contents needs
580 // to overflow in that direction
581 if (!style().isFlippedBlocksWritingMode()) {
582 UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction();
583 if (transaction && transaction->view == &view()) {
584 transaction->blocks.add(this);
585 return;
586 }
587 }
588 if (layer())
589 layer()->updateScrollInfoAfterLayout();
590}
591
592void RenderBlock::layout()
593{
594 StackStats::LayoutCheckPoint layoutCheckPoint;
595 OverflowEventDispatcher dispatcher(this);
596
597 // Table cells call layoutBlock directly, so don't add any logic here. Put code into
598 // layoutBlock().
599 layoutBlock(false);
600
601 // It's safe to check for control clip here, since controls can never be table cells.
602 // If we have a lightweight clip, there can never be any overflow from children.
603 UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction();
604 bool isDelayingUpdateScrollInfoAfterLayoutInView = transaction && transaction->view == &view();
605 if (hasControlClip() && m_overflow && !isDelayingUpdateScrollInfoAfterLayoutInView)
606 clearLayoutOverflow();
607
608 invalidateBackgroundObscurationStatus();
609}
610
611static RenderBlockRareData* getBlockRareData(const RenderBlock& block)
612{
613 return gRareDataMap ? gRareDataMap->get(&block) : nullptr;
614}
615
616static RenderBlockRareData& ensureBlockRareData(const RenderBlock& block)
617{
618 if (!gRareDataMap)
619 gRareDataMap = new RenderBlockRareDataMap;
620
621 auto& rareData = gRareDataMap->add(&block, nullptr).iterator->value;
622 if (!rareData)
623 rareData = std::make_unique<RenderBlockRareData>();
624 return *rareData.get();
625}
626
627void RenderBlock::preparePaginationBeforeBlockLayout(bool& relayoutChildren)
628{
629 // Fragments changing widths can force us to relayout our children.
630 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
631 if (fragmentedFlow)
632 fragmentedFlow->logicalWidthChangedInFragmentsForBlock(this, relayoutChildren);
633}
634
635bool RenderBlock::recomputeLogicalWidth()
636{
637 LayoutUnit oldWidth = logicalWidth();
638
639 updateLogicalWidth();
640
641 bool hasBorderOrPaddingLogicalWidthChanged = this->hasBorderOrPaddingLogicalWidthChanged();
642 setShouldForceRelayoutChildren(false);
643
644 return oldWidth != logicalWidth() || hasBorderOrPaddingLogicalWidthChanged;
645}
646
647void RenderBlock::layoutBlock(bool, LayoutUnit)
648{
649 ASSERT_NOT_REACHED();
650 clearNeedsLayout();
651}
652
653void RenderBlock::addOverflowFromChildren()
654{
655 if (childrenInline())
656 addOverflowFromInlineChildren();
657 else
658 addOverflowFromBlockChildren();
659
660 // If this block is flowed inside a flow thread, make sure its overflow is propagated to the containing fragments.
661 if (m_overflow) {
662 if (RenderFragmentedFlow* containingFragmentedFlow = enclosingFragmentedFlow())
663 containingFragmentedFlow->addFragmentsVisualOverflow(this, m_overflow->visualOverflowRect());
664 }
665}
666
667// Overflow is always relative to the border-box of the element in question.
668// Therefore, if the element has a vertical scrollbar placed on the left, an overflow rect at x=2px would conceptually intersect the scrollbar.
669void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool)
670{
671 clearOverflow();
672 addOverflowFromChildren();
673
674 addOverflowFromPositionedObjects();
675
676 if (hasOverflowClip()) {
677 // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins
678 // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always
679 // be considered reachable.
680 LayoutRect clientRect(flippedClientBoxRect());
681 LayoutRect rectToApply;
682 if (isHorizontalWritingMode())
683 rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1_lu, std::max(0_lu, oldClientAfterEdge - clientRect.y()));
684 else
685 rectToApply = LayoutRect(clientRect.x(), clientRect.y(), std::max(0_lu, oldClientAfterEdge - clientRect.x()), 1_lu);
686 addLayoutOverflow(rectToApply);
687 if (hasRenderOverflow())
688 m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge);
689 }
690
691 // Add visual overflow from box-shadow, border-image-outset and outline.
692 addVisualEffectOverflow();
693
694 // Add visual overflow from theme.
695 addVisualOverflowFromTheme();
696}
697
698void RenderBlock::clearLayoutOverflow()
699{
700 if (!m_overflow)
701 return;
702
703 if (visualOverflowRect() == borderBoxRect()) {
704 // FIXME: Implement complete solution for fragments overflow.
705 clearOverflow();
706 return;
707 }
708
709 m_overflow->setLayoutOverflow(borderBoxRect());
710}
711
712void RenderBlock::addOverflowFromBlockChildren()
713{
714 for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) {
715 if (!child->isFloatingOrOutOfFlowPositioned())
716 addOverflowFromChild(child);
717 }
718}
719
720void RenderBlock::addOverflowFromPositionedObjects()
721{
722 TrackedRendererListHashSet* positionedDescendants = positionedObjects();
723 if (!positionedDescendants)
724 return;
725
726 for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) {
727 RenderBox* positionedObject = *it;
728
729 // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content.
730 if (positionedObject->style().position() != PositionType::Fixed)
731 addOverflowFromChild(positionedObject, { positionedObject->x(), positionedObject->y() });
732 }
733}
734
735void RenderBlock::addVisualOverflowFromTheme()
736{
737 if (!style().hasAppearance())
738 return;
739
740 FloatRect inflatedRect = borderBoxRect();
741 theme().adjustRepaintRect(*this, inflatedRect);
742 addVisualOverflow(snappedIntRect(LayoutRect(inflatedRect)));
743
744 if (RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow())
745 fragmentedFlow->addFragmentsVisualOverflowFromTheme(this);
746}
747
748LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox& child, LayoutUnit childMarginStart, RenderFragmentContainer* fragment)
749{
750 LayoutUnit startPosition = startOffsetForContent(fragment);
751
752 // Add in our start margin.
753 LayoutUnit oldPosition = startPosition + childMarginStart;
754 LayoutUnit newPosition = oldPosition;
755
756 LayoutUnit blockOffset = logicalTopForChild(child);
757 if (fragment)
758 blockOffset = std::max(blockOffset, blockOffset + (fragment->logicalTopForFragmentedFlowContent() - offsetFromLogicalTopOfFirstPage()));
759
760 LayoutUnit startOff = startOffsetForLineInFragment(blockOffset, DoNotIndentText, fragment, logicalHeightForChild(child));
761
762 if (style().textAlign() != TextAlignMode::WebKitCenter && !child.style().marginStartUsing(&style()).isAuto()) {
763 if (childMarginStart < 0)
764 startOff += childMarginStart;
765 newPosition = std::max(newPosition, startOff); // Let the float sit in the child's margin if it can fit.
766 } else if (startOff != startPosition)
767 newPosition = startOff + childMarginStart;
768
769 return newPosition - oldPosition;
770}
771
772void RenderBlock::setLogicalLeftForChild(RenderBox& child, LayoutUnit logicalLeft, ApplyLayoutDeltaMode applyDelta)
773{
774 if (isHorizontalWritingMode()) {
775 if (applyDelta == ApplyLayoutDelta)
776 view().frameView().layoutContext().addLayoutDelta(LayoutSize(child.x() - logicalLeft, 0_lu));
777 child.setX(logicalLeft);
778 } else {
779 if (applyDelta == ApplyLayoutDelta)
780 view().frameView().layoutContext().addLayoutDelta(LayoutSize(0_lu, child.y() - logicalLeft));
781 child.setY(logicalLeft);
782 }
783}
784
785void RenderBlock::setLogicalTopForChild(RenderBox& child, LayoutUnit logicalTop, ApplyLayoutDeltaMode applyDelta)
786{
787 if (isHorizontalWritingMode()) {
788 if (applyDelta == ApplyLayoutDelta)
789 view().frameView().layoutContext().addLayoutDelta(LayoutSize(0_lu, child.y() - logicalTop));
790 child.setY(logicalTop);
791 } else {
792 if (applyDelta == ApplyLayoutDelta)
793 view().frameView().layoutContext().addLayoutDelta(LayoutSize(child.x() - logicalTop, 0_lu));
794 child.setX(logicalTop);
795 }
796}
797
798void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox& child)
799{
800 if (child.isOutOfFlowPositioned())
801 return;
802
803 // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into
804 // an auto value. Add a method to determine this, so that we can avoid the relayout.
805 if (relayoutChildren || (child.hasRelativeLogicalHeight() && !isRenderView()))
806 child.setChildNeedsLayout(MarkOnlyThis);
807
808 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
809 if (relayoutChildren && child.needsPreferredWidthsRecalculation())
810 child.setPreferredLogicalWidthsDirty(true, MarkOnlyThis);
811}
812
813void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants()
814{
815 if (!percentHeightDescendantsMap)
816 return;
817
818 TrackedRendererListHashSet* descendants = percentHeightDescendantsMap->get(this);
819 if (!descendants)
820 return;
821
822 for (auto it = descendants->begin(), end = descendants->end(); it != end; ++it) {
823 auto* box = *it;
824 while (box != this) {
825 if (box->normalChildNeedsLayout())
826 break;
827 box->setChildNeedsLayout(MarkOnlyThis);
828
829 // If the width of an image is affected by the height of a child (e.g., an image with an aspect ratio),
830 // then we have to dirty preferred widths, since even enclosing blocks can become dirty as a result.
831 // (A horizontal flexbox that contains an inline image wrapped in an anonymous block for example.)
832 if (box->hasAspectRatio())
833 box->setPreferredLogicalWidthsDirty(true);
834 auto* containingBlock = box->containingBlock();
835 // Mark the svg ancestor chain dirty as we walk to the containing block. containingBlock() just skips them. See webkit.org/b/183874.
836 if (is<SVGElement>(box->element()) && containingBlock != box->parent()) {
837 auto* ancestor = box->parent();
838 ASSERT(ancestor->isDescendantOf(containingBlock));
839 while (ancestor != containingBlock) {
840 ancestor->setChildNeedsLayout(MarkOnlyThis);
841 // This is the topmost SVG root, no need to go any further.
842 if (is<SVGSVGElement>(ancestor->element()) && !downcast<SVGSVGElement>(*ancestor->element()).ownerSVGElement())
843 break;
844 ancestor = ancestor->parent();
845 }
846 }
847 box = containingBlock;
848 ASSERT(box);
849 if (!box)
850 break;
851 }
852 }
853}
854
855void RenderBlock::simplifiedNormalFlowLayout()
856{
857 if (childrenInline()) {
858 ListHashSet<RootInlineBox*> lineBoxes;
859 for (InlineWalker walker(*this); !walker.atEnd(); walker.advance()) {
860 RenderObject& renderer = *walker.current();
861 if (!renderer.isOutOfFlowPositioned() && (renderer.isReplaced() || renderer.isFloating())) {
862 RenderBox& box = downcast<RenderBox>(renderer);
863 box.layoutIfNeeded();
864 if (box.inlineBoxWrapper())
865 lineBoxes.add(&box.inlineBoxWrapper()->root());
866 } else if (is<RenderText>(renderer) || (is<RenderInline>(renderer) && !walker.atEndOfInline()))
867 renderer.clearNeedsLayout();
868 }
869
870 // FIXME: Glyph overflow will get lost in this case, but not really a big deal.
871 // FIXME: Find a way to invalidate the knownToHaveNoOverflow flag on the InlineBoxes.
872 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
873 for (auto it = lineBoxes.begin(), end = lineBoxes.end(); it != end; ++it) {
874 RootInlineBox* box = *it;
875 box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap);
876 }
877 } else {
878 for (auto* box = firstChildBox(); box; box = box->nextSiblingBox()) {
879 if (!box->isOutOfFlowPositioned())
880 box->layoutIfNeeded();
881 }
882 }
883}
884
885bool RenderBlock::canPerformSimplifiedLayout() const
886{
887 return (posChildNeedsLayout() || needsSimplifiedNormalFlowLayout()) && !normalChildNeedsLayout() && !selfNeedsLayout();
888}
889
890bool RenderBlock::simplifiedLayout()
891{
892 if (!canPerformSimplifiedLayout())
893 return false;
894
895 LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
896 if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly())
897 return false;
898
899 // Lay out positioned descendants or objects that just need to recompute overflow.
900 if (needsSimplifiedNormalFlowLayout())
901 simplifiedNormalFlowLayout();
902
903 // Make sure a forced break is applied after the content if we are a flow thread in a simplified layout.
904 // This ensures the size information is correctly computed for the last auto-height fragment receiving content.
905 if (is<RenderFragmentedFlow>(*this))
906 downcast<RenderFragmentedFlow>(*this).applyBreakAfterContent(clientLogicalBottom());
907
908 // Lay out our positioned objects if our positioned child bit is set.
909 // Also, if an absolute position element inside a relative positioned container moves, and the absolute element has a fixed position
910 // child, neither the fixed element nor its container learn of the movement since posChildNeedsLayout() is only marked as far as the
911 // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them
912 // are statically positioned and thus need to move with their absolute ancestors.
913 bool canContainFixedPosObjects = canContainFixedPositionObjects();
914 if (posChildNeedsLayout() || canContainFixedPosObjects)
915 layoutPositionedObjects(false, !posChildNeedsLayout() && canContainFixedPosObjects);
916
917 // Recompute our overflow information.
918 // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only
919 // updating our overflow if we either used to have overflow or if the new temporary object has overflow.
920 // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and
921 // lowestPosition on every relayout so it's not a regression.
922 // computeOverflow expects the bottom edge before we clamp our height. Since this information isn't available during
923 // simplifiedLayout, we cache the value in m_overflow.
924 LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom();
925 computeOverflow(oldClientAfterEdge, true);
926
927 updateLayerTransform();
928
929 updateScrollInfoAfterLayout();
930
931 clearNeedsLayout();
932 return true;
933}
934
935void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderBox& positionedChild)
936{
937 if (positionedChild.style().position() != PositionType::Fixed)
938 return;
939
940 bool hasStaticBlockPosition = positionedChild.style().hasStaticBlockPosition(isHorizontalWritingMode());
941 bool hasStaticInlinePosition = positionedChild.style().hasStaticInlinePosition(isHorizontalWritingMode());
942 if (!hasStaticBlockPosition && !hasStaticInlinePosition)
943 return;
944
945 auto* parent = positionedChild.parent();
946 while (parent && !is<RenderView>(*parent) && parent->style().position() != PositionType::Absolute)
947 parent = parent->parent();
948 if (!parent || parent->style().position() != PositionType::Absolute)
949 return;
950
951 if (hasStaticInlinePosition) {
952 LogicalExtentComputedValues computedValues;
953 positionedChild.computeLogicalWidthInFragment(computedValues);
954 LayoutUnit newLeft = computedValues.m_position;
955 if (newLeft != positionedChild.logicalLeft())
956 positionedChild.setChildNeedsLayout(MarkOnlyThis);
957 } else if (hasStaticBlockPosition) {
958 LayoutUnit oldTop = positionedChild.logicalTop();
959 positionedChild.updateLogicalHeight();
960 if (positionedChild.logicalTop() != oldTop)
961 positionedChild.setChildNeedsLayout(MarkOnlyThis);
962 }
963}
964
965LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox& child) const
966{
967 // A margin has three types: fixed, percentage, and auto (variable).
968 // Auto and percentage margins become 0 when computing min/max width.
969 // Fixed margins can be added in as is.
970 Length marginLeft = child.style().marginStartUsing(&style());
971 Length marginRight = child.style().marginEndUsing(&style());
972 LayoutUnit margin;
973 if (marginLeft.isFixed())
974 margin += marginLeft.value();
975 if (marginRight.isFixed())
976 margin += marginRight.value();
977 return margin;
978}
979
980void RenderBlock::layoutPositionedObject(RenderBox& r, bool relayoutChildren, bool fixedPositionObjectsOnly)
981{
982 estimateFragmentRangeForBoxChild(r);
983
984 // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So
985 // if this is a fixed position element, mark it for layout if it has an abspos ancestor and needs to move with that ancestor, i.e.
986 // it has static position.
987 markFixedPositionObjectForLayoutIfNeeded(r);
988 if (fixedPositionObjectsOnly) {
989 r.layoutIfNeeded();
990 return;
991 }
992
993 // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the
994 // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned
995 // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is
996 // positioned explicitly) this should not incur a performance penalty.
997 if (relayoutChildren || (r.style().hasStaticBlockPosition(isHorizontalWritingMode()) && r.parent() != this))
998 r.setChildNeedsLayout(MarkOnlyThis);
999
1000 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
1001 if (relayoutChildren && r.needsPreferredWidthsRecalculation())
1002 r.setPreferredLogicalWidthsDirty(true, MarkOnlyThis);
1003
1004 r.markForPaginationRelayoutIfNeeded();
1005
1006 // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width
1007 // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout.
1008 if (r.needsPositionedMovementLayoutOnly() && r.tryLayoutDoingPositionedMovementOnly())
1009 r.clearNeedsLayout();
1010
1011 // If we are paginated or in a line grid, compute a vertical position for our object now.
1012 // If it's wrong we'll lay out again.
1013 LayoutUnit oldLogicalTop;
1014 bool needsBlockDirectionLocationSetBeforeLayout = r.needsLayout() && view().frameView().layoutContext().layoutState()->needsBlockDirectionLocationSetBeforeLayout();
1015 if (needsBlockDirectionLocationSetBeforeLayout) {
1016 if (isHorizontalWritingMode() == r.isHorizontalWritingMode())
1017 r.updateLogicalHeight();
1018 else
1019 r.updateLogicalWidth();
1020 oldLogicalTop = logicalTopForChild(r);
1021 }
1022
1023 r.layoutIfNeeded();
1024
1025 auto* parent = r.parent();
1026 bool layoutChanged = false;
1027 if (parent->isFlexibleBox() && downcast<RenderFlexibleBox>(parent)->setStaticPositionForPositionedLayout(r)) {
1028 // The static position of an abspos child of a flexbox depends on its size
1029 // (for example, they can be centered). So we may have to reposition the
1030 // item after layout.
1031 // FIXME: We could probably avoid a layout here and just reposition?
1032 layoutChanged = true;
1033 }
1034
1035 // Lay out again if our estimate was wrong.
1036 if (layoutChanged || (needsBlockDirectionLocationSetBeforeLayout && logicalTopForChild(r) != oldLogicalTop)) {
1037 r.setChildNeedsLayout(MarkOnlyThis);
1038 r.layoutIfNeeded();
1039 }
1040
1041 if (updateFragmentRangeForBoxChild(r)) {
1042 r.setNeedsLayout(MarkOnlyThis);
1043 r.layoutIfNeeded();
1044 }
1045}
1046
1047void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly)
1048{
1049 TrackedRendererListHashSet* positionedDescendants = positionedObjects();
1050 if (!positionedDescendants)
1051 return;
1052
1053 // Do not cache positionedDescendants->end() in a local variable, since |positionedDescendants| can be mutated
1054 // as it is walked. We always need to fetch the new end() value dynamically.
1055 for (auto it = positionedDescendants->begin(); it != positionedDescendants->end(); ++it)
1056 layoutPositionedObject(**it, relayoutChildren, fixedPositionObjectsOnly);
1057}
1058
1059void RenderBlock::markPositionedObjectsForLayout()
1060{
1061 TrackedRendererListHashSet* positionedDescendants = positionedObjects();
1062 if (!positionedDescendants)
1063 return;
1064
1065 for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) {
1066 RenderBox* r = *it;
1067 r->setChildNeedsLayout();
1068 }
1069}
1070
1071void RenderBlock::markForPaginationRelayoutIfNeeded()
1072{
1073 auto* layoutState = view().frameView().layoutContext().layoutState();
1074 if (needsLayout() || !layoutState->isPaginated())
1075 return;
1076
1077 if (layoutState->pageLogicalHeightChanged() || (layoutState->pageLogicalHeight() && layoutState->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset()))
1078 setChildNeedsLayout(MarkOnlyThis);
1079}
1080
1081void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1082{
1083 LayoutPoint adjustedPaintOffset = paintOffset + location();
1084 PaintPhase phase = paintInfo.phase;
1085
1086 // Check if we need to do anything at all.
1087 // FIXME: Could eliminate the isDocumentElementRenderer() check if we fix background painting so that the RenderView
1088 // paints the root's background.
1089 if (!isDocumentElementRenderer()) {
1090 LayoutRect overflowBox = overflowRectForPaintRejection();
1091 flipForWritingMode(overflowBox);
1092 overflowBox.moveBy(adjustedPaintOffset);
1093 if (!overflowBox.intersects(paintInfo.rect)
1094#if PLATFORM(IOS_FAMILY)
1095 // FIXME: This may be applicable to non-iOS ports.
1096 && (!hasLayer() || !layer()->isComposited())
1097#endif
1098 )
1099 return;
1100 }
1101
1102 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset);
1103 paintObject(paintInfo, adjustedPaintOffset);
1104 if (pushedClip)
1105 popContentsClip(paintInfo, phase, adjustedPaintOffset);
1106
1107 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
1108 // z-index. We paint after we painted the background/border, so that the scrollbars will
1109 // sit above the background/border.
1110 if ((phase == PaintPhase::BlockBackground || phase == PaintPhase::ChildBlockBackground) && hasOverflowClip() && layer()
1111 && style().visibility() == Visibility::Visible && paintInfo.shouldPaintWithinRoot(*this) && !paintInfo.paintRootBackgroundOnly())
1112 layer()->paintOverflowControls(paintInfo.context(), roundedIntPoint(adjustedPaintOffset), snappedIntRect(paintInfo.rect));
1113}
1114
1115void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1116{
1117 // Style is non-final if the element has a pending stylesheet before it. We end up with renderers with such styles if a script
1118 // forces renderer construction by querying something layout dependent.
1119 // Avoid FOUC by not painting. Switching to final style triggers repaint.
1120 if (style().isNotFinal())
1121 return;
1122
1123 if (childrenInline())
1124 paintInlineChildren(paintInfo, paintOffset);
1125 else {
1126 PaintPhase newPhase = (paintInfo.phase == PaintPhase::ChildOutlines) ? PaintPhase::Outline : paintInfo.phase;
1127 newPhase = (newPhase == PaintPhase::ChildBlockBackgrounds) ? PaintPhase::ChildBlockBackground : newPhase;
1128
1129 // We don't paint our own background, but we do let the kids paint their backgrounds.
1130 PaintInfo paintInfoForChild(paintInfo);
1131 paintInfoForChild.phase = newPhase;
1132 paintInfoForChild.updateSubtreePaintRootForChildren(this);
1133
1134 // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit
1135 // NSViews. Do not add any more code for this.
1136 bool usePrintRect = !view().printRect().isEmpty();
1137 paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
1138 }
1139}
1140
1141void RenderBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
1142{
1143 for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) {
1144 if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect))
1145 return;
1146 }
1147}
1148
1149bool RenderBlock::paintChild(RenderBox& child, PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect, PaintBlockType paintType)
1150{
1151 if (child.isExcludedAndPlacedInBorder())
1152 return true;
1153
1154 // Check for page-break-before: always, and if it's set, break and bail.
1155 bool checkBeforeAlways = !childrenInline() && (usePrintRect && alwaysPageBreak(child.style().breakBefore()));
1156 LayoutUnit absoluteChildY = paintOffset.y() + child.y();
1157 if (checkBeforeAlways
1158 && absoluteChildY > paintInfo.rect.y()
1159 && absoluteChildY < paintInfo.rect.maxY()) {
1160 view().setBestTruncatedAt(absoluteChildY, this, true);
1161 return false;
1162 }
1163
1164 if (!child.isFloating() && child.isReplaced() && usePrintRect && child.height() <= view().printRect().height()) {
1165 // Paginate block-level replaced elements.
1166 if (absoluteChildY + child.height() > view().printRect().maxY()) {
1167 if (absoluteChildY < view().truncatedAt())
1168 view().setBestTruncatedAt(absoluteChildY, &child);
1169 // If we were able to truncate, don't paint.
1170 if (absoluteChildY >= view().truncatedAt())
1171 return false;
1172 }
1173 }
1174
1175 LayoutPoint childPoint = flipForWritingModeForChild(&child, paintOffset);
1176 if (!child.hasSelfPaintingLayer() && !child.isFloating()) {
1177 if (paintType == PaintAsInlineBlock)
1178 child.paintAsInlineBlock(paintInfoForChild, childPoint);
1179 else
1180 child.paint(paintInfoForChild, childPoint);
1181 }
1182
1183 // Check for page-break-after: always, and if it's set, break and bail.
1184 bool checkAfterAlways = !childrenInline() && (usePrintRect && alwaysPageBreak(child.style().breakAfter()));
1185 if (checkAfterAlways
1186 && (absoluteChildY + child.height()) > paintInfo.rect.y()
1187 && (absoluteChildY + child.height()) < paintInfo.rect.maxY()) {
1188 view().setBestTruncatedAt(absoluteChildY + child.height() + std::max<LayoutUnit>(0, child.collapsedMarginAfter()), this, true);
1189 return false;
1190 }
1191
1192 return true;
1193}
1194
1195void RenderBlock::paintCaret(PaintInfo& paintInfo, const LayoutPoint& paintOffset, CaretType type)
1196{
1197 // Paint the caret if the FrameSelection says so or if caret browsing is enabled
1198 RenderBlock* caretPainter;
1199 bool isContentEditable;
1200 if (type == CursorCaret) {
1201 caretPainter = frame().selection().caretRendererWithoutUpdatingLayout();
1202 isContentEditable = frame().selection().selection().hasEditableStyle();
1203 } else {
1204 caretPainter = page().dragCaretController().caretRenderer();
1205 isContentEditable = page().dragCaretController().isContentEditable();
1206 }
1207
1208 if (caretPainter == this && (isContentEditable || settings().caretBrowsingEnabled())) {
1209 if (type == CursorCaret)
1210 frame().selection().paintCaret(paintInfo.context(), paintOffset, paintInfo.rect);
1211 else
1212 page().dragCaretController().paintDragCaret(&frame(), paintInfo.context(), paintOffset, paintInfo.rect);
1213 }
1214}
1215
1216void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1217{
1218 PaintPhase paintPhase = paintInfo.phase;
1219
1220 // 1. paint background, borders etc
1221 if ((paintPhase == PaintPhase::BlockBackground || paintPhase == PaintPhase::ChildBlockBackground) && style().visibility() == Visibility::Visible) {
1222 if (hasVisibleBoxDecorations())
1223 paintBoxDecorations(paintInfo, paintOffset);
1224 }
1225
1226 // Paint legends just above the border before we scroll or clip.
1227 if (paintPhase == PaintPhase::BlockBackground || paintPhase == PaintPhase::ChildBlockBackground || paintPhase == PaintPhase::Selection)
1228 paintExcludedChildrenInBorder(paintInfo, paintOffset);
1229
1230 if (paintPhase == PaintPhase::Mask && style().visibility() == Visibility::Visible) {
1231 paintMask(paintInfo, paintOffset);
1232 return;
1233 }
1234
1235 if (paintPhase == PaintPhase::ClippingMask && style().visibility() == Visibility::Visible) {
1236 paintClippingMask(paintInfo, paintOffset);
1237 return;
1238 }
1239
1240 // If just painting the root background, then return.
1241 if (paintInfo.paintRootBackgroundOnly())
1242 return;
1243
1244 if (paintPhase == PaintPhase::EventRegion) {
1245 auto borderRect = LayoutRect(paintOffset, size());
1246
1247 if (visibleToHitTesting()) {
1248 auto borderRegion = approximateAsRegion(style().getRoundedBorderFor(borderRect));
1249 paintInfo.eventRegionContext->unite(borderRegion, style());
1250 }
1251
1252 // No need to check descendants if we don't have overflow and the area is already covered.
1253 bool needsTraverseDescendants = hasVisualOverflow() || !paintInfo.eventRegionContext->contains(enclosingIntRect(borderRect));
1254#if PLATFORM(IOS_FAMILY) && ENABLE(POINTER_EVENTS)
1255 needsTraverseDescendants = needsTraverseDescendants || document().touchActionElements();
1256#endif
1257 if (!needsTraverseDescendants)
1258 return;
1259 }
1260
1261 // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div).
1262 LayoutPoint scrolledOffset = paintOffset;
1263 scrolledOffset.moveBy(-scrollPosition());
1264
1265 // Column rules need to account for scrolling and clipping.
1266 // FIXME: Clipping of column rules does not work. We will need a separate paint phase for column rules I suspect in order to get
1267 // clipping correct (since it has to paint as background but is still considered "contents").
1268 if ((paintPhase == PaintPhase::BlockBackground || paintPhase == PaintPhase::ChildBlockBackground) && style().visibility() == Visibility::Visible)
1269 paintColumnRules(paintInfo, scrolledOffset);
1270
1271 // Done with backgrounds, borders and column rules.
1272 if (paintPhase == PaintPhase::BlockBackground)
1273 return;
1274
1275 // 2. paint contents
1276 if (paintPhase != PaintPhase::SelfOutline)
1277 paintContents(paintInfo, scrolledOffset);
1278
1279 // 3. paint selection
1280 // FIXME: Make this work with multi column layouts. For now don't fill gaps.
1281 bool isPrinting = document().printing();
1282 if (!isPrinting)
1283 paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks.
1284
1285 // 4. paint floats.
1286 if (paintPhase == PaintPhase::Float || paintPhase == PaintPhase::Selection || paintPhase == PaintPhase::TextClip)
1287 paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhase::Selection || paintPhase == PaintPhase::TextClip);
1288
1289 // 5. paint outline.
1290 if ((paintPhase == PaintPhase::Outline || paintPhase == PaintPhase::SelfOutline) && hasOutline() && style().visibility() == Visibility::Visible)
1291 paintOutline(paintInfo, LayoutRect(paintOffset, size()));
1292
1293 // 6. paint continuation outlines.
1294 if ((paintPhase == PaintPhase::Outline || paintPhase == PaintPhase::ChildOutlines)) {
1295 RenderInline* inlineCont = inlineContinuation();
1296 if (inlineCont && inlineCont->hasOutline() && inlineCont->style().visibility() == Visibility::Visible) {
1297 RenderInline* inlineRenderer = downcast<RenderInline>(inlineCont->element()->renderer());
1298 RenderBlock* containingBlock = this->containingBlock();
1299
1300 bool inlineEnclosedInSelfPaintingLayer = false;
1301 for (RenderBoxModelObject* box = inlineRenderer; box != containingBlock; box = &box->parent()->enclosingBoxModelObject()) {
1302 if (box->hasSelfPaintingLayer()) {
1303 inlineEnclosedInSelfPaintingLayer = true;
1304 break;
1305 }
1306 }
1307
1308 // Do not add continuations for outline painting by our containing block if we are a relative positioned
1309 // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being
1310 // in the same layer.
1311 if (!inlineEnclosedInSelfPaintingLayer && !hasLayer())
1312 containingBlock->addContinuationWithOutline(inlineRenderer);
1313 else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && hasLayer()))
1314 inlineRenderer->paintOutline(paintInfo, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location());
1315 }
1316 paintContinuationOutlines(paintInfo, paintOffset);
1317 }
1318
1319 // 7. paint caret.
1320 // If the caret's node's render object's containing block is this block, and the paint action is PaintPhase::Foreground,
1321 // then paint the caret.
1322 if (paintPhase == PaintPhase::Foreground) {
1323 paintCaret(paintInfo, paintOffset, CursorCaret);
1324 paintCaret(paintInfo, paintOffset, DragCaret);
1325 }
1326}
1327
1328static ContinuationOutlineTableMap* continuationOutlineTable()
1329{
1330 static NeverDestroyed<ContinuationOutlineTableMap> table;
1331 return &table.get();
1332}
1333
1334void RenderBlock::addContinuationWithOutline(RenderInline* flow)
1335{
1336 // We can't make this work if the inline is in a layer. We'll just rely on the broken
1337 // way of painting.
1338 ASSERT(!flow->layer() && !flow->isContinuation());
1339
1340 ContinuationOutlineTableMap* table = continuationOutlineTable();
1341 ListHashSet<RenderInline*>* continuations = table->get(this);
1342 if (!continuations) {
1343 continuations = new ListHashSet<RenderInline*>;
1344 table->set(this, std::unique_ptr<ListHashSet<RenderInline*>>(continuations));
1345 }
1346
1347 continuations->add(flow);
1348}
1349
1350bool RenderBlock::paintsContinuationOutline(RenderInline* flow)
1351{
1352 ContinuationOutlineTableMap* table = continuationOutlineTable();
1353 if (table->isEmpty())
1354 return false;
1355
1356 ListHashSet<RenderInline*>* continuations = table->get(this);
1357 if (!continuations)
1358 return false;
1359
1360 return continuations->contains(flow);
1361}
1362
1363void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& paintOffset)
1364{
1365 ContinuationOutlineTableMap* table = continuationOutlineTable();
1366 if (table->isEmpty())
1367 return;
1368
1369 std::unique_ptr<ListHashSet<RenderInline*>> continuations = table->take(this);
1370 if (!continuations)
1371 return;
1372
1373 LayoutPoint accumulatedPaintOffset = paintOffset;
1374 // Paint each continuation outline.
1375 ListHashSet<RenderInline*>::iterator end = continuations->end();
1376 for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) {
1377 // Need to add in the coordinates of the intervening blocks.
1378 RenderInline* flow = *it;
1379 RenderBlock* block = flow->containingBlock();
1380 for ( ; block && block != this; block = block->containingBlock())
1381 accumulatedPaintOffset.moveBy(block->location());
1382 ASSERT(block);
1383 flow->paintOutline(info, accumulatedPaintOffset);
1384 }
1385}
1386
1387bool RenderBlock::shouldPaintSelectionGaps() const
1388{
1389 if (settings().selectionPaintingWithoutSelectionGapsEnabled())
1390 return false;
1391
1392 return selectionState() != SelectionNone && style().visibility() == Visibility::Visible && isSelectionRoot();
1393}
1394
1395bool RenderBlock::isSelectionRoot() const
1396{
1397 if (isPseudoElement())
1398 return false;
1399 ASSERT(element() || isAnonymous());
1400
1401 // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases.
1402 if (isTable())
1403 return false;
1404
1405 if (isBody() || isDocumentElementRenderer() || hasOverflowClip()
1406 || isPositioned() || isFloating()
1407 || isTableCell() || isInlineBlockOrInlineTable()
1408 || hasTransform() || hasReflection() || hasMask() || isWritingModeRoot()
1409 || isRenderFragmentedFlow() || style().columnSpan() == ColumnSpan::All)
1410 return true;
1411
1412 if (view().selection().start()) {
1413 Node* startElement = view().selection().start()->node();
1414 if (startElement && startElement->rootEditableElement() == element())
1415 return true;
1416 }
1417
1418 return false;
1419}
1420
1421GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* repaintContainer)
1422{
1423 ASSERT(!needsLayout());
1424
1425 if (!shouldPaintSelectionGaps())
1426 return GapRects();
1427
1428 FloatPoint containerPoint = localToContainerPoint(FloatPoint(), repaintContainer, UseTransforms);
1429 LayoutPoint offsetFromRepaintContainer(containerPoint - toFloatSize(scrollPosition()));
1430
1431 LogicalSelectionOffsetCaches cache(*this);
1432 LayoutUnit lastTop;
1433 LayoutUnit lastLeft = logicalLeftSelectionOffset(*this, lastTop, cache);
1434 LayoutUnit lastRight = logicalRightSelectionOffset(*this, lastTop, cache);
1435
1436 return selectionGaps(*this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight, cache);
1437}
1438
1439void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1440{
1441#if ENABLE(TEXT_SELECTION)
1442 if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhase::Foreground) {
1443 LogicalSelectionOffsetCaches cache(*this);
1444 LayoutUnit lastTop;
1445 LayoutUnit lastLeft = logicalLeftSelectionOffset(*this, lastTop, cache);
1446 LayoutUnit lastRight = logicalRightSelectionOffset(*this, lastTop, cache);
1447 GraphicsContextStateSaver stateSaver(paintInfo.context());
1448
1449 LayoutRect gapRectsBounds = selectionGaps(*this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, cache, &paintInfo);
1450 if (!gapRectsBounds.isEmpty()) {
1451 if (RenderLayer* layer = enclosingLayer()) {
1452 gapRectsBounds.moveBy(-paintOffset);
1453 if (!hasLayer()) {
1454 LayoutRect localBounds(gapRectsBounds);
1455 flipForWritingMode(localBounds);
1456 gapRectsBounds = localToContainerQuad(FloatRect(localBounds), &layer->renderer()).enclosingBoundingBox();
1457 if (layer->renderer().isBox())
1458 gapRectsBounds.moveBy(layer->renderBox()->scrollPosition());
1459 }
1460 layer->addBlockSelectionGapsBounds(gapRectsBounds);
1461 }
1462 }
1463 }
1464#else
1465 UNUSED_PARAM(paintInfo);
1466 UNUSED_PARAM(paintOffset);
1467#endif
1468}
1469
1470static void clipOutPositionedObjects(const PaintInfo* paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* positionedObjects)
1471{
1472 if (!positionedObjects)
1473 return;
1474
1475 TrackedRendererListHashSet::const_iterator end = positionedObjects->end();
1476 for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) {
1477 RenderBox* r = *it;
1478 paintInfo->context().clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height()));
1479 }
1480}
1481
1482LayoutUnit blockDirectionOffset(RenderBlock& rootBlock, const LayoutSize& offsetFromRootBlock)
1483{
1484 return rootBlock.isHorizontalWritingMode() ? offsetFromRootBlock.height() : offsetFromRootBlock.width();
1485}
1486
1487LayoutUnit inlineDirectionOffset(RenderBlock& rootBlock, const LayoutSize& offsetFromRootBlock)
1488{
1489 return rootBlock.isHorizontalWritingMode() ? offsetFromRootBlock.width() : offsetFromRootBlock.height();
1490}
1491
1492LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPhysicalPosition, const LayoutRect& logicalRect)
1493{
1494 LayoutRect result;
1495 if (isHorizontalWritingMode())
1496 result = logicalRect;
1497 else
1498 result = LayoutRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width());
1499 flipForWritingMode(result);
1500 result.moveBy(rootBlockPhysicalPosition);
1501 return result;
1502}
1503
1504GapRects RenderBlock::selectionGaps(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
1505 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1506{
1507 // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore.
1508 // Clip out floating and positioned objects when painting selection gaps.
1509 if (paintInfo) {
1510 // Note that we don't clip out overflow for positioned objects. We just stick to the border box.
1511 LayoutRect flippedBlockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height());
1512 rootBlock.flipForWritingMode(flippedBlockRect);
1513 flippedBlockRect.moveBy(rootBlockPhysicalPosition);
1514 clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), positionedObjects());
1515 if (isBody() || isDocumentElementRenderer()) { // The <body> must make sure to examine its containingBlock's positioned objects.
1516 for (RenderBlock* cb = containingBlock(); cb && !is<RenderView>(*cb); cb = cb->containingBlock())
1517 clipOutPositionedObjects(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects()); // FIXME: Not right for flipped writing modes.
1518 }
1519 clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock);
1520 }
1521
1522 // FIXME: overflow: auto/scroll fragments need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is
1523 // fixed).
1524 GapRects result;
1525 if (!isRenderBlockFlow()) // FIXME: Make multi-column selection gap filling work someday.
1526 return result;
1527
1528 if (hasTransform() || style().columnSpan() == ColumnSpan::All || isInFlowRenderFragmentedFlow()) {
1529 // FIXME: We should learn how to gap fill multiple columns and transforms eventually.
1530 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight();
1531 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache);
1532 lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache);
1533 return result;
1534 }
1535
1536 if (childrenInline())
1537 result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo);
1538 else
1539 result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo);
1540
1541 // Fill the vertical gap all the way to the bottom of our block if the selection extends past our block.
1542 if (&rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd) && !isRubyBase() && !isRubyText()) {
1543 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
1544 lastLogicalTop, lastLogicalLeft, lastLogicalRight, logicalHeight(), cache, paintInfo));
1545 }
1546
1547 return result;
1548}
1549
1550GapRects RenderBlock::inlineSelectionGaps(RenderBlock&, const LayoutPoint&, const LayoutSize&, LayoutUnit&, LayoutUnit&, LayoutUnit&, const LogicalSelectionOffsetCaches&, const PaintInfo*)
1551{
1552 ASSERT_NOT_REACHED();
1553 return GapRects();
1554}
1555
1556GapRects RenderBlock::blockSelectionGaps(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
1557 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1558{
1559 GapRects result;
1560
1561 // Jump right to the first block child that contains some selected objects.
1562 RenderBox* curr;
1563 for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { }
1564
1565 if (!curr)
1566 return result;
1567
1568 LogicalSelectionOffsetCaches childCache(*this, cache);
1569
1570 for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) {
1571 SelectionState childState = curr->selectionState();
1572 if (childState == SelectionBoth || childState == SelectionEnd)
1573 sawSelectionEnd = true;
1574
1575 if (curr->isFloatingOrOutOfFlowPositioned())
1576 continue; // We must be a normal flow object in order to even be considered.
1577
1578 if (curr->isInFlowPositioned() && curr->hasLayer()) {
1579 // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element.
1580 // Just disregard it completely.
1581 LayoutSize relOffset = curr->layer()->offsetForInFlowPosition();
1582 if (relOffset.width() || relOffset.height())
1583 continue;
1584 }
1585
1586 bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this.
1587 bool fillBlockGaps = (paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone)) && !isRubyBase() && !isRubyText();
1588 if (fillBlockGaps) {
1589 // We need to fill the vertical gap above this object.
1590 if (childState == SelectionEnd || childState == SelectionInside) {
1591 // Fill the gap above the object.
1592 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
1593 lastLogicalTop, lastLogicalLeft, lastLogicalRight, curr->logicalTop(), cache, paintInfo));
1594 }
1595
1596 // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past*
1597 // our object. We know this if the selection did not end inside our object.
1598 if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd))
1599 childState = SelectionNone;
1600
1601 // Fill side gaps on this object based off its state.
1602 bool leftGap, rightGap;
1603 getSelectionGapInfo(childState, leftGap, rightGap);
1604
1605 if (leftGap)
1606 result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo));
1607 if (rightGap)
1608 result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo));
1609
1610 // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as
1611 // they can without bumping into floating or positioned objects. Ideally they will go right up
1612 // to the border of the root selection block.
1613 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + curr->logicalBottom();
1614 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom(), cache);
1615 lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom(), cache);
1616 } else if (childState != SelectionNone && is<RenderBlock>(*curr)) {
1617 // We must be a block that has some selected object inside it, so recur.
1618 result.unite(downcast<RenderBlock>(*curr).selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()),
1619 lastLogicalTop, lastLogicalLeft, lastLogicalRight, childCache, paintInfo));
1620 }
1621 }
1622 return result;
1623}
1624
1625LayoutRect RenderBlock::blockSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
1626 LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1627{
1628 LayoutUnit logicalTop = lastLogicalTop;
1629 LayoutUnit logicalHeight = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalBottom - logicalTop;
1630 if (logicalHeight <= 0)
1631 return LayoutRect();
1632
1633 // Get the selection offsets for the bottom of the gap
1634 LayoutUnit logicalLeft = std::max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom, cache));
1635 LayoutUnit logicalRight = std::min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom, cache));
1636 LayoutUnit logicalWidth = logicalRight - logicalLeft;
1637 if (logicalWidth <= 0)
1638 return LayoutRect();
1639
1640 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight));
1641 if (paintInfo)
1642 paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selectionBackgroundColor());
1643 return gapRect;
1644}
1645
1646LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
1647 RenderBoxModelObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1648{
1649 LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop;
1650 LayoutUnit rootBlockLogicalLeft = std::max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache));
1651 LayoutUnit rootBlockLogicalRight = std::min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalLeft,
1652 std::min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)));
1653 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
1654 if (rootBlockLogicalWidth <= 0)
1655 return LayoutRect();
1656
1657 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
1658 if (paintInfo)
1659 paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selObj->selectionBackgroundColor());
1660 return gapRect;
1661}
1662
1663LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
1664 RenderBoxModelObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo)
1665{
1666 LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop;
1667 LayoutUnit rootBlockLogicalLeft = std::max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalRight,
1668 std::max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)));
1669 LayoutUnit rootBlockLogicalRight = std::min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache));
1670 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
1671 if (rootBlockLogicalWidth <= 0)
1672 return LayoutRect();
1673
1674 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
1675 if (paintInfo)
1676 paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selObj->selectionBackgroundColor());
1677 return gapRect;
1678}
1679
1680void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap)
1681{
1682 bool ltr = style().isLeftToRightDirection();
1683 leftGap = (state == RenderObject::SelectionInside) ||
1684 (state == RenderObject::SelectionEnd && ltr) ||
1685 (state == RenderObject::SelectionStart && !ltr);
1686 rightGap = (state == RenderObject::SelectionInside) ||
1687 (state == RenderObject::SelectionStart && ltr) ||
1688 (state == RenderObject::SelectionEnd && !ltr);
1689}
1690
1691LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock& rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache)
1692{
1693 LayoutUnit logicalLeft = logicalLeftOffsetForLine(position, DoNotIndentText);
1694 if (logicalLeft == logicalLeftOffsetForContent()) {
1695 if (&rootBlock != this) // The border can potentially be further extended by our containingBlock().
1696 return cache.containingBlockInfo(*this).logicalLeftSelectionOffset(rootBlock, position + logicalTop());
1697 return logicalLeft;
1698 }
1699
1700 RenderBlock* cb = this;
1701 const LogicalSelectionOffsetCaches* currentCache = &cache;
1702 while (cb != &rootBlock) {
1703 logicalLeft += cb->logicalLeft();
1704
1705 ASSERT(currentCache);
1706 auto info = currentCache->containingBlockInfo(*cb);
1707 cb = info.block();
1708 if (!cb)
1709 break;
1710 currentCache = info.cache();
1711 }
1712 return logicalLeft;
1713}
1714
1715LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock& rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache)
1716{
1717 LayoutUnit logicalRight = logicalRightOffsetForLine(position, DoNotIndentText);
1718 if (logicalRight == logicalRightOffsetForContent()) {
1719 if (&rootBlock != this) // The border can potentially be further extended by our containingBlock().
1720 return cache.containingBlockInfo(*this).logicalRightSelectionOffset(rootBlock, position + logicalTop());
1721 return logicalRight;
1722 }
1723
1724 RenderBlock* cb = this;
1725 const LogicalSelectionOffsetCaches* currentCache = &cache;
1726 while (cb != &rootBlock) {
1727 logicalRight += cb->logicalLeft();
1728
1729 ASSERT(currentCache);
1730 auto info = currentCache->containingBlockInfo(*cb);
1731 cb = info.block();
1732 if (!cb)
1733 break;
1734 currentCache = info.cache();
1735 }
1736 return logicalRight;
1737}
1738
1739TrackedRendererListHashSet* RenderBlock::positionedObjects() const
1740{
1741 return positionedDescendantsMap().positionedRenderers(*this);
1742}
1743
1744void RenderBlock::insertPositionedObject(RenderBox& positioned)
1745{
1746 ASSERT(!isAnonymousBlock());
1747 if (positioned.isRenderFragmentedFlow())
1748 return;
1749 // FIXME: Find out if we can do this as part of positioned.setChildNeedsLayout(MarkOnlyThis)
1750 if (positioned.needsLayout()) {
1751 // We should turn this bit on only while in layout.
1752 ASSERT(posChildNeedsLayout() || view().frameView().layoutContext().isInLayout());
1753 setPosChildNeedsLayoutBit(true);
1754 }
1755 positionedDescendantsMap().addDescendant(*this, positioned, isRenderView() ? PositionedDescendantsMap::MoveDescendantToEnd::Yes
1756 : PositionedDescendantsMap::MoveDescendantToEnd::No);
1757}
1758
1759void RenderBlock::removePositionedObject(const RenderBox& rendererToRemove)
1760{
1761 positionedDescendantsMap().removeDescendant(rendererToRemove);
1762}
1763
1764void RenderBlock::removePositionedObjects(const RenderBlock* newContainingBlockCandidate, ContainingBlockState containingBlockState)
1765{
1766 auto* positionedDescendants = positionedObjects();
1767 if (!positionedDescendants)
1768 return;
1769
1770 Vector<RenderBox*, 16> renderersToRemove;
1771 for (auto* renderer : *positionedDescendants) {
1772 if (newContainingBlockCandidate && !renderer->isDescendantOf(newContainingBlockCandidate))
1773 continue;
1774 renderersToRemove.append(renderer);
1775 if (containingBlockState == NewContainingBlock)
1776 renderer->setChildNeedsLayout(MarkOnlyThis);
1777 // It is the parent block's job to add positioned children to positioned objects list of its containing block.
1778 // Dirty the parent to ensure this happens.
1779 auto* parent = renderer->parent();
1780 while (parent && !parent->isRenderBlock())
1781 parent = parent->parent();
1782 if (parent)
1783 parent->setChildNeedsLayout();
1784 }
1785 for (auto* renderer : renderersToRemove)
1786 removePositionedObject(*renderer);
1787}
1788
1789void RenderBlock::addPercentHeightDescendant(RenderBox& descendant)
1790{
1791 insertIntoTrackedRendererMaps(*this, descendant);
1792}
1793
1794void RenderBlock::removePercentHeightDescendant(RenderBox& descendant)
1795{
1796 removeFromTrackedRendererMaps(descendant);
1797}
1798
1799TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const
1800{
1801 return percentHeightDescendantsMap ? percentHeightDescendantsMap->get(this) : nullptr;
1802}
1803
1804bool RenderBlock::hasPercentHeightContainerMap()
1805{
1806 return percentHeightContainerMap;
1807}
1808
1809bool RenderBlock::hasPercentHeightDescendant(RenderBox& descendant)
1810{
1811 // We don't null check percentHeightContainerMap since the caller
1812 // already ensures this and we need to call this function on every
1813 // descendant in clearPercentHeightDescendantsFrom().
1814 ASSERT(percentHeightContainerMap);
1815 return percentHeightContainerMap->contains(&descendant);
1816}
1817
1818void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox& descendant)
1819{
1820 // We query the map directly, rather than looking at style's
1821 // logicalHeight()/logicalMinHeight()/logicalMaxHeight() since those
1822 // can change with writing mode/directional changes.
1823 if (!hasPercentHeightContainerMap())
1824 return;
1825
1826 if (!hasPercentHeightDescendant(descendant))
1827 return;
1828
1829 removePercentHeightDescendant(descendant);
1830}
1831
1832void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox& parent)
1833{
1834 ASSERT(percentHeightContainerMap);
1835 for (RenderObject* child = parent.firstChild(); child; child = child->nextInPreOrder(&parent)) {
1836 if (!is<RenderBox>(*child))
1837 continue;
1838
1839 auto& box = downcast<RenderBox>(*child);
1840 if (!hasPercentHeightDescendant(box))
1841 continue;
1842
1843 removePercentHeightDescendant(box);
1844 }
1845}
1846
1847bool RenderBlock::isContainingBlockAncestorFor(RenderObject& renderer) const
1848{
1849 for (const auto* ancestor = renderer.containingBlock(); ancestor; ancestor = ancestor->containingBlock()) {
1850 if (ancestor == this)
1851 return true;
1852 }
1853 return false;
1854}
1855
1856LayoutUnit RenderBlock::textIndentOffset() const
1857{
1858 LayoutUnit cw;
1859 if (style().textIndent().isPercentOrCalculated())
1860 cw = containingBlock()->availableLogicalWidth();
1861 return minimumValueForLength(style().textIndent(), cw);
1862}
1863
1864LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderFragmentContainer* fragment) const
1865{
1866 LayoutUnit logicalLeftOffset = style().isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop();
1867 if (shouldPlaceBlockDirectionScrollbarOnLeft())
1868 logicalLeftOffset += verticalScrollbarWidth();
1869 if (!fragment)
1870 return logicalLeftOffset;
1871 LayoutRect boxRect = borderBoxRectInFragment(fragment);
1872 return logicalLeftOffset + (isHorizontalWritingMode() ? boxRect.x() : boxRect.y());
1873}
1874
1875LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderFragmentContainer* fragment) const
1876{
1877 LayoutUnit logicalRightOffset = style().isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop();
1878 if (shouldPlaceBlockDirectionScrollbarOnLeft())
1879 logicalRightOffset += verticalScrollbarWidth();
1880 logicalRightOffset += availableLogicalWidth();
1881 if (!fragment)
1882 return logicalRightOffset;
1883 LayoutRect boxRect = borderBoxRectInFragment(fragment);
1884 return logicalRightOffset - (logicalWidth() - (isHorizontalWritingMode() ? boxRect.maxX() : boxRect.maxY()));
1885}
1886
1887LayoutUnit RenderBlock::adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const
1888{
1889 LayoutUnit left = offsetFromFloats;
1890
1891 if (applyTextIndent && style().isLeftToRightDirection())
1892 left += textIndentOffset();
1893
1894 if (style().lineAlign() == LineAlign::None)
1895 return left;
1896
1897 // Push in our left offset so that it is aligned with the character grid.
1898 auto* layoutState = view().frameView().layoutContext().layoutState();
1899 if (!layoutState)
1900 return left;
1901
1902 RenderBlock* lineGrid = layoutState->lineGrid();
1903 if (!lineGrid || lineGrid->style().writingMode() != style().writingMode())
1904 return left;
1905
1906 // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge?
1907 float maxCharWidth = lineGrid->style().fontCascade().primaryFont().maxCharWidth();
1908 if (!maxCharWidth)
1909 return left;
1910
1911 LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height();
1912 LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height();
1913
1914 // Push in to the nearest character width (truncated so that we pixel snap left).
1915 // FIXME: Should be patched when subpixel layout lands, since this calculation doesn't have to pixel snap
1916 // any more (https://bugs.webkit.org/show_bug.cgi?id=79946).
1917 // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945).
1918 // FIXME: This doesn't work with columns or fragments (https://bugs.webkit.org/show_bug.cgi?id=79942).
1919 // FIXME: This doesn't work when the inline position of the object isn't set ahead of time.
1920 // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout.
1921 // (https://bugs.webkit.org/show_bug.cgi?id=79944)
1922 float remainder = fmodf(maxCharWidth - fmodf(left + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth);
1923 left += remainder;
1924 return left;
1925}
1926
1927LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const
1928{
1929 LayoutUnit right = offsetFromFloats;
1930
1931 if (applyTextIndent && !style().isLeftToRightDirection())
1932 right -= textIndentOffset();
1933
1934 if (style().lineAlign() == LineAlign::None)
1935 return right;
1936
1937 // Push in our right offset so that it is aligned with the character grid.
1938 auto* layoutState = view().frameView().layoutContext().layoutState();
1939 if (!layoutState)
1940 return right;
1941
1942 RenderBlock* lineGrid = layoutState->lineGrid();
1943 if (!lineGrid || lineGrid->style().writingMode() != style().writingMode())
1944 return right;
1945
1946 // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge?
1947 float maxCharWidth = lineGrid->style().fontCascade().primaryFont().maxCharWidth();
1948 if (!maxCharWidth)
1949 return right;
1950
1951 LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height();
1952 LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height();
1953
1954 // Push in to the nearest character width (truncated so that we pixel snap right).
1955 // FIXME: Should be patched when subpixel layout lands, since this calculation doesn't have to pixel snap
1956 // any more (https://bugs.webkit.org/show_bug.cgi?id=79946).
1957 // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945).
1958 // FIXME: This doesn't work with columns or fragments (https://bugs.webkit.org/show_bug.cgi?id=79942).
1959 // FIXME: This doesn't work when the inline position of the object isn't set ahead of time.
1960 // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout.
1961 // (https://bugs.webkit.org/show_bug.cgi?id=79944)
1962 float remainder = fmodf(fmodf(right + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth);
1963 right -= ceilf(remainder);
1964 return right;
1965}
1966
1967bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset)
1968{
1969 if (!scrollsOverflow())
1970 return false;
1971
1972 return layer()->hitTestOverflowControls(result, roundedIntPoint(locationInContainer - toLayoutSize(accumulatedOffset)));
1973}
1974
1975Node* RenderBlock::nodeForHitTest() const
1976{
1977 // If we are in the margins of block elements that are part of a
1978 // continuation we're actually still inside the enclosing element
1979 // that was split. Use the appropriate inner node.
1980 if (isRenderView())
1981 return &document();
1982 return continuation() ? continuation()->element() : element();
1983}
1984
1985bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
1986{
1987 LayoutPoint adjustedLocation(accumulatedOffset + location());
1988 LayoutSize localOffset = toLayoutSize(adjustedLocation);
1989
1990 if (!isRenderView()) {
1991 // Check if we need to do anything at all.
1992 LayoutRect overflowBox = visualOverflowRect();
1993 flipForWritingMode(overflowBox);
1994 overflowBox.moveBy(adjustedLocation);
1995 if (!locationInContainer.intersects(overflowBox))
1996 return false;
1997 }
1998
1999 if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) {
2000 updateHitTestResult(result, locationInContainer.point() - localOffset);
2001 // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet.
2002 if (result.addNodeToListBasedTestResult(nodeForHitTest(), request, locationInContainer) == HitTestProgress::Stop)
2003 return true;
2004 }
2005
2006 if (style().clipPath()) {
2007 switch (style().clipPath()->type()) {
2008 case ClipPathOperation::Shape: {
2009 auto& clipPath = downcast<ShapeClipPathOperation>(*style().clipPath());
2010
2011 LayoutRect referenceBoxRect;
2012 switch (clipPath.referenceBox()) {
2013 case CSSBoxType::MarginBox:
2014 referenceBoxRect = marginBoxRect();
2015 break;
2016 case CSSBoxType::BorderBox:
2017 referenceBoxRect = borderBoxRect();
2018 break;
2019 case CSSBoxType::PaddingBox:
2020 referenceBoxRect = paddingBoxRect();
2021 break;
2022 case CSSBoxType::ContentBox:
2023 referenceBoxRect = contentBoxRect();
2024 break;
2025 case CSSBoxType::BoxMissing:
2026 case CSSBoxType::FillBox:
2027 case CSSBoxType::StrokeBox:
2028 case CSSBoxType::ViewBox:
2029 referenceBoxRect = borderBoxRect();
2030 }
2031 if (!clipPath.pathForReferenceRect(referenceBoxRect).contains(locationInContainer.point() - localOffset, clipPath.windRule()))
2032 return false;
2033 break;
2034 }
2035 case ClipPathOperation::Reference: {
2036 const auto& referenceClipPathOperation = downcast<ReferenceClipPathOperation>(*style().clipPath());
2037 auto* element = document().getElementById(referenceClipPathOperation.fragment());
2038 if (!element || !element->renderer())
2039 break;
2040 if (!is<SVGClipPathElement>(*element))
2041 break;
2042 auto& clipper = downcast<RenderSVGResourceClipper>(*element->renderer());
2043 if (!clipper.hitTestClipContent(FloatRect(borderBoxRect()), FloatPoint(locationInContainer.point() - localOffset)))
2044 return false;
2045 break;
2046 }
2047 case ClipPathOperation::Box:
2048 break;
2049 }
2050 }
2051
2052 // If we have clipping, then we can't have any spillout.
2053 bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer();
2054 bool useClip = (hasControlClip() || useOverflowClip);
2055 bool checkChildren = !useClip || (hasControlClip() ? locationInContainer.intersects(controlClipRect(adjustedLocation)) : locationInContainer.intersects(overflowClipRect(adjustedLocation, nullptr, IncludeOverlayScrollbarSize)));
2056 if (checkChildren) {
2057 // Hit test descendants first.
2058 LayoutSize scrolledOffset(localOffset - toLayoutSize(scrollPosition()));
2059
2060 if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset)))
2061 return true;
2062 if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) {
2063 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
2064 return true;
2065 }
2066 }
2067
2068 if (!checkChildren && hitTestExcludedChildrenInBorder(request, result, locationInContainer, adjustedLocation, hitTestAction))
2069 return true;
2070
2071 // Check if the point is outside radii.
2072 if (!isRenderView() && style().hasBorderRadius()) {
2073 LayoutRect borderRect = borderBoxRect();
2074 borderRect.moveBy(adjustedLocation);
2075 RoundedRect border = style().getRoundedBorderFor(borderRect);
2076 if (!locationInContainer.intersects(border))
2077 return false;
2078 }
2079
2080 // Now hit test our background
2081 if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) {
2082 LayoutRect boundsRect(adjustedLocation, size());
2083 if (visibleToHitTesting() && locationInContainer.intersects(boundsRect)) {
2084 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
2085 if (result.addNodeToListBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
2086 return true;
2087 }
2088 }
2089
2090 return false;
2091}
2092
2093bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
2094{
2095 if (childrenInline() && !isTable())
2096 return hitTestInlineChildren(request, result, locationInContainer, accumulatedOffset, hitTestAction);
2097
2098 // Hit test our children.
2099 HitTestAction childHitTest = hitTestAction;
2100 if (hitTestAction == HitTestChildBlockBackgrounds)
2101 childHitTest = HitTestChildBlockBackground;
2102 for (auto* child = lastChildBox(); child; child = child->previousSiblingBox()) {
2103 LayoutPoint childPoint = flipForWritingModeForChild(child, accumulatedOffset);
2104 if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest))
2105 return true;
2106 }
2107
2108 return false;
2109}
2110
2111static inline bool isEditingBoundary(RenderElement* ancestor, RenderObject& child)
2112{
2113 ASSERT(!ancestor || ancestor->nonPseudoElement());
2114 ASSERT(child.nonPseudoNode());
2115 return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView())
2116 || ancestor->nonPseudoElement()->hasEditableStyle() == child.nonPseudoNode()->hasEditableStyle();
2117}
2118
2119// FIXME: This function should go on RenderObject as an instance method. Then
2120// all cases in which positionForPoint recurs could call this instead to
2121// prevent crossing editable boundaries. This would require many tests.
2122VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock& parent, RenderBox& child, const LayoutPoint& pointInParentCoordinates)
2123{
2124 LayoutPoint childLocation = child.location();
2125 if (child.isInFlowPositioned())
2126 childLocation += child.offsetForInFlowPosition();
2127
2128 // FIXME: This is wrong if the child's writing-mode is different from the parent's.
2129 LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation));
2130
2131 // If this is an anonymous renderer, we just recur normally
2132 Element* childElement= child.nonPseudoElement();
2133 if (!childElement)
2134 return child.positionForPoint(pointInChildCoordinates, nullptr);
2135
2136 // Otherwise, first make sure that the editability of the parent and child agree.
2137 // If they don't agree, then we return a visible position just before or after the child
2138 RenderElement* ancestor = &parent;
2139 while (ancestor && !ancestor->nonPseudoElement())
2140 ancestor = ancestor->parent();
2141
2142 // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal
2143 if (isEditingBoundary(ancestor, child))
2144 return child.positionForPoint(pointInChildCoordinates, nullptr);
2145
2146 // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child
2147 LayoutUnit childMiddle = parent.logicalWidthForChild(child) / 2;
2148 LayoutUnit logicalLeft = parent.isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y();
2149 if (logicalLeft < childMiddle)
2150 return ancestor->createVisiblePosition(childElement->computeNodeIndex(), DOWNSTREAM);
2151 return ancestor->createVisiblePosition(childElement->computeNodeIndex() + 1, UPSTREAM);
2152}
2153
2154VisiblePosition RenderBlock::positionForPointWithInlineChildren(const LayoutPoint&, const RenderFragmentContainer*)
2155{
2156 ASSERT_NOT_REACHED();
2157 return VisiblePosition();
2158}
2159
2160static inline bool isChildHitTestCandidate(const RenderBox& box)
2161{
2162 return box.height() && box.style().visibility() == Visibility::Visible && !box.isOutOfFlowPositioned() && !box.isInFlowRenderFragmentedFlow();
2163}
2164
2165// Valid candidates in a FragmentedFlow must be rendered by the fragment.
2166static inline bool isChildHitTestCandidate(const RenderBox& box, const RenderFragmentContainer* fragment, const LayoutPoint& point)
2167{
2168 if (!isChildHitTestCandidate(box))
2169 return false;
2170 if (!fragment)
2171 return true;
2172 const RenderBlock& block = is<RenderBlock>(box) ? downcast<RenderBlock>(box) : *box.containingBlock();
2173 return block.fragmentAtBlockOffset(point.y()) == fragment;
2174}
2175
2176VisiblePosition RenderBlock::positionForPoint(const LayoutPoint& point, const RenderFragmentContainer* fragment)
2177{
2178 if (isTable())
2179 return RenderBox::positionForPoint(point, fragment);
2180
2181 if (isReplaced()) {
2182 // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode.
2183 LayoutUnit pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.y();
2184 LayoutUnit pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x();
2185
2186 if (pointLogicalTop < 0 || (pointLogicalTop < logicalHeight() && pointLogicalLeft < 0))
2187 return createVisiblePosition(caretMinOffset(), DOWNSTREAM);
2188 if (pointLogicalTop >= logicalHeight() || (pointLogicalTop >= 0 && pointLogicalLeft >= logicalWidth()))
2189 return createVisiblePosition(caretMaxOffset(), DOWNSTREAM);
2190 }
2191
2192 LayoutPoint pointInContents = point;
2193 offsetForContents(pointInContents);
2194 LayoutPoint pointInLogicalContents(pointInContents);
2195 if (!isHorizontalWritingMode())
2196 pointInLogicalContents = pointInLogicalContents.transposedPoint();
2197
2198 if (childrenInline())
2199 return positionForPointWithInlineChildren(pointInLogicalContents, fragment);
2200
2201 RenderBox* lastCandidateBox = lastChildBox();
2202
2203 if (!fragment)
2204 fragment = fragmentAtBlockOffset(pointInLogicalContents.y());
2205
2206 while (lastCandidateBox && !isChildHitTestCandidate(*lastCandidateBox, fragment, pointInLogicalContents))
2207 lastCandidateBox = lastCandidateBox->previousSiblingBox();
2208
2209 bool blocksAreFlipped = style().isFlippedBlocksWritingMode();
2210 if (lastCandidateBox) {
2211 if (pointInLogicalContents.y() > logicalTopForChild(*lastCandidateBox)
2212 || (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopForChild(*lastCandidateBox)))
2213 return positionForPointRespectingEditingBoundaries(*this, *lastCandidateBox, pointInContents);
2214
2215 for (auto* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) {
2216 if (!isChildHitTestCandidate(*childBox, fragment, pointInLogicalContents))
2217 continue;
2218 LayoutUnit childLogicalBottom = logicalTopForChild(*childBox) + logicalHeightForChild(*childBox);
2219 if (is<RenderBlockFlow>(childBox))
2220 childLogicalBottom += downcast<RenderBlockFlow>(childBox)->lowestFloatLogicalBottom();
2221 // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3).
2222 if (isChildHitTestCandidate(*childBox, fragment, pointInLogicalContents) && (pointInLogicalContents.y() < childLogicalBottom
2223 || (blocksAreFlipped && pointInLogicalContents.y() == childLogicalBottom)))
2224 return positionForPointRespectingEditingBoundaries(*this, *childBox, pointInContents);
2225 }
2226 }
2227
2228 // We only get here if there are no hit test candidate children below the click.
2229 return RenderBox::positionForPoint(point, fragment);
2230}
2231
2232void RenderBlock::offsetForContents(LayoutPoint& offset) const
2233{
2234 offset = flipForWritingMode(offset);
2235 offset += toLayoutSize(scrollPosition());
2236 offset = flipForWritingMode(offset);
2237}
2238
2239void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
2240{
2241 ASSERT(!childrenInline());
2242
2243 computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth);
2244
2245 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
2246
2247 int scrollbarWidth = intrinsicScrollbarLogicalWidth();
2248 maxLogicalWidth += scrollbarWidth;
2249 minLogicalWidth += scrollbarWidth;
2250}
2251
2252void RenderBlock::computePreferredLogicalWidths()
2253{
2254 ASSERT(preferredLogicalWidthsDirty());
2255
2256 m_minPreferredLogicalWidth = 0;
2257 m_maxPreferredLogicalWidth = 0;
2258
2259 const RenderStyle& styleToUse = style();
2260 if (!isTableCell() && styleToUse.logicalWidth().isFixed() && styleToUse.logicalWidth().value() >= 0
2261 && !(isDeprecatedFlexItem() && !styleToUse.logicalWidth().intValue()))
2262 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalWidth().value());
2263 else
2264 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
2265
2266 if (styleToUse.logicalMinWidth().isFixed() && styleToUse.logicalMinWidth().value() > 0) {
2267 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value()));
2268 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value()));
2269 }
2270
2271 if (styleToUse.logicalMaxWidth().isFixed()) {
2272 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value()));
2273 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value()));
2274 }
2275
2276 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
2277 m_minPreferredLogicalWidth += borderAndPadding;
2278 m_maxPreferredLogicalWidth += borderAndPadding;
2279
2280 setPreferredLogicalWidthsDirty(false);
2281}
2282
2283void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
2284{
2285 const RenderStyle& styleToUse = style();
2286 bool nowrap = styleToUse.whiteSpace() == WhiteSpace::NoWrap;
2287
2288 RenderObject* child = firstChild();
2289 RenderBlock* containingBlock = this->containingBlock();
2290 LayoutUnit floatLeftWidth, floatRightWidth;
2291
2292 LayoutUnit childMinWidth;
2293 LayoutUnit childMaxWidth;
2294 bool hadExcludedChildren = computePreferredWidthsForExcludedChildren(childMinWidth, childMaxWidth);
2295 if (hadExcludedChildren) {
2296 minLogicalWidth = std::max(childMinWidth, minLogicalWidth);
2297 maxLogicalWidth = std::max(childMaxWidth, maxLogicalWidth);
2298 }
2299
2300 while (child) {
2301 // Positioned children don't affect the min/max width. Legends in fieldsets are skipped here
2302 // since they compute outside of any one layout system. Other children excluded from
2303 // normal layout are only used with block flows, so it's ok to calculate them here.
2304 if (child->isOutOfFlowPositioned() || child->isExcludedAndPlacedInBorder()) {
2305 child = child->nextSibling();
2306 continue;
2307 }
2308
2309 const RenderStyle& childStyle = child->style();
2310 if (child->isFloating() || (is<RenderBox>(*child) && downcast<RenderBox>(*child).avoidsFloats())) {
2311 LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth;
2312 if (childStyle.clear() == Clear::Left || childStyle.clear() == Clear::Both) {
2313 maxLogicalWidth = std::max(floatTotalWidth, maxLogicalWidth);
2314 floatLeftWidth = 0;
2315 }
2316 if (childStyle.clear() == Clear::Right || childStyle.clear() == Clear::Both) {
2317 maxLogicalWidth = std::max(floatTotalWidth, maxLogicalWidth);
2318 floatRightWidth = 0;
2319 }
2320 }
2321
2322 // A margin basically has three types: fixed, percentage, and auto (variable).
2323 // Auto and percentage margins simply become 0 when computing min/max width.
2324 // Fixed margins can be added in as is.
2325 Length startMarginLength = childStyle.marginStartUsing(&styleToUse);
2326 Length endMarginLength = childStyle.marginEndUsing(&styleToUse);
2327 LayoutUnit margin;
2328 LayoutUnit marginStart;
2329 LayoutUnit marginEnd;
2330 if (startMarginLength.isFixed())
2331 marginStart += startMarginLength.value();
2332 if (endMarginLength.isFixed())
2333 marginEnd += endMarginLength.value();
2334 margin = marginStart + marginEnd;
2335
2336 LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth;
2337 computeChildPreferredLogicalWidths(*child, childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth);
2338
2339 LayoutUnit w = childMinPreferredLogicalWidth + margin;
2340 minLogicalWidth = std::max(w, minLogicalWidth);
2341
2342 // IE ignores tables for calculation of nowrap. Makes some sense.
2343 if (nowrap && !child->isTable())
2344 maxLogicalWidth = std::max(w, maxLogicalWidth);
2345
2346 w = childMaxPreferredLogicalWidth + margin;
2347
2348 if (!child->isFloating()) {
2349 if (is<RenderBox>(*child) && downcast<RenderBox>(*child).avoidsFloats()) {
2350 // Determine a left and right max value based off whether or not the floats can fit in the
2351 // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin
2352 // is smaller than the float width.
2353 bool ltr = containingBlock ? containingBlock->style().isLeftToRightDirection() : styleToUse.isLeftToRightDirection();
2354 LayoutUnit marginLogicalLeft = ltr ? marginStart : marginEnd;
2355 LayoutUnit marginLogicalRight = ltr ? marginEnd : marginStart;
2356 LayoutUnit maxLeft = marginLogicalLeft > 0 ? std::max(floatLeftWidth, marginLogicalLeft) : floatLeftWidth + marginLogicalLeft;
2357 LayoutUnit maxRight = marginLogicalRight > 0 ? std::max(floatRightWidth, marginLogicalRight) : floatRightWidth + marginLogicalRight;
2358 w = childMaxPreferredLogicalWidth + maxLeft + maxRight;
2359 w = std::max(w, floatLeftWidth + floatRightWidth);
2360 }
2361 else
2362 maxLogicalWidth = std::max(floatLeftWidth + floatRightWidth, maxLogicalWidth);
2363 floatLeftWidth = floatRightWidth = 0;
2364 }
2365
2366 if (child->isFloating()) {
2367 if (childStyle.floating() == Float::Left)
2368 floatLeftWidth += w;
2369 else
2370 floatRightWidth += w;
2371 } else
2372 maxLogicalWidth = std::max(w, maxLogicalWidth);
2373
2374 child = child->nextSibling();
2375 }
2376
2377 // Always make sure these values are non-negative.
2378 minLogicalWidth = std::max<LayoutUnit>(0, minLogicalWidth);
2379 maxLogicalWidth = std::max<LayoutUnit>(0, maxLogicalWidth);
2380
2381 maxLogicalWidth = std::max(floatLeftWidth + floatRightWidth, maxLogicalWidth);
2382}
2383
2384void RenderBlock::computeChildPreferredLogicalWidths(RenderObject& child, LayoutUnit& minPreferredLogicalWidth, LayoutUnit& maxPreferredLogicalWidth) const
2385{
2386 if (child.isBox() && child.isHorizontalWritingMode() != isHorizontalWritingMode()) {
2387 // If the child is an orthogonal flow, child's height determines the width,
2388 // but the height is not available until layout.
2389 // http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-shrink-to-fit
2390 if (!child.needsLayout()) {
2391 minPreferredLogicalWidth = maxPreferredLogicalWidth = downcast<RenderBox>(child).logicalHeight();
2392 return;
2393 }
2394 minPreferredLogicalWidth = maxPreferredLogicalWidth = downcast<RenderBox>(child).computeLogicalHeightWithoutLayout();
2395 return;
2396 }
2397
2398 // The preferred widths of flexbox children should never depend on override sizes. They should
2399 // always be computed without regard for any overrides that are present.
2400 Optional<LayoutUnit> overrideHeight;
2401 Optional<LayoutUnit> overrideWidth;
2402
2403 if (child.isBox()) {
2404 auto& box = downcast<RenderBox>(child);
2405 if (box.isFlexItem()) {
2406 if (box.hasOverrideContentLogicalHeight())
2407 overrideHeight = Optional<LayoutUnit>(box.overrideContentLogicalHeight());
2408 if (box.hasOverrideContentLogicalWidth())
2409 overrideWidth = Optional<LayoutUnit>(box.overrideContentLogicalWidth());
2410 box.clearOverrideContentSize();
2411 }
2412 }
2413
2414 minPreferredLogicalWidth = child.minPreferredLogicalWidth();
2415 maxPreferredLogicalWidth = child.maxPreferredLogicalWidth();
2416
2417 if (child.isBox()) {
2418 auto& box = downcast<RenderBox>(child);
2419 if (overrideHeight)
2420 box.setOverrideContentLogicalHeight(overrideHeight.value());
2421 if (overrideWidth)
2422 box.setOverrideContentLogicalWidth(overrideWidth.value());
2423 }
2424
2425 // For non-replaced blocks if the inline size is min|max-content or a definite
2426 // size the min|max-content contribution is that size plus border, padding and
2427 // margin https://drafts.csswg.org/css-sizing/#block-intrinsic
2428 if (child.isRenderBlock()) {
2429 const Length& computedInlineSize = child.style().logicalWidth();
2430 if (computedInlineSize.isMaxContent())
2431 minPreferredLogicalWidth = maxPreferredLogicalWidth;
2432 else if (computedInlineSize.isMinContent())
2433 maxPreferredLogicalWidth = minPreferredLogicalWidth;
2434 }
2435}
2436
2437bool RenderBlock::hasLineIfEmpty() const
2438{
2439 if (!element())
2440 return false;
2441
2442 if (element()->isRootEditableElement())
2443 return true;
2444
2445 return false;
2446}
2447
2448LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
2449{
2450 // Inline blocks are replaced elements. Otherwise, just pass off to
2451 // the base class. If we're being queried as though we're the root line
2452 // box, then the fact that we're an inline-block is irrelevant, and we behave
2453 // just like a block.
2454 if (isReplaced() && linePositionMode == PositionOnContainingLine)
2455 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
2456
2457 if (firstLine && view().usesFirstLineRules()) {
2458 auto& s = firstLineStyle();
2459 if (&s != &style())
2460 return s.computedLineHeight();
2461 }
2462
2463 return style().computedLineHeight();
2464}
2465
2466int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
2467{
2468 // Inline blocks are replaced elements. Otherwise, just pass off to
2469 // the base class. If we're being queried as though we're the root line
2470 // box, then the fact that we're an inline-block is irrelevant, and we behave
2471 // just like a block.
2472 if (isReplaced() && linePositionMode == PositionOnContainingLine) {
2473 // For "leaf" theme objects, let the theme decide what the baseline position is.
2474 // FIXME: Might be better to have a custom CSS property instead, so that if the theme
2475 // is turned off, checkboxes/radios will still have decent baselines.
2476 // FIXME: Need to patch form controls to deal with vertical lines.
2477 if (style().hasAppearance() && !theme().isControlContainer(style().appearance()))
2478 return theme().baselinePosition(*this);
2479
2480 // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in
2481 // the normal flow. We make an exception for marquees, since their baselines are meaningless
2482 // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them.
2483 // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled
2484 // vertically (e.g., an overflow:hidden block that has had scrollTop moved).
2485 bool ignoreBaseline = (layer() && (layer()->marquee() || (direction == HorizontalLine ? (layer()->verticalScrollbar() || layer()->scrollOffset().y() != 0)
2486 : (layer()->horizontalScrollbar() || layer()->scrollOffset().x() != 0)))) || (isWritingModeRoot() && !isRubyRun());
2487
2488 Optional<int> baselinePos = ignoreBaseline ? Optional<int>() : inlineBlockBaseline(direction);
2489
2490 if (isDeprecatedFlexibleBox()) {
2491 // Historically, we did this check for all baselines. But we can't
2492 // remove this code from deprecated flexbox, because it effectively
2493 // breaks -webkit-line-clamp, which is used in the wild -- we would
2494 // calculate the baseline as if -webkit-line-clamp wasn't used.
2495 // For simplicity, we use this for all uses of deprecated flexbox.
2496 LayoutUnit bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth();
2497 if (baselinePos && baselinePos.value() > bottomOfContent)
2498 baselinePos = Optional<int>();
2499 }
2500 if (baselinePos)
2501 return direction == HorizontalLine ? marginTop() + baselinePos.value() : marginRight() + baselinePos.value();
2502
2503 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
2504 }
2505
2506 const RenderStyle& style = firstLine ? firstLineStyle() : this->style();
2507 const FontMetrics& fontMetrics = style.fontMetrics();
2508 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
2509}
2510
2511LayoutUnit RenderBlock::minLineHeightForReplacedRenderer(bool isFirstLine, LayoutUnit replacedHeight) const
2512{
2513 if (!document().inNoQuirksMode() && replacedHeight)
2514 return replacedHeight;
2515
2516 const RenderStyle& style = isFirstLine ? firstLineStyle() : this->style();
2517 if (!(style.lineBoxContain() & LineBoxContainBlock))
2518 return 0;
2519
2520 return std::max<LayoutUnit>(replacedHeight, lineHeight(isFirstLine, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
2521}
2522
2523Optional<int> RenderBlock::firstLineBaseline() const
2524{
2525 if (isWritingModeRoot() && !isRubyRun())
2526 return Optional<int>();
2527
2528 for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) {
2529 if (!curr->isFloatingOrOutOfFlowPositioned()) {
2530 if (Optional<int> result = curr->firstLineBaseline())
2531 return Optional<int>(curr->logicalTop() + result.value()); // Translate to our coordinate space.
2532 }
2533 }
2534
2535 return Optional<int>();
2536}
2537
2538Optional<int> RenderBlock::inlineBlockBaseline(LineDirectionMode lineDirection) const
2539{
2540 if (isWritingModeRoot() && !isRubyRun())
2541 return Optional<int>();
2542
2543 bool haveNormalFlowChild = false;
2544 for (auto* box = lastChildBox(); box; box = box->previousSiblingBox()) {
2545 if (box->isFloatingOrOutOfFlowPositioned())
2546 continue;
2547 haveNormalFlowChild = true;
2548 if (Optional<int> result = box->inlineBlockBaseline(lineDirection))
2549 return Optional<int>(box->logicalTop() + result.value()); // Translate to our coordinate space.
2550 }
2551
2552 if (!haveNormalFlowChild && hasLineIfEmpty()) {
2553 auto& fontMetrics = firstLineStyle().fontMetrics();
2554 return Optional<int>(fontMetrics.ascent()
2555 + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2
2556 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()));
2557 }
2558
2559 return Optional<int>();
2560}
2561
2562static inline bool isRenderBlockFlowOrRenderButton(RenderElement& renderElement)
2563{
2564 // We include isRenderButton in this check because buttons are implemented
2565 // using flex box but should still support first-line|first-letter.
2566 // The flex box and specs require that flex box and grid do not support
2567 // first-line|first-letter, though.
2568 // FIXME: Remove when buttons are implemented with align-items instead of
2569 // flex box.
2570 return renderElement.isRenderBlockFlow() || renderElement.isRenderButton();
2571}
2572
2573RenderBlock* RenderBlock::firstLineBlock() const
2574{
2575 RenderBlock* firstLineBlock = const_cast<RenderBlock*>(this);
2576 bool hasPseudo = false;
2577 while (true) {
2578 hasPseudo = firstLineBlock->style().hasPseudoStyle(PseudoId::FirstLine);
2579 if (hasPseudo)
2580 break;
2581 RenderElement* parentBlock = firstLineBlock->parent();
2582 if (firstLineBlock->isReplaced() || firstLineBlock->isFloating()
2583 || !parentBlock || parentBlock->firstChild() != firstLineBlock || !isRenderBlockFlowOrRenderButton(*parentBlock))
2584 break;
2585 firstLineBlock = downcast<RenderBlock>(parentBlock);
2586 }
2587
2588 if (!hasPseudo)
2589 return nullptr;
2590
2591 return firstLineBlock;
2592}
2593
2594static inline RenderBlock* findFirstLetterBlock(RenderBlock* start)
2595{
2596 RenderBlock* firstLetterBlock = start;
2597 while (true) {
2598 bool canHaveFirstLetterRenderer = firstLetterBlock->style().hasPseudoStyle(PseudoId::FirstLetter)
2599 && firstLetterBlock->canHaveGeneratedChildren()
2600 && isRenderBlockFlowOrRenderButton(*firstLetterBlock);
2601 if (canHaveFirstLetterRenderer)
2602 return firstLetterBlock;
2603
2604 RenderElement* parentBlock = firstLetterBlock->parent();
2605 if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock
2606 || !isRenderBlockFlowOrRenderButton(*parentBlock))
2607 return nullptr;
2608 firstLetterBlock = downcast<RenderBlock>(parentBlock);
2609 }
2610
2611 return nullptr;
2612}
2613
2614void RenderBlock::getFirstLetter(RenderObject*& firstLetter, RenderElement*& firstLetterContainer, RenderObject* skipObject)
2615{
2616 firstLetter = nullptr;
2617 firstLetterContainer = nullptr;
2618
2619 if (!view().usesFirstLetterRules())
2620 return;
2621
2622 // Don't recur
2623 if (style().styleType() == PseudoId::FirstLetter)
2624 return;
2625
2626 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find
2627 // an efficient way to check for that situation though before implementing anything.
2628 firstLetterContainer = findFirstLetterBlock(this);
2629 if (!firstLetterContainer)
2630 return;
2631
2632 // Drill into inlines looking for our first text descendant.
2633 firstLetter = firstLetterContainer->firstChild();
2634 while (firstLetter) {
2635 if (is<RenderText>(*firstLetter)) {
2636 if (firstLetter == skipObject) {
2637 firstLetter = firstLetter->nextSibling();
2638 continue;
2639 }
2640
2641 break;
2642 }
2643
2644 RenderElement& current = downcast<RenderElement>(*firstLetter);
2645 if (is<RenderListMarker>(current))
2646 firstLetter = current.nextSibling();
2647 else if (current.isFloatingOrOutOfFlowPositioned()) {
2648 if (current.style().styleType() == PseudoId::FirstLetter) {
2649 firstLetter = current.firstChild();
2650 break;
2651 }
2652 firstLetter = current.nextSibling();
2653 } else if (current.isReplaced() || is<RenderButton>(current) || is<RenderMenuList>(current))
2654 break;
2655 else if (current.isFlexibleBoxIncludingDeprecated() || current.isRenderGrid())
2656 firstLetter = current.nextSibling();
2657 else if (current.style().hasPseudoStyle(PseudoId::FirstLetter) && current.canHaveGeneratedChildren()) {
2658 // We found a lower-level node with first-letter, which supersedes the higher-level style
2659 firstLetterContainer = &current;
2660 firstLetter = current.firstChild();
2661 } else
2662 firstLetter = current.firstChild();
2663 }
2664
2665 if (!firstLetter)
2666 firstLetterContainer = nullptr;
2667}
2668
2669RenderFragmentedFlow* RenderBlock::cachedEnclosingFragmentedFlow() const
2670{
2671 RenderBlockRareData* rareData = getBlockRareData(*this);
2672
2673 if (!rareData || !rareData->m_enclosingFragmentedFlow)
2674 return nullptr;
2675
2676 return rareData->m_enclosingFragmentedFlow.value().get();
2677}
2678
2679bool RenderBlock::cachedEnclosingFragmentedFlowNeedsUpdate() const
2680{
2681 RenderBlockRareData* rareData = getBlockRareData(*this);
2682
2683 if (!rareData || !rareData->m_enclosingFragmentedFlow)
2684 return true;
2685
2686 return false;
2687}
2688
2689void RenderBlock::setCachedEnclosingFragmentedFlowNeedsUpdate()
2690{
2691 RenderBlockRareData& rareData = ensureBlockRareData(*this);
2692 rareData.m_enclosingFragmentedFlow = WTF::nullopt;
2693}
2694
2695RenderFragmentedFlow* RenderBlock::updateCachedEnclosingFragmentedFlow(RenderFragmentedFlow* fragmentedFlow) const
2696{
2697 RenderBlockRareData& rareData = ensureBlockRareData(*this);
2698 rareData.m_enclosingFragmentedFlow = makeWeakPtr(fragmentedFlow);
2699
2700 return fragmentedFlow;
2701}
2702
2703RenderFragmentedFlow* RenderBlock::locateEnclosingFragmentedFlow() const
2704{
2705 RenderBlockRareData* rareData = getBlockRareData(*this);
2706 if (!rareData || !rareData->m_enclosingFragmentedFlow)
2707 return updateCachedEnclosingFragmentedFlow(RenderBox::locateEnclosingFragmentedFlow());
2708
2709 ASSERT(rareData->m_enclosingFragmentedFlow.value() == RenderBox::locateEnclosingFragmentedFlow());
2710 return rareData->m_enclosingFragmentedFlow.value().get();
2711}
2712
2713void RenderBlock::resetEnclosingFragmentedFlowAndChildInfoIncludingDescendants(RenderFragmentedFlow*)
2714{
2715 if (fragmentedFlowState() == NotInsideFragmentedFlow)
2716 return;
2717
2718 if (cachedEnclosingFragmentedFlowNeedsUpdate())
2719 return;
2720
2721 auto* fragmentedFlow = cachedEnclosingFragmentedFlow();
2722 setCachedEnclosingFragmentedFlowNeedsUpdate();
2723 RenderElement::resetEnclosingFragmentedFlowAndChildInfoIncludingDescendants(fragmentedFlow);
2724}
2725
2726LayoutUnit RenderBlock::paginationStrut() const
2727{
2728 RenderBlockRareData* rareData = getBlockRareData(*this);
2729 return rareData ? rareData->m_paginationStrut : 0_lu;
2730}
2731
2732LayoutUnit RenderBlock::pageLogicalOffset() const
2733{
2734 RenderBlockRareData* rareData = getBlockRareData(*this);
2735 return rareData ? rareData->m_pageLogicalOffset : 0_lu;
2736}
2737
2738void RenderBlock::setPaginationStrut(LayoutUnit strut)
2739{
2740 RenderBlockRareData* rareData = getBlockRareData(*this);
2741 if (!rareData) {
2742 if (!strut)
2743 return;
2744 rareData = &ensureBlockRareData(*this);
2745 }
2746 rareData->m_paginationStrut = strut;
2747}
2748
2749void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset)
2750{
2751 RenderBlockRareData* rareData = getBlockRareData(*this);
2752 if (!rareData) {
2753 if (!logicalOffset)
2754 return;
2755 rareData = &ensureBlockRareData(*this);
2756 }
2757 rareData->m_pageLogicalOffset = logicalOffset;
2758}
2759
2760void RenderBlock::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
2761{
2762 // For blocks inside inlines, we include margins so that we run right up to the inline boxes
2763 // above and below us (thus getting merged with them to form a single irregular shape).
2764 if (auto* continuation = this->continuation()) {
2765 // FIXME: This is wrong for block-flows that are horizontal.
2766 // https://bugs.webkit.org/show_bug.cgi?id=46781
2767 rects.append(snappedIntRect(accumulatedOffset.x(), accumulatedOffset.y() - collapsedMarginBefore(), width(), height() + collapsedMarginBefore() + collapsedMarginAfter()));
2768 continuation->absoluteRects(rects, accumulatedOffset - toLayoutSize(location() + inlineContinuation()->containingBlock()->location()));
2769 } else
2770 rects.append(snappedIntRect(accumulatedOffset, size()));
2771}
2772
2773void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
2774{
2775 // For blocks inside inlines, we include margins so that we run right up to the inline boxes
2776 // above and below us (thus getting merged with them to form a single irregular shape).
2777 auto* continuation = this->continuation();
2778 FloatRect localRect = continuation
2779 ? FloatRect(0, -collapsedMarginBefore(), width(), height() + collapsedMarginBefore() + collapsedMarginAfter())
2780 : FloatRect(0, 0, width(), height());
2781
2782 // FIXME: This is wrong for block-flows that are horizontal.
2783 // https://bugs.webkit.org/show_bug.cgi?id=46781
2784 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
2785 if (!fragmentedFlow || !fragmentedFlow->absoluteQuadsForBox(quads, wasFixed, this, localRect.y(), localRect.maxY()))
2786 quads.append(localToAbsoluteQuad(localRect, UseTransforms, wasFixed));
2787
2788 if (continuation)
2789 continuation->absoluteQuads(quads, wasFixed);
2790}
2791
2792LayoutRect RenderBlock::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
2793{
2794 LayoutRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
2795 if (isContinuation())
2796 r.inflateY(collapsedMarginBefore()); // FIXME: This is wrong for block-flows that are horizontal.
2797 return r;
2798}
2799
2800void RenderBlock::updateDragState(bool dragOn)
2801{
2802 RenderBox::updateDragState(dragOn);
2803 if (RenderBoxModelObject* continuation = this->continuation())
2804 continuation->updateDragState(dragOn);
2805}
2806
2807const RenderStyle& RenderBlock::outlineStyleForRepaint() const
2808{
2809 if (auto* continuation = this->continuation())
2810 return continuation->style();
2811 return RenderElement::outlineStyleForRepaint();
2812}
2813
2814void RenderBlock::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
2815{
2816 if (result.innerNode())
2817 return;
2818
2819 if (Node* n = nodeForHitTest()) {
2820 result.setInnerNode(n);
2821 if (!result.innerNonSharedNode())
2822 result.setInnerNonSharedNode(n);
2823 result.setLocalPoint(point);
2824 }
2825}
2826
2827LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, unsigned caretOffset, LayoutUnit* extraWidthToEndOfLine)
2828{
2829 // Do the normal calculation in most cases.
2830 if (firstChild())
2831 return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine);
2832
2833 LayoutRect caretRect = localCaretRectForEmptyElement(width(), textIndentOffset());
2834
2835 // FIXME: Does this need to adjust for vertical orientation?
2836 if (extraWidthToEndOfLine)
2837 *extraWidthToEndOfLine = width() - caretRect.maxX();
2838
2839 return caretRect;
2840}
2841
2842void RenderBlock::addFocusRingRectsForInlineChildren(Vector<LayoutRect>&, const LayoutPoint&, const RenderLayerModelObject*)
2843{
2844 ASSERT_NOT_REACHED();
2845}
2846
2847void RenderBlock::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
2848{
2849 // For blocks inside inlines, we include margins so that we run right up to the inline boxes
2850 // above and below us (thus getting merged with them to form a single irregular shape).
2851 auto* inlineContinuation = this->inlineContinuation();
2852 if (inlineContinuation) {
2853 // FIXME: This check really isn't accurate.
2854 bool nextInlineHasLineBox = inlineContinuation->firstLineBox();
2855 // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block.
2856 // FIXME: This is wrong for block-flows that are horizontal.
2857 // https://bugs.webkit.org/show_bug.cgi?id=46781
2858 bool prevInlineHasLineBox = downcast<RenderInline>(*inlineContinuation->element()->renderer()).firstLineBox();
2859 float topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : 0_lu;
2860 float bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : 0_lu;
2861 LayoutRect rect(additionalOffset.x(), additionalOffset.y() - topMargin, width(), height() + topMargin + bottomMargin);
2862 if (!rect.isEmpty())
2863 rects.append(rect);
2864 } else if (width() && height())
2865 rects.append(LayoutRect(additionalOffset, size()));
2866
2867 if (!hasOverflowClip() && !hasControlClip()) {
2868 if (childrenInline())
2869 addFocusRingRectsForInlineChildren(rects, additionalOffset, paintContainer);
2870
2871 for (auto& box : childrenOfType<RenderBox>(*this)) {
2872 if (is<RenderListMarker>(box))
2873 continue;
2874
2875 FloatPoint pos;
2876 // FIXME: This doesn't work correctly with transforms.
2877 if (box.layer())
2878 pos = box.localToContainerPoint(FloatPoint(), paintContainer);
2879 else
2880 pos = FloatPoint(additionalOffset.x() + box.x(), additionalOffset.y() + box.y());
2881 box.addFocusRingRects(rects, flooredLayoutPoint(pos), paintContainer);
2882 }
2883 }
2884
2885 if (inlineContinuation)
2886 inlineContinuation->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + inlineContinuation->containingBlock()->location() - location())), paintContainer);
2887}
2888
2889RenderPtr<RenderBlock> RenderBlock::createAnonymousBlockWithStyleAndDisplay(Document& document, const RenderStyle& style, DisplayType display)
2890{
2891 // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ?
2892 RenderPtr<RenderBlock> newBox;
2893 if (display == DisplayType::Flex || display == DisplayType::InlineFlex)
2894 newBox = createRenderer<RenderFlexibleBox>(document, RenderStyle::createAnonymousStyleWithDisplay(style, DisplayType::Flex));
2895 else
2896 newBox = createRenderer<RenderBlockFlow>(document, RenderStyle::createAnonymousStyleWithDisplay(style, DisplayType::Block));
2897
2898 newBox->initializeStyle();
2899 return newBox;
2900}
2901
2902LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const
2903{
2904 auto* layoutState = view().frameView().layoutContext().layoutState();
2905 if (layoutState && !layoutState->isPaginated())
2906 return 0;
2907
2908 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
2909 if (fragmentedFlow)
2910 return fragmentedFlow->offsetFromLogicalTopOfFirstFragment(this);
2911
2912 if (layoutState) {
2913 ASSERT(layoutState->renderer() == this);
2914
2915 LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
2916 return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
2917 }
2918
2919 ASSERT_NOT_REACHED();
2920 return 0;
2921}
2922
2923RenderFragmentContainer* RenderBlock::fragmentAtBlockOffset(LayoutUnit blockOffset) const
2924{
2925 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
2926 if (!fragmentedFlow || !fragmentedFlow->hasValidFragmentInfo())
2927 return 0;
2928
2929 return fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstPage() + blockOffset, true);
2930}
2931
2932static bool canComputeFragmentRangeForBox(const RenderBlock& parentBlock, const RenderBox& childBox, const RenderFragmentedFlow* enclosingFragmentedFlow)
2933{
2934 if (!enclosingFragmentedFlow)
2935 return false;
2936
2937 if (!enclosingFragmentedFlow->hasFragments())
2938 return false;
2939
2940 if (!childBox.canHaveOutsideFragmentRange())
2941 return false;
2942
2943 return enclosingFragmentedFlow->hasCachedFragmentRangeForBox(parentBlock);
2944}
2945
2946bool RenderBlock::childBoxIsUnsplittableForFragmentation(const RenderBox& child) const
2947{
2948 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
2949 bool checkColumnBreaks = fragmentedFlow && fragmentedFlow->shouldCheckColumnBreaks();
2950 bool checkPageBreaks = !checkColumnBreaks && view().frameView().layoutContext().layoutState()->pageLogicalHeight();
2951 return child.isUnsplittableForPagination() || child.style().breakInside() == BreakInside::Avoid
2952 || (checkColumnBreaks && child.style().breakInside() == BreakInside::AvoidColumn)
2953 || (checkPageBreaks && child.style().breakInside() == BreakInside::AvoidPage);
2954}
2955
2956void RenderBlock::computeFragmentRangeForBoxChild(const RenderBox& box) const
2957{
2958 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
2959 ASSERT(canComputeFragmentRangeForBox(*this, box, fragmentedFlow));
2960
2961 RenderFragmentContainer* startFragment;
2962 RenderFragmentContainer* endFragment;
2963 LayoutUnit offsetFromLogicalTopOfFirstFragment = box.offsetFromLogicalTopOfFirstPage();
2964 if (childBoxIsUnsplittableForFragmentation(box))
2965 startFragment = endFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment, true);
2966 else {
2967 startFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment, true);
2968 endFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment + logicalHeightForChild(box), true);
2969 }
2970
2971 fragmentedFlow->setFragmentRangeForBox(box, startFragment, endFragment);
2972}
2973
2974void RenderBlock::estimateFragmentRangeForBoxChild(const RenderBox& box) const
2975{
2976 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
2977 if (!canComputeFragmentRangeForBox(*this, box, fragmentedFlow))
2978 return;
2979
2980 if (childBoxIsUnsplittableForFragmentation(box)) {
2981 computeFragmentRangeForBoxChild(box);
2982 return;
2983 }
2984
2985 auto estimatedValues = box.computeLogicalHeight(RenderFragmentedFlow::maxLogicalHeight(), logicalTopForChild(box));
2986 LayoutUnit offsetFromLogicalTopOfFirstFragment = box.offsetFromLogicalTopOfFirstPage();
2987 RenderFragmentContainer* startFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment, true);
2988 RenderFragmentContainer* endFragment = fragmentedFlow->fragmentAtBlockOffset(this, offsetFromLogicalTopOfFirstFragment + estimatedValues.m_extent, true);
2989
2990 fragmentedFlow->setFragmentRangeForBox(box, startFragment, endFragment);
2991}
2992
2993bool RenderBlock::updateFragmentRangeForBoxChild(const RenderBox& box) const
2994{
2995 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
2996 if (!canComputeFragmentRangeForBox(*this, box, fragmentedFlow))
2997 return false;
2998
2999 RenderFragmentContainer* startFragment = nullptr;
3000 RenderFragmentContainer* endFragment = nullptr;
3001 fragmentedFlow->getFragmentRangeForBox(&box, startFragment, endFragment);
3002
3003 computeFragmentRangeForBoxChild(box);
3004
3005 RenderFragmentContainer* newStartFragment = nullptr;
3006 RenderFragmentContainer* newEndFragment = nullptr;
3007 fragmentedFlow->getFragmentRangeForBox(&box, newStartFragment, newEndFragment);
3008
3009
3010 // Changing the start fragment means we shift everything and a relayout is needed.
3011 if (newStartFragment != startFragment)
3012 return true;
3013
3014 // The fragment range of the box has changed. Some boxes (e.g floats) may have been positioned assuming
3015 // a different range.
3016 if (box.needsLayoutAfterFragmentRangeChange() && newEndFragment != endFragment)
3017 return true;
3018
3019 return false;
3020}
3021
3022LayoutUnit RenderBlock::collapsedMarginBeforeForChild(const RenderBox& child) const
3023{
3024 // If the child has the same directionality as we do, then we can just return its
3025 // collapsed margin.
3026 if (!child.isWritingModeRoot())
3027 return child.collapsedMarginBefore();
3028
3029 // The child has a different directionality. If the child is parallel, then it's just
3030 // flipped relative to us. We can use the collapsed margin for the opposite edge.
3031 if (child.isHorizontalWritingMode() == isHorizontalWritingMode())
3032 return child.collapsedMarginAfter();
3033
3034 // The child is perpendicular to us, which means its margins don't collapse but are on the
3035 // "logical left/right" sides of the child box. We can just return the raw margin in this case.
3036 return marginBeforeForChild(child);
3037}
3038
3039LayoutUnit RenderBlock::collapsedMarginAfterForChild(const RenderBox& child) const
3040{
3041 // If the child has the same directionality as we do, then we can just return its
3042 // collapsed margin.
3043 if (!child.isWritingModeRoot())
3044 return child.collapsedMarginAfter();
3045
3046 // The child has a different directionality. If the child is parallel, then it's just
3047 // flipped relative to us. We can use the collapsed margin for the opposite edge.
3048 if (child.isHorizontalWritingMode() == isHorizontalWritingMode())
3049 return child.collapsedMarginBefore();
3050
3051 // The child is perpendicular to us, which means its margins don't collapse but are on the
3052 // "logical left/right" side of the child box. We can just return the raw margin in this case.
3053 return marginAfterForChild(child);
3054}
3055
3056bool RenderBlock::hasMarginBeforeQuirk(const RenderBox& child) const
3057{
3058 // If the child has the same directionality as we do, then we can just return its
3059 // margin quirk.
3060 if (!child.isWritingModeRoot())
3061 return is<RenderBlock>(child) ? downcast<RenderBlock>(child).hasMarginBeforeQuirk() : child.style().hasMarginBeforeQuirk();
3062
3063 // The child has a different directionality. If the child is parallel, then it's just
3064 // flipped relative to us. We can use the opposite edge.
3065 if (child.isHorizontalWritingMode() == isHorizontalWritingMode())
3066 return is<RenderBlock>(child) ? downcast<RenderBlock>(child).hasMarginAfterQuirk() : child.style().hasMarginAfterQuirk();
3067
3068 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about
3069 // whether or not authors specified quirky ems, since they're an implementation detail.
3070 return false;
3071}
3072
3073bool RenderBlock::hasMarginAfterQuirk(const RenderBox& child) const
3074{
3075 // If the child has the same directionality as we do, then we can just return its
3076 // margin quirk.
3077 if (!child.isWritingModeRoot())
3078 return is<RenderBlock>(child) ? downcast<RenderBlock>(child).hasMarginAfterQuirk() : child.style().hasMarginAfterQuirk();
3079
3080 // The child has a different directionality. If the child is parallel, then it's just
3081 // flipped relative to us. We can use the opposite edge.
3082 if (child.isHorizontalWritingMode() == isHorizontalWritingMode())
3083 return is<RenderBlock>(child) ? downcast<RenderBlock>(child).hasMarginBeforeQuirk() : child.style().hasMarginBeforeQuirk();
3084
3085 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about
3086 // whether or not authors specified quirky ems, since they're an implementation detail.
3087 return false;
3088}
3089
3090const char* RenderBlock::renderName() const
3091{
3092 if (isBody())
3093 return "RenderBody"; // FIXME: Temporary hack until we know that the regression tests pass.
3094 if (isFieldset())
3095 return "RenderFieldSet"; // FIXME: Remove eventually, but done to keep tests from breaking.
3096 if (isFloating())
3097 return "RenderBlock (floating)";
3098 if (isOutOfFlowPositioned())
3099 return "RenderBlock (positioned)";
3100 if (isAnonymousBlock())
3101 return "RenderBlock (anonymous)";
3102 // FIXME: Temporary hack while the new generated content system is being implemented.
3103 if (isPseudoElement())
3104 return "RenderBlock (generated)";
3105 if (isAnonymous())
3106 return "RenderBlock (generated)";
3107 if (isRelativelyPositioned())
3108 return "RenderBlock (relative positioned)";
3109 if (isStickilyPositioned())
3110 return "RenderBlock (sticky positioned)";
3111 return "RenderBlock";
3112}
3113
3114TextRun RenderBlock::constructTextRun(StringView stringView, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags)
3115{
3116 auto textDirection = TextDirection::LTR;
3117 bool directionalOverride = style.rtlOrdering() == Order::Visual;
3118 if (flags != DefaultTextRunFlags) {
3119 if (flags & RespectDirection)
3120 textDirection = style.direction();
3121 if (flags & RespectDirectionOverride)
3122 directionalOverride |= isOverride(style.unicodeBidi());
3123 }
3124 return TextRun(stringView, 0, 0, expansion, textDirection, directionalOverride);
3125}
3126
3127TextRun RenderBlock::constructTextRun(const String& string, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags)
3128{
3129 return constructTextRun(StringView(string), style, expansion, flags);
3130}
3131
3132TextRun RenderBlock::constructTextRun(const AtomicString& atomicString, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags)
3133{
3134 return constructTextRun(StringView(atomicString), style, expansion, flags);
3135}
3136
3137TextRun RenderBlock::constructTextRun(const RenderText& text, const RenderStyle& style, ExpansionBehavior expansion)
3138{
3139 return constructTextRun(text.stringView(), style, expansion);
3140}
3141
3142TextRun RenderBlock::constructTextRun(const RenderText& text, unsigned offset, unsigned length, const RenderStyle& style, ExpansionBehavior expansion)
3143{
3144 unsigned stop = offset + length;
3145 ASSERT(stop <= text.text().length());
3146 return constructTextRun(text.stringView(offset, stop), style, expansion);
3147}
3148
3149TextRun RenderBlock::constructTextRun(const LChar* characters, unsigned length, const RenderStyle& style, ExpansionBehavior expansion)
3150{
3151 return constructTextRun(StringView(characters, length), style, expansion);
3152}
3153
3154TextRun RenderBlock::constructTextRun(const UChar* characters, unsigned length, const RenderStyle& style, ExpansionBehavior expansion)
3155{
3156 return constructTextRun(StringView(characters, length), style, expansion);
3157}
3158
3159#ifndef NDEBUG
3160void RenderBlock::checkPositionedObjectsNeedLayout()
3161{
3162 auto* positionedDescendants = positionedObjects();
3163 if (!positionedDescendants)
3164 return;
3165
3166 for (auto* renderer : *positionedDescendants)
3167 ASSERT(!renderer->needsLayout());
3168}
3169
3170#endif
3171
3172bool RenderBlock::hasDefiniteLogicalHeight() const
3173{
3174 return (bool)availableLogicalHeightForPercentageComputation();
3175}
3176
3177Optional<LayoutUnit> RenderBlock::availableLogicalHeightForPercentageComputation() const
3178{
3179 Optional<LayoutUnit> availableHeight;
3180
3181 // For anonymous blocks that are skipped during percentage height calculation,
3182 // we consider them to have an indefinite height.
3183 if (skipContainingBlockForPercentHeightCalculation(*this, false))
3184 return availableHeight;
3185
3186 const auto& styleToUse = style();
3187
3188 // A positioned element that specified both top/bottom or that specifies
3189 // height should be treated as though it has a height explicitly specified
3190 // that can be used for any percentage computations.
3191 bool isOutOfFlowPositionedWithSpecifiedHeight = isOutOfFlowPositioned() && (!styleToUse.logicalHeight().isAuto() || (!styleToUse.logicalTop().isAuto() && !styleToUse.logicalBottom().isAuto()));
3192
3193 Optional<LayoutUnit> stretchedFlexHeight;
3194 if (isFlexItem())
3195 stretchedFlexHeight = downcast<RenderFlexibleBox>(parent())->childLogicalHeightForPercentageResolution(*this);
3196
3197 if (stretchedFlexHeight)
3198 availableHeight = stretchedFlexHeight;
3199 else if (isGridItem() && hasOverrideContentLogicalHeight())
3200 availableHeight = overrideContentLogicalHeight();
3201 else if (styleToUse.logicalHeight().isFixed()) {
3202 LayoutUnit contentBoxHeight = adjustContentBoxLogicalHeightForBoxSizing((LayoutUnit)styleToUse.logicalHeight().value());
3203 availableHeight = std::max(0_lu, constrainContentBoxLogicalHeightByMinMax(contentBoxHeight - scrollbarLogicalHeight(), WTF::nullopt));
3204 } else if (styleToUse.logicalHeight().isPercentOrCalculated() && !isOutOfFlowPositionedWithSpecifiedHeight) {
3205 Optional<LayoutUnit> heightWithScrollbar = computePercentageLogicalHeight(styleToUse.logicalHeight());
3206 if (heightWithScrollbar) {
3207 LayoutUnit contentBoxHeightWithScrollbar = adjustContentBoxLogicalHeightForBoxSizing(heightWithScrollbar.value());
3208 // We need to adjust for min/max height because this method does not
3209 // handle the min/max of the current block, its caller does. So the
3210 // return value from the recursive call will not have been adjusted
3211 // yet.
3212 LayoutUnit contentBoxHeight = constrainContentBoxLogicalHeightByMinMax(contentBoxHeightWithScrollbar - scrollbarLogicalHeight(), WTF::nullopt);
3213 availableHeight = std::max(0_lu, contentBoxHeight);
3214 }
3215 } else if (isOutOfFlowPositionedWithSpecifiedHeight) {
3216 // Don't allow this to affect the block' size() member variable, since this
3217 // can get called while the block is still laying out its kids.
3218 LogicalExtentComputedValues computedValues = computeLogicalHeight(logicalHeight(), 0_lu);
3219 availableHeight = computedValues.m_extent - borderAndPaddingLogicalHeight() - scrollbarLogicalHeight();
3220 } else if (isRenderView())
3221 availableHeight = view().pageOrViewLogicalHeight();
3222
3223 return availableHeight;
3224}
3225
3226void RenderBlock::layoutExcludedChildren(bool relayoutChildren)
3227{
3228 if (!isFieldset())
3229 return;
3230
3231 setIntrinsicBorderForFieldset(0);
3232
3233 RenderBox* box = findFieldsetLegend();
3234 if (!box)
3235 return;
3236
3237 box->setIsExcludedFromNormalLayout(true);
3238 for (auto& child : childrenOfType<RenderBox>(*this)) {
3239 if (&child == box || !child.isLegend())
3240 continue;
3241 child.setIsExcludedFromNormalLayout(false);
3242 }
3243
3244 RenderBox& legend = *box;
3245 if (relayoutChildren)
3246 legend.setChildNeedsLayout(MarkOnlyThis);
3247 legend.layoutIfNeeded();
3248
3249 LayoutUnit logicalLeft;
3250 if (style().isLeftToRightDirection()) {
3251 switch (legend.style().textAlign()) {
3252 case TextAlignMode::Center:
3253 logicalLeft = (logicalWidth() - logicalWidthForChild(legend)) / 2;
3254 break;
3255 case TextAlignMode::Right:
3256 logicalLeft = logicalWidth() - borderEnd() - paddingEnd() - logicalWidthForChild(legend);
3257 break;
3258 default:
3259 logicalLeft = borderStart() + paddingStart() + marginStartForChild(legend);
3260 break;
3261 }
3262 } else {
3263 switch (legend.style().textAlign()) {
3264 case TextAlignMode::Left:
3265 logicalLeft = borderStart() + paddingStart();
3266 break;
3267 case TextAlignMode::Center: {
3268 // Make sure that the extra pixel goes to the end side in RTL (since it went to the end side
3269 // in LTR).
3270 LayoutUnit centeredWidth = logicalWidth() - logicalWidthForChild(legend);
3271 logicalLeft = centeredWidth - centeredWidth / 2;
3272 break;
3273 }
3274 default:
3275 logicalLeft = logicalWidth() - borderStart() - paddingStart() - marginStartForChild(legend) - logicalWidthForChild(legend);
3276 break;
3277 }
3278 }
3279
3280 setLogicalLeftForChild(legend, logicalLeft);
3281
3282 LayoutUnit fieldsetBorderBefore = borderBefore();
3283 LayoutUnit legendLogicalHeight = logicalHeightForChild(legend);
3284 LayoutUnit legendAfterMargin = marginAfterForChild(legend);
3285 LayoutUnit topPositionForLegend = std::max(0_lu, (fieldsetBorderBefore - legendLogicalHeight) / 2);
3286 LayoutUnit bottomPositionForLegend = topPositionForLegend + legendLogicalHeight + legendAfterMargin;
3287
3288 // Place the legend now.
3289 setLogicalTopForChild(legend, topPositionForLegend);
3290
3291 // If the bottom of the legend (including its after margin) is below the fieldset border,
3292 // then we need to add in sufficient intrinsic border to account for this gap.
3293 // FIXME: Should we support the before margin of the legend? Not entirely clear.
3294 // FIXME: Consider dropping support for the after margin of the legend. Not sure other
3295 // browsers support that anyway.
3296 if (bottomPositionForLegend > fieldsetBorderBefore)
3297 setIntrinsicBorderForFieldset(bottomPositionForLegend - fieldsetBorderBefore);
3298
3299 // Now that the legend is included in the border extent, we can set our logical height
3300 // to the borderBefore (which includes the legend and its after margin if they were bigger
3301 // than the actual fieldset border) and then add in our padding before.
3302 setLogicalHeight(borderBefore() + paddingBefore());
3303}
3304
3305RenderBox* RenderBlock::findFieldsetLegend(FieldsetFindLegendOption option) const
3306{
3307 for (auto& legend : childrenOfType<RenderBox>(*this)) {
3308 if (option == FieldsetIgnoreFloatingOrOutOfFlow && legend.isFloatingOrOutOfFlowPositioned())
3309 continue;
3310 if (legend.isLegend())
3311 return const_cast<RenderBox*>(&legend);
3312 }
3313 return nullptr;
3314}
3315
3316void RenderBlock::adjustBorderBoxRectForPainting(LayoutRect& paintRect)
3317{
3318 if (!isFieldset() || !intrinsicBorderForFieldset())
3319 return;
3320
3321 auto* legend = findFieldsetLegend();
3322 if (!legend)
3323 return;
3324
3325 if (style().isHorizontalWritingMode()) {
3326 LayoutUnit yOff = std::max(0_lu, (legend->height() - RenderBox::borderBefore()) / 2);
3327 paintRect.setHeight(paintRect.height() - yOff);
3328 if (style().writingMode() == TopToBottomWritingMode)
3329 paintRect.setY(paintRect.y() + yOff);
3330 } else {
3331 LayoutUnit xOff = std::max(0_lu, (legend->width() - RenderBox::borderBefore()) / 2);
3332 paintRect.setWidth(paintRect.width() - xOff);
3333 if (style().writingMode() == LeftToRightWritingMode)
3334 paintRect.setX(paintRect.x() + xOff);
3335 }
3336}
3337
3338LayoutRect RenderBlock::paintRectToClipOutFromBorder(const LayoutRect& paintRect)
3339{
3340 LayoutRect clipRect;
3341 if (!isFieldset())
3342 return clipRect;
3343 auto* legend = findFieldsetLegend();
3344 if (!legend)
3345 return clipRect;
3346
3347 LayoutUnit borderExtent = RenderBox::borderBefore();
3348 if (style().isHorizontalWritingMode()) {
3349 clipRect.setX(paintRect.x() + legend->x());
3350 clipRect.setY(style().writingMode() == TopToBottomWritingMode ? paintRect.y() : paintRect.y() + paintRect.height() - borderExtent);
3351 clipRect.setWidth(legend->width());
3352 clipRect.setHeight(borderExtent);
3353 } else {
3354 clipRect.setX(style().writingMode() == LeftToRightWritingMode ? paintRect.x() : paintRect.x() + paintRect.width() - borderExtent);
3355 clipRect.setY(paintRect.y() + legend->y());
3356 clipRect.setWidth(borderExtent);
3357 clipRect.setHeight(legend->height());
3358 }
3359 return clipRect;
3360}
3361
3362LayoutUnit RenderBlock::intrinsicBorderForFieldset() const
3363{
3364 auto* rareData = getBlockRareData(*this);
3365 return rareData ? rareData->m_intrinsicBorderForFieldset : 0_lu;
3366}
3367
3368void RenderBlock::setIntrinsicBorderForFieldset(LayoutUnit padding)
3369{
3370 auto* rareData = getBlockRareData(*this);
3371 if (!rareData) {
3372 if (!padding)
3373 return;
3374 rareData = &ensureBlockRareData(*this);
3375 }
3376 rareData->m_intrinsicBorderForFieldset = padding;
3377}
3378
3379LayoutUnit RenderBlock::borderTop() const
3380{
3381 if (style().writingMode() != TopToBottomWritingMode || !intrinsicBorderForFieldset())
3382 return RenderBox::borderTop();
3383 return RenderBox::borderTop() + intrinsicBorderForFieldset();
3384}
3385
3386LayoutUnit RenderBlock::borderLeft() const
3387{
3388 if (style().writingMode() != LeftToRightWritingMode || !intrinsicBorderForFieldset())
3389 return RenderBox::borderLeft();
3390 return RenderBox::borderLeft() + intrinsicBorderForFieldset();
3391}
3392
3393LayoutUnit RenderBlock::borderBottom() const
3394{
3395 if (style().writingMode() != BottomToTopWritingMode || !intrinsicBorderForFieldset())
3396 return RenderBox::borderBottom();
3397 return RenderBox::borderBottom() + intrinsicBorderForFieldset();
3398}
3399
3400LayoutUnit RenderBlock::borderRight() const
3401{
3402 if (style().writingMode() != RightToLeftWritingMode || !intrinsicBorderForFieldset())
3403 return RenderBox::borderRight();
3404 return RenderBox::borderRight() + intrinsicBorderForFieldset();
3405}
3406
3407LayoutUnit RenderBlock::borderBefore() const
3408{
3409 return RenderBox::borderBefore() + intrinsicBorderForFieldset();
3410}
3411
3412bool RenderBlock::computePreferredWidthsForExcludedChildren(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
3413{
3414 if (!isFieldset())
3415 return false;
3416
3417 auto* legend = findFieldsetLegend();
3418 if (!legend)
3419 return false;
3420
3421 legend->setIsExcludedFromNormalLayout(true);
3422
3423 computeChildPreferredLogicalWidths(*legend, minWidth, maxWidth);
3424
3425 // These are going to be added in later, so we subtract them out to reflect the
3426 // fact that the legend is outside the scrollable area.
3427 auto scrollbarWidth = intrinsicScrollbarLogicalWidth();
3428 minWidth -= scrollbarWidth;
3429 maxWidth -= scrollbarWidth;
3430
3431 const auto& childStyle = legend->style();
3432 auto startMarginLength = childStyle.marginStartUsing(&style());
3433 auto endMarginLength = childStyle.marginEndUsing(&style());
3434 LayoutUnit margin;
3435 LayoutUnit marginStart;
3436 LayoutUnit marginEnd;
3437 if (startMarginLength.isFixed())
3438 marginStart += startMarginLength.value();
3439 if (endMarginLength.isFixed())
3440 marginEnd += endMarginLength.value();
3441 margin = marginStart + marginEnd;
3442
3443 minWidth += margin;
3444 maxWidth += margin;
3445
3446 return true;
3447}
3448
3449LayoutUnit RenderBlock::adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const
3450{
3451 // FIXME: We're doing this to match other browsers even though it's questionable.
3452 // Shouldn't height:100px mean the fieldset content gets 100px of height even if the
3453 // resulting fieldset becomes much taller because of the legend?
3454 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
3455 if (style().boxSizing() == BoxSizing::ContentBox)
3456 return height + bordersPlusPadding - intrinsicBorderForFieldset();
3457 return std::max(height, bordersPlusPadding);
3458}
3459
3460LayoutUnit RenderBlock::adjustContentBoxLogicalHeightForBoxSizing(Optional<LayoutUnit> height) const
3461{
3462 // FIXME: We're doing this to match other browsers even though it's questionable.
3463 // Shouldn't height:100px mean the fieldset content gets 100px of height even if the
3464 // resulting fieldset becomes much taller because of the legend?
3465 if (!height)
3466 return 0;
3467 LayoutUnit result = height.value();
3468 if (style().boxSizing() == BoxSizing::BorderBox)
3469 result -= borderAndPaddingLogicalHeight();
3470 else
3471 result -= intrinsicBorderForFieldset();
3472 return std::max(0_lu, result);
3473}
3474
3475void RenderBlock::paintExcludedChildrenInBorder(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
3476{
3477 if (!isFieldset())
3478 return;
3479
3480 RenderBox* box = findFieldsetLegend();
3481 if (!box || !box->isExcludedFromNormalLayout() || box->hasSelfPaintingLayer())
3482 return;
3483
3484 LayoutPoint childPoint = flipForWritingModeForChild(box, paintOffset);
3485 box->paintAsInlineBlock(paintInfo, childPoint);
3486}
3487
3488bool RenderBlock::hitTestExcludedChildrenInBorder(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
3489{
3490 if (!isFieldset())
3491 return false;
3492
3493 auto* legend = findFieldsetLegend();
3494 if (!legend || !legend->isExcludedFromNormalLayout() || legend->hasSelfPaintingLayer())
3495 return false;
3496
3497 HitTestAction childHitTest = hitTestAction;
3498 if (hitTestAction == HitTestChildBlockBackgrounds)
3499 childHitTest = HitTestChildBlockBackground;
3500 LayoutPoint childPoint = flipForWritingModeForChild(legend, accumulatedOffset);
3501 return legend->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest);
3502}
3503
3504} // namespace WebCore
3505