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.
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.
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.
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.
25 #include "core/layout/LayoutBlock.h"
27 #include "core/HTMLNames.h"
28 #include "core/dom/AXObjectCache.h"
29 #include "core/dom/Document.h"
30 #include "core/dom/Element.h"
31 #include "core/dom/StyleEngine.h"
32 #include "core/dom/shadow/ShadowRoot.h"
33 #include "core/editing/EditingUtilities.h"
34 #include "core/editing/Editor.h"
35 #include "core/editing/FrameSelection.h"
36 #include "core/fetch/ResourceLoadPriorityOptimizer.h"
37 #include "core/frame/FrameView.h"
38 #include "core/frame/LocalFrame.h"
39 #include "core/frame/Settings.h"
40 #include "core/html/HTMLMarqueeElement.h"
41 #include "core/layout/HitTestLocation.h"
42 #include "core/layout/HitTestResult.h"
43 #include "core/layout/LayoutAnalyzer.h"
44 #include "core/layout/LayoutDeprecatedFlexibleBox.h"
45 #include "core/layout/LayoutFlexibleBox.h"
46 #include "core/layout/LayoutFlowThread.h"
47 #include "core/layout/LayoutGrid.h"
48 #include "core/layout/LayoutInline.h"
49 #include "core/layout/LayoutMultiColumnSpannerPlaceholder.h"
50 #include "core/layout/LayoutObject.h"
51 #include "core/layout/LayoutTableCell.h"
52 #include "core/layout/LayoutTextCombine.h"
53 #include "core/layout/LayoutTextControl.h"
54 #include "core/layout/LayoutTextFragment.h"
55 #include "core/layout/LayoutTheme.h"
56 #include "core/layout/LayoutView.h"
57 #include "core/layout/TextAutosizer.h"
58 #include "core/layout/api/LineLayoutItem.h"
59 #include "core/layout/line/GlyphOverflow.h"
60 #include "core/layout/line/InlineIterator.h"
61 #include "core/layout/line/InlineTextBox.h"
62 #include "core/layout/shapes/ShapeOutsideInfo.h"
63 #include "core/layout/svg/LayoutSVGResourceClipper.h"
64 #include "core/page/Page.h"
65 #include "core/paint/BlockPainter.h"
66 #include "core/paint/BoxPainter.h"
67 #include "core/paint/DeprecatedPaintLayer.h"
68 #include "core/paint/LayoutObjectDrawingRecorder.h"
69 #include "core/style/ComputedStyle.h"
70 #include "core/style/ContentData.h"
71 #include "platform/RuntimeEnabledFeatures.h"
72 #include "platform/geometry/FloatQuad.h"
73 #include "platform/geometry/TransformState.h"
74 #include "wtf/StdLibExtras.h"
75 #include "wtf/TemporaryChange.h"
78 using namespace Unicode
;
82 using namespace HTMLNames
;
84 struct SameSizeAsLayoutBlock
: public LayoutBox
{
85 LayoutObjectChildList children
;
86 LineBoxList lineBoxes
;
90 static_assert(sizeof(LayoutBlock
) == sizeof(SameSizeAsLayoutBlock
), "LayoutBlock should stay small");
92 // This map keeps track of the positioned objects associated with a containing
95 // This map is populated during layout. It is kept across layouts to handle
96 // that we skip unchanged sub-trees during layout, in such a way that we are
97 // able to lay out deeply nested out-of-flow descendants if their containing
98 // block got laid out. The map could be invalidated during style change but
99 // keeping track of containing blocks at that time is complicated (we are in
100 // the middle of recomputing the style so we can't rely on any of its
101 // information), which is why it's easier to just update it for every layout.
102 static TrackedDescendantsMap
* gPositionedDescendantsMap
= nullptr;
104 // This map keeps track of the descendants whose 'height' is percentage associated
105 // with a containing block. Like |gPositionedDescendantsMap|, it is also recomputed
106 // for every layout (see the comment above about why).
107 static TrackedDescendantsMap
* gPercentHeightDescendantsMap
= nullptr;
109 static TrackedContainerMap
* gPositionedContainerMap
= nullptr;
110 static TrackedContainerMap
* gPercentHeightContainerMap
= nullptr;
113 DoubleSize scrollOffset
;
114 bool autoHorizontalScrollBarChanged
;
115 bool autoVerticalScrollBarChanged
;
116 bool hasOffset() const { return scrollOffset
!= DoubleSize(); }
117 bool scrollBarsChanged() const { return autoHorizontalScrollBarChanged
|| autoVerticalScrollBarChanged
; }
118 void merge(const ScrollInfo
& other
)
120 // We always keep the first scrollOffset we saw for this block, so don't copy that field.
121 autoHorizontalScrollBarChanged
|= other
.autoHorizontalScrollBarChanged
;
122 autoVerticalScrollBarChanged
|= other
.autoVerticalScrollBarChanged
;
125 typedef WTF::HashMap
<LayoutBlock
*, ScrollInfo
> DelayedUpdateScrollInfoMap
;
126 static int gDelayUpdateScrollInfo
= 0;
127 static DelayedUpdateScrollInfoMap
* gDelayedUpdateScrollInfoMap
= nullptr;
129 LayoutBlock::LayoutBlock(ContainerNode
* node
)
131 , m_hasMarginBeforeQuirk(false)
132 , m_hasMarginAfterQuirk(false)
133 , m_beingDestroyed(false)
134 , m_hasMarkupTruncation(false)
135 , m_widthAvailableToChildrenChanged(false)
136 , m_hasOnlySelfCollapsingChildren(false)
137 , m_descendantsWithFloatsMarkedForLayout(false)
138 , m_needsRecalcLogicalWidthAfterLayoutChildren(false)
140 // LayoutBlockFlow calls setChildrenInline(true).
141 // By default, subclasses do not have inline children.
144 static void removeBlockFromDescendantAndContainerMaps(LayoutBlock
* block
, TrackedDescendantsMap
*& descendantMap
, TrackedContainerMap
*& containerMap
)
146 if (OwnPtr
<TrackedLayoutBoxListHashSet
> descendantSet
= descendantMap
->take(block
)) {
147 for (auto& descendant
: *descendantSet
) {
148 TrackedContainerMap::iterator it
= containerMap
->find(descendant
);
149 ASSERT(it
!= containerMap
->end());
150 if (it
== containerMap
->end())
152 HashSet
<LayoutBlock
*>* containerSet
= it
->value
.get();
153 ASSERT(containerSet
->contains(block
));
154 containerSet
->remove(block
);
155 if (containerSet
->isEmpty())
156 containerMap
->remove(it
);
161 static void appendImageIfNotNull(Vector
<ImageResource
*>& imageResources
, const StyleImage
* styleImage
)
163 if (styleImage
&& styleImage
->cachedImage()) {
164 ImageResource
* imageResource
= styleImage
->cachedImage();
165 if (imageResource
&& !imageResource
->isLoaded())
166 imageResources
.append(styleImage
->cachedImage());
170 static void appendLayers(Vector
<ImageResource
*>& images
, const FillLayer
& styleLayer
)
172 for (const FillLayer
* layer
= &styleLayer
; layer
; layer
= layer
->next())
173 appendImageIfNotNull(images
, layer
->image());
176 static void appendImagesFromStyle(Vector
<ImageResource
*>& images
, const ComputedStyle
& blockStyle
)
178 appendLayers(images
, blockStyle
.backgroundLayers());
179 appendLayers(images
, blockStyle
.maskLayers());
181 const ContentData
* contentData
= blockStyle
.contentData();
182 if (contentData
&& contentData
->isImage())
183 appendImageIfNotNull(images
, toImageContentData(contentData
)->image());
184 if (blockStyle
.boxReflect())
185 appendImageIfNotNull(images
, blockStyle
.boxReflect()->mask().image());
186 appendImageIfNotNull(images
, blockStyle
.listStyleImage());
187 appendImageIfNotNull(images
, blockStyle
.borderImageSource());
188 appendImageIfNotNull(images
, blockStyle
.maskBoxImageSource());
189 if (blockStyle
.shapeOutside())
190 appendImageIfNotNull(images
, blockStyle
.shapeOutside()->image());
193 void LayoutBlock::removeFromGlobalMaps()
195 if (gPercentHeightDescendantsMap
)
196 removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap
, gPercentHeightContainerMap
);
197 if (gPositionedDescendantsMap
)
198 removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap
, gPositionedContainerMap
);
201 LayoutBlock::~LayoutBlock()
203 removeFromGlobalMaps();
206 void LayoutBlock::willBeDestroyed()
208 // Mark as being destroyed to avoid trouble with merges in removeChild().
209 m_beingDestroyed
= true;
211 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
212 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
213 children()->destroyLeftoverChildren();
215 // Destroy our continuation before anything other than anonymous children.
216 // The reason we don't destroy it before anonymous children is that they may
217 // have continuations of their own that are anonymous children of our continuation.
218 LayoutBoxModelObject
* continuation
= this->continuation();
220 continuation
->destroy();
221 setContinuation(nullptr);
224 if (!documentBeingDestroyed()) {
225 if (firstLineBox()) {
226 // We can't wait for LayoutBox::destroy to clear the selection,
227 // because by then we will have nuked the line boxes.
228 // FIXME: The FrameSelection should be responsible for this when it
229 // is notified of DOM mutations.
230 if (isSelectionBorder())
231 view()->clearSelection();
233 // If we are an anonymous block, then our line boxes might have children
234 // that will outlast this block. In the non-anonymous block case those
235 // children will be destroyed by the time we return from this function.
236 if (isAnonymousBlock()) {
237 for (InlineFlowBox
* box
= firstLineBox(); box
; box
= box
->nextLineBox()) {
238 while (InlineBox
* childBox
= box
->firstChild())
242 } else if (parent()) {
243 parent()->dirtyLinesFromChangedChild(this);
247 m_lineBoxes
.deleteLineBoxes();
249 if (UNLIKELY(gDelayedUpdateScrollInfoMap
!= 0))
250 gDelayedUpdateScrollInfoMap
->remove(this);
252 if (TextAutosizer
* textAutosizer
= document().textAutosizer())
253 textAutosizer
->destroy(this);
255 LayoutBox::willBeDestroyed();
258 void LayoutBlock::styleWillChange(StyleDifference diff
, const ComputedStyle
& newStyle
)
260 const ComputedStyle
* oldStyle
= style();
262 setReplaced(newStyle
.isDisplayInlineType());
264 if (oldStyle
&& parent()) {
265 bool oldHasTransformRelatedProperty
= oldStyle
->hasTransformRelatedProperty();
266 bool newHasTransformRelatedProperty
= newStyle
.hasTransformRelatedProperty();
267 bool oldStyleIsContainer
= oldStyle
->position() != StaticPosition
|| oldHasTransformRelatedProperty
;
269 if (oldStyleIsContainer
&& (newStyle
.position() == StaticPosition
|| (oldHasTransformRelatedProperty
&& !newHasTransformRelatedProperty
))) {
270 // Clear our positioned objects list. Our absolutely positioned descendants will be
271 // inserted into our containing block's positioned objects list during layout.
272 removePositionedObjects(0, NewContainingBlock
);
273 } else if (!oldStyleIsContainer
&& (newStyle
.position() != StaticPosition
|| newHasTransformRelatedProperty
)) {
274 // Remove our absolutely positioned descendants from their current containing block.
275 // They will be inserted into our positioned objects list during layout.
276 if (LayoutBlock
* cb
= containingBlockForAbsolutePosition())
277 cb
->removePositionedObjects(this, NewContainingBlock
);
281 LayoutBox::styleWillChange(diff
, newStyle
);
284 static bool borderOrPaddingLogicalWidthChanged(const ComputedStyle
& oldStyle
, const ComputedStyle
& newStyle
)
286 if (newStyle
.isHorizontalWritingMode()) {
287 return oldStyle
.borderLeftWidth() != newStyle
.borderLeftWidth()
288 || oldStyle
.borderRightWidth() != newStyle
.borderRightWidth()
289 || oldStyle
.paddingLeft() != newStyle
.paddingLeft()
290 || oldStyle
.paddingRight() != newStyle
.paddingRight();
293 return oldStyle
.borderTopWidth() != newStyle
.borderTopWidth()
294 || oldStyle
.borderBottomWidth() != newStyle
.borderBottomWidth()
295 || oldStyle
.paddingTop() != newStyle
.paddingTop()
296 || oldStyle
.paddingBottom() != newStyle
.paddingBottom();
299 void LayoutBlock::styleDidChange(StyleDifference diff
, const ComputedStyle
* oldStyle
)
301 LayoutBox::styleDidChange(diff
, oldStyle
);
303 if (isFloatingOrOutOfFlowPositioned() && oldStyle
&& !oldStyle
->isFloating() && !oldStyle
->hasOutOfFlowPosition() && parent() && parent()->isLayoutBlockFlow()) {
304 toLayoutBlock(parent())->removeAnonymousWrappersIfRequired();
305 // Reparent to an adjacent anonymous block if one is available.
306 if (previousSibling() && previousSibling()->isAnonymousBlock())
307 toLayoutBlock(parent())->moveChildTo(toLayoutBlock(previousSibling()), this, nullptr, false);
308 else if (nextSibling() && nextSibling()->isAnonymousBlock())
309 toLayoutBlock(parent())->moveChildTo(toLayoutBlock(nextSibling()), this, nextSibling()->slowFirstChild(), false);
312 const ComputedStyle
& newStyle
= styleRef();
314 if (oldStyle
&& parent()) {
315 if (oldStyle
->position() != newStyle
.position() && newStyle
.position() != StaticPosition
) {
316 // Remove our absolutely positioned descendants from their new containing block,
317 // in case containingBlock() changes by the change to the position property.
318 // See styleWillChange() for other cases.
319 if (LayoutBlock
* cb
= containingBlock())
320 cb
->removePositionedObjects(this, NewContainingBlock
);
324 if (TextAutosizer
* textAutosizer
= document().textAutosizer())
325 textAutosizer
->record(this);
327 propagateStyleToAnonymousChildren(true);
329 // It's possible for our border/padding to change, but for the overall logical width of the block to
330 // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true.
331 m_widthAvailableToChildrenChanged
|= oldStyle
&& diff
.needsFullLayout() && needsLayout() && borderOrPaddingLogicalWidthChanged(*oldStyle
, newStyle
);
333 // If the style has unloaded images, want to notify the ResourceLoadPriorityOptimizer so that
334 // network priorities can be set.
335 Vector
<ImageResource
*> images
;
336 appendImagesFromStyle(images
, newStyle
);
337 if (images
.isEmpty())
338 ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->removeLayoutObject(this);
340 ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->addLayoutObject(this);
343 void LayoutBlock::invalidatePaintOfSubtreesIfNeeded(PaintInvalidationState
& childPaintInvalidationState
)
345 LayoutBox::invalidatePaintOfSubtreesIfNeeded(childPaintInvalidationState
);
347 // Take care of positioned objects. This is required as PaintInvalidationState keeps a single clip rect.
348 if (TrackedLayoutBoxListHashSet
* positionedObjects
= this->positionedObjects()) {
349 for (auto* box
: *positionedObjects
) {
351 // One of the layoutObjects we're skipping over here may be the child's paint invalidation container,
352 // so we can't pass our own paint invalidation container along.
353 const LayoutBoxModelObject
& paintInvalidationContainerForChild
= *box
->containerForPaintInvalidation();
355 // If it's a new paint invalidation container, we won't have properly accumulated the offset into the
356 // PaintInvalidationState.
357 // FIXME: Teach PaintInvalidationState to handle this case. crbug.com/371485
358 if (paintInvalidationContainerForChild
!= childPaintInvalidationState
.paintInvalidationContainer()) {
359 ForceHorriblySlowRectMapping
slowRectMapping(&childPaintInvalidationState
);
360 PaintInvalidationState
disabledPaintInvalidationState(childPaintInvalidationState
, *this, paintInvalidationContainerForChild
);
361 box
->invalidateTreeIfNeeded(disabledPaintInvalidationState
);
365 // If the positioned layoutObject is absolutely positioned and it is inside
366 // a relatively positioned inline element, we need to account for
367 // the inline elements position in PaintInvalidationState.
368 if (box
->style()->position() == AbsolutePosition
) {
369 LayoutObject
* container
= box
->container(&paintInvalidationContainerForChild
, 0);
370 if (container
->isInFlowPositioned() && container
->isLayoutInline()) {
371 // FIXME: We should be able to use PaintInvalidationState for this.
372 // Currently, we will place absolutely positioned elements inside
373 // relatively positioned inline blocks in the wrong location. crbug.com/371485
374 ForceHorriblySlowRectMapping
slowRectMapping(&childPaintInvalidationState
);
375 PaintInvalidationState
disabledPaintInvalidationState(childPaintInvalidationState
, *this, paintInvalidationContainerForChild
);
376 box
->invalidateTreeIfNeeded(disabledPaintInvalidationState
);
381 box
->invalidateTreeIfNeeded(childPaintInvalidationState
);
386 inline static void invalidateDisplayItemClientForStartOfContinuationsIfNeeded(const LayoutBlock
& block
)
388 // If the block is a continuation or containing block of an inline continuation, invalidate the
389 // start object of the continuations if it has focus ring because change of continuation may change
390 // the shape of the focus ring.
391 if (!block
.isAnonymous())
394 LayoutObject
* startOfContinuations
= nullptr;
395 if (LayoutInline
* inlineElementContinuation
= block
.inlineElementContinuation()) {
396 // This block is an anonymous block continuation.
397 startOfContinuations
= inlineElementContinuation
->node()->layoutObject();
398 } else if (LayoutObject
* firstChild
= block
.firstChild()) {
399 // This block is the anonymous containing block of an inline element continuation.
400 if (firstChild
->isElementContinuation())
401 startOfContinuations
= firstChild
->node()->layoutObject();
403 if (startOfContinuations
&& startOfContinuations
->styleRef().outlineStyleIsAuto())
404 startOfContinuations
->invalidateDisplayItemClient(*startOfContinuations
);
407 void LayoutBlock::invalidateDisplayItemClients(const LayoutBoxModelObject
& paintInvalidationContainer
) const
409 LayoutBox::invalidateDisplayItemClients(paintInvalidationContainer
);
410 invalidateDisplayItemClientForStartOfContinuationsIfNeeded(*this);
413 void LayoutBlock::addChildIgnoringContinuation(LayoutObject
* newChild
, LayoutObject
* beforeChild
)
415 if (beforeChild
&& beforeChild
->parent() != this) {
416 LayoutObject
* beforeChildContainer
= beforeChild
->parent();
417 while (beforeChildContainer
->parent() != this)
418 beforeChildContainer
= beforeChildContainer
->parent();
419 ASSERT(beforeChildContainer
);
421 if (beforeChildContainer
->isAnonymous()) {
422 // If the requested beforeChild is not one of our children, then this is because
423 // there is an anonymous container within this object that contains the beforeChild.
424 LayoutObject
* beforeChildAnonymousContainer
= beforeChildContainer
;
425 if (beforeChildAnonymousContainer
->isAnonymousBlock()
426 // Full screen layoutObjects and full screen placeholders act as anonymous blocks, not tables:
427 || beforeChildAnonymousContainer
->isLayoutFullScreen()
428 || beforeChildAnonymousContainer
->isLayoutFullScreenPlaceholder()
430 // Insert the child into the anonymous block box instead of here.
431 if (newChild
->isInline() || newChild
->isFloatingOrOutOfFlowPositioned() || beforeChild
->parent()->slowFirstChild() != beforeChild
)
432 beforeChild
->parent()->addChild(newChild
, beforeChild
);
434 addChild(newChild
, beforeChild
->parent());
438 ASSERT(beforeChildAnonymousContainer
->isTable());
439 if (newChild
->isTablePart()) {
440 // Insert into the anonymous table.
441 beforeChildAnonymousContainer
->addChild(newChild
, beforeChild
);
445 beforeChild
= splitAnonymousBoxesAroundChild(beforeChild
);
447 ASSERT(beforeChild
->parent() == this);
448 if (beforeChild
->parent() != this) {
449 // We should never reach here. If we do, we need to use the
450 // safe fallback to use the topmost beforeChild container.
451 beforeChild
= beforeChildContainer
;
456 bool madeBoxesNonInline
= false;
458 // A block has to either have all of its children inline, or all of its children as blocks.
459 // So, if our children are currently inline and a block child has to be inserted, we move all our
460 // inline children into anonymous block boxes.
461 if (childrenInline() && !newChild
->isInline() && !newChild
->isFloatingOrOutOfFlowPositioned()) {
462 // This is a block with inline content. Wrap the inline content in anonymous blocks.
463 makeChildrenNonInline(beforeChild
);
464 madeBoxesNonInline
= true;
466 if (beforeChild
&& beforeChild
->parent() != this) {
467 beforeChild
= beforeChild
->parent();
468 ASSERT(beforeChild
->isAnonymousBlock());
469 ASSERT(beforeChild
->parent() == this);
471 } else if (!childrenInline() && (newChild
->isFloatingOrOutOfFlowPositioned() || newChild
->isInline())) {
472 // If we're inserting an inline child but all of our children are blocks, then we have to make sure
473 // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise
474 // a new one is created and inserted into our list of children in the appropriate position.
475 LayoutObject
* afterChild
= beforeChild
? beforeChild
->previousSibling() : lastChild();
477 if (afterChild
&& afterChild
->isAnonymousBlock()) {
478 afterChild
->addChild(newChild
);
482 if (newChild
->isInline()) {
483 // No suitable existing anonymous box - create a new one.
484 LayoutBlock
* newBox
= createAnonymousBlock();
485 LayoutBox::addChild(newBox
, beforeChild
);
486 // Reparent adjacent floating or out-of-flow siblings to the new box.
487 LayoutObject
* child
= newBox
->previousSibling();
488 while (child
&& child
->isFloatingOrOutOfFlowPositioned()) {
489 LayoutObject
* sibling
= child
->previousSibling();
490 moveChildTo(newBox
, child
, newBox
->firstChild(), false);
493 newBox
->addChild(newChild
);
494 child
= newBox
->nextSibling();
495 while (child
&& child
->isFloatingOrOutOfFlowPositioned()) {
496 LayoutObject
* sibling
= child
->nextSibling();
497 moveChildTo(newBox
, child
, nullptr, false);
504 LayoutBox::addChild(newChild
, beforeChild
);
506 if (madeBoxesNonInline
&& parent() && isAnonymousBlock() && parent()->isLayoutBlock())
507 toLayoutBlock(parent())->removeLeftoverAnonymousBlock(this);
508 // this object may be dead here
511 void LayoutBlock::addChild(LayoutObject
* newChild
, LayoutObject
* beforeChild
)
513 addChildIgnoringContinuation(newChild
, beforeChild
);
516 static void getInlineRun(LayoutObject
* start
, LayoutObject
* boundary
,
517 LayoutObject
*& inlineRunStart
,
518 LayoutObject
*& inlineRunEnd
)
520 // Beginning at |start| we find the largest contiguous run of inlines that
521 // we can. We denote the run with start and end points, |inlineRunStart|
522 // and |inlineRunEnd|. Note that these two values may be the same if
523 // we encounter only one inline.
525 // We skip any non-inlines we encounter as long as we haven't found any
528 // |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary|
529 // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered
532 // Start by skipping as many non-inlines as we can.
533 LayoutObject
* curr
= start
;
536 while (curr
&& !(curr
->isInline() || curr
->isFloatingOrOutOfFlowPositioned()))
537 curr
= curr
->nextSibling();
539 inlineRunStart
= inlineRunEnd
= curr
;
542 return; // No more inline children to be found.
544 sawInline
= curr
->isInline();
546 curr
= curr
->nextSibling();
547 while (curr
&& (curr
->isInline() || curr
->isFloatingOrOutOfFlowPositioned()) && (curr
!= boundary
)) {
549 if (curr
->isInline())
551 curr
= curr
->nextSibling();
553 } while (!sawInline
);
556 void LayoutBlock::deleteLineBoxTree()
558 ASSERT(!m_lineBoxes
.firstLineBox());
561 void LayoutBlock::makeChildrenNonInline(LayoutObject
*insertionPoint
)
563 // makeChildrenNonInline takes a block whose children are *all* inline and it
564 // makes sure that inline children are coalesced under anonymous
565 // blocks. If |insertionPoint| is defined, then it represents the insertion point for
566 // the new block child that is causing us to have to wrap all the inlines. This
567 // means that we cannot coalesce inlines before |insertionPoint| with inlines following
568 // |insertionPoint|, because the new child is going to be inserted in between the inlines,
570 ASSERT(isInlineBlockOrInlineTable() || !isInline());
571 ASSERT(!insertionPoint
|| insertionPoint
->parent() == this);
573 setChildrenInline(false);
575 LayoutObject
* child
= firstChild();
582 LayoutObject
* inlineRunStart
;
583 LayoutObject
* inlineRunEnd
;
584 getInlineRun(child
, insertionPoint
, inlineRunStart
, inlineRunEnd
);
589 child
= inlineRunEnd
->nextSibling();
591 LayoutBlock
* block
= createAnonymousBlock();
592 children()->insertChildNode(this, block
, inlineRunStart
);
593 moveChildrenTo(block
, inlineRunStart
, child
);
597 for (LayoutObject
*c
= firstChild(); c
; c
= c
->nextSibling())
598 ASSERT(!c
->isInline());
601 setShouldDoFullPaintInvalidation();
604 void LayoutBlock::removeLeftoverAnonymousBlock(LayoutBlock
* child
)
606 ASSERT(child
->isAnonymousBlock());
607 ASSERT(!child
->childrenInline());
608 ASSERT(child
->parent() == this);
610 if (child
->continuation())
613 // Promote all the leftover anonymous block's children (to become children of this block
614 // instead). We still want to keep the leftover block in the tree for a moment, for notification
615 // purposes done further below (flow threads and grids).
616 child
->moveAllChildrenTo(this, child
->nextSibling());
618 // Remove all the information in the flow thread associated with the leftover anonymous block.
619 child
->removeFromLayoutFlowThread();
621 // LayoutGrid keeps track of its children, we must notify it about changes in the tree.
622 if (child
->parent()->isLayoutGrid())
623 toLayoutGrid(child
->parent())->dirtyGrid();
625 // Now remove the leftover anonymous block from the tree, and destroy it. We'll rip it out
626 // manually from the tree before destroying it, because we don't want to trigger any tree
627 // adjustments with regards to anonymous blocks (or any other kind of undesired chain-reaction).
628 children()->removeChildNode(this, child
, false);
632 static bool canMergeContiguousAnonymousBlocks(LayoutObject
* oldChild
, LayoutObject
* prev
, LayoutObject
* next
)
634 if (oldChild
->documentBeingDestroyed() || oldChild
->isInline() || oldChild
->virtualContinuation())
637 if ((prev
&& (!prev
->isAnonymousBlock() || toLayoutBlock(prev
)->continuation() || toLayoutBlock(prev
)->beingDestroyed()))
638 || (next
&& (!next
->isAnonymousBlock() || toLayoutBlock(next
)->continuation() || toLayoutBlock(next
)->beingDestroyed())))
641 if ((prev
&& (prev
->isRubyRun() || prev
->isRubyBase()))
642 || (next
&& (next
->isRubyRun() || next
->isRubyBase())))
648 void LayoutBlock::removeAnonymousWrappersIfRequired()
650 ASSERT(isLayoutBlockFlow());
651 Vector
<LayoutBox
*, 16> blocksToRemove
;
652 for (LayoutBox
* child
= firstChildBox(); child
; child
= child
->nextSiblingBox()) {
653 if (child
->isFloatingOrOutOfFlowPositioned())
656 // There are still block children in the container, so any anonymous wrappers are still needed.
657 if (!child
->isAnonymousBlock())
659 // We can't remove anonymous wrappers if they contain continuations as this means there are block children present.
660 if (child
->isLayoutBlock() && toLayoutBlock(child
)->continuation())
662 // We are only interested in removing anonymous wrappers if there are inline siblings underneath them.
663 if (!child
->childrenInline())
666 if (child
->isAnonymousBlock())
667 blocksToRemove
.append(child
);
670 for (size_t i
= 0; i
< blocksToRemove
.size(); i
++)
671 collapseAnonymousBlockChild(this, toLayoutBlock(blocksToRemove
[i
]));
674 void LayoutBlock::collapseAnonymousBlockChild(LayoutBlock
* parent
, LayoutBlock
* child
)
676 // It's possible that this block's destruction may have been triggered by the
677 // child's removal. Just bail if the anonymous child block is already being
678 // destroyed. See crbug.com/282088
679 if (child
->beingDestroyed())
681 parent
->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(LayoutInvalidationReason::ChildAnonymousBlockChanged
);
682 parent
->setChildrenInline(child
->childrenInline());
683 LayoutObject
* nextSibling
= child
->nextSibling();
685 parent
->children()->removeChildNode(parent
, child
, child
->hasLayer());
686 child
->moveAllChildrenTo(parent
, nextSibling
, child
->hasLayer());
687 // Explicitly delete the child's line box tree, or the special anonymous
688 // block handling in willBeDestroyed will cause problems.
689 child
->deleteLineBoxTree();
693 static inline bool shouldMakeChildrenInline(const LayoutBlock
* block
)
695 if (!block
->isLayoutBlockFlow())
697 LayoutObject
* child
= block
->firstChild();
699 // TODO(rhogan): If we encounter anonymous blocks with inline children we should fold them in here.
700 if (!child
->isFloatingOrOutOfFlowPositioned())
702 child
= child
->nextSibling();
707 void LayoutBlock::removeChild(LayoutObject
* oldChild
)
709 // No need to waste time in merging or removing empty anonymous blocks.
710 // We can just bail out if our document is getting destroyed.
711 if (documentBeingDestroyed()) {
712 LayoutBox::removeChild(oldChild
);
716 // If this child is a block, and if our previous and next siblings are
717 // both anonymous blocks with inline content, then we can go ahead and
718 // fold the inline content back together.
719 LayoutObject
* prev
= oldChild
->previousSibling();
720 LayoutObject
* next
= oldChild
->nextSibling();
721 bool canMergeAnonymousBlocks
= canMergeContiguousAnonymousBlocks(oldChild
, prev
, next
);
722 if (canMergeAnonymousBlocks
&& prev
&& next
) {
723 prev
->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(LayoutInvalidationReason::AnonymousBlockChange
);
724 LayoutBlockFlow
* nextBlock
= toLayoutBlockFlow(next
);
725 LayoutBlockFlow
* prevBlock
= toLayoutBlockFlow(prev
);
727 if (prev
->childrenInline() != next
->childrenInline()) {
728 LayoutBlock
* inlineChildrenBlock
= prev
->childrenInline() ? prevBlock
: nextBlock
;
729 LayoutBlock
* blockChildrenBlock
= prev
->childrenInline() ? nextBlock
: prevBlock
;
731 // Place the inline children block inside of the block children block instead of deleting it.
732 ASSERT(!inlineChildrenBlock
->continuation());
733 bool inlineChildrenBlockHasLayer
= inlineChildrenBlock
->hasLayer();
734 children()->removeChildNode(this, inlineChildrenBlock
, inlineChildrenBlockHasLayer
);
736 // Now just put the inlineChildrenBlock inside the blockChildrenBlock.
737 blockChildrenBlock
->children()->insertChildNode(blockChildrenBlock
, inlineChildrenBlock
, prev
== inlineChildrenBlock
? blockChildrenBlock
->firstChild() : 0,
738 inlineChildrenBlockHasLayer
|| blockChildrenBlock
->hasLayer());
739 next
->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(LayoutInvalidationReason::AnonymousBlockChange
);
741 // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child
742 // of "this". we null out prev or next so that is not used later in the function.
743 if (inlineChildrenBlock
== prevBlock
)
748 // Take all the children out of the |next| block and put them in
750 nextBlock
->moveAllChildrenIncludingFloatsTo(prevBlock
, nextBlock
->hasLayer() || prevBlock
->hasLayer());
752 // Delete the now-empty block's lines and nuke it.
753 nextBlock
->deleteLineBoxTree();
754 nextBlock
->destroy();
759 LayoutBox::removeChild(oldChild
);
761 LayoutObject
* child
= prev
? prev
: next
;
762 if (canMergeAnonymousBlocks
&& child
&& !child
->previousSibling() && !child
->nextSibling() && canCollapseAnonymousBlockChild()) {
763 // The removal has knocked us down to containing only a single anonymous
764 // box. We can go ahead and pull the content right back up into our
766 collapseAnonymousBlockChild(this, toLayoutBlock(child
));
767 } else if (((prev
&& prev
->isAnonymousBlock()) || (next
&& next
->isAnonymousBlock())) && canCollapseAnonymousBlockChild()) {
768 // It's possible that the removal has knocked us down to a single anonymous
769 // block with pseudo-style element siblings (e.g. first-letter). If these
770 // are floating, then we need to pull the content up also.
771 LayoutBlock
* anonymousBlock
= toLayoutBlock((prev
&& prev
->isAnonymousBlock()) ? prev
: next
);
772 if ((anonymousBlock
->previousSibling() || anonymousBlock
->nextSibling())
773 && (!anonymousBlock
->previousSibling() || (anonymousBlock
->previousSibling()->style()->styleType() != NOPSEUDO
&& anonymousBlock
->previousSibling()->isFloating() && !anonymousBlock
->previousSibling()->previousSibling()))
774 && (!anonymousBlock
->nextSibling() || (anonymousBlock
->nextSibling()->style()->styleType() != NOPSEUDO
&& anonymousBlock
->nextSibling()->isFloating() && !anonymousBlock
->nextSibling()->nextSibling()))) {
775 collapseAnonymousBlockChild(this, anonymousBlock
);
780 // If this was our last child be sure to clear out our line boxes.
781 if (childrenInline())
784 // If we are an empty anonymous block in the continuation chain,
785 // we need to remove ourself and fix the continuation chain.
786 if (!beingDestroyed() && isAnonymousBlockContinuation() && !oldChild
->isListMarker()) {
787 LayoutObject
* containingBlockIgnoringAnonymous
= containingBlock();
788 while (containingBlockIgnoringAnonymous
&& containingBlockIgnoringAnonymous
->isAnonymous())
789 containingBlockIgnoringAnonymous
= containingBlockIgnoringAnonymous
->containingBlock();
790 for (LayoutObject
* curr
= this; curr
; curr
= curr
->previousInPreOrder(containingBlockIgnoringAnonymous
)) {
791 if (curr
->virtualContinuation() != this)
794 // Found our previous continuation. We just need to point it to
795 // |this|'s next continuation.
796 LayoutBoxModelObject
* nextContinuation
= continuation();
797 if (curr
->isLayoutInline())
798 toLayoutInline(curr
)->setContinuation(nextContinuation
);
799 else if (curr
->isLayoutBlock())
800 toLayoutBlock(curr
)->setContinuation(nextContinuation
);
802 ASSERT_NOT_REACHED();
806 setContinuation(nullptr);
809 } else if (!beingDestroyed() && !oldChild
->isFloatingOrOutOfFlowPositioned() && shouldMakeChildrenInline(this)) {
810 // If the child we're removing means that we can now treat all children as inline without the need for anonymous blocks, then do that.
811 setChildrenInline(true);
815 bool LayoutBlock::isSelfCollapsingBlock() const
817 // We are not self-collapsing if we
818 // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
820 // (c) have border/padding,
821 // (d) have a min-height
822 // (e) have specified that one of our margins can't collapse using a CSS extension
823 // (f) establish a new block formatting context.
825 // The early exit must be done before we check for clean layout.
826 // We should be able to give a quick answer if the box is a relayout boundary.
827 // Being a relayout boundary implies a block formatting context, and also
828 // our internal layout shouldn't affect our container in any way.
829 if (createsNewFormattingContext())
832 // Placeholder elements are not laid out until the dimensions of their parent text control are known, so they
833 // don't get layout until their parent has had layout - this is unique in the layout tree and means
834 // when we call isSelfCollapsingBlock on them we find that they still need layout.
835 ASSERT(!needsLayout() || (node() && node()->isElementNode() && toElement(node())->shadowPseudoId() == "-webkit-input-placeholder"));
837 if (logicalHeight() > 0
838 || isTable() || borderAndPaddingLogicalHeight()
839 || style()->logicalMinHeight().isPositive()
840 || style()->marginBeforeCollapse() == MSEPARATE
|| style()->marginAfterCollapse() == MSEPARATE
)
843 Length logicalHeightLength
= style()->logicalHeight();
844 bool hasAutoHeight
= logicalHeightLength
.isAuto();
845 if (logicalHeightLength
.hasPercent() && !document().inQuirksMode()) {
846 hasAutoHeight
= true;
847 for (LayoutBlock
* cb
= containingBlock(); !cb
->isLayoutView(); cb
= cb
->containingBlock()) {
848 if (cb
->style()->logicalHeight().isFixed() || cb
->isTableCell())
849 hasAutoHeight
= false;
853 // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
854 // on whether we have content that is all self-collapsing or not.
855 // TODO(alancutter): Make this work correctly for calc lengths.
856 if (hasAutoHeight
|| ((logicalHeightLength
.isFixed() || logicalHeightLength
.hasPercent()) && logicalHeightLength
.isZero())) {
857 // If the block has inline children, see if we generated any line boxes. If we have any
858 // line boxes, then we can't be self-collapsing, since we have content.
859 if (childrenInline())
860 return !firstLineBox();
862 // Whether or not we collapse is dependent on whether all our normal flow children
863 // are also self-collapsing.
864 if (m_hasOnlySelfCollapsingChildren
)
866 for (LayoutBox
* child
= firstChildBox(); child
; child
= child
->nextSiblingBox()) {
867 if (child
->isFloatingOrOutOfFlowPositioned())
869 if (!child
->isSelfCollapsingBlock())
877 void LayoutBlock::startDelayUpdateScrollInfo()
879 if (gDelayUpdateScrollInfo
== 0) {
880 ASSERT(!gDelayedUpdateScrollInfoMap
);
881 gDelayedUpdateScrollInfoMap
= new DelayedUpdateScrollInfoMap
;
883 ASSERT(gDelayedUpdateScrollInfoMap
);
884 ++gDelayUpdateScrollInfo
;
887 void LayoutBlock::finishDelayUpdateScrollInfo()
889 --gDelayUpdateScrollInfo
;
890 ASSERT(gDelayUpdateScrollInfo
>= 0);
891 if (gDelayUpdateScrollInfo
== 0) {
892 ASSERT(gDelayedUpdateScrollInfoMap
);
894 OwnPtr
<DelayedUpdateScrollInfoMap
> infoMap(adoptPtr(gDelayedUpdateScrollInfoMap
));
895 gDelayedUpdateScrollInfoMap
= nullptr;
897 for (auto block
: *infoMap
) {
898 if (block
.key
->hasOverflowClip()) {
899 DeprecatedPaintLayerScrollableArea
* scrollableArea
= block
.key
->layer()->scrollableArea();
900 ScrollInfo
& scrollInfo
= block
.value
;
901 scrollableArea
->finalizeScrollDimensions(scrollInfo
.scrollOffset
, scrollInfo
.autoHorizontalScrollBarChanged
, scrollInfo
.autoVerticalScrollBarChanged
);
907 void LayoutBlock::updateScrollInfoAfterLayout()
909 if (hasOverflowClip()) {
910 if (style()->isFlippedBlocksWritingMode()) {
911 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937
912 // Workaround for now. We cannot delay the scroll info for overflow
913 // for items with opposite writing directions, as the contents needs
914 // to overflow in that direction
915 layer()->scrollableArea()->updateAfterLayout();
919 if (gDelayUpdateScrollInfo
) {
920 LayoutUnit logicalWidthExcludingScrollbar
= logicalWidth() - scrollbarLogicalWidth();
921 LayoutUnit logicalHeightExcludingScrollbar
= logicalHeight() - scrollbarLogicalHeight();
922 ScrollInfo scrollInfo
;
923 layer()->scrollableArea()->updateScrollDimensions(scrollInfo
.scrollOffset
, scrollInfo
.autoHorizontalScrollBarChanged
, scrollInfo
.autoVerticalScrollBarChanged
);
924 DelayedUpdateScrollInfoMap::AddResult scrollInfoIterator
= gDelayedUpdateScrollInfoMap
->add(this, scrollInfo
);
925 if (!scrollInfoIterator
.isNewEntry
)
926 scrollInfoIterator
.storedValue
->value
.merge(scrollInfo
);
927 if (scrollInfo
.autoHorizontalScrollBarChanged
)
928 setLogicalHeight(logicalHeightExcludingScrollbar
+ scrollbarLogicalHeight());
929 if (scrollInfo
.autoVerticalScrollBarChanged
)
930 setLogicalWidth(logicalWidthExcludingScrollbar
+ scrollbarLogicalWidth());
932 layer()->scrollableArea()->updateAfterLayout();
937 void LayoutBlock::layout()
939 LayoutAnalyzer::Scope
analyzer(*this);
941 // Table cells call layoutBlock directly, so don't add any logic here. Put code into
945 // It's safe to check for control clip here, since controls can never be table cells.
946 // If we have a lightweight clip, there can never be any overflow from children.
947 if (hasControlClip() && m_overflow
)
948 clearLayoutOverflow();
950 invalidateBackgroundObscurationStatus();
953 bool LayoutBlock::updateImageLoadingPriorities()
955 Vector
<ImageResource
*> images
;
956 appendImagesFromStyle(images
, styleRef());
958 if (images
.isEmpty())
961 LayoutRect viewBounds
= viewRect();
962 LayoutRect
objectBounds(absoluteContentBox());
963 // The object bounds might be empty right now, so intersects will fail since it doesn't deal
964 // with empty rects. Use LayoutRect::contains in that case.
966 if (!objectBounds
.isEmpty())
967 isVisible
= viewBounds
.intersects(objectBounds
);
969 isVisible
= viewBounds
.contains(objectBounds
);
971 ResourceLoadPriorityOptimizer::VisibilityStatus status
= isVisible
?
972 ResourceLoadPriorityOptimizer::Visible
: ResourceLoadPriorityOptimizer::NotVisible
;
974 LayoutRect screenArea
;
975 if (!objectBounds
.isEmpty()) {
976 screenArea
= viewBounds
;
977 screenArea
.intersect(objectBounds
);
980 for (auto* imageResource
: images
)
981 ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->notifyImageResourceVisibility(imageResource
, status
, screenArea
);
986 bool LayoutBlock::widthAvailableToChildrenHasChanged()
988 bool widthAvailableToChildrenHasChanged
= m_widthAvailableToChildrenChanged
;
989 m_widthAvailableToChildrenChanged
= false;
991 // If we use border-box sizing, have percentage padding, and our parent has changed width then the width available to our children has changed even
992 // though our own width has remained the same.
993 widthAvailableToChildrenHasChanged
|= style()->boxSizing() == BORDER_BOX
&& needsPreferredWidthsRecalculation() && view()->layoutState()->containingBlockLogicalWidthChanged();
995 return widthAvailableToChildrenHasChanged
;
998 bool LayoutBlock::updateLogicalWidthAndColumnWidth()
1000 LayoutUnit oldWidth
= logicalWidth();
1001 updateLogicalWidth();
1002 return oldWidth
!= logicalWidth() || widthAvailableToChildrenHasChanged();
1005 void LayoutBlock::layoutBlock(bool)
1007 ASSERT_NOT_REACHED();
1011 void LayoutBlock::addOverflowFromChildren()
1013 if (childrenInline())
1014 toLayoutBlockFlow(this)->addOverflowFromInlineChildren();
1016 addOverflowFromBlockChildren();
1019 void LayoutBlock::computeOverflow(LayoutUnit oldClientAfterEdge
)
1023 // Add overflow from children.
1024 addOverflowFromChildren();
1026 // Add in the overflow from positioned objects.
1027 addOverflowFromPositionedObjects();
1029 if (hasOverflowClip()) {
1030 // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins
1031 // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always
1032 // be considered reachable.
1033 LayoutRect
clientRect(noOverflowRect());
1034 LayoutRect rectToApply
;
1035 if (isHorizontalWritingMode())
1036 rectToApply
= LayoutRect(clientRect
.x(), clientRect
.y(), 1, std::max
<LayoutUnit
>(0, oldClientAfterEdge
- clientRect
.y()));
1038 rectToApply
= LayoutRect(clientRect
.x(), clientRect
.y(), std::max
<LayoutUnit
>(0, oldClientAfterEdge
- clientRect
.x()), 1);
1039 addLayoutOverflow(rectToApply
);
1040 if (hasOverflowModel())
1041 m_overflow
->setLayoutClientAfterEdge(oldClientAfterEdge
);
1044 addVisualEffectOverflow();
1046 addVisualOverflowFromTheme();
1049 void LayoutBlock::addOverflowFromBlockChildren()
1051 for (LayoutBox
* child
= firstChildBox(); child
; child
= child
->nextSiblingBox()) {
1052 if (!child
->isFloatingOrOutOfFlowPositioned() && !child
->isColumnSpanAll())
1053 addOverflowFromChild(child
);
1057 void LayoutBlock::addOverflowFromPositionedObjects()
1059 TrackedLayoutBoxListHashSet
* positionedDescendants
= positionedObjects();
1060 if (!positionedDescendants
)
1063 for (auto* positionedObject
: *positionedDescendants
) {
1064 // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content.
1065 if (positionedObject
->style()->position() != FixedPosition
)
1066 addOverflowFromChild(positionedObject
, toLayoutSize(positionedObject
->location()));
1070 void LayoutBlock::addVisualOverflowFromTheme()
1072 if (!style()->hasAppearance())
1075 IntRect inflatedRect
= pixelSnappedBorderBoxRect();
1076 LayoutTheme::theme().addVisualOverflow(*this, inflatedRect
);
1077 addVisualOverflow(LayoutRect(inflatedRect
));
1080 bool LayoutBlock::createsNewFormattingContext() const
1082 return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || isFlexItemIncludingDeprecated()
1083 || style()->specifiesColumns() || isLayoutFlowThread() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot()
1084 || isDocumentElement() || isColumnSpanAll() || isGridItem();
1087 void LayoutBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren
, LayoutBox
& child
)
1089 if (child
.isOutOfFlowPositioned()) {
1090 // It's rather useless to mark out-of-flow children at this point. We may not be their
1091 // containing block (and if we are, it's just pure luck), so this would be the wrong place
1092 // for it. Furthermore, it would cause trouble for out-of-flow descendants of column
1093 // spanners, if the containing block is outside the spanner but inside the multicol container.
1096 // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into
1097 // an auto value. Add a method to determine this, so that we can avoid the relayout.
1098 bool hasRelativeLogicalHeight
= child
.hasRelativeLogicalHeight()
1099 || (child
.isAnonymous() && this->hasRelativeLogicalHeight())
1100 || child
.stretchesToViewport();
1101 if (relayoutChildren
|| (hasRelativeLogicalHeight
&& !isLayoutView()))
1102 child
.setChildNeedsLayout(MarkOnlyThis
);
1104 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
1105 if (relayoutChildren
&& child
.needsPreferredWidthsRecalculation())
1106 child
.setPreferredLogicalWidthsDirty(MarkOnlyThis
);
1109 void LayoutBlock::simplifiedNormalFlowLayout()
1111 if (childrenInline()) {
1112 ListHashSet
<RootInlineBox
*> lineBoxes
;
1113 ASSERT_WITH_SECURITY_IMPLICATION(isLayoutBlockFlow());
1114 for (InlineWalker
walker(toLayoutBlockFlow(this)); !walker
.atEnd(); walker
.advance()) {
1115 LayoutObject
* o
= walker
.current();
1116 if (!o
->isOutOfFlowPositioned() && (o
->isReplaced() || o
->isFloating())) {
1117 o
->layoutIfNeeded();
1118 if (toLayoutBox(o
)->inlineBoxWrapper()) {
1119 RootInlineBox
& box
= toLayoutBox(o
)->inlineBoxWrapper()->root();
1120 lineBoxes
.add(&box
);
1122 } else if (o
->isText() || (o
->isLayoutInline() && !walker
.atEndOfInline())) {
1123 o
->clearNeedsLayout();
1127 // FIXME: Glyph overflow will get lost in this case, but not really a big deal.
1128 GlyphOverflowAndFallbackFontsMap textBoxDataMap
;
1129 for (ListHashSet
<RootInlineBox
*>::const_iterator it
= lineBoxes
.begin(); it
!= lineBoxes
.end(); ++it
) {
1130 RootInlineBox
* box
= *it
;
1131 box
->computeOverflow(box
->lineTop(), box
->lineBottom(), textBoxDataMap
);
1134 for (LayoutBox
* box
= firstChildBox(); box
; box
= box
->nextSiblingBox()) {
1135 if (!box
->isOutOfFlowPositioned()) {
1136 if (box
->isLayoutMultiColumnSpannerPlaceholder())
1137 toLayoutMultiColumnSpannerPlaceholder(box
)->markForLayoutIfObjectInFlowThreadNeedsLayout();
1138 box
->layoutIfNeeded();
1144 bool LayoutBlock::simplifiedLayout()
1146 // Check if we need to do a full layout.
1147 if (normalChildNeedsLayout() || selfNeedsLayout())
1150 // Check that we actually need to do a simplified layout.
1151 if (!posChildNeedsLayout() && !(needsSimplifiedNormalFlowLayout() || needsPositionedMovementLayout()))
1156 // LayoutState needs this deliberate scope to pop before paint invalidation.
1157 LayoutState
state(*this, locationOffset());
1159 if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly())
1162 TextAutosizer::LayoutScope
textAutosizerLayoutScope(this);
1164 // Lay out positioned descendants or objects that just need to recompute overflow.
1165 if (needsSimplifiedNormalFlowLayout())
1166 simplifiedNormalFlowLayout();
1168 // Lay out our positioned objects if our positioned child bit is set.
1169 // Also, if an absolute position element inside a relative positioned container moves, and the absolute element has a fixed position
1170 // child, neither the fixed element nor its container learn of the movement since posChildNeedsLayout() is only marked as far as the
1171 // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them
1172 // are statically positioned and thus need to move with their absolute ancestors.
1173 bool canContainFixedPosObjects
= canContainFixedPositionObjects();
1174 if (posChildNeedsLayout() || needsPositionedMovementLayout() || canContainFixedPosObjects
)
1175 layoutPositionedObjects(false, needsPositionedMovementLayout() ? ForcedLayoutAfterContainingBlockMoved
: (!posChildNeedsLayout() && canContainFixedPosObjects
? LayoutOnlyFixedPositionedObjects
: DefaultLayout
));
1177 // Recompute our overflow information.
1178 // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only
1179 // updating our overflow if we either used to have overflow or if the new temporary object has overflow.
1180 // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and
1181 // lowestPosition on every relayout so it's not a regression.
1182 // computeOverflow expects the bottom edge before we clamp our height. Since this information isn't available during
1183 // simplifiedLayout, we cache the value in m_overflow.
1184 LayoutUnit oldClientAfterEdge
= hasOverflowModel() ? m_overflow
->layoutClientAfterEdge() : clientLogicalBottom();
1185 computeOverflow(oldClientAfterEdge
);
1188 updateLayerTransformAfterLayout();
1190 updateScrollInfoAfterLayout();
1194 if (LayoutAnalyzer
* analyzer
= frameView()->layoutAnalyzer())
1195 analyzer
->increment(LayoutAnalyzer::LayoutObjectsThatNeedSimplifiedLayout
);
1200 void LayoutBlock::markFixedPositionObjectForLayoutIfNeeded(LayoutObject
* child
, SubtreeLayoutScope
& layoutScope
)
1202 if (child
->style()->position() != FixedPosition
)
1205 bool hasStaticBlockPosition
= child
->style()->hasStaticBlockPosition(isHorizontalWritingMode());
1206 bool hasStaticInlinePosition
= child
->style()->hasStaticInlinePosition(isHorizontalWritingMode());
1207 if (!hasStaticBlockPosition
&& !hasStaticInlinePosition
)
1210 LayoutObject
* o
= child
->parent();
1211 while (o
&& !o
->isLayoutView() && o
->style()->position() != AbsolutePosition
)
1213 if (o
->style()->position() != AbsolutePosition
)
1216 LayoutBox
* box
= toLayoutBox(child
);
1217 if (hasStaticInlinePosition
) {
1218 LogicalExtentComputedValues computedValues
;
1219 box
->computeLogicalWidth(computedValues
);
1220 LayoutUnit newLeft
= computedValues
.m_position
;
1221 if (newLeft
!= box
->logicalLeft())
1222 layoutScope
.setChildNeedsLayout(child
);
1223 } else if (hasStaticBlockPosition
) {
1224 LayoutUnit oldTop
= box
->logicalTop();
1225 box
->updateLogicalHeight();
1226 if (box
->logicalTop() != oldTop
)
1227 layoutScope
.setChildNeedsLayout(child
);
1231 LayoutUnit
LayoutBlock::marginIntrinsicLogicalWidthForChild(LayoutBox
& child
) const
1233 // A margin has three types: fixed, percentage, and auto (variable).
1234 // Auto and percentage margins become 0 when computing min/max width.
1235 // Fixed margins can be added in as is.
1236 Length marginLeft
= child
.style()->marginStartUsing(style());
1237 Length marginRight
= child
.style()->marginEndUsing(style());
1238 LayoutUnit margin
= 0;
1239 if (marginLeft
.isFixed())
1240 margin
+= marginLeft
.value();
1241 if (marginRight
.isFixed())
1242 margin
+= marginRight
.value();
1246 static bool needsLayoutDueToStaticPosition(LayoutBox
* child
)
1248 // When a non-positioned block element moves, it may have positioned children that are
1249 // implicitly positioned relative to the non-positioned block.
1250 const ComputedStyle
* style
= child
->style();
1251 bool isHorizontal
= style
->isHorizontalWritingMode();
1252 if (style
->hasStaticBlockPosition(isHorizontal
)) {
1253 LayoutBox::LogicalExtentComputedValues computedValues
;
1254 LayoutUnit currentLogicalTop
= child
->logicalTop();
1255 LayoutUnit currentLogicalHeight
= child
->logicalHeight();
1256 child
->computeLogicalHeight(currentLogicalHeight
, currentLogicalTop
, computedValues
);
1257 if (computedValues
.m_position
!= currentLogicalTop
|| computedValues
.m_extent
!= currentLogicalHeight
)
1260 if (style
->hasStaticInlinePosition(isHorizontal
)) {
1261 LayoutBox::LogicalExtentComputedValues computedValues
;
1262 LayoutUnit currentLogicalLeft
= child
->logicalLeft();
1263 LayoutUnit currentLogicalWidth
= child
->logicalWidth();
1264 child
->computeLogicalWidth(computedValues
);
1265 if (computedValues
.m_position
!= currentLogicalLeft
|| computedValues
.m_extent
!= currentLogicalWidth
)
1271 void LayoutBlock::layoutPositionedObjects(bool relayoutChildren
, PositionedLayoutBehavior info
)
1273 TrackedLayoutBoxListHashSet
* positionedDescendants
= positionedObjects();
1274 if (!positionedDescendants
)
1277 for (auto* positionedObject
: *positionedDescendants
) {
1278 positionedObject
->setMayNeedPaintInvalidation();
1280 SubtreeLayoutScope
layoutScope(*positionedObject
);
1281 // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So
1282 // 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.
1283 // it has static position.
1284 markFixedPositionObjectForLayoutIfNeeded(positionedObject
, layoutScope
);
1285 if (info
== LayoutOnlyFixedPositionedObjects
) {
1286 positionedObject
->layoutIfNeeded();
1290 if (!positionedObject
->normalChildNeedsLayout() && (relayoutChildren
|| needsLayoutDueToStaticPosition(positionedObject
)))
1291 layoutScope
.setChildNeedsLayout(positionedObject
);
1293 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
1294 if (relayoutChildren
&& positionedObject
->needsPreferredWidthsRecalculation())
1295 positionedObject
->setPreferredLogicalWidthsDirty(MarkOnlyThis
);
1297 if (!positionedObject
->needsLayout())
1298 positionedObject
->markForPaginationRelayoutIfNeeded(layoutScope
);
1300 // FIXME: We should be able to do a r->setNeedsPositionedMovementLayout() here instead of a full layout. Need
1301 // to investigate why it does not trigger the correct invalidations in that case. crbug.com/350756
1302 if (info
== ForcedLayoutAfterContainingBlockMoved
)
1303 positionedObject
->setNeedsLayout(LayoutInvalidationReason::AncestorMoved
, MarkOnlyThis
);
1305 positionedObject
->layoutIfNeeded();
1309 void LayoutBlock::markPositionedObjectsForLayout()
1311 if (TrackedLayoutBoxListHashSet
* positionedDescendants
= positionedObjects()) {
1312 for (auto* descendant
: *positionedDescendants
)
1313 descendant
->setChildNeedsLayout();
1317 void LayoutBlock::markForPaginationRelayoutIfNeeded(SubtreeLayoutScope
& layoutScope
)
1319 ASSERT(!needsLayout());
1323 if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(*this, logicalTop()) != pageLogicalOffset()))
1324 layoutScope
.setChildNeedsLayout(this);
1327 void LayoutBlock::paint(const PaintInfo
& paintInfo
, const LayoutPoint
& paintOffset
)
1329 BlockPainter(*this).paint(paintInfo
, paintOffset
);
1332 void LayoutBlock::paintChildren(const PaintInfo
& paintInfo
, const LayoutPoint
& paintOffset
)
1334 BlockPainter(*this).paintChildren(paintInfo
, paintOffset
);
1337 void LayoutBlock::paintObject(const PaintInfo
& paintInfo
, const LayoutPoint
& paintOffset
)
1339 BlockPainter(*this).paintObject(paintInfo
, paintOffset
);
1342 LayoutInline
* LayoutBlock::inlineElementContinuation() const
1344 LayoutBoxModelObject
* continuation
= this->continuation();
1345 return continuation
&& continuation
->isInline() ? toLayoutInline(continuation
) : 0;
1348 bool LayoutBlock::isSelectionRoot() const
1350 if (isPseudoElement())
1352 ASSERT(node() || isAnonymous());
1354 // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases.
1358 if (isBody() || isDocumentElement() || hasOverflowClip()
1359 || isPositioned() || isFloating()
1360 || isTableCell() || isInlineBlockOrInlineTable()
1361 || hasTransformRelatedProperty() || hasReflection() || hasMask() || isWritingModeRoot()
1362 || isLayoutFlowThread() || isFlexItemIncludingDeprecated())
1365 if (view() && view()->selectionStart()) {
1366 Node
* startElement
= view()->selectionStart()->node();
1367 if (startElement
&& startElement
->rootEditableElement() == node())
1374 LayoutUnit
LayoutBlock::blockDirectionOffset(const LayoutSize
& offsetFromBlock
) const
1376 return isHorizontalWritingMode() ? offsetFromBlock
.height() : offsetFromBlock
.width();
1379 LayoutUnit
LayoutBlock::inlineDirectionOffset(const LayoutSize
& offsetFromBlock
) const
1381 return isHorizontalWritingMode() ? offsetFromBlock
.width() : offsetFromBlock
.height();
1384 LayoutRect
LayoutBlock::logicalRectToPhysicalRect(const LayoutPoint
& rootBlockPhysicalPosition
, const LayoutRect
& logicalRect
) const
1387 if (isHorizontalWritingMode())
1388 result
= logicalRect
;
1390 result
= LayoutRect(logicalRect
.y(), logicalRect
.x(), logicalRect
.height(), logicalRect
.width());
1391 flipForWritingMode(result
);
1392 result
.moveBy(rootBlockPhysicalPosition
);
1396 LayoutUnit
LayoutBlock::logicalLeftSelectionOffset(const LayoutBlock
* rootBlock
, LayoutUnit position
) const
1398 // The border can potentially be further extended by our containingBlock().
1399 if (rootBlock
!= this)
1400 return containingBlock()->logicalLeftSelectionOffset(rootBlock
, position
+ logicalTop());
1401 return logicalLeftOffsetForContent();
1404 LayoutUnit
LayoutBlock::logicalRightSelectionOffset(const LayoutBlock
* rootBlock
, LayoutUnit position
) const
1406 // The border can potentially be further extended by our containingBlock().
1407 if (rootBlock
!= this)
1408 return containingBlock()->logicalRightSelectionOffset(rootBlock
, position
+ logicalTop());
1409 return logicalRightOffsetForContent();
1412 LayoutBlock
* LayoutBlock::blockBeforeWithinSelectionRoot(LayoutSize
& offset
) const
1414 if (isSelectionRoot())
1417 const LayoutObject
* object
= this;
1418 LayoutObject
* sibling
;
1420 sibling
= object
->previousSibling();
1421 while (sibling
&& (!sibling
->isLayoutBlock() || toLayoutBlock(sibling
)->isSelectionRoot()))
1422 sibling
= sibling
->previousSibling();
1424 offset
-= LayoutSize(toLayoutBlock(object
)->logicalLeft(), toLayoutBlock(object
)->logicalTop());
1425 object
= object
->parent();
1426 } while (!sibling
&& object
&& object
->isLayoutBlock() && !toLayoutBlock(object
)->isSelectionRoot());
1431 LayoutBlock
* beforeBlock
= toLayoutBlock(sibling
);
1433 offset
+= LayoutSize(beforeBlock
->logicalLeft(), beforeBlock
->logicalTop());
1435 LayoutObject
* child
= beforeBlock
->lastChild();
1436 while (child
&& child
->isLayoutBlock()) {
1437 beforeBlock
= toLayoutBlock(child
);
1438 offset
+= LayoutSize(beforeBlock
->logicalLeft(), beforeBlock
->logicalTop());
1439 child
= beforeBlock
->lastChild();
1444 void LayoutBlock::setSelectionState(SelectionState state
)
1446 LayoutBox::setSelectionState(state
);
1448 if (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes())
1449 inlineBoxWrapper()->root().setHasSelectedChildren(state
!= SelectionNone
);
1452 void LayoutBlock::insertIntoTrackedLayoutBoxMaps(LayoutBox
* descendant
, TrackedDescendantsMap
*& descendantsMap
, TrackedContainerMap
*& containerMap
)
1454 if (!descendantsMap
) {
1455 descendantsMap
= new TrackedDescendantsMap
;
1456 containerMap
= new TrackedContainerMap
;
1459 TrackedLayoutBoxListHashSet
* descendantSet
= descendantsMap
->get(this);
1460 if (!descendantSet
) {
1461 descendantSet
= new TrackedLayoutBoxListHashSet
;
1462 descendantsMap
->set(this, adoptPtr(descendantSet
));
1464 bool added
= descendantSet
->add(descendant
).isNewEntry
;
1466 ASSERT(containerMap
->get(descendant
));
1467 ASSERT(containerMap
->get(descendant
)->contains(this));
1471 HashSet
<LayoutBlock
*>* containerSet
= containerMap
->get(descendant
);
1472 if (!containerSet
) {
1473 containerSet
= new HashSet
<LayoutBlock
*>;
1474 containerMap
->set(descendant
, adoptPtr(containerSet
));
1476 ASSERT(!containerSet
->contains(this));
1477 containerSet
->add(this);
1480 void LayoutBlock::removeFromTrackedLayoutBoxMaps(LayoutBox
* descendant
, TrackedDescendantsMap
*& descendantsMap
, TrackedContainerMap
*& containerMap
)
1482 if (!descendantsMap
)
1485 OwnPtr
<HashSet
<LayoutBlock
*>> containerSet
= containerMap
->take(descendant
);
1489 for (auto* container
: *containerSet
) {
1490 // FIXME: Disabling this assert temporarily until we fix the layout
1491 // bugs associated with positioned objects not properly cleared from
1492 // their ancestor chain before being moved. See webkit bug 93766.
1493 // ASSERT(descendant->isDescendantOf(container));
1495 TrackedDescendantsMap::iterator descendantsMapIterator
= descendantsMap
->find(container
);
1496 ASSERT(descendantsMapIterator
!= descendantsMap
->end());
1497 if (descendantsMapIterator
== descendantsMap
->end())
1499 TrackedLayoutBoxListHashSet
* descendantSet
= descendantsMapIterator
->value
.get();
1500 ASSERT(descendantSet
->contains(descendant
));
1501 descendantSet
->remove(descendant
);
1502 if (descendantSet
->isEmpty())
1503 descendantsMap
->remove(descendantsMapIterator
);
1507 TrackedLayoutBoxListHashSet
* LayoutBlock::positionedObjects() const
1509 if (gPositionedDescendantsMap
)
1510 return gPositionedDescendantsMap
->get(this);
1514 void LayoutBlock::insertPositionedObject(LayoutBox
* o
)
1516 ASSERT(!isAnonymousBlock());
1517 insertIntoTrackedLayoutBoxMaps(o
, gPositionedDescendantsMap
, gPositionedContainerMap
);
1520 void LayoutBlock::removePositionedObject(LayoutBox
* o
)
1522 removeFromTrackedLayoutBoxMaps(o
, gPositionedDescendantsMap
, gPositionedContainerMap
);
1525 void LayoutBlock::removePositionedObjects(LayoutBlock
* o
, ContainingBlockState containingBlockState
)
1527 TrackedLayoutBoxListHashSet
* positionedDescendants
= positionedObjects();
1528 if (!positionedDescendants
)
1531 Vector
<LayoutBox
*, 16> deadObjects
;
1532 for (auto* positionedObject
: *positionedDescendants
) {
1533 if (!o
|| positionedObject
->isDescendantOf(o
)) {
1534 if (containingBlockState
== NewContainingBlock
) {
1535 positionedObject
->setChildNeedsLayout(MarkOnlyThis
);
1536 if (positionedObject
->needsPreferredWidthsRecalculation())
1537 positionedObject
->setPreferredLogicalWidthsDirty(MarkOnlyThis
);
1540 // It is parent blocks job to add positioned child to positioned objects list of its containing block
1541 // Parent layout needs to be invalidated to ensure this happens.
1542 LayoutObject
* p
= positionedObject
->parent();
1543 while (p
&& !p
->isLayoutBlock())
1546 p
->setChildNeedsLayout();
1548 deadObjects
.append(positionedObject
);
1552 for (unsigned i
= 0; i
< deadObjects
.size(); i
++)
1553 removePositionedObject(deadObjects
.at(i
));
1556 void LayoutBlock::addPercentHeightDescendant(LayoutBox
* descendant
)
1558 insertIntoTrackedLayoutBoxMaps(descendant
, gPercentHeightDescendantsMap
, gPercentHeightContainerMap
);
1561 void LayoutBlock::removePercentHeightDescendant(LayoutBox
* descendant
)
1563 removeFromTrackedLayoutBoxMaps(descendant
, gPercentHeightDescendantsMap
, gPercentHeightContainerMap
);
1566 TrackedLayoutBoxListHashSet
* LayoutBlock::percentHeightDescendants() const
1568 return gPercentHeightDescendantsMap
? gPercentHeightDescendantsMap
->get(this) : 0;
1571 bool LayoutBlock::hasPercentHeightContainerMap()
1573 return gPercentHeightContainerMap
;
1576 bool LayoutBlock::hasPercentHeightDescendant(LayoutBox
* descendant
)
1578 // We don't null check gPercentHeightContainerMap since the caller
1579 // already ensures this and we need to call this function on every
1580 // descendant in clearPercentHeightDescendantsFrom().
1581 ASSERT(gPercentHeightContainerMap
);
1582 return gPercentHeightContainerMap
->contains(descendant
);
1585 void LayoutBlock::dirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope
& layoutScope
)
1587 if (!gPercentHeightDescendantsMap
)
1590 TrackedLayoutBoxListHashSet
* descendants
= gPercentHeightDescendantsMap
->get(this);
1594 for (auto* box
: *descendants
) {
1595 while (box
!= this) {
1596 if (box
->normalChildNeedsLayout())
1598 layoutScope
.setChildNeedsLayout(box
);
1599 box
= box
->containingBlock();
1607 void LayoutBlock::removePercentHeightDescendantIfNeeded(LayoutBox
* descendant
)
1609 // We query the map directly, rather than looking at style's
1610 // logicalHeight()/logicalMinHeight()/logicalMaxHeight() since those
1611 // can change with writing mode/directional changes.
1612 if (!hasPercentHeightContainerMap())
1615 if (!hasPercentHeightDescendant(descendant
))
1618 removePercentHeightDescendant(descendant
);
1621 void LayoutBlock::clearPercentHeightDescendantsFrom(LayoutBox
* parent
)
1623 ASSERT(gPercentHeightContainerMap
);
1624 for (LayoutObject
* curr
= parent
->slowFirstChild(); curr
; curr
= curr
->nextInPreOrder(parent
)) {
1628 LayoutBox
* box
= toLayoutBox(curr
);
1629 if (!hasPercentHeightDescendant(box
))
1632 removePercentHeightDescendant(box
);
1636 LayoutUnit
LayoutBlock::textIndentOffset() const
1639 if (style()->textIndent().hasPercent())
1640 cw
= containingBlock()->availableLogicalWidth();
1641 return minimumValueForLength(style()->textIndent(), cw
);
1644 void LayoutBlock::markLinesDirtyInBlockRange(LayoutUnit logicalTop
, LayoutUnit logicalBottom
, RootInlineBox
* highest
)
1646 if (logicalTop
>= logicalBottom
)
1649 RootInlineBox
* lowestDirtyLine
= lastRootBox();
1650 RootInlineBox
* afterLowest
= lowestDirtyLine
;
1651 while (lowestDirtyLine
&& lowestDirtyLine
->lineBottomWithLeading() >= logicalBottom
&& logicalBottom
< LayoutUnit::max()) {
1652 afterLowest
= lowestDirtyLine
;
1653 lowestDirtyLine
= lowestDirtyLine
->prevRootBox();
1656 while (afterLowest
&& afterLowest
!= highest
&& (afterLowest
->lineBottomWithLeading() >= logicalTop
|| afterLowest
->lineBottomWithLeading() < 0)) {
1657 afterLowest
->markDirty();
1658 afterLowest
= afterLowest
->prevRootBox();
1662 bool LayoutBlock::isPointInOverflowControl(HitTestResult
& result
, const LayoutPoint
& locationInContainer
, const LayoutPoint
& accumulatedOffset
)
1664 if (!scrollsOverflow())
1667 return layer()->scrollableArea()->hitTestOverflowControls(result
, roundedIntPoint(locationInContainer
- toLayoutSize(accumulatedOffset
)));
1670 Node
* LayoutBlock::nodeForHitTest() const
1672 // If we are in the margins of block elements that are part of a
1673 // continuation we're actually still inside the enclosing element
1674 // that was split. Use the appropriate inner node.
1675 return isAnonymousBlockContinuation() ? continuation()->node() : node();
1678 bool LayoutBlock::nodeAtPoint(HitTestResult
& result
, const HitTestLocation
& locationInContainer
, const LayoutPoint
& accumulatedOffset
, HitTestAction hitTestAction
)
1680 LayoutPoint
adjustedLocation(accumulatedOffset
+ location());
1681 LayoutSize localOffset
= toLayoutSize(adjustedLocation
);
1683 if (!isLayoutView()) {
1684 // Check if we need to do anything at all.
1685 // If we have clipping, then we can't have any spillout.
1686 LayoutRect overflowBox
= hasOverflowClip() ? borderBoxRect() : visualOverflowRect();
1687 flipForWritingMode(overflowBox
);
1688 overflowBox
.moveBy(adjustedLocation
);
1689 if (!locationInContainer
.intersects(overflowBox
))
1693 if ((hitTestAction
== HitTestBlockBackground
|| hitTestAction
== HitTestChildBlockBackground
)
1694 && visibleToHitTestRequest(result
.hitTestRequest())
1695 && isPointInOverflowControl(result
, locationInContainer
.point(), adjustedLocation
)) {
1696 updateHitTestResult(result
, locationInContainer
.point() - localOffset
);
1697 // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet.
1698 if (!result
.addNodeToListBasedTestResult(nodeForHitTest(), locationInContainer
))
1702 if (style()->clipPath()) {
1703 switch (style()->clipPath()->type()) {
1704 case ClipPathOperation::SHAPE
: {
1705 ShapeClipPathOperation
* clipPath
= toShapeClipPathOperation(style()->clipPath());
1706 // FIXME: handle marginBox etc.
1707 if (!clipPath
->path(FloatRect(borderBoxRect())).contains(FloatPoint(locationInContainer
.point() - localOffset
), clipPath
->windRule()))
1711 case ClipPathOperation::REFERENCE
:
1712 ReferenceClipPathOperation
* referenceClipPathOperation
= toReferenceClipPathOperation(style()->clipPath());
1713 Element
* element
= document().getElementById(referenceClipPathOperation
->fragment());
1714 if (isSVGClipPathElement(element
) && element
->layoutObject()) {
1715 LayoutSVGResourceClipper
* clipper
= toLayoutSVGResourceClipper(toLayoutSVGResourceContainer(element
->layoutObject()));
1716 if (!clipper
->hitTestClipContent(FloatRect(borderBoxRect()), FloatPoint(locationInContainer
.point() - localOffset
)))
1723 // If we have clipping, then we can't have any spillout.
1724 bool useOverflowClip
= hasOverflowClip() && !hasSelfPaintingLayer();
1725 bool useClip
= (hasControlClip() || useOverflowClip
);
1726 bool checkChildren
= !useClip
;
1727 if (!checkChildren
) {
1728 if (hasControlClip()) {
1729 checkChildren
= locationInContainer
.intersects(controlClipRect(adjustedLocation
));
1731 LayoutRect clipRect
= overflowClipRect(adjustedLocation
, IncludeOverlayScrollbarSize
);
1732 if (style()->hasBorderRadius())
1733 checkChildren
= locationInContainer
.intersects(style()->getRoundedBorderFor(clipRect
));
1735 checkChildren
= locationInContainer
.intersects(clipRect
);
1738 if (checkChildren
) {
1739 // Hit test descendants first.
1740 LayoutSize
scrolledOffset(localOffset
);
1741 if (hasOverflowClip())
1742 scrolledOffset
-= scrolledContentOffset();
1744 // Hit test contents
1745 if (hitTestContents(result
, locationInContainer
, toLayoutPoint(scrolledOffset
), hitTestAction
)) {
1746 updateHitTestResult(result
, flipForWritingMode(locationInContainer
.point() - localOffset
));
1749 if (hitTestAction
== HitTestFloat
&& hitTestFloats(result
, locationInContainer
, toLayoutPoint(scrolledOffset
)))
1753 // Check if the point is outside radii.
1754 if (style()->hasBorderRadius()) {
1755 LayoutRect borderRect
= borderBoxRect();
1756 borderRect
.moveBy(adjustedLocation
);
1757 FloatRoundedRect border
= style()->getRoundedBorderFor(borderRect
);
1758 if (!locationInContainer
.intersects(border
))
1762 // Now hit test our background
1763 if (hitTestAction
== HitTestBlockBackground
|| hitTestAction
== HitTestChildBlockBackground
) {
1764 LayoutRect
boundsRect(adjustedLocation
, size());
1765 if (visibleToHitTestRequest(result
.hitTestRequest()) && locationInContainer
.intersects(boundsRect
)) {
1766 updateHitTestResult(result
, flipForWritingMode(locationInContainer
.point() - localOffset
));
1767 if (!result
.addNodeToListBasedTestResult(nodeForHitTest(), locationInContainer
, boundsRect
))
1775 bool LayoutBlock::hitTestContents(HitTestResult
& result
, const HitTestLocation
& locationInContainer
, const LayoutPoint
& accumulatedOffset
, HitTestAction hitTestAction
)
1777 if (childrenInline() && !isTable()) {
1778 // We have to hit-test our line boxes.
1779 if (m_lineBoxes
.hitTest(LineLayoutBoxModel(this), result
, locationInContainer
, accumulatedOffset
, hitTestAction
))
1782 // Hit test our children.
1783 HitTestAction childHitTest
= hitTestAction
;
1784 if (hitTestAction
== HitTestChildBlockBackgrounds
)
1785 childHitTest
= HitTestChildBlockBackground
;
1786 for (LayoutBox
* child
= lastChildBox(); child
; child
= child
->previousSiblingBox()) {
1787 LayoutPoint childPoint
= flipForWritingModeForChild(child
, accumulatedOffset
);
1788 if (!child
->hasSelfPaintingLayer() && !child
->isFloating() && !child
->isColumnSpanAll() && child
->nodeAtPoint(result
, locationInContainer
, childPoint
, childHitTest
))
1796 Position
LayoutBlock::positionForBox(InlineBox
*box
, bool start
) const
1801 if (!box
->lineLayoutItem().nonPseudoNode())
1802 return Position::editingPositionOf(nonPseudoNode(), start
? caretMinOffset() : caretMaxOffset());
1804 if (!box
->isInlineTextBox())
1805 return Position::editingPositionOf(box
->lineLayoutItem().nonPseudoNode(), start
? box
->lineLayoutItem().caretMinOffset() : box
->lineLayoutItem().caretMaxOffset());
1807 InlineTextBox
* textBox
= toInlineTextBox(box
);
1808 return Position::editingPositionOf(box
->lineLayoutItem().nonPseudoNode(), start
? textBox
->start() : textBox
->start() + textBox
->len());
1811 static inline bool isEditingBoundary(LayoutObject
* ancestor
, LayoutObject
* child
)
1813 ASSERT(!ancestor
|| ancestor
->nonPseudoNode());
1814 ASSERT(child
&& child
->nonPseudoNode());
1815 return !ancestor
|| !ancestor
->parent() || (ancestor
->hasLayer() && ancestor
->parent()->isLayoutView())
1816 || ancestor
->nonPseudoNode()->hasEditableStyle() == child
->nonPseudoNode()->hasEditableStyle();
1819 // FIXME: This function should go on LayoutObject as an instance method. Then
1820 // all cases in which positionForPoint recurs could call this instead to
1821 // prevent crossing editable boundaries. This would require many tests.
1822 static PositionWithAffinity
positionForPointRespectingEditingBoundaries(LayoutBlock
* parent
, LayoutBox
* child
, const LayoutPoint
& pointInParentCoordinates
)
1824 LayoutPoint childLocation
= child
->location();
1825 if (child
->isInFlowPositioned())
1826 childLocation
+= child
->offsetForInFlowPosition();
1828 // FIXME: This is wrong if the child's writing-mode is different from the parent's.
1829 LayoutPoint
pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates
- childLocation
));
1831 // If this is an anonymous layoutObject, we just recur normally
1832 Node
* childNode
= child
->nonPseudoNode();
1834 return child
->positionForPoint(pointInChildCoordinates
);
1836 // Otherwise, first make sure that the editability of the parent and child agree.
1837 // If they don't agree, then we return a visible position just before or after the child
1838 LayoutObject
* ancestor
= parent
;
1839 while (ancestor
&& !ancestor
->nonPseudoNode())
1840 ancestor
= ancestor
->parent();
1842 // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal
1843 if (isEditingBoundary(ancestor
, child
))
1844 return child
->positionForPoint(pointInChildCoordinates
);
1846 // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child
1847 LayoutUnit childMiddle
= parent
->logicalWidthForChild(*child
) / 2;
1848 LayoutUnit logicalLeft
= parent
->isHorizontalWritingMode() ? pointInChildCoordinates
.x() : pointInChildCoordinates
.y();
1849 if (logicalLeft
< childMiddle
)
1850 return ancestor
->createPositionWithAffinity(childNode
->nodeIndex());
1851 return ancestor
->createPositionWithAffinity(childNode
->nodeIndex() + 1, TextAffinity::Upstream
);
1854 PositionWithAffinity
LayoutBlock::positionForPointWithInlineChildren(const LayoutPoint
& pointInLogicalContents
)
1856 ASSERT(childrenInline());
1858 if (!firstRootBox())
1859 return createPositionWithAffinity(0);
1861 bool linesAreFlipped
= style()->isFlippedLinesWritingMode();
1862 bool blocksAreFlipped
= style()->isFlippedBlocksWritingMode();
1864 // look for the closest line box in the root box which is at the passed-in y coordinate
1865 InlineBox
* closestBox
= nullptr;
1866 RootInlineBox
* firstRootBoxWithChildren
= nullptr;
1867 RootInlineBox
* lastRootBoxWithChildren
= nullptr;
1868 for (RootInlineBox
* root
= firstRootBox(); root
; root
= root
->nextRootBox()) {
1869 if (!root
->firstLeafChild())
1871 if (!firstRootBoxWithChildren
)
1872 firstRootBoxWithChildren
= root
;
1874 if (!linesAreFlipped
&& root
->isFirstAfterPageBreak() && (pointInLogicalContents
.y() < root
->lineTopWithLeading()
1875 || (blocksAreFlipped
&& pointInLogicalContents
.y() == root
->lineTopWithLeading())))
1878 lastRootBoxWithChildren
= root
;
1880 // check if this root line box is located at this y coordinate
1881 if (pointInLogicalContents
.y() < root
->selectionBottom() || (blocksAreFlipped
&& pointInLogicalContents
.y() == root
->selectionBottom())) {
1882 if (linesAreFlipped
) {
1883 RootInlineBox
* nextRootBoxWithChildren
= root
->nextRootBox();
1884 while (nextRootBoxWithChildren
&& !nextRootBoxWithChildren
->firstLeafChild())
1885 nextRootBoxWithChildren
= nextRootBoxWithChildren
->nextRootBox();
1887 if (nextRootBoxWithChildren
&& nextRootBoxWithChildren
->isFirstAfterPageBreak() && (pointInLogicalContents
.y() > nextRootBoxWithChildren
->lineTopWithLeading()
1888 || (!blocksAreFlipped
&& pointInLogicalContents
.y() == nextRootBoxWithChildren
->lineTopWithLeading())))
1891 closestBox
= root
->closestLeafChildForLogicalLeftPosition(pointInLogicalContents
.x());
1897 bool moveCaretToBoundary
= document().frame()->editor().behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom();
1899 if (!moveCaretToBoundary
&& !closestBox
&& lastRootBoxWithChildren
) {
1900 // y coordinate is below last root line box, pretend we hit it
1901 closestBox
= lastRootBoxWithChildren
->closestLeafChildForLogicalLeftPosition(pointInLogicalContents
.x());
1905 if (moveCaretToBoundary
) {
1906 LayoutUnit firstRootBoxWithChildrenTop
= std::min
<LayoutUnit
>(firstRootBoxWithChildren
->selectionTop(), firstRootBoxWithChildren
->logicalTop());
1907 if (pointInLogicalContents
.y() < firstRootBoxWithChildrenTop
1908 || (blocksAreFlipped
&& pointInLogicalContents
.y() == firstRootBoxWithChildrenTop
)) {
1909 InlineBox
* box
= firstRootBoxWithChildren
->firstLeafChild();
1910 if (box
->isLineBreak()) {
1911 if (InlineBox
* newBox
= box
->nextLeafChildIgnoringLineBreak())
1914 // y coordinate is above first root line box, so return the start of the first
1915 return PositionWithAffinity(positionForBox(box
, true));
1919 // pass the box a top position that is inside it
1920 LayoutPoint
point(pointInLogicalContents
.x(), closestBox
->root().blockDirectionPointInLine());
1921 if (!isHorizontalWritingMode())
1922 point
= point
.transposedPoint();
1923 if (closestBox
->lineLayoutItem().isReplaced())
1924 return positionForPointRespectingEditingBoundaries(this, &toLayoutBox(closestBox
->layoutObject()), point
);
1925 return closestBox
->lineLayoutItem().positionForPoint(point
);
1928 if (lastRootBoxWithChildren
) {
1929 // We hit this case for Mac behavior when the Y coordinate is below the last box.
1930 ASSERT(moveCaretToBoundary
);
1931 InlineBox
* logicallyLastBox
;
1932 if (lastRootBoxWithChildren
->getLogicalEndBoxWithNode(logicallyLastBox
))
1933 return PositionWithAffinity(positionForBox(logicallyLastBox
, false));
1936 // Can't reach this. We have a root line box, but it has no kids.
1937 // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text
1938 // seems to hit this code path.
1939 return createPositionWithAffinity(0);
1942 static inline bool isChildHitTestCandidate(LayoutBox
* box
)
1944 return box
->size().height() && box
->style()->visibility() == VISIBLE
&& !box
->isFloatingOrOutOfFlowPositioned() && !box
->isLayoutFlowThread();
1947 PositionWithAffinity
LayoutBlock::positionForPoint(const LayoutPoint
& point
)
1950 return LayoutBox::positionForPoint(point
);
1953 // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode.
1954 LayoutUnit pointLogicalLeft
= isHorizontalWritingMode() ? point
.x() : point
.y();
1955 LayoutUnit pointLogicalTop
= isHorizontalWritingMode() ? point
.y() : point
.x();
1957 if (pointLogicalLeft
< 0)
1958 return createPositionWithAffinity(caretMinOffset());
1959 if (pointLogicalLeft
>= logicalWidth())
1960 return createPositionWithAffinity(caretMaxOffset());
1961 if (pointLogicalTop
< 0)
1962 return createPositionWithAffinity(caretMinOffset());
1963 if (pointLogicalTop
>= logicalHeight())
1964 return createPositionWithAffinity(caretMaxOffset());
1967 LayoutPoint pointInContents
= point
;
1968 offsetForContents(pointInContents
);
1969 LayoutPoint
pointInLogicalContents(pointInContents
);
1970 if (!isHorizontalWritingMode())
1971 pointInLogicalContents
= pointInLogicalContents
.transposedPoint();
1973 if (childrenInline())
1974 return positionForPointWithInlineChildren(pointInLogicalContents
);
1976 LayoutBox
* lastCandidateBox
= lastChildBox();
1977 while (lastCandidateBox
&& !isChildHitTestCandidate(lastCandidateBox
))
1978 lastCandidateBox
= lastCandidateBox
->previousSiblingBox();
1980 bool blocksAreFlipped
= style()->isFlippedBlocksWritingMode();
1981 if (lastCandidateBox
) {
1982 if (pointInLogicalContents
.y() > logicalTopForChild(*lastCandidateBox
)
1983 || (!blocksAreFlipped
&& pointInLogicalContents
.y() == logicalTopForChild(*lastCandidateBox
)))
1984 return positionForPointRespectingEditingBoundaries(this, lastCandidateBox
, pointInContents
);
1986 for (LayoutBox
* childBox
= firstChildBox(); childBox
; childBox
= childBox
->nextSiblingBox()) {
1987 if (!isChildHitTestCandidate(childBox
))
1989 LayoutUnit childLogicalBottom
= logicalTopForChild(*childBox
) + logicalHeightForChild(*childBox
);
1990 // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3).
1991 if (isChildHitTestCandidate(childBox
) && (pointInLogicalContents
.y() < childLogicalBottom
1992 || (blocksAreFlipped
&& pointInLogicalContents
.y() == childLogicalBottom
)))
1993 return positionForPointRespectingEditingBoundaries(this, childBox
, pointInContents
);
1997 // We only get here if there are no hit test candidate children below the click.
1998 return LayoutBox::positionForPoint(point
);
2001 void LayoutBlock::offsetForContents(LayoutPoint
& offset
) const
2003 offset
= flipForWritingMode(offset
);
2005 if (hasOverflowClip())
2006 offset
+= LayoutSize(scrolledContentOffset());
2008 offset
= flipForWritingMode(offset
);
2011 int LayoutBlock::columnGap() const
2013 if (style()->hasNormalColumnGap())
2014 return style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins.
2015 return static_cast<int>(style()->columnGap());
2018 void LayoutBlock::computeIntrinsicLogicalWidths(LayoutUnit
& minLogicalWidth
, LayoutUnit
& maxLogicalWidth
) const
2020 if (childrenInline()) {
2021 // FIXME: Remove this const_cast.
2022 toLayoutBlockFlow(const_cast<LayoutBlock
*>(this))->computeInlinePreferredLogicalWidths(minLogicalWidth
, maxLogicalWidth
);
2024 computeBlockPreferredLogicalWidths(minLogicalWidth
, maxLogicalWidth
);
2027 maxLogicalWidth
= std::max(minLogicalWidth
, maxLogicalWidth
);
2029 if (isHTMLMarqueeElement(node()) && toHTMLMarqueeElement(node())->isHorizontal())
2030 minLogicalWidth
= LayoutUnit();
2032 if (isTableCell()) {
2033 Length tableCellWidth
= toLayoutTableCell(this)->styleOrColLogicalWidth();
2034 if (tableCellWidth
.isFixed() && tableCellWidth
.value() > 0)
2035 maxLogicalWidth
= std::max(minLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(tableCellWidth
.value()));
2038 int scrollbarWidth
= intrinsicScrollbarLogicalWidth();
2039 maxLogicalWidth
+= scrollbarWidth
;
2040 minLogicalWidth
+= scrollbarWidth
;
2043 void LayoutBlock::computePreferredLogicalWidths()
2045 ASSERT(preferredLogicalWidthsDirty());
2047 m_minPreferredLogicalWidth
= 0;
2048 m_maxPreferredLogicalWidth
= 0;
2050 // FIXME: The isFixed() calls here should probably be checking for isSpecified since you
2051 // should be able to use percentage, calc or viewport relative values for width.
2052 const ComputedStyle
& styleToUse
= styleRef();
2053 if (!isTableCell() && styleToUse
.logicalWidth().isFixed() && styleToUse
.logicalWidth().value() >= 0
2054 && !(isDeprecatedFlexItem() && !styleToUse
.logicalWidth().intValue()))
2055 m_minPreferredLogicalWidth
= m_maxPreferredLogicalWidth
= adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalWidth().value());
2057 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth
, m_maxPreferredLogicalWidth
);
2059 if (styleToUse
.logicalMinWidth().isFixed() && styleToUse
.logicalMinWidth().value() > 0) {
2060 m_maxPreferredLogicalWidth
= std::max(m_maxPreferredLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalMinWidth().value()));
2061 m_minPreferredLogicalWidth
= std::max(m_minPreferredLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalMinWidth().value()));
2064 if (styleToUse
.logicalMaxWidth().isFixed()) {
2065 m_maxPreferredLogicalWidth
= std::min(m_maxPreferredLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalMaxWidth().value()));
2066 m_minPreferredLogicalWidth
= std::min(m_minPreferredLogicalWidth
, adjustContentBoxLogicalWidthForBoxSizing(styleToUse
.logicalMaxWidth().value()));
2069 // Table layout uses integers, ceil the preferred widths to ensure that they can contain the contents.
2070 if (isTableCell()) {
2071 m_minPreferredLogicalWidth
= m_minPreferredLogicalWidth
.ceil();
2072 m_maxPreferredLogicalWidth
= m_maxPreferredLogicalWidth
.ceil();
2075 LayoutUnit borderAndPadding
= borderAndPaddingLogicalWidth();
2076 m_minPreferredLogicalWidth
+= borderAndPadding
;
2077 m_maxPreferredLogicalWidth
+= borderAndPadding
;
2079 clearPreferredLogicalWidthsDirty();
2082 void LayoutBlock::computeBlockPreferredLogicalWidths(LayoutUnit
& minLogicalWidth
, LayoutUnit
& maxLogicalWidth
) const
2084 const ComputedStyle
& styleToUse
= styleRef();
2085 bool nowrap
= styleToUse
.whiteSpace() == NOWRAP
;
2087 LayoutObject
* child
= firstChild();
2088 LayoutBlock
* containingBlock
= this->containingBlock();
2089 LayoutUnit floatLeftWidth
= 0, floatRightWidth
= 0;
2091 // Positioned children don't affect the min/max width. Spanners only affect the min/max
2092 // width of the multicol container, not the flow thread.
2093 if (child
->isOutOfFlowPositioned() || child
->isColumnSpanAll()) {
2094 child
= child
->nextSibling();
2098 RefPtr
<ComputedStyle
> childStyle
= child
->mutableStyle();
2099 if (child
->isFloating() || (child
->isBox() && toLayoutBox(child
)->avoidsFloats())) {
2100 LayoutUnit floatTotalWidth
= floatLeftWidth
+ floatRightWidth
;
2101 if (childStyle
->clear() & CLEFT
) {
2102 maxLogicalWidth
= std::max(floatTotalWidth
, maxLogicalWidth
);
2105 if (childStyle
->clear() & CRIGHT
) {
2106 maxLogicalWidth
= std::max(floatTotalWidth
, maxLogicalWidth
);
2107 floatRightWidth
= 0;
2111 // A margin basically has three types: fixed, percentage, and auto (variable).
2112 // Auto and percentage margins simply become 0 when computing min/max width.
2113 // Fixed margins can be added in as is.
2114 Length startMarginLength
= childStyle
->marginStartUsing(&styleToUse
);
2115 Length endMarginLength
= childStyle
->marginEndUsing(&styleToUse
);
2116 LayoutUnit margin
= 0;
2117 LayoutUnit marginStart
= 0;
2118 LayoutUnit marginEnd
= 0;
2119 if (startMarginLength
.isFixed())
2120 marginStart
+= startMarginLength
.value();
2121 if (endMarginLength
.isFixed())
2122 marginEnd
+= endMarginLength
.value();
2123 margin
= marginStart
+ marginEnd
;
2125 LayoutUnit childMinPreferredLogicalWidth
, childMaxPreferredLogicalWidth
;
2126 computeChildPreferredLogicalWidths(*child
, childMinPreferredLogicalWidth
, childMaxPreferredLogicalWidth
);
2128 LayoutUnit w
= childMinPreferredLogicalWidth
+ margin
;
2129 minLogicalWidth
= std::max(w
, minLogicalWidth
);
2131 // IE ignores tables for calculation of nowrap. Makes some sense.
2132 if (nowrap
&& !child
->isTable())
2133 maxLogicalWidth
= std::max(w
, maxLogicalWidth
);
2135 w
= childMaxPreferredLogicalWidth
+ margin
;
2137 if (!child
->isFloating()) {
2138 if (child
->isBox() && toLayoutBox(child
)->avoidsFloats()) {
2139 // Determine a left and right max value based off whether or not the floats can fit in the
2140 // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin
2141 // is smaller than the float width.
2142 bool ltr
= containingBlock
? containingBlock
->style()->isLeftToRightDirection() : styleToUse
.isLeftToRightDirection();
2143 LayoutUnit marginLogicalLeft
= ltr
? marginStart
: marginEnd
;
2144 LayoutUnit marginLogicalRight
= ltr
? marginEnd
: marginStart
;
2145 LayoutUnit maxLeft
= marginLogicalLeft
> 0 ? std::max(floatLeftWidth
, marginLogicalLeft
) : floatLeftWidth
+ marginLogicalLeft
;
2146 LayoutUnit maxRight
= marginLogicalRight
> 0 ? std::max(floatRightWidth
, marginLogicalRight
) : floatRightWidth
+ marginLogicalRight
;
2147 w
= childMaxPreferredLogicalWidth
+ maxLeft
+ maxRight
;
2148 w
= std::max(w
, floatLeftWidth
+ floatRightWidth
);
2150 maxLogicalWidth
= std::max(floatLeftWidth
+ floatRightWidth
, maxLogicalWidth
);
2152 floatLeftWidth
= floatRightWidth
= 0;
2155 if (child
->isFloating()) {
2156 if (childStyle
->floating() == LeftFloat
)
2157 floatLeftWidth
+= w
;
2159 floatRightWidth
+= w
;
2161 maxLogicalWidth
= std::max(w
, maxLogicalWidth
);
2164 child
= child
->nextSibling();
2167 // Always make sure these values are non-negative.
2168 minLogicalWidth
= std::max
<LayoutUnit
>(0, minLogicalWidth
);
2169 maxLogicalWidth
= std::max
<LayoutUnit
>(0, maxLogicalWidth
);
2171 maxLogicalWidth
= std::max(floatLeftWidth
+ floatRightWidth
, maxLogicalWidth
);
2174 void LayoutBlock::computeChildPreferredLogicalWidths(LayoutObject
& child
, LayoutUnit
& minPreferredLogicalWidth
, LayoutUnit
& maxPreferredLogicalWidth
) const
2176 if (child
.isBox() && child
.isHorizontalWritingMode() != isHorizontalWritingMode()) {
2177 // If the child is an orthogonal flow, child's height determines the width, but the height is not available until layout.
2178 // http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-shrink-to-fit
2179 if (!child
.needsLayout()) {
2180 minPreferredLogicalWidth
= maxPreferredLogicalWidth
= toLayoutBox(child
).logicalHeight();
2183 m_needsRecalcLogicalWidthAfterLayoutChildren
= true;
2184 minPreferredLogicalWidth
= maxPreferredLogicalWidth
= toLayoutBox(child
).computeLogicalHeightWithoutLayout();
2187 minPreferredLogicalWidth
= child
.minPreferredLogicalWidth();
2188 maxPreferredLogicalWidth
= child
.maxPreferredLogicalWidth();
2189 if (child
.isLayoutBlock() && toLayoutBlock(child
).needsRecalcLogicalWidthAfterLayoutChildren())
2190 m_needsRecalcLogicalWidthAfterLayoutChildren
= true;
2193 bool LayoutBlock::hasLineIfEmpty() const
2198 if (node()->isRootEditableElement())
2201 if (node()->isShadowRoot() && isHTMLInputElement(*toShadowRoot(node())->host()))
2207 LayoutUnit
LayoutBlock::lineHeight(bool firstLine
, LineDirectionMode direction
, LinePositionMode linePositionMode
) const
2209 // Inline blocks are replaced elements. Otherwise, just pass off to
2210 // the base class. If we're being queried as though we're the root line
2211 // box, then the fact that we're an inline-block is irrelevant, and we behave
2212 // just like a block.
2213 if (isReplaced() && linePositionMode
== PositionOnContainingLine
)
2214 return LayoutBox::lineHeight(firstLine
, direction
, linePositionMode
);
2216 const ComputedStyle
& style
= styleRef(firstLine
&& document().styleEngine().usesFirstLineRules());
2217 return style
.computedLineHeight();
2220 int LayoutBlock::beforeMarginInLineDirection(LineDirectionMode direction
) const
2222 return direction
== HorizontalLine
? marginTop() : marginRight();
2225 int LayoutBlock::baselinePosition(FontBaseline baselineType
, bool firstLine
, LineDirectionMode direction
, LinePositionMode linePositionMode
) const
2227 // Inline blocks are replaced elements. Otherwise, just pass off to
2228 // the base class. If we're being queried as though we're the root line
2229 // box, then the fact that we're an inline-block is irrelevant, and we behave
2230 // just like a block.
2231 if (isInline() && linePositionMode
== PositionOnContainingLine
) {
2232 // For "leaf" theme objects, let the theme decide what the baseline position is.
2233 // FIXME: Might be better to have a custom CSS property instead, so that if the theme
2234 // is turned off, checkboxes/radios will still have decent baselines.
2235 // FIXME: Need to patch form controls to deal with vertical lines.
2236 if (style()->hasAppearance() && !LayoutTheme::theme().isControlContainer(style()->appearance()))
2237 return LayoutTheme::theme().baselinePosition(this);
2239 // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in
2241 // We give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled
2242 // vertically (e.g., an overflow:hidden block that has had scrollTop moved).
2243 bool ignoreBaseline
= (layer() && layer()->scrollableArea()
2244 && (direction
== HorizontalLine
2245 ? (layer()->scrollableArea()->verticalScrollbar() || layer()->scrollableArea()->scrollYOffset())
2246 : (layer()->scrollableArea()->horizontalScrollbar() || layer()->scrollableArea()->scrollXOffset())))
2247 || (isWritingModeRoot() && !isRubyRun());
2249 int baselinePos
= ignoreBaseline
? -1 : inlineBlockBaseline(direction
);
2251 if (isDeprecatedFlexibleBox()) {
2252 // Historically, we did this check for all baselines. But we can't
2253 // remove this code from deprecated flexbox, because it effectively
2254 // breaks -webkit-line-clamp, which is used in the wild -- we would
2255 // calculate the baseline as if -webkit-line-clamp wasn't used.
2256 // For simplicity, we use this for all uses of deprecated flexbox.
2257 LayoutUnit bottomOfContent
= direction
== HorizontalLine
? size().height() - borderBottom() - paddingBottom() - horizontalScrollbarHeight() : size().width() - borderLeft() - paddingLeft() - verticalScrollbarWidth();
2258 if (baselinePos
> bottomOfContent
)
2261 if (baselinePos
!= -1)
2262 return beforeMarginInLineDirection(direction
) + baselinePos
;
2264 return LayoutBox::baselinePosition(baselineType
, firstLine
, direction
, linePositionMode
);
2267 // If we're not replaced, we'll only get called with PositionOfInteriorLineBoxes.
2268 // Note that inline-block counts as replaced here.
2269 ASSERT(linePositionMode
== PositionOfInteriorLineBoxes
);
2271 const FontMetrics
& fontMetrics
= style(firstLine
)->fontMetrics();
2272 return fontMetrics
.ascent(baselineType
) + (lineHeight(firstLine
, direction
, linePositionMode
) - fontMetrics
.height()) / 2;
2275 LayoutUnit
LayoutBlock::minLineHeightForReplacedObject(bool isFirstLine
, LayoutUnit replacedHeight
) const
2277 if (!document().inNoQuirksMode() && replacedHeight
)
2278 return replacedHeight
;
2280 return std::max
<LayoutUnit
>(replacedHeight
, lineHeight(isFirstLine
, isHorizontalWritingMode() ? HorizontalLine
: VerticalLine
, PositionOfInteriorLineBoxes
));
2283 int LayoutBlock::firstLineBoxBaseline() const
2285 if (isWritingModeRoot() && !isRubyRun())
2288 if (childrenInline()) {
2290 return firstLineBox()->logicalTop() + style(true)->fontMetrics().ascent(firstRootBox()->baselineType());
2293 for (LayoutBox
* curr
= firstChildBox(); curr
; curr
= curr
->nextSiblingBox()) {
2294 if (!curr
->isFloatingOrOutOfFlowPositioned()) {
2295 int result
= curr
->firstLineBoxBaseline();
2297 return curr
->logicalTop() + result
; // Translate to our coordinate space.
2303 int LayoutBlock::inlineBlockBaseline(LineDirectionMode direction
) const
2305 if (!style()->isOverflowVisible()) {
2306 // We are not calling LayoutBox::baselinePosition here because the caller should add the margin-top/margin-right, not us.
2307 return direction
== HorizontalLine
? size().height() + marginBottom() : size().width() + marginLeft();
2310 return lastLineBoxBaseline(direction
);
2313 int LayoutBlock::lastLineBoxBaseline(LineDirectionMode lineDirection
) const
2315 if (isWritingModeRoot() && !isRubyRun())
2318 if (childrenInline()) {
2319 if (!firstLineBox() && hasLineIfEmpty()) {
2320 const FontMetrics
& fontMetrics
= firstLineStyle()->fontMetrics();
2321 return fontMetrics
.ascent()
2322 + (lineHeight(true, lineDirection
, PositionOfInteriorLineBoxes
) - fontMetrics
.height()) / 2
2323 + (lineDirection
== HorizontalLine
? borderTop() + paddingTop() : borderRight() + paddingRight());
2326 return lastLineBox()->logicalTop() + style(lastLineBox() == firstLineBox())->fontMetrics().ascent(lastRootBox()->baselineType());
2330 bool haveNormalFlowChild
= false;
2331 for (LayoutBox
* curr
= lastChildBox(); curr
; curr
= curr
->previousSiblingBox()) {
2332 if (!curr
->isFloatingOrOutOfFlowPositioned()) {
2333 haveNormalFlowChild
= true;
2334 int result
= curr
->inlineBlockBaseline(lineDirection
);
2336 return curr
->logicalTop() + result
; // Translate to our coordinate space.
2339 if (!haveNormalFlowChild
&& hasLineIfEmpty()) {
2340 const FontMetrics
& fontMetrics
= firstLineStyle()->fontMetrics();
2341 return fontMetrics
.ascent()
2342 + (lineHeight(true, lineDirection
, PositionOfInteriorLineBoxes
) - fontMetrics
.height()) / 2
2343 + (lineDirection
== HorizontalLine
? borderTop() + paddingTop() : borderRight() + paddingRight());
2348 static inline bool isLayoutBlockFlowOrLayoutButton(LayoutObject
* layoutObject
)
2350 // We include isLayoutButton in this check because buttons are implemented
2351 // using flex box but should still support first-line|first-letter.
2352 // The flex box and grid specs require that flex box and grid do not
2353 // support first-line|first-letter, though.
2354 // FIXME: Remove when buttons are implemented with align-items instead
2356 return layoutObject
->isLayoutBlockFlow() || layoutObject
->isLayoutButton();
2359 LayoutBlock
* LayoutBlock::firstLineBlock() const
2361 LayoutBlock
* firstLineBlock
= const_cast<LayoutBlock
*>(this);
2362 bool hasPseudo
= false;
2364 hasPseudo
= firstLineBlock
->style()->hasPseudoStyle(FIRST_LINE
);
2367 LayoutObject
* parentBlock
= firstLineBlock
->parent();
2368 if (firstLineBlock
->isReplaced() || firstLineBlock
->isFloatingOrOutOfFlowPositioned()
2370 || !isLayoutBlockFlowOrLayoutButton(parentBlock
))
2372 ASSERT_WITH_SECURITY_IMPLICATION(parentBlock
->isLayoutBlock());
2373 if (toLayoutBlock(parentBlock
)->firstChild() != firstLineBlock
)
2375 firstLineBlock
= toLayoutBlock(parentBlock
);
2381 return firstLineBlock
;
2384 // Helper methods for obtaining the last line, computing line counts and heights for line counts
2385 // (crawling into blocks).
2386 static bool shouldCheckLines(LayoutObject
* obj
)
2388 return !obj
->isFloatingOrOutOfFlowPositioned()
2389 && obj
->isLayoutBlock() && obj
->style()->height().isAuto()
2390 && (!obj
->isDeprecatedFlexibleBox() || obj
->style()->boxOrient() == VERTICAL
);
2393 static int getHeightForLineCount(LayoutBlock
* block
, int l
, bool includeBottom
, int& count
)
2395 if (block
->style()->visibility() == VISIBLE
) {
2396 if (block
->isLayoutBlockFlow() && block
->childrenInline()) {
2397 for (RootInlineBox
* box
= toLayoutBlockFlow(block
)->firstRootBox(); box
; box
= box
->nextRootBox()) {
2399 return box
->lineBottom() + (includeBottom
? (block
->borderBottom() + block
->paddingBottom()) : LayoutUnit());
2402 LayoutBox
* normalFlowChildWithoutLines
= nullptr;
2403 for (LayoutBox
* obj
= block
->firstChildBox(); obj
; obj
= obj
->nextSiblingBox()) {
2404 if (shouldCheckLines(obj
)) {
2405 int result
= getHeightForLineCount(toLayoutBlock(obj
), l
, false, count
);
2407 return result
+ obj
->location().y() + (includeBottom
? (block
->borderBottom() + block
->paddingBottom()) : LayoutUnit());
2408 } else if (!obj
->isFloatingOrOutOfFlowPositioned()) {
2409 normalFlowChildWithoutLines
= obj
;
2412 if (normalFlowChildWithoutLines
&& l
== 0)
2413 return normalFlowChildWithoutLines
->location().y() + normalFlowChildWithoutLines
->size().height();
2420 RootInlineBox
* LayoutBlock::lineAtIndex(int i
) const
2424 if (style()->visibility() != VISIBLE
)
2427 if (childrenInline()) {
2428 for (RootInlineBox
* box
= firstRootBox(); box
; box
= box
->nextRootBox()) {
2433 for (LayoutObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
2434 if (!shouldCheckLines(child
))
2436 if (RootInlineBox
* box
= toLayoutBlock(child
)->lineAtIndex(i
))
2444 int LayoutBlock::lineCount(const RootInlineBox
* stopRootInlineBox
, bool* found
) const
2448 if (style()->visibility() == VISIBLE
) {
2449 if (childrenInline()) {
2450 for (RootInlineBox
* box
= firstRootBox(); box
; box
= box
->nextRootBox()) {
2452 if (box
== stopRootInlineBox
) {
2459 for (LayoutObject
* obj
= firstChild(); obj
; obj
= obj
->nextSibling()) {
2460 if (shouldCheckLines(obj
)) {
2461 bool recursiveFound
= false;
2462 count
+= toLayoutBlock(obj
)->lineCount(stopRootInlineBox
, &recursiveFound
);
2463 if (recursiveFound
) {
2475 int LayoutBlock::heightForLineCount(int l
)
2478 return getHeightForLineCount(this, l
, true, count
);
2481 void LayoutBlock::clearTruncation()
2483 if (style()->visibility() == VISIBLE
) {
2484 if (childrenInline() && hasMarkupTruncation()) {
2485 setHasMarkupTruncation(false);
2486 for (RootInlineBox
* box
= firstRootBox(); box
; box
= box
->nextRootBox())
2487 box
->clearTruncation();
2489 for (LayoutObject
* obj
= firstChild(); obj
; obj
= obj
->nextSibling()) {
2490 if (shouldCheckLines(obj
))
2491 toLayoutBlock(obj
)->clearTruncation();
2497 void LayoutBlock::absoluteRects(Vector
<IntRect
>& rects
, const LayoutPoint
& accumulatedOffset
) const
2499 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
2500 // inline boxes above and below us (thus getting merged with them to form a single irregular
2502 if (isAnonymousBlockContinuation()) {
2503 // FIXME: This is wrong for vertical writing-modes.
2504 // https://bugs.webkit.org/show_bug.cgi?id=46781
2505 LayoutRect
rect(accumulatedOffset
, size());
2506 rect
.expand(collapsedMarginBoxLogicalOutsets());
2507 rects
.append(pixelSnappedIntRect(rect
));
2508 continuation()->absoluteRects(rects
, accumulatedOffset
- toLayoutSize(location() +
2509 inlineElementContinuation()->containingBlock()->location()));
2511 rects
.append(pixelSnappedIntRect(accumulatedOffset
, size()));
2515 void LayoutBlock::absoluteQuads(Vector
<FloatQuad
>& quads
, bool* wasFixed
) const
2517 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
2518 // inline boxes above and below us (thus getting merged with them to form a single irregular
2520 if (isAnonymousBlockContinuation()) {
2521 // FIXME: This is wrong for vertical writing-modes.
2522 // https://bugs.webkit.org/show_bug.cgi?id=46781
2523 LayoutRect
localRect(LayoutPoint(), size());
2524 localRect
.expand(collapsedMarginBoxLogicalOutsets());
2525 quads
.append(localToAbsoluteQuad(FloatRect(localRect
), 0 /* mode */, wasFixed
));
2526 continuation()->absoluteQuads(quads
, wasFixed
);
2528 quads
.append(LayoutBox::localToAbsoluteQuad(FloatRect(0, 0, size().width().toFloat(), size().height().toFloat()), 0 /* mode */, wasFixed
));
2532 LayoutObject
* LayoutBlock::hoverAncestor() const
2534 return isAnonymousBlockContinuation() ? continuation() : LayoutBox::hoverAncestor();
2537 void LayoutBlock::updateDragState(bool dragOn
)
2539 LayoutBox::updateDragState(dragOn
);
2540 if (LayoutBoxModelObject
* continuation
= this->continuation())
2541 continuation
->updateDragState(dragOn
);
2544 void LayoutBlock::childBecameNonInline(LayoutObject
*)
2546 makeChildrenNonInline();
2547 if (isAnonymousBlock() && parent() && parent()->isLayoutBlock())
2548 toLayoutBlock(parent())->removeLeftoverAnonymousBlock(this);
2549 // |this| may be dead here
2552 void LayoutBlock::updateHitTestResult(HitTestResult
& result
, const LayoutPoint
& point
)
2554 if (result
.innerNode())
2557 if (Node
* n
= nodeForHitTest())
2558 result
.setNodeAndPosition(n
, point
);
2561 // An inline-block uses its inlineBox as the inlineBoxWrapper,
2562 // so the firstChild() is nullptr if the only child is an empty inline-block.
2563 inline bool LayoutBlock::isInlineBoxWrapperActuallyChild() const
2565 return isInlineBlockOrInlineTable() && !size().isEmpty() && node() && editingIgnoresContent(node());
2568 LayoutRect
LayoutBlock::localCaretRect(InlineBox
* inlineBox
, int caretOffset
, LayoutUnit
* extraWidthToEndOfLine
)
2570 // Do the normal calculation in most cases.
2571 if (firstChild() || isInlineBoxWrapperActuallyChild())
2572 return LayoutBox::localCaretRect(inlineBox
, caretOffset
, extraWidthToEndOfLine
);
2574 LayoutRect caretRect
= localCaretRectForEmptyElement(size().width(), textIndentOffset());
2576 if (extraWidthToEndOfLine
)
2577 *extraWidthToEndOfLine
= size().width() - caretRect
.maxX();
2582 void LayoutBlock::addOutlineRects(Vector
<LayoutRect
>& rects
, const LayoutPoint
& additionalOffset
, IncludeBlockVisualOverflowOrNot includeBlockOverflows
) const
2584 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
2585 // inline boxes above and below us (thus getting merged with them to form a single irregular
2587 const LayoutInline
* inlineElementContinuation
= this->inlineElementContinuation();
2588 if (inlineElementContinuation
) {
2589 // FIXME: This check really isn't accurate.
2590 bool nextInlineHasLineBox
= inlineElementContinuation
->firstLineBox();
2591 // FIXME: This is wrong. The principal layoutObject may not be the continuation preceding this block.
2592 // FIXME: This is wrong for vertical writing-modes.
2593 // https://bugs.webkit.org/show_bug.cgi?id=46781
2594 bool prevInlineHasLineBox
= toLayoutInline(inlineElementContinuation
->node()->layoutObject())->firstLineBox();
2595 LayoutUnit topMargin
= prevInlineHasLineBox
? collapsedMarginBefore() : LayoutUnit();
2596 LayoutUnit bottomMargin
= nextInlineHasLineBox
? collapsedMarginAfter() : LayoutUnit();
2597 if (topMargin
|| bottomMargin
) {
2598 LayoutRect
rect(additionalOffset
, size());
2599 rect
.expandEdges(topMargin
, 0, bottomMargin
, 0);
2602 } else if (!isAnonymous()) { // For anonymous blocks, the children add outline rects.
2603 rects
.append(LayoutRect(additionalOffset
, size()));
2606 if (includeBlockOverflows
== IncludeBlockVisualOverflow
&& !hasOverflowClip() && !hasControlClip()) {
2607 for (RootInlineBox
* curr
= firstRootBox(); curr
; curr
= curr
->nextRootBox()) {
2608 LayoutUnit top
= std::max
<LayoutUnit
>(curr
->lineTop(), curr
->top());
2609 LayoutUnit bottom
= std::min
<LayoutUnit
>(curr
->lineBottom(), curr
->top() + curr
->height());
2610 LayoutRect
rect(additionalOffset
.x() + curr
->x(), additionalOffset
.y() + top
, curr
->width(), bottom
- top
);
2611 if (!rect
.isEmpty())
2615 addOutlineRectsForNormalChildren(rects
, additionalOffset
, includeBlockOverflows
);
2616 if (TrackedLayoutBoxListHashSet
* positionedObjects
= this->positionedObjects()) {
2617 for (auto* box
: *positionedObjects
)
2618 addOutlineRectsForDescendant(*box
, rects
, additionalOffset
, includeBlockOverflows
);
2622 if (inlineElementContinuation
)
2623 inlineElementContinuation
->addOutlineRects(rects
, additionalOffset
+ (inlineElementContinuation
->containingBlock()->location() - location()), includeBlockOverflows
);
2626 void LayoutBlock::computeSelfHitTestRects(Vector
<LayoutRect
>& rects
, const LayoutPoint
& layerOffset
) const
2628 LayoutBox::computeSelfHitTestRects(rects
, layerOffset
);
2630 if (hasHorizontalLayoutOverflow() || hasVerticalLayoutOverflow()) {
2631 for (RootInlineBox
* curr
= firstRootBox(); curr
; curr
= curr
->nextRootBox()) {
2632 LayoutUnit top
= std::max
<LayoutUnit
>(curr
->lineTop(), curr
->top());
2633 LayoutUnit bottom
= std::min
<LayoutUnit
>(curr
->lineBottom(), curr
->top() + curr
->height());
2634 LayoutRect
rect(layerOffset
.x() + curr
->x(), layerOffset
.y() + top
, curr
->width(), bottom
- top
);
2635 // It's common for this rect to be entirely contained in our box, so exclude that simple case.
2636 if (!rect
.isEmpty() && (rects
.isEmpty() || !rects
[0].contains(rect
)))
2642 LayoutBox
* LayoutBlock::createAnonymousBoxWithSameTypeAs(const LayoutObject
* parent
) const
2644 return createAnonymousWithParentAndDisplay(parent
, style()->display());
2647 LayoutUnit
LayoutBlock::nextPageLogicalTop(LayoutUnit logicalOffset
, PageBoundaryRule pageBoundaryRule
) const
2649 LayoutUnit pageLogicalHeight
= pageLogicalHeightForOffset(logicalOffset
);
2650 if (!pageLogicalHeight
)
2651 return logicalOffset
;
2653 return logicalOffset
+ pageRemainingLogicalHeightForOffset(logicalOffset
, pageBoundaryRule
);
2656 LayoutUnit
LayoutBlock::pageLogicalHeightForOffset(LayoutUnit offset
) const
2658 LayoutView
* layoutView
= view();
2659 LayoutFlowThread
* flowThread
= flowThreadContainingBlock();
2661 return layoutView
->layoutState()->pageLogicalHeight();
2662 return flowThread
->pageLogicalHeightForOffset(offset
+ offsetFromLogicalTopOfFirstPage());
2665 LayoutUnit
LayoutBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset
, PageBoundaryRule pageBoundaryRule
) const
2667 LayoutView
* layoutView
= view();
2668 offset
+= offsetFromLogicalTopOfFirstPage();
2670 LayoutFlowThread
* flowThread
= flowThreadContainingBlock();
2672 LayoutUnit pageLogicalHeight
= layoutView
->layoutState()->pageLogicalHeight();
2673 LayoutUnit remainingHeight
= pageLogicalHeight
- intMod(offset
, pageLogicalHeight
);
2674 if (pageBoundaryRule
== AssociateWithFormerPage
) {
2675 // An offset exactly at a page boundary will act as being part of the former page in
2676 // question (i.e. no remaining space), rather than being part of the latter (i.e. one
2677 // whole page length of remaining space).
2678 remainingHeight
= intMod(remainingHeight
, pageLogicalHeight
);
2680 return remainingHeight
;
2683 return flowThread
->pageRemainingLogicalHeightForOffset(offset
, pageBoundaryRule
);
2686 void LayoutBlock::setPageBreak(LayoutUnit offset
, LayoutUnit spaceShortage
)
2688 if (LayoutFlowThread
* flowThread
= flowThreadContainingBlock())
2689 flowThread
->setPageBreak(offsetFromLogicalTopOfFirstPage() + offset
, spaceShortage
);
2692 void LayoutBlock::updateMinimumPageHeight(LayoutUnit offset
, LayoutUnit minHeight
)
2694 if (LayoutFlowThread
* flowThread
= flowThreadContainingBlock())
2695 flowThread
->updateMinimumPageHeight(offsetFromLogicalTopOfFirstPage() + offset
, minHeight
);
2698 LayoutUnit
LayoutBlock::collapsedMarginBeforeForChild(const LayoutBox
& child
) const
2700 // If the child has the same directionality as we do, then we can just return its
2701 // collapsed margin.
2702 if (!child
.isWritingModeRoot())
2703 return child
.collapsedMarginBefore();
2705 // The child has a different directionality. If the child is parallel, then it's just
2706 // flipped relative to us. We can use the collapsed margin for the opposite edge.
2707 if (child
.isHorizontalWritingMode() == isHorizontalWritingMode())
2708 return child
.collapsedMarginAfter();
2710 // The child is perpendicular to us, which means its margins don't collapse but are on the
2711 // "logical left/right" sides of the child box. We can just return the raw margin in this case.
2712 return marginBeforeForChild(child
);
2715 LayoutUnit
LayoutBlock::collapsedMarginAfterForChild(const LayoutBox
& child
) const
2717 // If the child has the same directionality as we do, then we can just return its
2718 // collapsed margin.
2719 if (!child
.isWritingModeRoot())
2720 return child
.collapsedMarginAfter();
2722 // The child has a different directionality. If the child is parallel, then it's just
2723 // flipped relative to us. We can use the collapsed margin for the opposite edge.
2724 if (child
.isHorizontalWritingMode() == isHorizontalWritingMode())
2725 return child
.collapsedMarginBefore();
2727 // The child is perpendicular to us, which means its margins don't collapse but are on the
2728 // "logical left/right" side of the child box. We can just return the raw margin in this case.
2729 return marginAfterForChild(child
);
2732 bool LayoutBlock::hasMarginBeforeQuirk(const LayoutBox
* child
) const
2734 // If the child has the same directionality as we do, then we can just return its
2736 if (!child
->isWritingModeRoot())
2737 return child
->isLayoutBlock() ? toLayoutBlock(child
)->hasMarginBeforeQuirk() : child
->style()->hasMarginBeforeQuirk();
2739 // The child has a different directionality. If the child is parallel, then it's just
2740 // flipped relative to us. We can use the opposite edge.
2741 if (child
->isHorizontalWritingMode() == isHorizontalWritingMode())
2742 return child
->isLayoutBlock() ? toLayoutBlock(child
)->hasMarginAfterQuirk() : child
->style()->hasMarginAfterQuirk();
2744 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about
2745 // whether or not authors specified quirky ems, since they're an implementation detail.
2749 bool LayoutBlock::hasMarginAfterQuirk(const LayoutBox
* child
) const
2751 // If the child has the same directionality as we do, then we can just return its
2753 if (!child
->isWritingModeRoot())
2754 return child
->isLayoutBlock() ? toLayoutBlock(child
)->hasMarginAfterQuirk() : child
->style()->hasMarginAfterQuirk();
2756 // The child has a different directionality. If the child is parallel, then it's just
2757 // flipped relative to us. We can use the opposite edge.
2758 if (child
->isHorizontalWritingMode() == isHorizontalWritingMode())
2759 return child
->isLayoutBlock() ? toLayoutBlock(child
)->hasMarginBeforeQuirk() : child
->style()->hasMarginBeforeQuirk();
2761 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about
2762 // whether or not authors specified quirky ems, since they're an implementation detail.
2766 const char* LayoutBlock::name() const
2768 ASSERT_NOT_REACHED();
2769 return "LayoutBlock";
2772 LayoutBlock
* LayoutBlock::createAnonymousWithParentAndDisplay(const LayoutObject
* parent
, EDisplay display
)
2774 // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ?
2775 EDisplay newDisplay
;
2776 LayoutBlock
* newBox
= nullptr;
2777 if (display
== FLEX
|| display
== INLINE_FLEX
) {
2778 newBox
= LayoutFlexibleBox::createAnonymous(&parent
->document());
2781 newBox
= LayoutBlockFlow::createAnonymous(&parent
->document());
2785 RefPtr
<ComputedStyle
> newStyle
= ComputedStyle::createAnonymousStyleWithDisplay(parent
->styleRef(), newDisplay
);
2786 parent
->updateAnonymousChildStyle(*newBox
, *newStyle
);
2787 newBox
->setStyle(newStyle
.release());
2791 static bool recalcNormalFlowChildOverflowIfNeeded(LayoutObject
* layoutObject
)
2793 if (layoutObject
->isOutOfFlowPositioned() || !layoutObject
->needsOverflowRecalcAfterStyleChange())
2796 ASSERT(layoutObject
->isLayoutBlock());
2797 return toLayoutBlock(layoutObject
)->recalcOverflowAfterStyleChange();
2800 bool LayoutBlock::recalcChildOverflowAfterStyleChange()
2802 ASSERT(childNeedsOverflowRecalcAfterStyleChange());
2803 clearChildNeedsOverflowRecalcAfterStyleChange();
2805 bool childrenOverflowChanged
= false;
2807 if (childrenInline()) {
2808 ListHashSet
<RootInlineBox
*> lineBoxes
;
2809 ASSERT_WITH_SECURITY_IMPLICATION(isLayoutBlockFlow());
2810 for (InlineWalker
walker(toLayoutBlockFlow(this)); !walker
.atEnd(); walker
.advance()) {
2811 LayoutObject
* layoutObject
= walker
.current();
2812 if (recalcNormalFlowChildOverflowIfNeeded(layoutObject
)) {
2813 childrenOverflowChanged
= true;
2814 if (InlineBox
* inlineBoxWrapper
= toLayoutBlock(layoutObject
)->inlineBoxWrapper())
2815 lineBoxes
.add(&inlineBoxWrapper
->root());
2819 // FIXME: Glyph overflow will get lost in this case, but not really a big deal.
2820 GlyphOverflowAndFallbackFontsMap textBoxDataMap
;
2821 for (ListHashSet
<RootInlineBox
*>::const_iterator it
= lineBoxes
.begin(); it
!= lineBoxes
.end(); ++it
) {
2822 RootInlineBox
* box
= *it
;
2823 box
->computeOverflow(box
->lineTop(), box
->lineBottom(), textBoxDataMap
);
2826 for (LayoutBox
* box
= firstChildBox(); box
; box
= box
->nextSiblingBox()) {
2827 if (recalcNormalFlowChildOverflowIfNeeded(box
))
2828 childrenOverflowChanged
= true;
2832 TrackedLayoutBoxListHashSet
* positionedDescendants
= positionedObjects();
2833 if (!positionedDescendants
)
2834 return childrenOverflowChanged
;
2836 for (auto* box
: *positionedDescendants
) {
2837 if (!box
->needsOverflowRecalcAfterStyleChange())
2839 LayoutBlock
* block
= toLayoutBlock(box
);
2840 if (!block
->recalcOverflowAfterStyleChange() || box
->style()->position() == FixedPosition
)
2843 childrenOverflowChanged
= true;
2845 return childrenOverflowChanged
;
2848 bool LayoutBlock::recalcOverflowAfterStyleChange()
2850 ASSERT(needsOverflowRecalcAfterStyleChange());
2852 bool childrenOverflowChanged
= false;
2853 if (childNeedsOverflowRecalcAfterStyleChange())
2854 childrenOverflowChanged
= recalcChildOverflowAfterStyleChange();
2856 if (!selfNeedsOverflowRecalcAfterStyleChange() && !childrenOverflowChanged
)
2859 clearSelfNeedsOverflowRecalcAfterStyleChange();
2860 // If the current block needs layout, overflow will be recalculated during
2861 // layout time anyway. We can safely exit here.
2865 LayoutUnit oldClientAfterEdge
= hasOverflowModel() ? m_overflow
->layoutClientAfterEdge() : clientLogicalBottom();
2866 computeOverflow(oldClientAfterEdge
);
2868 if (hasOverflowClip())
2869 layer()->scrollableArea()->updateAfterOverflowRecalc();
2871 return !hasOverflowClip();
2874 // Called when a positioned object moves but doesn't necessarily change size. A simplified layout is attempted
2875 // that just updates the object's position. If the size does change, the object remains dirty.
2876 bool LayoutBlock::tryLayoutDoingPositionedMovementOnly()
2878 LayoutUnit oldWidth
= logicalWidth();
2879 LogicalExtentComputedValues computedValues
;
2880 logicalExtentAfterUpdatingLogicalWidth(logicalTop(), computedValues
);
2881 // If we shrink to fit our width may have changed, so we still need full layout.
2882 if (oldWidth
!= computedValues
.m_extent
)
2884 setLogicalWidth(computedValues
.m_extent
);
2885 setLogicalLeft(computedValues
.m_position
);
2886 setMarginStart(computedValues
.m_margins
.m_start
);
2887 setMarginEnd(computedValues
.m_margins
.m_end
);
2889 LayoutUnit oldHeight
= logicalHeight();
2890 LayoutUnit oldIntrinsicContentLogicalHeight
= intrinsicContentLogicalHeight();
2892 setIntrinsicContentLogicalHeight(contentLogicalHeight());
2893 computeLogicalHeight(oldHeight
, logicalTop(), computedValues
);
2895 if (hasPercentHeightDescendants() && oldHeight
!= computedValues
.m_extent
) {
2896 setIntrinsicContentLogicalHeight(oldIntrinsicContentLogicalHeight
);
2900 setLogicalHeight(computedValues
.m_extent
);
2901 setLogicalTop(computedValues
.m_position
);
2902 setMarginBefore(computedValues
.m_margins
.m_before
);
2903 setMarginAfter(computedValues
.m_margins
.m_after
);
2909 void LayoutBlock::checkPositionedObjectsNeedLayout()
2911 if (!gPositionedDescendantsMap
)
2914 if (TrackedLayoutBoxListHashSet
* positionedDescendantSet
= positionedObjects()) {
2915 TrackedLayoutBoxListHashSet::const_iterator end
= positionedDescendantSet
->end();
2916 for (TrackedLayoutBoxListHashSet::const_iterator it
= positionedDescendantSet
->begin(); it
!= end
; ++it
) {
2917 LayoutBox
* currBox
= *it
;
2918 ASSERT(!currBox
->needsLayout());
2927 void LayoutBlock::showLineTreeAndMark(const InlineBox
* markedBox1
, const char* markedLabel1
, const InlineBox
* markedBox2
, const char* markedLabel2
, const LayoutObject
* obj
) const
2930 for (const RootInlineBox
* root
= firstRootBox(); root
; root
= root
->nextRootBox())
2931 root
->showLineTreeAndMark(markedBox1
, markedLabel1
, markedBox2
, markedLabel2
, obj
, 1);
2936 } // namespace blink