/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef RenderBlock_h #define RenderBlock_h #include "ColumnInfo.h" #include "GapRects.h" #include "PODIntervalTree.h" #include "RenderBox.h" #include "RenderLineBoxList.h" #include "RootInlineBox.h" #include "TextBreakIterator.h" #include "TextRun.h" #include #include #if ENABLE(CSS_SHAPES) #include "ShapeInsideInfo.h" #include "ShapeValue.h" #endif namespace WebCore { class BidiContext; class InlineIterator; class LayoutStateMaintainer; class LineLayoutState; class LineWidth; class LogicalSelectionOffsetCaches; class RenderInline; class RenderText; struct BidiRun; struct PaintInfo; class LineInfo; class RenderRubyRun; #if ENABLE(CSS_SHAPES) class BasicShape; #endif class TextLayout; class WordMeasurement; template class BidiResolver; template class BidiRunList; template struct MidpointState; typedef BidiResolver InlineBidiResolver; typedef MidpointState LineMidpointState; typedef WTF::ListHashSet TrackedRendererListHashSet; typedef WTF::HashMap > TrackedDescendantsMap; typedef WTF::HashMap > > TrackedContainerMap; typedef Vector WordMeasurements; enum CaretType { CursorCaret, DragCaret }; enum ContainingBlockState { NewContainingBlock, SameContainingBlock }; enum TextRunFlag { DefaultTextRunFlags = 0, RespectDirection = 1 << 0, RespectDirectionOverride = 1 << 1 }; typedef unsigned TextRunFlags; class RenderBlock : public RenderBox { public: friend class LineLayoutState; #ifndef NDEBUG // Used by the PODIntervalTree for debugging the FloatingObject. template friend struct ValueToString; #endif explicit RenderBlock(ContainerNode*); virtual ~RenderBlock(); static RenderBlock* createAnonymous(Document*); RenderObject* firstChild() const { ASSERT(children() == virtualChildren()); return children()->firstChild(); } RenderObject* lastChild() const { ASSERT(children() == virtualChildren()); return children()->lastChild(); } const RenderObjectChildList* children() const { return &m_children; } RenderObjectChildList* children() { return &m_children; } bool beingDestroyed() const { return m_beingDestroyed; } // These two functions are overridden for inline-block. virtual LayoutUnit lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } const RenderLineBoxList* lineBoxes() const { return &m_lineBoxes; } InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } void deleteLineBoxTree(); virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); virtual void removeChild(RenderObject*); virtual void layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight = 0); void insertPositionedObject(RenderBox*); static void removePositionedObject(RenderBox*); void removePositionedObjects(RenderBlock*, ContainingBlockState = SameContainingBlock); void removeFloatingObjects(); TrackedRendererListHashSet* positionedObjects() const; bool hasPositionedObjects() const { TrackedRendererListHashSet* objects = positionedObjects(); return objects && !objects->isEmpty(); } void addPercentHeightDescendant(RenderBox*); static void removePercentHeightDescendant(RenderBox*); TrackedRendererListHashSet* percentHeightDescendants() const; static bool hasPercentHeightContainerMap(); static bool hasPercentHeightDescendant(RenderBox*); static void clearPercentHeightDescendantsFrom(RenderBox*); static void removePercentHeightDescendantIfNeeded(RenderBox*); void setHasMarkupTruncation(bool b) { m_hasMarkupTruncation = b; } bool hasMarkupTruncation() const { return m_hasMarkupTruncation; } void setHasMarginBeforeQuirk(bool b) { m_hasMarginBeforeQuirk = b; } void setHasMarginAfterQuirk(bool b) { m_hasMarginAfterQuirk = b; } bool hasMarginBeforeQuirk() const { return m_hasMarginBeforeQuirk; } bool hasMarginAfterQuirk() const { return m_hasMarginAfterQuirk; } bool hasMarginBeforeQuirk(const RenderBox* child) const; bool hasMarginAfterQuirk(const RenderBox* child) const; RootInlineBox* createAndAppendRootInlineBox(); bool generatesLineBoxesForInlineChild(RenderObject*); void markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove = 0, bool inLayout = true); void markSiblingsWithFloatsForLayout(RenderBox* floatToRemove = 0); void markPositionedObjectsForLayout(); virtual void markForPaginationRelayoutIfNeeded(); bool containsFloats() const { return m_floatingObjects && !m_floatingObjects->set().isEmpty(); } bool containsFloat(RenderBox*) const; // Versions that can compute line offsets with the region and page offset passed in. Used for speed to avoid having to // compute the region all over again when you already know it. LayoutUnit availableLogicalWidthForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { return max(0, logicalRightOffsetForLine(position, shouldIndentText, region, logicalHeight) - logicalLeftOffsetForLine(position, shouldIndentText, region, logicalHeight)); } LayoutUnit logicalRightOffsetForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { return logicalRightOffsetForLine(position, logicalRightOffsetForContent(region), shouldIndentText, 0, logicalHeight); } LayoutUnit logicalLeftOffsetForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(region), shouldIndentText, 0, logicalHeight); } LayoutUnit startOffsetForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { return style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, shouldIndentText, region, logicalHeight) : logicalWidth() - logicalRightOffsetForLine(position, shouldIndentText, region, logicalHeight); } LayoutUnit endOffsetForLine(LayoutUnit position, bool shouldIndentText, RenderRegion* region, LayoutUnit logicalHeight = 0) const { return !style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, shouldIndentText, region, logicalHeight) : logicalWidth() - logicalRightOffsetForLine(position, shouldIndentText, region, logicalHeight); } LayoutUnit availableLogicalWidthForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { return availableLogicalWidthForLine(position, shouldIndentText, regionAtBlockOffset(position), logicalHeight); } LayoutUnit logicalRightOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { return logicalRightOffsetForLine(position, logicalRightOffsetForContent(position), shouldIndentText, 0, logicalHeight); } LayoutUnit logicalLeftOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(position), shouldIndentText, 0, logicalHeight); } LayoutUnit pixelSnappedLogicalLeftOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { return roundToInt(logicalLeftOffsetForLine(position, shouldIndentText, logicalHeight)); } LayoutUnit pixelSnappedLogicalRightOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { // FIXME: Multicolumn layouts break carrying over subpixel values to the logical right offset because the lines may be shifted // by a subpixel value for all but the first column. This can lead to the actual pixel snapped width of the column being off // by one pixel when rendered versus layed out, which can result in the line being clipped. For now, we have to floor. // https://bugs.webkit.org/show_bug.cgi?id=105461 return floorToInt(logicalRightOffsetForLine(position, shouldIndentText, logicalHeight)); } LayoutUnit startOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { return style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, shouldIndentText, logicalHeight) : logicalWidth() - logicalRightOffsetForLine(position, shouldIndentText, logicalHeight); } LayoutUnit endOffsetForLine(LayoutUnit position, bool shouldIndentText, LayoutUnit logicalHeight = 0) const { return !style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(position, shouldIndentText, logicalHeight) : logicalWidth() - logicalRightOffsetForLine(position, shouldIndentText, logicalHeight); } LayoutUnit startAlignedOffsetForLine(LayoutUnit position, bool shouldIndentText); LayoutUnit textIndentOffset() const; virtual VisiblePosition positionForPoint(const LayoutPoint&); // Block flows subclass availableWidth to handle multi column layout (shrinking the width available to children when laying out.) virtual LayoutUnit availableLogicalWidth() const; LayoutPoint flipForWritingModeIncludingColumns(const LayoutPoint&) const; void adjustStartEdgeForWritingModeIncludingColumns(LayoutRect&) const; RootInlineBox* firstRootBox() const { return static_cast(firstLineBox()); } RootInlineBox* lastRootBox() const { return static_cast(lastLineBox()); } bool containsNonZeroBidiLevel() const; GapRects selectionGapRectsForRepaint(const RenderLayerModelObject* repaintContainer); LayoutRect logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches&, const PaintInfo*); LayoutRect logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches&, const PaintInfo*); void getSelectionGapInfo(SelectionState, bool& leftGap, bool& rightGap); RenderBlock* blockBeforeWithinSelectionRoot(LayoutSize& offset) const; LayoutRect logicalRectToPhysicalRect(const LayoutPoint& physicalPosition, const LayoutRect& logicalRect); // Helper methods for computing line counts and heights for line counts. RootInlineBox* lineAtIndex(int) const; int lineCount(const RootInlineBox* = 0, bool* = 0) const; int heightForLineCount(int); void clearTruncation(); void adjustRectForColumns(LayoutRect&) const; virtual void adjustForColumns(LayoutSize&, const LayoutPoint&) const; void adjustForColumnRect(LayoutSize& offset, const LayoutPoint& locationInContainer) const; void addContinuationWithOutline(RenderInline*); bool paintsContinuationOutline(RenderInline*); virtual RenderBoxModelObject* virtualContinuation() const { return continuation(); } bool isAnonymousBlockContinuation() const { return continuation() && isAnonymousBlock(); } RenderInline* inlineElementContinuation() const; RenderBlock* blockElementContinuation() const; using RenderBoxModelObject::continuation; using RenderBoxModelObject::setContinuation; static RenderBlock* createAnonymousWithParentRendererAndDisplay(const RenderObject*, EDisplay = BLOCK); static RenderBlock* createAnonymousColumnsWithParentRenderer(const RenderObject*); static RenderBlock* createAnonymousColumnSpanWithParentRenderer(const RenderObject*); RenderBlock* createAnonymousBlock(EDisplay display = BLOCK) const { return createAnonymousWithParentRendererAndDisplay(this, display); } RenderBlock* createAnonymousColumnsBlock() const { return createAnonymousColumnsWithParentRenderer(this); } RenderBlock* createAnonymousColumnSpanBlock() const { return createAnonymousColumnSpanWithParentRenderer(this); } static void collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* child); virtual RenderBox* createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const OVERRIDE; static bool shouldSkipCreatingRunsForObject(RenderObject* obj) { return obj->isFloating() || (obj->isOutOfFlowPositioned() && !obj->style()->isOriginalDisplayInlineType() && !obj->container()->isRenderInline()); } static void appendRunsForObject(BidiRunList&, int start, int end, RenderObject*, InlineBidiResolver&); static TextRun constructTextRun(RenderObject* context, const Font& font, const String& string, RenderStyle* style, TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, TextRunFlags = DefaultTextRunFlags); static TextRun constructTextRun(RenderObject* context, const Font& font, const RenderText* text, RenderStyle* style, TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion); static TextRun constructTextRun(RenderObject* context, const Font& font, const RenderText* text, unsigned offset, unsigned length, RenderStyle* style, TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion); static TextRun constructTextRun(RenderObject* context, const Font& font, const RenderText* text, unsigned offset, RenderStyle* style, TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion); #if ENABLE(8BIT_TEXTRUN) static TextRun constructTextRun(RenderObject* context, const Font& font, const LChar* characters, int length, RenderStyle* style, TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion); #endif static TextRun constructTextRun(RenderObject* context, const Font& font, const UChar* characters, int length, RenderStyle* style, TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion); ColumnInfo* columnInfo() const; int columnGap() const; void updateColumnInfoFromStyle(RenderStyle*); LayoutUnit initialBlockOffsetForPainting() const; LayoutUnit blockDeltaForPaintingNextColumn() const; // These two functions take the ColumnInfo* to avoid repeated lookups of the info in the global HashMap. unsigned columnCount(ColumnInfo*) const; LayoutRect columnRectAt(ColumnInfo*, unsigned) const; LayoutUnit paginationStrut() const { return m_rareData ? m_rareData->m_paginationStrut : LayoutUnit(); } void setPaginationStrut(LayoutUnit); bool shouldBreakAtLineToAvoidWidow() const { return m_rareData && m_rareData->m_shouldBreakAtLineToAvoidWidow; } void clearShouldBreakAtLineToAvoidWidow() const; RootInlineBox* lineBreakToAvoidWidow() const { return m_rareData ? m_rareData->m_lineBreakToAvoidWidow : 0; } void setBreakAtLineToAvoidWidow(RootInlineBox*); // The page logical offset is the object's offset from the top of the page in the page progression // direction (so an x-offset in vertical text and a y-offset for horizontal text). LayoutUnit pageLogicalOffset() const { return m_rareData ? m_rareData->m_pageLogicalOffset : LayoutUnit(); } void setPageLogicalOffset(LayoutUnit); RootInlineBox* lineGridBox() const { return m_rareData ? m_rareData->m_lineGridBox : 0; } void setLineGridBox(RootInlineBox* box) { if (!m_rareData) m_rareData = adoptPtr(new RenderBlockRareData(this)); if (m_rareData->m_lineGridBox) m_rareData->m_lineGridBox->destroy(renderArena()); m_rareData->m_lineGridBox = box; } void layoutLineGridBox(); // Accessors for logical width/height and margins in the containing block's block-flow direction. enum ApplyLayoutDeltaMode { ApplyLayoutDelta, DoNotApplyLayoutDelta }; LayoutUnit logicalWidthForChild(const RenderBox* child) { return isHorizontalWritingMode() ? child->width() : child->height(); } LayoutUnit logicalHeightForChild(const RenderBox* child) { return isHorizontalWritingMode() ? child->height() : child->width(); } LayoutUnit logicalTopForChild(const RenderBox* child) { return isHorizontalWritingMode() ? child->y() : child->x(); } void setLogicalLeftForChild(RenderBox* child, LayoutUnit logicalLeft, ApplyLayoutDeltaMode = DoNotApplyLayoutDelta); void setLogicalTopForChild(RenderBox* child, LayoutUnit logicalTop, ApplyLayoutDeltaMode = DoNotApplyLayoutDelta); LayoutUnit marginBeforeForChild(const RenderBoxModelObject* child) const { return child->marginBefore(style()); } LayoutUnit marginAfterForChild(const RenderBoxModelObject* child) const { return child->marginAfter(style()); } LayoutUnit marginStartForChild(const RenderBoxModelObject* child) const { return child->marginStart(style()); } LayoutUnit marginEndForChild(const RenderBoxModelObject* child) const { return child->marginEnd(style()); } void setMarginStartForChild(RenderBox* child, LayoutUnit value) const { child->setMarginStart(value, style()); } void setMarginEndForChild(RenderBox* child, LayoutUnit value) const { child->setMarginEnd(value, style()); } void setMarginBeforeForChild(RenderBox* child, LayoutUnit value) const { child->setMarginBefore(value, style()); } void setMarginAfterForChild(RenderBox* child, LayoutUnit value) const { child->setMarginAfter(value, style()); } LayoutUnit collapsedMarginBeforeForChild(const RenderBox* child) const; LayoutUnit collapsedMarginAfterForChild(const RenderBox* child) const; void updateLogicalWidthForAlignment(const ETextAlign&, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount); virtual void updateFirstLetter(); class MarginValues { public: MarginValues(LayoutUnit beforePos, LayoutUnit beforeNeg, LayoutUnit afterPos, LayoutUnit afterNeg) : m_positiveMarginBefore(beforePos) , m_negativeMarginBefore(beforeNeg) , m_positiveMarginAfter(afterPos) , m_negativeMarginAfter(afterNeg) { } LayoutUnit positiveMarginBefore() const { return m_positiveMarginBefore; } LayoutUnit negativeMarginBefore() const { return m_negativeMarginBefore; } LayoutUnit positiveMarginAfter() const { return m_positiveMarginAfter; } LayoutUnit negativeMarginAfter() const { return m_negativeMarginAfter; } void setPositiveMarginBefore(LayoutUnit pos) { m_positiveMarginBefore = pos; } void setNegativeMarginBefore(LayoutUnit neg) { m_negativeMarginBefore = neg; } void setPositiveMarginAfter(LayoutUnit pos) { m_positiveMarginAfter = pos; } void setNegativeMarginAfter(LayoutUnit neg) { m_negativeMarginAfter = neg; } private: LayoutUnit m_positiveMarginBefore; LayoutUnit m_negativeMarginBefore; LayoutUnit m_positiveMarginAfter; LayoutUnit m_negativeMarginAfter; }; MarginValues marginValuesForChild(RenderBox* child) const; virtual void scrollbarsChanged(bool /*horizontalScrollbarChanged*/, bool /*verticalScrollbarChanged*/) { }; LayoutUnit logicalLeftOffsetForContent(RenderRegion*) const; LayoutUnit logicalRightOffsetForContent(RenderRegion*) const; LayoutUnit availableLogicalWidthForContent(RenderRegion* region) const { return max(0, logicalRightOffsetForContent(region) - logicalLeftOffsetForContent(region)); } LayoutUnit startOffsetForContent(RenderRegion* region) const { return style()->isLeftToRightDirection() ? logicalLeftOffsetForContent(region) : logicalWidth() - logicalRightOffsetForContent(region); } LayoutUnit endOffsetForContent(RenderRegion* region) const { return !style()->isLeftToRightDirection() ? logicalLeftOffsetForContent(region) : logicalWidth() - logicalRightOffsetForContent(region); } LayoutUnit logicalLeftOffsetForContent(LayoutUnit blockOffset) const { return logicalLeftOffsetForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit logicalRightOffsetForContent(LayoutUnit blockOffset) const { return logicalRightOffsetForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit availableLogicalWidthForContent(LayoutUnit blockOffset) const { return availableLogicalWidthForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit startOffsetForContent(LayoutUnit blockOffset) const { return startOffsetForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit endOffsetForContent(LayoutUnit blockOffset) const { return endOffsetForContent(regionAtBlockOffset(blockOffset)); } LayoutUnit logicalLeftOffsetForContent() const { return isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); } LayoutUnit logicalRightOffsetForContent() const { return logicalLeftOffsetForContent() + availableLogicalWidth(); } LayoutUnit startOffsetForContent() const { return style()->isLeftToRightDirection() ? logicalLeftOffsetForContent() : logicalWidth() - logicalRightOffsetForContent(); } LayoutUnit endOffsetForContent() const { return !style()->isLeftToRightDirection() ? logicalLeftOffsetForContent() : logicalWidth() - logicalRightOffsetForContent(); } void setStaticInlinePositionForChild(RenderBox*, LayoutUnit blockOffset, LayoutUnit inlinePosition); void updateStaticInlinePositionForChild(RenderBox*, LayoutUnit logicalTop); LayoutUnit computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, RenderRegion* = 0); void placeRunInIfNeeded(RenderObject* newChild); bool runInIsPlacedIntoSiblingBlock(RenderObject* runIn); #ifndef NDEBUG void checkPositionedObjectsNeedLayout(); void showLineTreeAndMark(const InlineBox* = 0, const char* = 0, const InlineBox* = 0, const char* = 0, const RenderObject* = 0) const; #endif #if ENABLE(CSS_SHAPES) ShapeInsideInfo* ensureShapeInsideInfo() { if (!m_rareData || !m_rareData->m_shapeInsideInfo) setShapeInsideInfo(ShapeInsideInfo::createInfo(this)); return m_rareData->m_shapeInsideInfo.get(); } ShapeInsideInfo* shapeInsideInfo() const { if (!m_rareData || !m_rareData->m_shapeInsideInfo) return 0; return ShapeInsideInfo::isEnabledFor(this) ? m_rareData->m_shapeInsideInfo.get() : 0; } void setShapeInsideInfo(PassOwnPtr value) { if (!m_rareData) m_rareData = adoptPtr(new RenderBlockRareData(this)); m_rareData->m_shapeInsideInfo = value; } void markShapeInsideDescendantsForLayout(); ShapeInsideInfo* layoutShapeInsideInfo() const; bool allowsShapeInsideInfoSharing() const { return !isInline() && !isFloating(); } #endif protected: virtual void willBeDestroyed(); LayoutUnit maxPositiveMarginBefore() const { return m_rareData ? m_rareData->m_margins.positiveMarginBefore() : RenderBlockRareData::positiveMarginBeforeDefault(this); } LayoutUnit maxNegativeMarginBefore() const { return m_rareData ? m_rareData->m_margins.negativeMarginBefore() : RenderBlockRareData::negativeMarginBeforeDefault(this); } LayoutUnit maxPositiveMarginAfter() const { return m_rareData ? m_rareData->m_margins.positiveMarginAfter() : RenderBlockRareData::positiveMarginAfterDefault(this); } LayoutUnit maxNegativeMarginAfter() const { return m_rareData ? m_rareData->m_margins.negativeMarginAfter() : RenderBlockRareData::negativeMarginAfterDefault(this); } void setMaxMarginBeforeValues(LayoutUnit pos, LayoutUnit neg); void setMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg); void setMustDiscardMarginBefore(bool = true); void setMustDiscardMarginAfter(bool = true); bool mustDiscardMarginBefore() const; bool mustDiscardMarginAfter() const; bool mustDiscardMarginBeforeForChild(const RenderBox*) const; bool mustDiscardMarginAfterForChild(const RenderBox*) const; bool mustSeparateMarginBeforeForChild(const RenderBox*) const; bool mustSeparateMarginAfterForChild(const RenderBox*) const; void initMaxMarginValues() { if (m_rareData) { m_rareData->m_margins = MarginValues(RenderBlockRareData::positiveMarginBeforeDefault(this) , RenderBlockRareData::negativeMarginBeforeDefault(this), RenderBlockRareData::positiveMarginAfterDefault(this), RenderBlockRareData::negativeMarginAfterDefault(this)); m_rareData->m_paginationStrut = 0; m_rareData->m_discardMarginBefore = false; m_rareData->m_discardMarginAfter = false; } } virtual void layout(); void layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly = false); void markFixedPositionObjectForLayoutIfNeeded(RenderObject* child); virtual void paint(PaintInfo&, const LayoutPoint&); virtual void paintObject(PaintInfo&, const LayoutPoint&); virtual void paintChildren(PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect); bool paintChild(RenderBox*, PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect); LayoutUnit logicalRightOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining = 0, LayoutUnit logicalHeight = 0) const { return adjustLogicalRightOffsetForLine(logicalRightFloatOffsetForLine(logicalTop, fixedOffset, heightRemaining, logicalHeight, ShapeOutsideFloatShapeOffset), applyTextIndent); } LayoutUnit logicalLeftOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining = 0, LayoutUnit logicalHeight = 0) const { return adjustLogicalLeftOffsetForLine(logicalLeftFloatOffsetForLine(logicalTop, fixedOffset, heightRemaining, logicalHeight, ShapeOutsideFloatShapeOffset), applyTextIndent); } LayoutUnit logicalRightOffsetForLineIgnoringShapeOutside(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining = 0, LayoutUnit logicalHeight = 0) const { return adjustLogicalRightOffsetForLine(logicalRightFloatOffsetForLine(logicalTop, fixedOffset, heightRemaining, logicalHeight, ShapeOutsideFloatMarginBoxOffset), applyTextIndent); } LayoutUnit logicalLeftOffsetForLineIgnoringShapeOutside(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining = 0, LayoutUnit logicalHeight = 0) const { return adjustLogicalLeftOffsetForLine(logicalLeftFloatOffsetForLine(logicalTop, fixedOffset, heightRemaining, logicalHeight, ShapeOutsideFloatMarginBoxOffset), applyTextIndent); } virtual ETextAlign textAlignmentForLine(bool endsWithSoftBreak) const; virtual void adjustInlineDirectionLineBounds(int /* expansionOpportunityCount */, float& /* logicalLeft */, float& /* logicalWidth */) const { } virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE; virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const OVERRIDE; virtual void computePreferredLogicalWidths() OVERRIDE; virtual int firstLineBoxBaseline() const; virtual int inlineBlockBaseline(LineDirectionMode) const OVERRIDE; int lastLineBoxBaseline(LineDirectionMode) const; virtual void updateHitTestResult(HitTestResult&, const LayoutPoint&); // Delay update scrollbar until finishDelayRepaint() will be // called. This function is used when a flexbox is laying out its // descendant. If multiple calls are made to startDelayRepaint(), // finishDelayRepaint() will do nothing until finishDelayRepaint() // is called the same number of times. static void startDelayUpdateScrollInfo(); static void finishDelayUpdateScrollInfo(); void updateScrollInfoAfterLayout(); virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); virtual bool hasLineIfEmpty() const; bool simplifiedLayout(); virtual void simplifiedNormalFlowLayout(); void setDesiredColumnCountAndWidth(int, LayoutUnit); public: void computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats = false); void clearLayoutOverflow(); protected: virtual void addOverflowFromChildren(); void addOverflowFromFloats(); void addOverflowFromPositionedObjects(); void addOverflowFromBlockChildren(); void addOverflowFromInlineChildren(); void addVisualOverflowFromTheme(); virtual void addFocusRingRects(Vector&, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer = 0) OVERRIDE; #if ENABLE(SVG) // Only used by RenderSVGText, which explicitely overrides RenderBlock::layoutBlock(), do NOT use for anything else. void forceLayoutInlineChildren() { LayoutUnit repaintLogicalTop = 0; LayoutUnit repaintLogicalBottom = 0; clearFloats(); layoutInlineChildren(true, repaintLogicalTop, repaintLogicalBottom); } #endif bool updateRegionsAndShapesBeforeChildLayout(RenderFlowThread*); void updateRegionsAndShapesAfterChildLayout(RenderFlowThread*, bool heightChanged = false); void computeRegionRangeForBlock(RenderFlowThread*); void updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox*); virtual void checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight); private: enum ShapeOutsideFloatOffsetMode { ShapeOutsideFloatShapeOffset, ShapeOutsideFloatMarginBoxOffset }; LayoutUnit logicalRightFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining, LayoutUnit logicalHeight, ShapeOutsideFloatOffsetMode) const; LayoutUnit logicalLeftFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining, LayoutUnit logicalHeight, ShapeOutsideFloatOffsetMode) const; LayoutUnit adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const; LayoutUnit adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const; #if ENABLE(CSS_SHAPES) void computeShapeSize(); void updateShapeInsideInfoAfterStyleChange(const ShapeValue*, const ShapeValue* oldShape); #endif virtual RenderObjectChildList* virtualChildren() { return children(); } virtual const RenderObjectChildList* virtualChildren() const { return children(); } virtual const char* renderName() const; virtual bool isRenderBlock() const { return true; } virtual bool isBlockFlow() const { return (!isInline() || isReplaced()) && !isTable(); } virtual bool isInlineBlockOrInlineTable() const { return isInline() && isReplaced(); } void makeChildrenNonInline(RenderObject* insertionPoint = 0); virtual void removeLeftoverAnonymousBlock(RenderBlock* child); void moveAllChildrenIncludingFloatsTo(RenderBlock* toBlock, bool fullRemoveInsert); virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } void addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild); void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild); void addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild); virtual void addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild = 0); virtual bool isSelfCollapsingBlock() const; virtual LayoutUnit collapsedMarginBefore() const { return maxPositiveMarginBefore() - maxNegativeMarginBefore(); } virtual LayoutUnit collapsedMarginAfter() const { return maxPositiveMarginAfter() - maxNegativeMarginAfter(); } virtual void repaintOverhangingFloats(bool paintAllDescendants) OVERRIDE; void layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloatLogicalBottom); void layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom); BidiRun* handleTrailingSpaces(BidiRunList&, BidiContext*); void insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*&, TrackedContainerMap*&); static void removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*&, TrackedContainerMap*&); virtual RootInlineBox* createRootInlineBox(); // Subclassed by SVG and Ruby. // Called to lay out the legend for a fieldset or the ruby text of a ruby run. virtual RenderObject* layoutSpecialExcludedChild(bool /*relayoutChildren*/) { return 0; } void createFirstLetterRenderer(RenderObject* firstLetterBlock, RenderObject* currentChild); void updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer); Node* nodeForHitTest() const; struct FloatWithRect { FloatWithRect(RenderBox* f) : object(f) , rect(LayoutRect(f->x() - f->marginLeft(), f->y() - f->marginTop(), f->width() + f->marginWidth(), f->height() + f->marginHeight())) , everHadLayout(f->everHadLayout()) { } RenderBox* object; LayoutRect rect; bool everHadLayout; }; struct FloatingObject { WTF_MAKE_NONCOPYABLE(FloatingObject); WTF_MAKE_FAST_ALLOCATED; public: // Note that Type uses bits so you can use FloatLeftRight as a mask to query for both left and right. enum Type { FloatLeft = 1, FloatRight = 2, FloatLeftRight = 3 }; FloatingObject(EFloat type) : m_renderer(0) , m_originatingLine(0) , m_paginationStrut(0) , m_shouldPaint(true) , m_isDescendant(false) , m_isPlaced(false) #ifndef NDEBUG , m_isInPlacedTree(false) #endif { ASSERT(type != NoFloat); if (type == LeftFloat) m_type = FloatLeft; else if (type == RightFloat) m_type = FloatRight; } FloatingObject(Type type, const LayoutRect& frameRect) : m_renderer(0) , m_originatingLine(0) , m_frameRect(frameRect) , m_paginationStrut(0) , m_type(type) , m_shouldPaint(true) , m_isDescendant(false) , m_isPlaced(true) #ifndef NDEBUG , m_isInPlacedTree(false) #endif { } FloatingObject* clone() const { FloatingObject* cloneObject = new FloatingObject(type(), m_frameRect); cloneObject->m_renderer = m_renderer; cloneObject->m_originatingLine = m_originatingLine; cloneObject->m_paginationStrut = m_paginationStrut; cloneObject->m_shouldPaint = m_shouldPaint; cloneObject->m_isDescendant = m_isDescendant; cloneObject->m_isPlaced = m_isPlaced; return cloneObject; } Type type() const { return static_cast(m_type); } RenderBox* renderer() const { return m_renderer; } bool isPlaced() const { return m_isPlaced; } void setIsPlaced(bool placed = true) { m_isPlaced = placed; } inline LayoutUnit x() const { ASSERT(isPlaced()); return m_frameRect.x(); } inline LayoutUnit maxX() const { ASSERT(isPlaced()); return m_frameRect.maxX(); } inline LayoutUnit y() const { ASSERT(isPlaced()); return m_frameRect.y(); } inline LayoutUnit maxY() const { ASSERT(isPlaced()); return m_frameRect.maxY(); } inline LayoutUnit width() const { return m_frameRect.width(); } inline LayoutUnit height() const { return m_frameRect.height(); } void setX(LayoutUnit x) { ASSERT(!isInPlacedTree()); m_frameRect.setX(x); } void setY(LayoutUnit y) { ASSERT(!isInPlacedTree()); m_frameRect.setY(y); } void setWidth(LayoutUnit width) { ASSERT(!isInPlacedTree()); m_frameRect.setWidth(width); } void setHeight(LayoutUnit height) { ASSERT(!isInPlacedTree()); m_frameRect.setHeight(height); } const LayoutRect& frameRect() const { ASSERT(isPlaced()); return m_frameRect; } void setFrameRect(const LayoutRect& frameRect) { ASSERT(!isInPlacedTree()); m_frameRect = frameRect; } #ifndef NDEBUG bool isInPlacedTree() const { return m_isInPlacedTree; } void setIsInPlacedTree(bool value) { m_isInPlacedTree = value; } #endif bool shouldPaint() const { return m_shouldPaint; } void setShouldPaint(bool shouldPaint) { m_shouldPaint = shouldPaint; } bool isDescendant() const { return m_isDescendant; } void setIsDescendant(bool isDescendant) { m_isDescendant = isDescendant; } RenderBox* m_renderer; RootInlineBox* m_originatingLine; LayoutRect m_frameRect; int m_paginationStrut; private: unsigned m_type : 2; // Type (left or right aligned) unsigned m_shouldPaint : 1; unsigned m_isDescendant : 1; unsigned m_isPlaced : 1; #ifndef NDEBUG unsigned m_isInPlacedTree : 1; #endif }; LayoutPoint flipFloatForWritingModeForChild(const FloatingObject*, const LayoutPoint&) const; LayoutUnit logicalTopForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->y() : child->x(); } LayoutUnit logicalBottomForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->maxY() : child->maxX(); } LayoutUnit logicalLeftForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->x() : child->y(); } LayoutUnit logicalRightForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->maxX() : child->maxY(); } LayoutUnit logicalWidthForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->width() : child->height(); } int pixelSnappedLogicalTopForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->frameRect().pixelSnappedY() : child->frameRect().pixelSnappedX(); } int pixelSnappedLogicalBottomForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->frameRect().pixelSnappedMaxY() : child->frameRect().pixelSnappedMaxX(); } int pixelSnappedLogicalLeftForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->frameRect().pixelSnappedX() : child->frameRect().pixelSnappedY(); } int pixelSnappedLogicalRightForFloat(const FloatingObject* child) const { return isHorizontalWritingMode() ? child->frameRect().pixelSnappedMaxX() : child->frameRect().pixelSnappedMaxY(); } void setLogicalTopForFloat(FloatingObject* child, LayoutUnit logicalTop) { if (isHorizontalWritingMode()) child->setY(logicalTop); else child->setX(logicalTop); } void setLogicalLeftForFloat(FloatingObject* child, LayoutUnit logicalLeft) { if (isHorizontalWritingMode()) child->setX(logicalLeft); else child->setY(logicalLeft); } void setLogicalHeightForFloat(FloatingObject* child, LayoutUnit logicalHeight) { if (isHorizontalWritingMode()) child->setHeight(logicalHeight); else child->setWidth(logicalHeight); } void setLogicalWidthForFloat(FloatingObject* child, LayoutUnit logicalWidth) { if (isHorizontalWritingMode()) child->setWidth(logicalWidth); else child->setHeight(logicalWidth); } LayoutUnit xPositionForFloatIncludingMargin(const FloatingObject* child) const { if (isHorizontalWritingMode()) return child->x() + child->renderer()->marginLeft(); else return child->x() + marginBeforeForChild(child->renderer()); } LayoutUnit yPositionForFloatIncludingMargin(const FloatingObject* child) const { if (isHorizontalWritingMode()) return child->y() + marginBeforeForChild(child->renderer()); else return child->y() + child->renderer()->marginTop(); } LayoutPoint computeLogicalLocationForFloat(const FloatingObject*, LayoutUnit logicalTopOffset) const; // The following functions' implementations are in RenderBlockLineLayout.cpp. struct RenderTextInfo { // Destruction of m_layout requires TextLayout to be a complete type, so the constructor and destructor are made non-inline to avoid compilation errors. RenderTextInfo(); ~RenderTextInfo(); RenderText* m_text; OwnPtr m_layout; LazyLineBreakIterator m_lineBreakIterator; const Font* m_font; }; class LineBreaker { public: LineBreaker(RenderBlock* block) : m_block(block) { reset(); } InlineIterator nextLineBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&); bool lineWasHyphenated() { return m_hyphenated; } const Vector& positionedObjects() { return m_positionedObjects; } EClear clear() { return m_clear; } private: void reset(); InlineIterator nextSegmentBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&); void skipTrailingWhitespace(InlineIterator&, const LineInfo&); void skipLeadingWhitespace(InlineBidiResolver&, LineInfo&, FloatingObject* lastFloatFromPreviousLine, LineWidth&); RenderBlock* m_block; bool m_hyphenated; EClear m_clear; Vector m_positionedObjects; }; void checkFloatsInCleanLine(RootInlineBox*, Vector&, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat); RootInlineBox* determineStartPosition(LineLayoutState&, InlineBidiResolver&); void determineEndPosition(LineLayoutState&, RootInlineBox* startBox, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus); bool matchedEndLine(LineLayoutState&, const InlineBidiResolver&, const InlineIterator& endLineStart, const BidiStatus& endLineStatus); bool checkPaginationAndFloatsAtEndLine(LineLayoutState&); RootInlineBox* constructLine(BidiRunList&, const LineInfo&); InlineFlowBox* createLineBoxes(RenderObject*, const LineInfo&, InlineBox* childBox, bool startsNewSegment); void setMarginsForRubyRun(BidiRun*, RenderRubyRun*, RenderObject*, const LineInfo&); BidiRun* computeInlineDirectionPositionsForSegment(RootInlineBox*, const LineInfo&, ETextAlign, float& logicalLeft, float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache&, WordMeasurements&); void computeInlineDirectionPositionsForLine(RootInlineBox*, const LineInfo&, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&, WordMeasurements&); void computeBlockDirectionPositionsForLine(RootInlineBox*, BidiRun*, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&); void deleteEllipsisLineBoxes(); void checkLinesForTextOverflow(); // Positions new floats and also adjust all floats encountered on the line if any of them // have to move to the next page/column. bool positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo&, LineWidth&); void appendFloatingObjectToLastLine(FloatingObject*); // End of functions defined in RenderBlockLineLayout.cpp. void paintFloats(PaintInfo&, const LayoutPoint&, bool preservePhase = false); void paintContents(PaintInfo&, const LayoutPoint&); void paintColumnContents(PaintInfo&, const LayoutPoint&, bool paintFloats = false); void paintColumnRules(PaintInfo&, const LayoutPoint&); void paintSelection(PaintInfo&, const LayoutPoint&); void paintCaret(PaintInfo&, const LayoutPoint&, CaretType); FloatingObject* insertFloatingObject(RenderBox*); void removeFloatingObject(RenderBox*); void removeFloatingObjectsBelow(FloatingObject*, int logicalOffset); // Called from lineWidth, to position the floats added in the last line. // Returns true if and only if it has positioned any floats. bool positionNewFloats(); void clearFloats(); LayoutUnit getClearDelta(RenderBox* child, LayoutUnit yPos); virtual bool avoidsFloats() const; bool hasOverhangingFloats() { return parent() && !hasColumns() && containsFloats() && lowestFloatLogicalBottom() > logicalHeight(); } bool hasOverhangingFloat(RenderBox*); void addIntrudingFloats(RenderBlock* prev, LayoutUnit xoffset, LayoutUnit yoffset); LayoutUnit addOverhangingFloats(RenderBlock* child, bool makeChildPaintOtherFloats); LayoutUnit lowestFloatLogicalBottom(FloatingObject::Type = FloatingObject::FloatLeftRight) const; LayoutUnit nextFloatLogicalBottomBelow(LayoutUnit) const; virtual bool hitTestColumns(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction); virtual bool hitTestContents(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction); bool hitTestFloats(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset); virtual bool isPointInOverflowControl(HitTestResult&, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset); // FIXME: Make this method const so we can remove the const_cast in computeIntrinsicLogicalWidths. void computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth); void computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const; // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline // children. virtual RenderBlock* firstLineBlock() const; virtual LayoutRect rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const OVERRIDE; virtual RenderStyle* outlineStyleForRepaint() const; virtual RenderObject* hoverAncestor() const; virtual void updateDragState(bool dragOn); virtual void childBecameNonInline(RenderObject* child); virtual LayoutRect selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool /*clipToVisibleContent*/) OVERRIDE { return selectionGapRectsForRepaint(repaintContainer); } virtual bool shouldPaintSelectionGaps() const; bool isSelectionRoot() const; GapRects selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches&, const PaintInfo* = 0); GapRects inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches&, const PaintInfo*); GapRects blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches&, const PaintInfo*); LayoutRect blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const LogicalSelectionOffsetCaches&, const PaintInfo*); LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches&); LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches&); friend class LogicalSelectionOffsetCaches; virtual void absoluteRects(Vector&, const LayoutPoint& accumulatedOffset) const; virtual void absoluteQuads(Vector&, bool* wasFixed) const; LayoutUnit desiredColumnWidth() const; unsigned desiredColumnCount() const; void paintContinuationOutlines(PaintInfo&, const LayoutPoint&); virtual LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = 0); void adjustPointToColumnContents(LayoutPoint&) const; void fitBorderToLinesIfNeeded(); // Shrink the box in which the border paints if border-fit is set. void adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutUnit& right) const; // Helper function for borderFitAdjust void markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest = 0); void newLine(EClear); Position positionForBox(InlineBox*, bool start = true) const; VisiblePosition positionForPointWithInlineChildren(const LayoutPoint&); virtual void calcColumnWidth(); void makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlock* newBlockBox, RenderObject* newChild); bool expandsToEncloseOverhangingFloats() const; void splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, RenderObject* beforeChild, RenderBoxModelObject* oldCont); void splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, RenderObject* newChild, RenderBoxModelObject* oldCont); RenderBlock* clone() const; RenderBlock* continuationBefore(RenderObject* beforeChild); RenderBlock* containingColumnsBlock(bool allowAnonymousColumnBlock = true); RenderBlock* columnsBlockForSpanningElement(RenderObject* newChild); class MarginInfo { // Collapsing flags for whether we can collapse our margins with our children's margins. bool m_canCollapseWithChildren : 1; bool m_canCollapseMarginBeforeWithChildren : 1; bool m_canCollapseMarginAfterWithChildren : 1; // Whether or not we are a quirky container, i.e., do we collapse away top and bottom // margins in our container. Table cells and the body are the common examples. We // also have a custom style property for Safari RSS to deal with TypePad blog articles. bool m_quirkContainer : 1; // This flag tracks whether we are still looking at child margins that can all collapse together at the beginning of a block. // They may or may not collapse with the top margin of the block (|m_canCollapseTopWithChildren| tells us that), but they will // always be collapsing with one another. This variable can remain set to true through multiple iterations // as long as we keep encountering self-collapsing blocks. bool m_atBeforeSideOfBlock : 1; // This flag is set when we know we're examining bottom margins and we know we're at the bottom of the block. bool m_atAfterSideOfBlock : 1; // These variables are used to detect quirky margins that we need to collapse away (in table cells // and in the body element). bool m_hasMarginBeforeQuirk : 1; bool m_hasMarginAfterQuirk : 1; bool m_determinedMarginBeforeQuirk : 1; bool m_discardMargin : 1; // These flags track the previous maximal positive and negative margins. LayoutUnit m_positiveMargin; LayoutUnit m_negativeMargin; public: MarginInfo(RenderBlock*, LayoutUnit beforeBorderPadding, LayoutUnit afterBorderPadding); void setAtBeforeSideOfBlock(bool b) { m_atBeforeSideOfBlock = b; } void setAtAfterSideOfBlock(bool b) { m_atAfterSideOfBlock = b; } void clearMargin() { m_positiveMargin = 0; m_negativeMargin = 0; } void setHasMarginBeforeQuirk(bool b) { m_hasMarginBeforeQuirk = b; } void setHasMarginAfterQuirk(bool b) { m_hasMarginAfterQuirk = b; } void setDeterminedMarginBeforeQuirk(bool b) { m_determinedMarginBeforeQuirk = b; } void setPositiveMargin(LayoutUnit p) { ASSERT(!m_discardMargin); m_positiveMargin = p; } void setNegativeMargin(LayoutUnit n) { ASSERT(!m_discardMargin); m_negativeMargin = n; } void setPositiveMarginIfLarger(LayoutUnit p) { ASSERT(!m_discardMargin); if (p > m_positiveMargin) m_positiveMargin = p; } void setNegativeMarginIfLarger(LayoutUnit n) { ASSERT(!m_discardMargin); if (n > m_negativeMargin) m_negativeMargin = n; } void setMargin(LayoutUnit p, LayoutUnit n) { ASSERT(!m_discardMargin); m_positiveMargin = p; m_negativeMargin = n; } void setCanCollapseMarginAfterWithChildren(bool collapse) { m_canCollapseMarginAfterWithChildren = collapse; } void setDiscardMargin(bool value) { m_discardMargin = value; } bool atBeforeSideOfBlock() const { return m_atBeforeSideOfBlock; } bool canCollapseWithMarginBefore() const { return m_atBeforeSideOfBlock && m_canCollapseMarginBeforeWithChildren; } bool canCollapseWithMarginAfter() const { return m_atAfterSideOfBlock && m_canCollapseMarginAfterWithChildren; } bool canCollapseMarginBeforeWithChildren() const { return m_canCollapseMarginBeforeWithChildren; } bool canCollapseMarginAfterWithChildren() const { return m_canCollapseMarginAfterWithChildren; } bool quirkContainer() const { return m_quirkContainer; } bool determinedMarginBeforeQuirk() const { return m_determinedMarginBeforeQuirk; } bool hasMarginBeforeQuirk() const { return m_hasMarginBeforeQuirk; } bool hasMarginAfterQuirk() const { return m_hasMarginAfterQuirk; } LayoutUnit positiveMargin() const { return m_positiveMargin; } LayoutUnit negativeMargin() const { return m_negativeMargin; } bool discardMargin() const { return m_discardMargin; } LayoutUnit margin() const { return m_positiveMargin - m_negativeMargin; } }; void layoutBlockChild(RenderBox* child, MarginInfo&, LayoutUnit& previousFloatLogicalBottom, LayoutUnit& maxFloatLogicalBottom); void adjustPositionedBlock(RenderBox* child, const MarginInfo&); void adjustFloatingBlock(const MarginInfo&); RenderBoxModelObject* createReplacementRunIn(RenderBoxModelObject* runIn); void moveRunInUnderSiblingBlockIfNeeded(RenderObject* runIn); void moveRunInToOriginalPosition(RenderObject* runIn); LayoutUnit collapseMargins(RenderBox* child, MarginInfo&); LayoutUnit clearFloatsIfNeeded(RenderBox* child, MarginInfo&, LayoutUnit oldTopPosMargin, LayoutUnit oldTopNegMargin, LayoutUnit yPos); LayoutUnit estimateLogicalTopPosition(RenderBox* child, const MarginInfo&, LayoutUnit& estimateWithoutPagination); void marginBeforeEstimateForChild(RenderBox*, LayoutUnit&, LayoutUnit&, bool&) const; void handleAfterSideOfBlock(LayoutUnit top, LayoutUnit bottom, MarginInfo&); void setCollapsedBottomMargin(const MarginInfo&); // End helper functions and structs used by layoutBlockChildren. // Helper function for layoutInlineChildren() RootInlineBox* createLineBoxesFromBidiRuns(BidiRunList&, const InlineIterator& end, LineInfo&, VerticalPositionCache&, BidiRun* trailingSpaceRun, WordMeasurements&); void layoutRunsAndFloats(LineLayoutState&, bool hasInlineChild); void layoutRunsAndFloatsInRange(LineLayoutState&, InlineBidiResolver&, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines); #if ENABLE(CSS_SHAPES) void updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo*&, LayoutUnit&, LineLayoutState&); void updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo*&, LineLayoutState&); bool adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo*, LayoutUnit, LineLayoutState&, InlineBidiResolver&, FloatingObject*, InlineIterator&, WordMeasurements&); #endif const InlineIterator& restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver&, const InlineIterator&); void linkToEndLineIfNeeded(LineLayoutState&); static void repaintDirtyFloats(Vector& floats); protected: void dirtyForLayoutFromPercentageHeightDescendants(); void determineLogicalLeftPositionForChild(RenderBox* child, ApplyLayoutDeltaMode = DoNotApplyLayoutDelta); // Pagination routines. virtual bool relayoutForPagination(bool hasSpecifiedPageLogicalHeight, LayoutUnit pageLogicalHeight, LayoutStateMaintainer&); // Returns the logicalOffset at the top of the next page. If the offset passed in is already at the top of the current page, // then nextPageLogicalTop with ExcludePageBoundary will still move to the top of the next page. nextPageLogicalTop with // IncludePageBoundary set will not. // // For a page height of 800px, the first rule will return 800 if the value passed in is 0. The second rule will simply return 0. enum PageBoundaryRule { ExcludePageBoundary, IncludePageBoundary }; LayoutUnit nextPageLogicalTop(LayoutUnit logicalOffset, PageBoundaryRule = ExcludePageBoundary) const; bool hasNextPage(LayoutUnit logicalOffset, PageBoundaryRule = ExcludePageBoundary) const; virtual ColumnInfo::PaginationUnit paginationUnit() const; LayoutUnit applyBeforeBreak(RenderBox* child, LayoutUnit logicalOffset); // If the child has a before break, then return a new yPos that shifts to the top of the next page/column. LayoutUnit applyAfterBreak(RenderBox* child, LayoutUnit logicalOffset, MarginInfo&); // If the child has an after break, then return a new offset that shifts to the top of the next page/column. public: LayoutUnit pageLogicalTopForOffset(LayoutUnit offset) const; LayoutUnit pageLogicalHeightForOffset(LayoutUnit offset) const; LayoutUnit pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule = IncludePageBoundary) const; protected: bool pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, LayoutUnit logicalOffset, LayoutUnit minimumLogicalHeight) const; // A page break is required at some offset due to space shortage in the current fragmentainer. void setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage); // Update minimum page height required to avoid fragmentation where it shouldn't occur (inside // unbreakable content, between orphans and widows, etc.). This will be used as a hint to the // column balancer to help set a good minimum column height. void updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight); LayoutUnit adjustForUnsplittableChild(RenderBox* child, LayoutUnit logicalOffset, bool includeMargins = false); // If the child is unsplittable and can't fit on the current page, return the top of the next page/column. void adjustLinePositionForPagination(RootInlineBox*, LayoutUnit& deltaOffset, RenderFlowThread*); // Computes a deltaOffset value that put a line at the top of the next page if it doesn't fit on the current page. LayoutUnit adjustBlockChildForPagination(LayoutUnit logicalTopAfterClear, LayoutUnit estimateWithoutPagination, RenderBox* child, bool atBeforeSideOfBlock); // Adjust from painting offsets to the local coords of this renderer void offsetForContents(LayoutPoint&) const; // This function is called to test a line box that has moved in the block direction to see if it has ended up in a new // region/page/column that has a different available line width than the old one. Used to know when you have to dirty a // line, i.e., that it can't be re-used. bool lineWidthForPaginatedLineChanged(RootInlineBox*, LayoutUnit lineDelta, RenderFlowThread*) const; bool logicalWidthChangedInRegions(RenderFlowThread*) const; virtual bool requiresColumns(int desiredColumnCount) const; virtual bool updateLogicalWidthAndColumnWidth(); virtual bool canCollapseAnonymousBlockChild() const { return true; } public: virtual LayoutUnit offsetFromLogicalTopOfFirstPage() const; RenderRegion* regionAtBlockOffset(LayoutUnit) const; RenderRegion* clampToStartAndEndRegions(RenderRegion*) const; protected: struct FloatingObjectHashFunctions { static unsigned hash(FloatingObject* key) { return DefaultHash::Hash::hash(key->m_renderer); } static bool equal(FloatingObject* a, FloatingObject* b) { return a->m_renderer == b->m_renderer; } static const bool safeToCompareToEmptyOrDeleted = true; }; struct FloatingObjectHashTranslator { static unsigned hash(RenderBox* key) { return DefaultHash::Hash::hash(key); } static bool equal(FloatingObject* a, RenderBox* b) { return a->m_renderer == b; } }; typedef ListHashSet FloatingObjectSet; typedef FloatingObjectSet::const_iterator FloatingObjectSetIterator; typedef PODInterval FloatingObjectInterval; typedef PODIntervalTree FloatingObjectTree; typedef PODFreeListArena::Node> IntervalArena; template class FloatIntervalSearchAdapter { public: typedef FloatingObjectInterval IntervalType; FloatIntervalSearchAdapter(const RenderBlock* renderer, int lowValue, int highValue, LayoutUnit& offset, LayoutUnit* heightRemaining) : m_renderer(renderer) , m_lowValue(lowValue) , m_highValue(highValue) , m_offset(offset) , m_heightRemaining(heightRemaining) #if ENABLE(CSS_SHAPES) , m_last(0) #endif { } inline int lowValue() const { return m_lowValue; } inline int highValue() const { return m_highValue; } void collectIfNeeded(const IntervalType&) const; #if ENABLE(CSS_SHAPES) // When computing the offset caused by the floats on a given line, if // the outermost float on that line has a shape-outside, the inline // content that butts up against that float must be positioned using // the contours of the shape, not the margin box of the float. // We save the last float encountered so that the offset can be // computed correctly by the code using this adapter. const FloatingObject* lastFloat() const { return m_last; } #endif private: const RenderBlock* m_renderer; int m_lowValue; int m_highValue; LayoutUnit& m_offset; LayoutUnit* m_heightRemaining; #if ENABLE(CSS_SHAPES) // This member variable is mutable because the collectIfNeeded method // is declared as const, even though it doesn't actually respect that // contract. It modifies other member variables via loopholes in the // const behavior. Instead of using loopholes, I decided it was better // to make the fact that this is modified in a const method explicit. mutable const FloatingObject* m_last; #endif }; void createFloatingObjects(); public: class FloatingObjects { WTF_MAKE_NONCOPYABLE(FloatingObjects); WTF_MAKE_FAST_ALLOCATED; public: void clear(); void add(FloatingObject*); void remove(FloatingObject*); void addPlacedObject(FloatingObject*); void removePlacedObject(FloatingObject*); void setHorizontalWritingMode(bool b = true) { m_horizontalWritingMode = b; } bool hasLeftObjects() const { return m_leftObjectsCount > 0; } bool hasRightObjects() const { return m_rightObjectsCount > 0; } const FloatingObjectSet& set() const { return m_set; } const FloatingObjectTree& placedFloatsTree() { computePlacedFloatsTreeIfNeeded(); return m_placedFloatsTree; } private: FloatingObjects(const RenderBlock*, bool horizontalWritingMode); void computePlacedFloatsTree(); inline void computePlacedFloatsTreeIfNeeded() { if (!m_placedFloatsTree.isInitialized()) computePlacedFloatsTree(); } void increaseObjectsCount(FloatingObject::Type); void decreaseObjectsCount(FloatingObject::Type); FloatingObjectInterval intervalForFloatingObject(FloatingObject*); FloatingObjectSet m_set; FloatingObjectTree m_placedFloatsTree; unsigned m_leftObjectsCount; unsigned m_rightObjectsCount; bool m_horizontalWritingMode; const RenderBlock* m_renderer; friend void RenderBlock::createFloatingObjects(); }; // Allocated only when some of these fields have non-default values struct RenderBlockRareData { WTF_MAKE_NONCOPYABLE(RenderBlockRareData); WTF_MAKE_FAST_ALLOCATED; public: RenderBlockRareData(const RenderBlock* block) : m_margins(positiveMarginBeforeDefault(block), negativeMarginBeforeDefault(block), positiveMarginAfterDefault(block), negativeMarginAfterDefault(block)) , m_paginationStrut(0) , m_pageLogicalOffset(0) , m_lineGridBox(0) , m_lineBreakToAvoidWidow(0) , m_shouldBreakAtLineToAvoidWidow(false) , m_discardMarginBefore(false) , m_discardMarginAfter(false) { } static LayoutUnit positiveMarginBeforeDefault(const RenderBlock* block) { return std::max(block->marginBefore(), 0); } static LayoutUnit negativeMarginBeforeDefault(const RenderBlock* block) { return std::max(-block->marginBefore(), 0); } static LayoutUnit positiveMarginAfterDefault(const RenderBlock* block) { return std::max(block->marginAfter(), 0); } static LayoutUnit negativeMarginAfterDefault(const RenderBlock* block) { return std::max(-block->marginAfter(), 0); } MarginValues m_margins; LayoutUnit m_paginationStrut; LayoutUnit m_pageLogicalOffset; RootInlineBox* m_lineGridBox; RootInlineBox* m_lineBreakToAvoidWidow; #if ENABLE(CSS_SHAPES) OwnPtr m_shapeInsideInfo; #endif bool m_shouldBreakAtLineToAvoidWidow : 1; bool m_discardMarginBefore : 1; bool m_discardMarginAfter : 1; }; protected: OwnPtr m_floatingObjects; OwnPtr m_rareData; RenderObjectChildList m_children; RenderLineBoxList m_lineBoxes; // All of the root line boxes created for this block flow. For example,
Hello
world.
will have two total lines for the
. mutable signed m_lineHeight : 27; unsigned m_hasMarginBeforeQuirk : 1; // Note these quirk values can't be put in RenderBlockRareData since they are set too frequently. unsigned m_hasMarginAfterQuirk : 1; unsigned m_beingDestroyed : 1; unsigned m_hasMarkupTruncation : 1; unsigned m_hasBorderOrPaddingLogicalWidthChanged : 1; // RenderRubyBase objects need to be able to split and merge, moving their children around // (calling moveChildTo, moveAllChildrenTo, and makeChildrenNonInline). friend class RenderRubyBase; friend class LineWidth; // Needs to know FloatingObject private: // Used to store state between styleWillChange and styleDidChange static bool s_canPropagateFloatIntoSibling; }; inline RenderBlock* toRenderBlock(RenderObject* object) { ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderBlock()); return static_cast(object); } inline const RenderBlock* toRenderBlock(const RenderObject* object) { ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderBlock()); return static_cast(object); } // This will catch anyone doing an unnecessary cast. void toRenderBlock(const RenderBlock*); #ifndef NDEBUG // These structures are used by PODIntervalTree for debugging purposes. template <> struct ValueToString { static String string(const int value); }; template<> struct ValueToString { static String string(const RenderBlock::FloatingObject*); }; #endif } // namespace WebCore #endif // RenderBlock_h