This source file includes following definitions.
- m_hasOnlySelfCollapsingChildren
- removeBlockFromDescendantAndContainerMaps
- appendImageIfNotNull
- appendLayers
- appendImagesFromStyle
- willBeDestroyed
- styleWillChange
- borderOrPaddingLogicalWidthChanged
- styleDidChange
- continuationBefore
- addChildToContinuation
- addChildToAnonymousColumnBlocks
- containingColumnsBlock
- clone
- splitBlocks
- splitFlow
- makeChildrenAnonymousColumnBlocks
- columnsBlockForSpanningElement
- addChildIgnoringAnonymousColumnBlocks
- addChild
- addChildIgnoringContinuation
- getInlineRun
- deleteLineBoxTree
- makeChildrenNonInline
- removeLeftoverAnonymousBlock
- canMergeContiguousAnonymousBlocks
- collapseAnonymousBlockChild
- removeChild
- isSelfCollapsingBlock
- startDelayUpdateScrollInfo
- finishDelayUpdateScrollInfo
- updateScrollInfoAfterLayout
- layout
- updateImageLoadingPriorities
- logicalOffsetFromShapeAncestorContainer
- computeRegionRangeForBlock
- updateLogicalWidthAndColumnWidth
- layoutBlock
- addOverflowFromChildren
- computeOverflow
- addOverflowFromBlockChildren
- addOverflowFromPositionedObjects
- addVisualOverflowFromTheme
- createsBlockFormattingContext
- updateBlockChildDirtyBitsBeforeLayout
- simplifiedNormalFlowLayout
- simplifiedLayout
- markFixedPositionObjectForLayoutIfNeeded
- marginIntrinsicLogicalWidthForChild
- layoutPositionedObjects
- markPositionedObjectsForLayout
- markForPaginationRelayoutIfNeeded
- paint
- paintColumnRules
- paintColumnContents
- paintContents
- paintChildren
- paintChild
- paintChildAsInlineBlock
- paintAsInlineBlock
- hasCaret
- paintCaret
- paintObject
- inlineElementContinuation
- blockElementContinuation
- continuationOutlineTable
- addContinuationWithOutline
- paintsContinuationOutline
- paintContinuationOutlines
- shouldPaintSelectionGaps
- isSelectionRoot
- selectionGapRectsForRepaint
- paintSelection
- clipOutPositionedObjects
- blockDirectionOffset
- inlineDirectionOffset
- logicalRectToPhysicalRect
- selectionGaps
- blockSelectionGaps
- blockSelectionGap
- logicalLeftSelectionGap
- logicalRightSelectionGap
- getSelectionGapInfo
- logicalLeftSelectionOffset
- logicalRightSelectionOffset
- blockBeforeWithinSelectionRoot
- insertIntoTrackedRendererMaps
- removeFromTrackedRendererMaps
- positionedObjects
- insertPositionedObject
- removePositionedObject
- removePositionedObjects
- addPercentHeightDescendant
- removePercentHeightDescendant
- percentHeightDescendants
- hasPercentHeightContainerMap
- hasPercentHeightDescendant
- dirtyForLayoutFromPercentageHeightDescendants
- removePercentHeightDescendantIfNeeded
- clearPercentHeightDescendantsFrom
- textIndentOffset
- markLinesDirtyInBlockRange
- avoidsFloats
- isPointInOverflowControl
- nodeForHitTest
- nodeAtPoint
- m_logicalLeft
- advance
- columnRect
- hasMore
- adjust
- update
- hitTestColumns
- adjustForColumnRect
- hitTestContents
- positionForBox
- isEditingBoundary
- positionForPointRespectingEditingBoundaries
- positionForPointWithInlineChildren
- isChildHitTestCandidate
- positionForPoint
- offsetForContents
- availableLogicalWidth
- columnGap
- calcColumnWidth
- requiresColumns
- setDesiredColumnCountAndWidth
- desiredColumnWidth
- columnInfo
- columnCount
- columnRectAt
- adjustPointToColumnContents
- adjustRectForColumns
- flipForWritingModeIncludingColumns
- adjustStartEdgeForWritingModeIncludingColumns
- adjustForColumns
- computeIntrinsicLogicalWidths
- computePreferredLogicalWidths
- adjustIntrinsicLogicalWidthsForColumns
- computeBlockPreferredLogicalWidths
- hasLineIfEmpty
- lineHeight
- beforeMarginInLineDirection
- baselinePosition
- minLineHeightForReplacedRenderer
- firstLineBoxBaseline
- inlineBlockBaseline
- lastLineBoxBaseline
- firstLineBlock
- styleForFirstLetter
- findFirstLetterBlock
- updateFirstLetterStyle
- createFirstLetterRenderer
- isRendererAllowedForFirstLetter
- m_firstLetterFound
- renderers
- containerBlock
- findTextRenderers
- rendererTextForFirstLetter
- processTextRenderer
- advancePositionWhile
- isPunctuationForFirstLetter
- isSpaceForFirstLetter
- updateFirstLetter
- shouldCheckLines
- getHeightForLineCount
- lineAtIndex
- lineCount
- heightForLineCount
- adjustForBorderFit
- fitBorderToLinesIfNeeded
- clearTruncation
- setPaginationStrut
- setPageLogicalOffset
- setBreakAtLineToAvoidWidow
- setDidBreakAtLineToAvoidWidow
- clearDidBreakAtLineToAvoidWidow
- clearShouldBreakAtLineToAvoidWidow
- absoluteRects
- absoluteQuads
- rectWithOutlineForRepaint
- hoverAncestor
- updateDragState
- outlineStyleForRepaint
- childBecameNonInline
- updateHitTestResult
- localCaretRect
- addFocusRingRects
- computeSelfHitTestRects
- createAnonymousBoxWithSameTypeAs
- nextPageLogicalTop
- pageLogicalTopForOffset
- pageLogicalHeightForOffset
- pageRemainingLogicalHeightForOffset
- adjustForUnsplittableChild
- pushToNextPageWithMinimumLogicalHeight
- setPageBreak
- updateMinimumPageHeight
- calculateMinimumPageHeight
- adjustLinePositionForPagination
- offsetFromLogicalTopOfFirstPage
- regionAtBlockOffset
- collapsedMarginBeforeForChild
- collapsedMarginAfterForChild
- hasMarginBeforeQuirk
- hasMarginAfterQuirk
- renderName
- createAnonymousWithParentRendererAndDisplay
- createAnonymousColumnsWithParentRenderer
- createAnonymousColumnSpanWithParentRenderer
- checkPositionedObjectsNeedLayout
- showLineTreeAndMark
#include "config.h"
#include "core/rendering/RenderBlock.h"
#include "HTMLNames.h"
#include "core/accessibility/AXObjectCache.h"
#include "core/dom/Document.h"
#include "core/dom/Element.h"
#include "core/events/OverflowEvent.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/editing/Editor.h"
#include "core/editing/FrameSelection.h"
#include "core/fetch/ResourceLoadPriorityOptimizer.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/page/Page.h"
#include "core/frame/Settings.h"
#include "core/rendering/FastTextAutosizer.h"
#include "core/rendering/GraphicsContextAnnotator.h"
#include "core/rendering/HitTestLocation.h"
#include "core/rendering/HitTestResult.h"
#include "core/rendering/InlineIterator.h"
#include "core/rendering/InlineTextBox.h"
#include "core/rendering/LayoutRectRecorder.h"
#include "core/rendering/LayoutRepainter.h"
#include "core/rendering/PaintInfo.h"
#include "core/rendering/RenderCombineText.h"
#include "core/rendering/RenderDeprecatedFlexibleBox.h"
#include "core/rendering/RenderFlexibleBox.h"
#include "core/rendering/RenderFlowThread.h"
#include "core/rendering/RenderInline.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderMarquee.h"
#include "core/rendering/RenderRegion.h"
#include "core/rendering/RenderTableCell.h"
#include "core/rendering/RenderTextControl.h"
#include "core/rendering/RenderTextFragment.h"
#include "core/rendering/RenderTheme.h"
#include "core/rendering/RenderView.h"
#include "core/rendering/shapes/ShapeOutsideInfo.h"
#include "core/rendering/style/ContentData.h"
#include "core/rendering/style/RenderStyle.h"
#include "platform/geometry/FloatQuad.h"
#include "platform/geometry/TransformState.h"
#include "platform/graphics/GraphicsContextCullSaver.h"
#include "platform/graphics/GraphicsContextStateSaver.h"
#include "wtf/StdLibExtras.h"
#include "wtf/TemporaryChange.h"
using namespace std;
using namespace WTF;
using namespace Unicode;
namespace WebCore {
using namespace HTMLNames;
struct SameSizeAsRenderBlock : public RenderBox {
void* pointers[1];
RenderObjectChildList children;
RenderLineBoxList lineBoxes;
uint32_t bitfields;
};
struct SameSizeAsRenderBlockRareData {
int paginationStrut;
int pageLogicalOffset;
uint32_t bitfields;
};
COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small);
COMPILE_ASSERT(sizeof(RenderBlock::RenderBlockRareData) == sizeof(SameSizeAsRenderBlockRareData), RenderBlockRareData_should_stay_small);
typedef WTF::HashMap<const RenderBox*, OwnPtr<ColumnInfo> > ColumnInfoMap;
static ColumnInfoMap* gColumnInfoMap = 0;
static TrackedDescendantsMap* gPositionedDescendantsMap = 0;
static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0;
static TrackedContainerMap* gPositionedContainerMap = 0;
static TrackedContainerMap* gPercentHeightContainerMap = 0;
typedef WTF::HashMap<RenderBlock*, OwnPtr<ListHashSet<RenderInline*> > > ContinuationOutlineTableMap;
typedef WTF::HashSet<RenderBlock*> DelayedUpdateScrollInfoSet;
static int gDelayUpdateScrollInfo = 0;
static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0;
static bool gColumnFlowSplitEnabled = true;
class OverflowEventDispatcher {
WTF_MAKE_NONCOPYABLE(OverflowEventDispatcher);
public:
OverflowEventDispatcher(const RenderBlock* block)
: m_block(block)
{
m_shouldDispatchEvent = !m_block->isAnonymous() && m_block->hasOverflowClip() && m_block->document().hasListenerType(Document::OVERFLOWCHANGED_LISTENER);
if (m_shouldDispatchEvent) {
m_hadHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow();
m_hadVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow();
}
}
~OverflowEventDispatcher()
{
if (!m_shouldDispatchEvent)
return;
bool hasHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow();
bool hasVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow();
bool horizontalLayoutOverflowChanged = hasHorizontalLayoutOverflow != m_hadHorizontalLayoutOverflow;
bool verticalLayoutOverflowChanged = hasVerticalLayoutOverflow != m_hadVerticalLayoutOverflow;
if (!horizontalLayoutOverflowChanged && !verticalLayoutOverflowChanged)
return;
RefPtrWillBeRawPtr<OverflowEvent> event = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow);
event->setTarget(m_block->node());
m_block->document().enqueueAnimationFrameEvent(event.release());
}
private:
const RenderBlock* m_block;
bool m_shouldDispatchEvent;
bool m_hadHorizontalLayoutOverflow;
bool m_hadVerticalLayoutOverflow;
};
RenderBlock::RenderBlock(ContainerNode* node)
: RenderBox(node)
, m_lineHeight(-1)
, m_hasMarginBeforeQuirk(false)
, m_hasMarginAfterQuirk(false)
, m_beingDestroyed(false)
, m_hasMarkupTruncation(false)
, m_hasBorderOrPaddingLogicalWidthChanged(false)
, m_hasOnlySelfCollapsingChildren(false)
{
setChildrenInline(true);
}
static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap)
{
if (OwnPtr<TrackedRendererListHashSet> descendantSet = descendantMap->take(block)) {
TrackedRendererListHashSet::iterator end = descendantSet->end();
for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) {
TrackedContainerMap::iterator it = containerMap->find(*descendant);
ASSERT(it != containerMap->end());
if (it == containerMap->end())
continue;
HashSet<RenderBlock*>* containerSet = it->value.get();
ASSERT(containerSet->contains(block));
containerSet->remove(block);
if (containerSet->isEmpty())
containerMap->remove(it);
}
}
}
static void appendImageIfNotNull(Vector<ImageResource*>& imageResources, const StyleImage* styleImage)
{
if (styleImage && styleImage->cachedImage()) {
ImageResource* imageResource = styleImage->cachedImage();
if (imageResource && !imageResource->isLoaded())
imageResources.append(styleImage->cachedImage());
}
}
static void appendLayers(Vector<ImageResource*>& images, const FillLayer* styleLayer)
{
for (const FillLayer* layer = styleLayer; layer; layer = layer->next()) {
appendImageIfNotNull(images, layer->image());
}
}
static void appendImagesFromStyle(Vector<ImageResource*>& images, RenderStyle& blockStyle)
{
appendLayers(images, blockStyle.backgroundLayers());
appendLayers(images, blockStyle.maskLayers());
const ContentData* contentData = blockStyle.contentData();
if (contentData && contentData->isImage()) {
const ImageContentData* imageContentData = static_cast<const ImageContentData*>(contentData);
appendImageIfNotNull(images, imageContentData->image());
}
if (blockStyle.boxReflect())
appendImageIfNotNull(images, blockStyle.boxReflect()->mask().image());
appendImageIfNotNull(images, blockStyle.listStyleImage());
appendImageIfNotNull(images, blockStyle.borderImageSource());
appendImageIfNotNull(images, blockStyle.maskBoxImageSource());
}
RenderBlock::~RenderBlock()
{
if (hasColumns())
gColumnInfoMap->take(this);
if (gPercentHeightDescendantsMap)
removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap);
if (gPositionedDescendantsMap)
removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, gPositionedContainerMap);
}
void RenderBlock::willBeDestroyed()
{
m_beingDestroyed = true;
children()->destroyLeftoverChildren();
RenderBoxModelObject* continuation = this->continuation();
if (continuation) {
continuation->destroy();
setContinuation(0);
}
if (!documentBeingDestroyed()) {
if (firstLineBox()) {
if (isSelectionBorder())
view()->clearSelection();
if (isAnonymousBlock()) {
for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) {
while (InlineBox* childBox = box->firstChild())
childBox->remove();
}
}
} else if (parent())
parent()->dirtyLinesFromChangedChild(this);
}
m_lineBoxes.deleteLineBoxes();
if (UNLIKELY(gDelayedUpdateScrollInfoSet != 0))
gDelayedUpdateScrollInfoSet->remove(this);
if (FastTextAutosizer* textAutosizer = document().fastTextAutosizer())
textAutosizer->destroy(this);
RenderBox::willBeDestroyed();
}
void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
{
RenderStyle* oldStyle = style();
setReplaced(newStyle.isDisplayInlineType());
if (oldStyle && parent() && diff == StyleDifferenceLayout && oldStyle->position() != newStyle.position()) {
if (newStyle.position() == StaticPosition)
removePositionedObjects(0, NewContainingBlock);
else if (oldStyle->position() == StaticPosition) {
RenderObject* cb = parent();
while (cb && (cb->style()->position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) {
if (cb->style()->position() == RelativePosition && cb->isInline() && !cb->isReplaced()) {
cb = cb->containingBlock();
break;
}
cb = cb->parent();
}
if (cb->isRenderBlock())
toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock);
}
}
RenderBox::styleWillChange(diff, newStyle);
}
static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle)
{
if (newStyle->isHorizontalWritingMode())
return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth()
|| oldStyle->borderRightWidth() != newStyle->borderRightWidth()
|| oldStyle->paddingLeft() != newStyle->paddingLeft()
|| oldStyle->paddingRight() != newStyle->paddingRight();
return oldStyle->borderTopWidth() != newStyle->borderTopWidth()
|| oldStyle->borderBottomWidth() != newStyle->borderBottomWidth()
|| oldStyle->paddingTop() != newStyle->paddingTop()
|| oldStyle->paddingBottom() != newStyle->paddingBottom();
}
void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderBox::styleDidChange(diff, oldStyle);
RenderStyle* newStyle = style();
if (!isAnonymousBlock()) {
for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) {
RenderBoxModelObject* nextCont = currCont->continuation();
currCont->setContinuation(0);
currCont->setStyle(newStyle);
currCont->setContinuation(nextCont);
}
}
if (FastTextAutosizer* textAutosizer = document().fastTextAutosizer())
textAutosizer->record(this);
propagateStyleToAnonymousChildren(true);
invalidateLineHeight();
m_hasBorderOrPaddingLogicalWidthChanged = oldStyle && diff == StyleDifferenceLayout && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, newStyle);
Vector<ImageResource*> images;
appendImagesFromStyle(images, *newStyle);
if (images.isEmpty())
ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->removeRenderObject(this);
else
ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->addRenderObject(this);
}
RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild)
{
if (beforeChild && beforeChild->parent() == this)
return this;
RenderBlock* curr = toRenderBlock(continuation());
RenderBlock* nextToLast = this;
RenderBlock* last = this;
while (curr) {
if (beforeChild && beforeChild->parent() == curr) {
if (curr->firstChild() == beforeChild)
return last;
return curr;
}
nextToLast = last;
last = curr;
curr = toRenderBlock(curr->continuation());
}
if (!beforeChild && !last->firstChild())
return nextToLast;
return last;
}
void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
{
RenderBlock* flow = continuationBefore(beforeChild);
ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock());
RenderBoxModelObject* beforeChildParent = 0;
if (beforeChild)
beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
else {
RenderBoxModelObject* cont = flow->continuation();
if (cont)
beforeChildParent = cont;
else
beforeChildParent = flow;
}
if (newChild->isFloatingOrOutOfFlowPositioned()) {
beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
return;
}
bool childIsNormal = newChild->isInline() || !newChild->style()->columnSpan();
bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style()->columnSpan();
bool flowIsNormal = flow->isInline() || !flow->style()->columnSpan();
if (flow == beforeChildParent) {
flow->addChildIgnoringContinuation(newChild, beforeChild);
return;
}
if (childIsNormal == bcpIsNormal) {
beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
return;
}
if (flowIsNormal == childIsNormal) {
flow->addChildIgnoringContinuation(newChild, 0);
return;
}
beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
}
void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild)
{
ASSERT(!continuation());
RenderBlock* beforeChildParent = 0;
if (beforeChild) {
RenderObject* curr = beforeChild;
while (curr && curr->parent() != this)
curr = curr->parent();
beforeChildParent = toRenderBlock(curr);
ASSERT(beforeChildParent);
ASSERT(beforeChildParent->isAnonymousColumnsBlock() || beforeChildParent->isAnonymousColumnSpanBlock());
} else
beforeChildParent = toRenderBlock(lastChild());
if (newChild->isFloatingOrOutOfFlowPositioned()) {
beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
return;
}
bool newChildHasColumnSpan = newChild->style()->columnSpan() && !newChild->isInline();
bool beforeChildParentHoldsColumnSpans = beforeChildParent->isAnonymousColumnSpanBlock();
if (newChildHasColumnSpan == beforeChildParentHoldsColumnSpans) {
beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
return;
}
if (!beforeChild) {
RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock();
children()->appendChildNode(this, newBox);
newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0);
return;
}
RenderObject* immediateChild = beforeChild;
bool isPreviousBlockViable = true;
while (immediateChild->parent() != this) {
if (isPreviousBlockViable)
isPreviousBlockViable = !immediateChild->previousSibling();
immediateChild = immediateChild->parent();
}
if (isPreviousBlockViable && immediateChild->previousSibling()) {
toRenderBlock(immediateChild->previousSibling())->addChildIgnoringAnonymousColumnBlocks(newChild, 0);
return;
}
RenderObject* newBeforeChild = splitAnonymousBoxesAroundChild(beforeChild);
RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock();
children()->insertChildNode(this, newBox, newBeforeChild);
newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0);
return;
}
RenderBlockFlow* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBlock)
{
RenderBlock* firstChildIgnoringAnonymousWrappers = 0;
for (RenderObject* curr = this; curr; curr = curr->parent()) {
if (!curr->isRenderBlock() || curr->isFloatingOrOutOfFlowPositioned() || curr->isTableCell() || curr->isRoot() || curr->isRenderView() || curr->hasOverflowClip()
|| curr->isInlineBlockOrInlineTable())
return 0;
if (!curr->isRenderBlockFlow() || curr->isListItem())
return 0;
RenderBlockFlow* currBlock = toRenderBlockFlow(curr);
if (!currBlock->createsAnonymousWrapper())
firstChildIgnoringAnonymousWrappers = currBlock;
if (currBlock->style()->specifiesColumns() && (allowAnonymousColumnBlock || !currBlock->isAnonymousColumnsBlock()))
return toRenderBlockFlow(firstChildIgnoringAnonymousWrappers);
if (currBlock->isAnonymousColumnSpanBlock())
return 0;
}
return 0;
}
RenderBlock* RenderBlock::clone() const
{
RenderBlock* cloneBlock;
if (isAnonymousBlock()) {
cloneBlock = createAnonymousBlock();
cloneBlock->setChildrenInline(childrenInline());
}
else {
RenderObject* cloneRenderer = toElement(node())->createRenderer(style());
cloneBlock = toRenderBlock(cloneRenderer);
cloneBlock->setStyle(style());
cloneBlock->setChildrenInline(cloneBlock->firstChild() ? cloneBlock->firstChild()->isInline() : childrenInline());
}
cloneBlock->setFlowThreadState(flowThreadState());
return cloneBlock;
}
void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock,
RenderBlock* middleBlock,
RenderObject* beforeChild, RenderBoxModelObject* oldCont)
{
RenderBlock* cloneBlock = clone();
if (!isAnonymousBlock())
cloneBlock->setContinuation(oldCont);
if (!beforeChild && isAfterContent(lastChild()))
beforeChild = lastChild();
if (beforeChild && childrenInline())
deleteLineBoxTree();
moveChildrenTo(cloneBlock, beforeChild, 0, true);
if (!cloneBlock->isAnonymousBlock())
middleBlock->setContinuation(cloneBlock);
RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
RenderBoxModelObject* currChild = this;
RenderObject* currChildNextSibling = currChild->nextSibling();
while (curr && curr->isDescendantOf(fromBlock) && curr != fromBlock) {
ASSERT_WITH_SECURITY_IMPLICATION(curr->isRenderBlock());
RenderBlock* blockCurr = toRenderBlock(curr);
RenderBlock* cloneChild = cloneBlock;
cloneBlock = blockCurr->clone();
cloneBlock->addChildIgnoringContinuation(cloneChild, 0);
if (!blockCurr->isAnonymousBlock()) {
oldCont = blockCurr->continuation();
blockCurr->setContinuation(cloneBlock);
cloneBlock->setContinuation(oldCont);
}
blockCurr->moveChildrenTo(cloneBlock, currChildNextSibling, 0, true);
currChild = curr;
currChildNextSibling = currChild->nextSibling();
curr = toRenderBoxModelObject(curr->parent());
}
toBlock->children()->appendChildNode(toBlock, cloneBlock);
fromBlock->moveChildrenTo(toBlock, currChildNextSibling, 0, true);
}
void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
RenderObject* newChild, RenderBoxModelObject* oldCont)
{
RenderBlock* pre = 0;
RenderBlock* block = containingColumnsBlock();
block->deleteLineBoxTree();
bool madeNewBeforeBlock = false;
if (block->isAnonymousColumnsBlock()) {
pre = block;
pre->removePositionedObjects(0);
if (block->isRenderBlockFlow())
toRenderBlockFlow(pre)->removeFloatingObjects();
block = toRenderBlock(block->parent());
} else {
pre = block->createAnonymousColumnsBlock();
pre->setChildrenInline(false);
madeNewBeforeBlock = true;
}
RenderBlock* post = block->createAnonymousColumnsBlock();
post->setChildrenInline(false);
RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
if (madeNewBeforeBlock)
block->children()->insertChildNode(block, pre, boxFirst);
block->children()->insertChildNode(block, newBlockBox, boxFirst);
block->children()->insertChildNode(block, post, boxFirst);
block->setChildrenInline(false);
if (madeNewBeforeBlock)
block->moveChildrenTo(pre, boxFirst, 0, true);
splitBlocks(pre, post, newBlockBox, beforeChild, oldCont);
newBlockBox->setChildrenInline(false);
newBlockBox->addChild(newChild);
pre->setNeedsLayoutAndPrefWidthsRecalc();
block->setNeedsLayoutAndPrefWidthsRecalc();
post->setNeedsLayoutAndPrefWidthsRecalc();
}
void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlockFlow* newBlockBox, RenderObject* newChild)
{
RenderBlockFlow* pre = 0;
RenderBlockFlow* post = 0;
RenderBlock* block = this;
block->deleteLineBoxTree();
if (beforeChild && beforeChild->parent() != this)
beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
if (beforeChild != firstChild()) {
pre = block->createAnonymousColumnsBlock();
pre->setChildrenInline(block->childrenInline());
}
if (beforeChild) {
post = block->createAnonymousColumnsBlock();
post->setChildrenInline(block->childrenInline());
}
RenderObject* boxFirst = block->firstChild();
if (pre)
block->children()->insertChildNode(block, pre, boxFirst);
block->children()->insertChildNode(block, newBlockBox, boxFirst);
if (post)
block->children()->insertChildNode(block, post, boxFirst);
block->setChildrenInline(false);
block->moveChildrenTo(pre, boxFirst, beforeChild, true);
block->moveChildrenTo(post, beforeChild, 0, true);
newBlockBox->setChildrenInline(false);
newBlockBox->addChild(newChild);
if (pre)
pre->setNeedsLayoutAndPrefWidthsRecalc();
block->setNeedsLayoutAndPrefWidthsRecalc();
if (post)
post->setNeedsLayoutAndPrefWidthsRecalc();
}
RenderBlockFlow* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild)
{
RenderBlockFlow* columnsBlockAncestor = 0;
if (!newChild->isText() && newChild->style()->columnSpan() && !newChild->isBeforeOrAfterContent()
&& !newChild->isFloatingOrOutOfFlowPositioned() && !newChild->isInline() && !isAnonymousColumnSpanBlock()) {
columnsBlockAncestor = containingColumnsBlock(false);
if (columnsBlockAncestor) {
RenderObject* curr = this;
while (curr && curr != columnsBlockAncestor) {
if (curr->isRenderBlock() && toRenderBlock(curr)->continuation()) {
columnsBlockAncestor = 0;
break;
}
curr = curr->parent();
}
}
}
return columnsBlockAncestor;
}
void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild)
{
if (beforeChild && beforeChild->parent() != this) {
RenderObject* beforeChildContainer = beforeChild->parent();
while (beforeChildContainer->parent() != this)
beforeChildContainer = beforeChildContainer->parent();
ASSERT(beforeChildContainer);
if (beforeChildContainer->isAnonymous()) {
RenderObject* beforeChildAnonymousContainer = beforeChildContainer;
if (beforeChildAnonymousContainer->isAnonymousBlock()
|| beforeChildAnonymousContainer->isRenderFullScreen()
|| beforeChildAnonymousContainer->isRenderFullScreenPlaceholder()
) {
if (newChild->isInline() || newChild->isFloatingOrOutOfFlowPositioned() || beforeChild->parent()->firstChild() != beforeChild)
beforeChild->parent()->addChild(newChild, beforeChild);
else
addChild(newChild, beforeChild->parent());
return;
}
ASSERT(beforeChildAnonymousContainer->isTable());
if (newChild->isTablePart()) {
beforeChildAnonymousContainer->addChild(newChild, beforeChild);
return;
}
beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
ASSERT(beforeChild->parent() == this);
if (beforeChild->parent() != this) {
beforeChild = beforeChildContainer;
}
}
}
if (gColumnFlowSplitEnabled) {
RenderBlockFlow* columnsBlockAncestor = columnsBlockForSpanningElement(newChild);
if (columnsBlockAncestor) {
TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false);
RenderBlockFlow* newBox = createAnonymousColumnSpanBlock();
if (columnsBlockAncestor != this && !isRenderFlowThread()) {
RenderBoxModelObject* oldContinuation = continuation();
if (!isAnonymousBlock())
setContinuation(newBox);
splitFlow(beforeChild, newBox, newChild, oldContinuation);
return;
}
makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild);
return;
}
}
bool madeBoxesNonInline = false;
if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned()) {
makeChildrenNonInline(beforeChild);
madeBoxesNonInline = true;
if (beforeChild && beforeChild->parent() != this) {
beforeChild = beforeChild->parent();
ASSERT(beforeChild->isAnonymousBlock());
ASSERT(beforeChild->parent() == this);
}
} else if (!childrenInline() && (newChild->isFloatingOrOutOfFlowPositioned() || newChild->isInline())) {
RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild();
if (afterChild && afterChild->isAnonymousBlock()) {
afterChild->addChild(newChild);
return;
}
if (newChild->isInline()) {
RenderBlock* newBox = createAnonymousBlock();
RenderBox::addChild(newBox, beforeChild);
newBox->addChild(newChild);
return;
}
}
RenderBox::addChild(newChild, beforeChild);
if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock())
toRenderBlock(parent())->removeLeftoverAnonymousBlock(this);
}
void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
if (continuation() && !isAnonymousBlock())
addChildToContinuation(newChild, beforeChild);
else
addChildIgnoringContinuation(newChild, beforeChild);
}
void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
{
if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock()))
addChildToAnonymousColumnBlocks(newChild, beforeChild);
else
addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
}
static void getInlineRun(RenderObject* start, RenderObject* boundary,
RenderObject*& inlineRunStart,
RenderObject*& inlineRunEnd)
{
RenderObject * curr = start;
bool sawInline;
do {
while (curr && !(curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()))
curr = curr->nextSibling();
inlineRunStart = inlineRunEnd = curr;
if (!curr)
return;
sawInline = curr->isInline();
curr = curr->nextSibling();
while (curr && (curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()) && (curr != boundary)) {
inlineRunEnd = curr;
if (curr->isInline())
sawInline = true;
curr = curr->nextSibling();
}
} while (!sawInline);
}
void RenderBlock::deleteLineBoxTree()
{
m_lineBoxes.deleteLineBoxTree();
if (AXObjectCache* cache = document().existingAXObjectCache())
cache->recomputeIsIgnored(this);
}
void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint)
{
ASSERT(isInlineBlockOrInlineTable() || !isInline());
ASSERT(!insertionPoint || insertionPoint->parent() == this);
setChildrenInline(false);
RenderObject *child = firstChild();
if (!child)
return;
deleteLineBoxTree();
while (child) {
RenderObject *inlineRunStart, *inlineRunEnd;
getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd);
if (!inlineRunStart)
break;
child = inlineRunEnd->nextSibling();
RenderBlock* block = createAnonymousBlock();
children()->insertChildNode(this, block, inlineRunStart);
moveChildrenTo(block, inlineRunStart, child);
}
#ifndef NDEBUG
for (RenderObject *c = firstChild(); c; c = c->nextSibling())
ASSERT(!c->isInline());
#endif
repaint();
}
void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child)
{
ASSERT(child->isAnonymousBlock());
ASSERT(!child->childrenInline());
if (child->continuation() || (child->firstChild() && (child->isAnonymousColumnSpanBlock() || child->isAnonymousColumnsBlock())))
return;
RenderObject* firstAnChild = child->m_children.firstChild();
RenderObject* lastAnChild = child->m_children.lastChild();
if (firstAnChild) {
RenderObject* o = firstAnChild;
while (o) {
o->setParent(this);
o = o->nextSibling();
}
firstAnChild->setPreviousSibling(child->previousSibling());
lastAnChild->setNextSibling(child->nextSibling());
if (child->previousSibling())
child->previousSibling()->setNextSibling(firstAnChild);
if (child->nextSibling())
child->nextSibling()->setPreviousSibling(lastAnChild);
if (child == m_children.firstChild())
m_children.setFirstChild(firstAnChild);
if (child == m_children.lastChild())
m_children.setLastChild(lastAnChild);
} else {
if (child == m_children.firstChild())
m_children.setFirstChild(child->nextSibling());
if (child == m_children.lastChild())
m_children.setLastChild(child->previousSibling());
if (child->previousSibling())
child->previousSibling()->setNextSibling(child->nextSibling());
if (child->nextSibling())
child->nextSibling()->setPreviousSibling(child->previousSibling());
}
child->children()->setFirstChild(0);
child->m_next = 0;
child->removeFromRenderFlowThread();
child->setParent(0);
child->setPreviousSibling(0);
child->setNextSibling(0);
child->destroy();
}
static bool canMergeContiguousAnonymousBlocks(RenderObject* oldChild, RenderObject* prev, RenderObject* next)
{
if (oldChild->documentBeingDestroyed() || oldChild->isInline() || oldChild->virtualContinuation())
return false;
if ((prev && (!prev->isAnonymousBlock() || toRenderBlock(prev)->continuation() || toRenderBlock(prev)->beingDestroyed()))
|| (next && (!next->isAnonymousBlock() || toRenderBlock(next)->continuation() || toRenderBlock(next)->beingDestroyed())))
return false;
if ((prev && (prev->isRubyRun() || prev->isRubyBase()))
|| (next && (next->isRubyRun() || next->isRubyBase())))
return false;
if (!prev || !next)
return true;
return prev->isAnonymousColumnsBlock() == next->isAnonymousColumnsBlock()
&& prev->isAnonymousColumnSpanBlock() == next->isAnonymousColumnSpanBlock();
}
void RenderBlock::collapseAnonymousBlockChild(RenderBlock* parent, RenderBlock* child)
{
if (child->beingDestroyed())
return;
parent->setNeedsLayoutAndPrefWidthsRecalc();
parent->setChildrenInline(child->childrenInline());
RenderObject* nextSibling = child->nextSibling();
RenderFlowThread* childFlowThread = child->flowThreadContainingBlock();
CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread);
parent->children()->removeChildNode(parent, child, child->hasLayer());
child->moveAllChildrenTo(parent, nextSibling, child->hasLayer());
child->deleteLineBoxTree();
child->destroy();
}
void RenderBlock::removeChild(RenderObject* oldChild)
{
if (documentBeingDestroyed()) {
RenderBox::removeChild(oldChild);
return;
}
TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false);
RenderObject* prev = oldChild->previousSibling();
RenderObject* next = oldChild->nextSibling();
bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next);
if (canMergeAnonymousBlocks && prev && next) {
prev->setNeedsLayoutAndPrefWidthsRecalc();
RenderBlockFlow* nextBlock = toRenderBlockFlow(next);
RenderBlockFlow* prevBlock = toRenderBlockFlow(prev);
if (prev->childrenInline() != next->childrenInline()) {
RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock;
RenderBlock* blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock;
ASSERT(!inlineChildrenBlock->continuation());
RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
bool inlineChildrenBlockHasLayer = inlineChildrenBlock->hasLayer();
inlineChildrenBlock->setStyle(newStyle);
children()->removeChildNode(this, inlineChildrenBlock, inlineChildrenBlockHasLayer);
blockChildrenBlock->children()->insertChildNode(blockChildrenBlock, inlineChildrenBlock, prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : 0,
inlineChildrenBlockHasLayer || blockChildrenBlock->hasLayer());
next->setNeedsLayoutAndPrefWidthsRecalc();
if (inlineChildrenBlock == prevBlock)
prev = 0;
else
next = 0;
} else {
nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer());
nextBlock->deleteLineBoxTree();
nextBlock->destroy();
next = 0;
}
}
RenderBox::removeChild(oldChild);
RenderObject* child = prev ? prev : next;
if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canCollapseAnonymousBlockChild()) {
collapseAnonymousBlockChild(this, toRenderBlock(child));
} else if (((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) && canCollapseAnonymousBlockChild()) {
RenderBlock* anonymousBlock = toRenderBlock((prev && prev->isAnonymousBlock()) ? prev : next);
if ((anonymousBlock->previousSibling() || anonymousBlock->nextSibling())
&& (!anonymousBlock->previousSibling() || (anonymousBlock->previousSibling()->style()->styleType() != NOPSEUDO && anonymousBlock->previousSibling()->isFloating() && !anonymousBlock->previousSibling()->previousSibling()))
&& (!anonymousBlock->nextSibling() || (anonymousBlock->nextSibling()->style()->styleType() != NOPSEUDO && anonymousBlock->nextSibling()->isFloating() && !anonymousBlock->nextSibling()->nextSibling()))) {
collapseAnonymousBlockChild(this, anonymousBlock);
}
}
if (!firstChild()) {
if (childrenInline())
deleteLineBoxTree();
if (!beingDestroyed() && isAnonymousBlockContinuation() && !oldChild->isListMarker()) {
RenderObject* containingBlockIgnoringAnonymous = containingBlock();
while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymousBlock())
containingBlockIgnoringAnonymous = containingBlockIgnoringAnonymous->containingBlock();
for (RenderObject* curr = this; curr; curr = curr->previousInPreOrder(containingBlockIgnoringAnonymous)) {
if (curr->virtualContinuation() != this)
continue;
RenderBoxModelObject* nextContinuation = continuation();
if (curr->isRenderInline())
toRenderInline(curr)->setContinuation(nextContinuation);
else if (curr->isRenderBlock())
toRenderBlock(curr)->setContinuation(nextContinuation);
else
ASSERT_NOT_REACHED();
break;
}
setContinuation(0);
destroy();
}
}
}
bool RenderBlock::isSelfCollapsingBlock() const
{
ASSERT(!needsLayout() || (node() && node()->isElementNode() && toElement(node())->shadowPseudoId() == "-webkit-input-placeholder"));
if (createsBlockFormattingContext())
return false;
if (logicalHeight() > 0
|| isTable() || borderAndPaddingLogicalHeight()
|| style()->logicalMinHeight().isPositive()
|| style()->marginBeforeCollapse() == MSEPARATE || style()->marginAfterCollapse() == MSEPARATE)
return false;
Length logicalHeightLength = style()->logicalHeight();
bool hasAutoHeight = logicalHeightLength.isAuto();
if (logicalHeightLength.isPercent() && !document().inQuirksMode()) {
hasAutoHeight = true;
for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) {
if (cb->style()->logicalHeight().isFixed() || cb->isTableCell())
hasAutoHeight = false;
}
}
if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) {
if (childrenInline())
return !firstLineBox();
if (m_hasOnlySelfCollapsingChildren)
return true;
for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
if (child->isFloatingOrOutOfFlowPositioned())
continue;
if (!child->isSelfCollapsingBlock())
return false;
}
return true;
}
return false;
}
void RenderBlock::startDelayUpdateScrollInfo()
{
if (gDelayUpdateScrollInfo == 0) {
ASSERT(!gDelayedUpdateScrollInfoSet);
gDelayedUpdateScrollInfoSet = new DelayedUpdateScrollInfoSet;
}
ASSERT(gDelayedUpdateScrollInfoSet);
++gDelayUpdateScrollInfo;
}
void RenderBlock::finishDelayUpdateScrollInfo()
{
--gDelayUpdateScrollInfo;
ASSERT(gDelayUpdateScrollInfo >= 0);
if (gDelayUpdateScrollInfo == 0) {
ASSERT(gDelayedUpdateScrollInfoSet);
OwnPtr<DelayedUpdateScrollInfoSet> infoSet(adoptPtr(gDelayedUpdateScrollInfoSet));
gDelayedUpdateScrollInfoSet = 0;
for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) {
RenderBlock* block = *it;
if (block->hasOverflowClip()) {
block->layer()->scrollableArea()->updateAfterLayout();
}
}
}
}
void RenderBlock::updateScrollInfoAfterLayout()
{
if (hasOverflowClip()) {
if (style()->isFlippedBlocksWritingMode()) {
layer()->scrollableArea()->updateAfterLayout();
return;
}
if (gDelayUpdateScrollInfo)
gDelayedUpdateScrollInfoSet->add(this);
else
layer()->scrollableArea()->updateAfterLayout();
}
}
void RenderBlock::layout()
{
OverflowEventDispatcher dispatcher(this);
LayoutRectRecorder recorder(*this);
updateFirstLetter();
layoutBlock(false);
if (hasControlClip() && m_overflow)
clearLayoutOverflow();
invalidateBackgroundObscurationStatus();
}
bool RenderBlock::updateImageLoadingPriorities()
{
Vector<ImageResource*> images;
appendImagesFromStyle(images, *style());
if (images.isEmpty())
return false;
LayoutRect viewBounds = viewRect();
LayoutRect objectBounds = absoluteContentBox();
bool isVisible;
if (!objectBounds.isEmpty())
isVisible = viewBounds.intersects(objectBounds);
else
isVisible = viewBounds.contains(objectBounds);
ResourceLoadPriorityOptimizer::VisibilityStatus status = isVisible ?
ResourceLoadPriorityOptimizer::Visible : ResourceLoadPriorityOptimizer::NotVisible;
LayoutRect screenArea;
if (!objectBounds.isEmpty()) {
screenArea = viewBounds;
screenArea.intersect(objectBounds);
}
for (Vector<ImageResource*>::iterator it = images.begin(), end = images.end(); it != end; ++it)
ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->notifyImageResourceVisibility(*it, status, screenArea);
return true;
}
LayoutSize RenderBlock::logicalOffsetFromShapeAncestorContainer(const RenderBlock* container) const
{
const RenderBlock* currentBlock = this;
LayoutRect blockRect(currentBlock->borderBoxRect());
while (currentBlock && !currentBlock->isRenderFlowThread() && currentBlock != container) {
RenderBlock* containerBlock = currentBlock->containingBlock();
ASSERT(containerBlock);
if (!containerBlock)
return LayoutSize();
if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) {
if (containerBlock->style()->isFlippedBlocksWritingMode()) {
if (containerBlock->isHorizontalWritingMode())
blockRect.setY(currentBlock->height() - blockRect.maxY());
else
blockRect.setX(currentBlock->width() - blockRect.maxX());
}
currentBlock->flipForWritingMode(blockRect);
}
blockRect.moveBy(currentBlock->location());
currentBlock = containerBlock;
}
LayoutSize result = isHorizontalWritingMode() ? LayoutSize(blockRect.x(), blockRect.y()) : LayoutSize(blockRect.y(), blockRect.x());
return result;
}
void RenderBlock::computeRegionRangeForBlock(RenderFlowThread* flowThread)
{
if (flowThread)
flowThread->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage());
}
bool RenderBlock::updateLogicalWidthAndColumnWidth()
{
LayoutUnit oldWidth = logicalWidth();
LayoutUnit oldColumnWidth = desiredColumnWidth();
updateLogicalWidth();
calcColumnWidth();
bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged;
m_hasBorderOrPaddingLogicalWidthChanged = false;
return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth() || hasBorderOrPaddingLogicalWidthChanged;
}
void RenderBlock::layoutBlock(bool)
{
ASSERT_NOT_REACHED();
clearNeedsLayout();
}
void RenderBlock::addOverflowFromChildren()
{
if (!hasColumns()) {
if (childrenInline())
toRenderBlockFlow(this)->addOverflowFromInlineChildren();
else
addOverflowFromBlockChildren();
} else {
ColumnInfo* colInfo = columnInfo();
if (columnCount(colInfo)) {
LayoutRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1);
addLayoutOverflow(lastRect);
addContentsVisualOverflow(lastRect);
}
}
}
void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool)
{
m_overflow.clear();
addOverflowFromChildren();
addOverflowFromPositionedObjects();
if (hasOverflowClip()) {
LayoutRect clientRect(noOverflowRect());
LayoutRect rectToApply;
if (isHorizontalWritingMode())
rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, max<LayoutUnit>(0, oldClientAfterEdge - clientRect.y()));
else
rectToApply = LayoutRect(clientRect.x(), clientRect.y(), max<LayoutUnit>(0, oldClientAfterEdge - clientRect.x()), 1);
addLayoutOverflow(rectToApply);
if (hasRenderOverflow())
m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge);
}
addVisualEffectOverflow();
addVisualOverflowFromTheme();
}
void RenderBlock::addOverflowFromBlockChildren()
{
for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
if (!child->isFloatingOrOutOfFlowPositioned())
addOverflowFromChild(child);
}
}
void RenderBlock::addOverflowFromPositionedObjects()
{
TrackedRendererListHashSet* positionedDescendants = positionedObjects();
if (!positionedDescendants)
return;
RenderBox* positionedObject;
TrackedRendererListHashSet::iterator end = positionedDescendants->end();
for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) {
positionedObject = *it;
if (positionedObject->style()->position() != FixedPosition)
addOverflowFromChild(positionedObject, LayoutSize(positionedObject->x(), positionedObject->y()));
}
}
void RenderBlock::addVisualOverflowFromTheme()
{
if (!style()->hasAppearance())
return;
IntRect inflatedRect = pixelSnappedBorderBoxRect();
RenderTheme::theme().adjustRepaintRect(this, inflatedRect);
addVisualOverflow(inflatedRect);
}
bool RenderBlock::createsBlockFormattingContext() const
{
return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated())
|| style()->specifiesColumns() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot() || style()->columnSpan();
}
void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox* child)
{
if (relayoutChildren || (child->hasRelativeLogicalHeight() && !isRenderView()))
child->setChildNeedsLayout(MarkOnlyThis);
if (relayoutChildren && child->needsPreferredWidthsRecalculation())
child->setPreferredLogicalWidthsDirty(MarkOnlyThis);
}
void RenderBlock::simplifiedNormalFlowLayout()
{
if (childrenInline()) {
ListHashSet<RootInlineBox*> lineBoxes;
for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) {
RenderObject* o = walker.current();
if (!o->isOutOfFlowPositioned() && (o->isReplaced() || o->isFloating())) {
o->layoutIfNeeded();
if (toRenderBox(o)->inlineBoxWrapper()) {
RootInlineBox& box = toRenderBox(o)->inlineBoxWrapper()->root();
lineBoxes.add(&box);
}
} else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) {
o->clearNeedsLayout();
}
}
GlyphOverflowAndFallbackFontsMap textBoxDataMap;
for (ListHashSet<RootInlineBox*>::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) {
RootInlineBox* box = *it;
box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap);
}
} else {
for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) {
if (!box->isOutOfFlowPositioned())
box->layoutIfNeeded();
}
}
}
bool RenderBlock::simplifiedLayout()
{
if ((!posChildNeedsLayout() && !needsSimplifiedNormalFlowLayout()) || normalChildNeedsLayout() || selfNeedsLayout())
return false;
{
LayoutStateMaintainer statePusher(*this, locationOffset());
if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly())
return false;
FastTextAutosizer::LayoutScope fastTextAutosizerLayoutScope(this);
if (needsSimplifiedNormalFlowLayout())
simplifiedNormalFlowLayout();
if (isRenderFlowThread())
toRenderFlowThread(this)->applyBreakAfterContent(clientLogicalBottom());
bool canContainFixedPosObjects = canContainFixedPositionObjects();
if (posChildNeedsLayout() || canContainFixedPosObjects)
layoutPositionedObjects(false, !posChildNeedsLayout() && canContainFixedPosObjects ? LayoutOnlyFixedPositionedObjects : DefaultLayout);
LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom();
computeOverflow(oldClientAfterEdge, true);
}
updateLayerTransform();
updateScrollInfoAfterLayout();
clearNeedsLayout();
return true;
}
void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderObject* child, SubtreeLayoutScope& layoutScope)
{
if (child->style()->position() != FixedPosition)
return;
bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontalWritingMode());
bool hasStaticInlinePosition = child->style()->hasStaticInlinePosition(isHorizontalWritingMode());
if (!hasStaticBlockPosition && !hasStaticInlinePosition)
return;
RenderObject* o = child->parent();
while (o && !o->isRenderView() && o->style()->position() != AbsolutePosition)
o = o->parent();
if (o->style()->position() != AbsolutePosition)
return;
RenderBox* box = toRenderBox(child);
if (hasStaticInlinePosition) {
LogicalExtentComputedValues computedValues;
box->computeLogicalWidth(computedValues);
LayoutUnit newLeft = computedValues.m_position;
if (newLeft != box->logicalLeft())
layoutScope.setChildNeedsLayout(child);
} else if (hasStaticBlockPosition) {
LayoutUnit oldTop = box->logicalTop();
box->updateLogicalHeight();
if (box->logicalTop() != oldTop)
layoutScope.setChildNeedsLayout(child);
}
}
LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox* child) const
{
Length marginLeft = child->style()->marginStartUsing(style());
Length marginRight = child->style()->marginEndUsing(style());
LayoutUnit margin = 0;
if (marginLeft.isFixed())
margin += marginLeft.value();
if (marginRight.isFixed())
margin += marginRight.value();
return margin;
}
void RenderBlock::layoutPositionedObjects(bool relayoutChildren, PositionedLayoutBehavior info)
{
TrackedRendererListHashSet* positionedDescendants = positionedObjects();
if (!positionedDescendants)
return;
if (hasColumns())
view()->layoutState()->clearPaginationInformation();
RenderBox* r;
TrackedRendererListHashSet::iterator end = positionedDescendants->end();
for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) {
r = *it;
LayoutRectRecorder recorder(*r);
SubtreeLayoutScope layoutScope(r);
markFixedPositionObjectForLayoutIfNeeded(r, layoutScope);
if (info == LayoutOnlyFixedPositionedObjects) {
r->layoutIfNeeded();
continue;
}
if (relayoutChildren || (r->style()->hasStaticBlockPosition(isHorizontalWritingMode()) && r->parent() != this))
layoutScope.setChildNeedsLayout(r);
if (relayoutChildren && r->needsPreferredWidthsRecalculation())
r->setPreferredLogicalWidthsDirty(MarkOnlyThis);
if (!r->needsLayout())
r->markForPaginationRelayoutIfNeeded(layoutScope);
if (r->needsPositionedMovementLayoutOnly() && r->tryLayoutDoingPositionedMovementOnly())
r->clearNeedsLayout();
LayoutUnit oldLogicalTop = 0;
bool needsBlockDirectionLocationSetBeforeLayout = r->needsLayout() && view()->layoutState()->needsBlockDirectionLocationSetBeforeLayout();
if (needsBlockDirectionLocationSetBeforeLayout) {
if (isHorizontalWritingMode() == r->isHorizontalWritingMode())
r->updateLogicalHeight();
else
r->updateLogicalWidth();
oldLogicalTop = logicalTopForChild(r);
}
if (info == ForcedLayoutAfterContainingBlockMoved)
r->setNeedsLayout();
r->layoutIfNeeded();
if (needsBlockDirectionLocationSetBeforeLayout && logicalTopForChild(r) != oldLogicalTop)
r->forceChildLayout();
}
if (hasColumns())
view()->layoutState()->setColumnInfo(columnInfo());
}
void RenderBlock::markPositionedObjectsForLayout()
{
TrackedRendererListHashSet* positionedDescendants = positionedObjects();
if (positionedDescendants) {
RenderBox* r;
TrackedRendererListHashSet::iterator end = positionedDescendants->end();
for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) {
r = *it;
r->setChildNeedsLayout();
}
}
}
void RenderBlock::markForPaginationRelayoutIfNeeded(SubtreeLayoutScope& layoutScope)
{
ASSERT(!needsLayout());
if (needsLayout())
return;
if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(*this, logicalTop()) != pageLogicalOffset()))
layoutScope.setChildNeedsLayout(this);
}
void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
LayoutPoint adjustedPaintOffset = paintOffset + location();
PaintPhase phase = paintInfo.phase;
LayoutRect overflowBox;
if (!isRoot()) {
overflowBox = overflowRectForPaintRejection();
flipForWritingMode(overflowBox);
overflowBox.moveBy(adjustedPaintOffset);
if (!overflowBox.intersects(paintInfo.rect))
return;
}
ContentsClipBehavior contentsClipBehavior = ForceContentsClip;
if (hasOverflowClip() && !hasControlClip() && !(shouldPaintSelectionGaps() && phase == PaintPhaseForeground) && !hasCaret())
contentsClipBehavior = SkipContentsClipIfPossible;
bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, contentsClipBehavior);
{
GraphicsContextCullSaver cullSaver(*paintInfo.context);
bool shouldCull = document().settings()->containerCullingEnabled() && !pushedClip && !isRoot()
&& firstChild() && lastChild() && firstChild() != lastChild();
if (shouldCull)
cullSaver.cull(overflowBox);
paintObject(paintInfo, adjustedPaintOffset);
}
if (pushedClip)
popContentsClip(paintInfo, phase, adjustedPaintOffset);
if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this) && !paintInfo.paintRootBackgroundOnly())
layer()->scrollableArea()->paintOverflowControls(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect, false );
}
void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (paintInfo.context->paintingDisabled())
return;
const Color& ruleColor = resolveColor(CSSPropertyWebkitColumnRuleColor);
bool ruleTransparent = style()->columnRuleIsTransparent();
EBorderStyle ruleStyle = style()->columnRuleStyle();
LayoutUnit ruleThickness = style()->columnRuleWidth();
LayoutUnit colGap = columnGap();
bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent;
if (!renderRule)
return;
ColumnInfo* colInfo = columnInfo();
unsigned colCount = columnCount(colInfo);
bool antialias = shouldAntialiasLines(paintInfo.context);
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
bool leftToRight = style()->isLeftToRightDirection();
LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth();
LayoutUnit ruleAdd = logicalLeftOffsetForContent();
LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth();
LayoutUnit inlineDirectionSize = colInfo->desiredColumnWidth();
BoxSide boxSide = isHorizontalWritingMode()
? leftToRight ? BSLeft : BSRight
: leftToRight ? BSTop : BSBottom;
for (unsigned i = 0; i < colCount; i++) {
if (leftToRight) {
ruleLogicalLeft += inlineDirectionSize + colGap / 2;
currLogicalLeftOffset += inlineDirectionSize + colGap;
} else {
ruleLogicalLeft -= (inlineDirectionSize + colGap / 2);
currLogicalLeftOffset -= (inlineDirectionSize + colGap);
}
if (i < colCount - 1) {
LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft();
LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth();
LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd;
LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness;
IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom);
drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
}
ruleLogicalLeft = currLogicalLeftOffset;
}
} else {
bool topToBottom = !style()->isFlippedBlocksWritingMode();
LayoutUnit ruleLeft = isHorizontalWritingMode()
? borderLeft() + paddingLeft()
: colGap / 2 - colGap - ruleThickness / 2 + borderBefore() + paddingBefore();
LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness;
LayoutUnit ruleTop = isHorizontalWritingMode()
? colGap / 2 - colGap - ruleThickness / 2 + borderBefore() + paddingBefore()
: borderStart() + paddingStart();
LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight();
LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight);
if (!topToBottom) {
if (isHorizontalWritingMode())
ruleRect.setY(height() - ruleRect.maxY());
else
ruleRect.setX(width() - ruleRect.maxX());
}
ruleRect.moveBy(paintOffset);
BoxSide boxSide = isHorizontalWritingMode()
? topToBottom ? BSTop : BSBottom
: topToBottom ? BSLeft : BSRight;
LayoutSize step(0, topToBottom ? colInfo->columnHeight() + colGap : -(colInfo->columnHeight() + colGap));
if (!isHorizontalWritingMode())
step = step.transposedSize();
for (unsigned i = 1; i < colCount; i++) {
ruleRect.move(step);
IntRect pixelSnappedRuleRect = pixelSnappedIntRect(ruleRect);
drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
}
}
}
void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool paintingFloats)
{
GraphicsContext* context = paintInfo.context;
ColumnInfo* colInfo = columnInfo();
unsigned colCount = columnCount(colInfo);
if (!colCount)
return;
LayoutUnit currLogicalTopOffset = 0;
LayoutUnit colGap = columnGap();
for (unsigned i = 0; i < colCount; i++) {
LayoutRect colRect = columnRectAt(colInfo, i);
flipForWritingMode(colRect);
LayoutUnit logicalLeftOffset = (isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent();
LayoutSize offset = isHorizontalWritingMode() ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset);
if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) {
if (isHorizontalWritingMode())
offset.expand(0, colRect.y() - borderTop() - paddingTop());
else
offset.expand(colRect.x() - borderLeft() - paddingLeft(), 0);
}
colRect.moveBy(paintOffset);
PaintInfo info(paintInfo);
info.rect.intersect(pixelSnappedIntRect(colRect));
if (!info.rect.isEmpty()) {
GraphicsContextStateSaver stateSaver(*context);
LayoutRect clipRect(colRect);
if (i < colCount - 1) {
if (isHorizontalWritingMode())
clipRect.expand(colGap / 2, 0);
else
clipRect.expand(0, colGap / 2);
}
context->clip(pixelSnappedIntRect(clipRect));
LayoutPoint adjustedPaintOffset = paintOffset + offset;
if (paintingFloats)
paintFloats(info, adjustedPaintOffset, paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip);
else
paintContents(info, adjustedPaintOffset);
}
LayoutUnit blockDelta = (isHorizontalWritingMode() ? colRect.height() : colRect.width());
if (style()->isFlippedBlocksWritingMode())
currLogicalTopOffset += blockDelta;
else
currLogicalTopOffset -= blockDelta;
}
}
void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (document().didLayoutWithPendingStylesheets() && !isRenderView())
return;
if (childrenInline())
m_lineBoxes.paint(this, paintInfo, paintOffset);
else {
PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase;
newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase;
PaintInfo paintInfoForChild(paintInfo);
paintInfoForChild.phase = newPhase;
paintInfoForChild.updatePaintingRootForChildren(this);
paintChildren(paintInfoForChild, paintOffset);
}
}
void RenderBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox())
paintChild(child, paintInfo, paintOffset);
}
void RenderBlock::paintChild(RenderBox* child, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
LayoutPoint childPoint = flipForWritingModeForChild(child, paintOffset);
if (!child->hasSelfPaintingLayer() && !child->isFloating())
child->paint(paintInfo, childPoint);
}
void RenderBlock::paintChildAsInlineBlock(RenderBox* child, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
LayoutPoint childPoint = flipForWritingModeForChild(child, paintOffset);
if (!child->hasSelfPaintingLayer() && !child->isFloating())
paintAsInlineBlock(child, paintInfo, childPoint);
}
void RenderBlock::paintAsInlineBlock(RenderObject* renderer, PaintInfo& paintInfo, const LayoutPoint& childPoint)
{
if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
return;
bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip;
PaintInfo info(paintInfo);
info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground;
renderer->paint(info, childPoint);
if (!preservePhase) {
info.phase = PaintPhaseChildBlockBackgrounds;
renderer->paint(info, childPoint);
info.phase = PaintPhaseFloat;
renderer->paint(info, childPoint);
info.phase = PaintPhaseForeground;
renderer->paint(info, childPoint);
info.phase = PaintPhaseOutline;
renderer->paint(info, childPoint);
}
}
bool RenderBlock::hasCaret(CaretType type) const
{
bool caretBrowsing = frame()->settings() && frame()->settings()->caretBrowsingEnabled();
RenderObject* caretPainter;
bool isContentEditable;
if (type == CursorCaret) {
caretPainter = frame()->selection().caretRenderer();
isContentEditable = frame()->selection().rendererIsEditable();
} else {
caretPainter = frame()->page()->dragCaretController().caretRenderer();
isContentEditable = frame()->page()->dragCaretController().isContentEditable();
}
return caretPainter == this && (isContentEditable || caretBrowsing);
}
void RenderBlock::paintCaret(PaintInfo& paintInfo, const LayoutPoint& paintOffset, CaretType type)
{
if (!hasCaret(type))
return;
if (type == CursorCaret)
frame()->selection().paintCaret(paintInfo.context, paintOffset, paintInfo.rect);
else
frame()->page()->dragCaretController().paintDragCaret(frame(), paintInfo.context, paintOffset, paintInfo.rect);
}
void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
PaintPhase paintPhase = paintInfo.phase;
LayoutPoint scrolledOffset = paintOffset;
if (hasOverflowClip())
scrolledOffset.move(-scrolledContentOffset());
if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) {
if (hasBoxDecorations())
paintBoxDecorations(paintInfo, paintOffset);
if (hasColumns() && !paintInfo.paintRootBackgroundOnly())
paintColumnRules(paintInfo, scrolledOffset);
}
if (paintPhase == PaintPhaseMask && style()->visibility() == VISIBLE) {
paintMask(paintInfo, paintOffset);
return;
}
if (paintPhase == PaintPhaseClippingMask && style()->visibility() == VISIBLE) {
paintClippingMask(paintInfo, paintOffset);
return;
}
if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly())
return;
if (paintPhase != PaintPhaseSelfOutline) {
if (hasColumns())
paintColumnContents(paintInfo, scrolledOffset);
else
paintContents(paintInfo, scrolledOffset);
}
bool isPrinting = document().printing();
if (!isPrinting && !hasColumns())
paintSelection(paintInfo, scrolledOffset);
if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) {
if (hasColumns())
paintColumnContents(paintInfo, scrolledOffset, true);
else
paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip);
}
if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE)
paintOutline(paintInfo, LayoutRect(paintOffset, size()));
if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) {
RenderInline* inlineCont = inlineElementContinuation();
if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE) {
RenderInline* inlineRenderer = toRenderInline(inlineCont->node()->renderer());
RenderBlock* cb = containingBlock();
bool inlineEnclosedInSelfPaintingLayer = false;
for (RenderBoxModelObject* box = inlineRenderer; box != cb; box = box->parent()->enclosingBoxModelObject()) {
if (box->hasSelfPaintingLayer()) {
inlineEnclosedInSelfPaintingLayer = true;
break;
}
}
if (!inlineEnclosedInSelfPaintingLayer && !hasLayer())
cb->addContinuationWithOutline(inlineRenderer);
else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && hasLayer()))
inlineRenderer->paintOutline(paintInfo, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location());
}
paintContinuationOutlines(paintInfo, paintOffset);
}
if (paintPhase == PaintPhaseForeground) {
paintCaret(paintInfo, paintOffset, CursorCaret);
paintCaret(paintInfo, paintOffset, DragCaret);
}
}
RenderInline* RenderBlock::inlineElementContinuation() const
{
RenderBoxModelObject* continuation = this->continuation();
return continuation && continuation->isInline() ? toRenderInline(continuation) : 0;
}
RenderBlock* RenderBlock::blockElementContinuation() const
{
RenderBoxModelObject* currentContinuation = continuation();
if (!currentContinuation || currentContinuation->isInline())
return 0;
RenderBlock* nextContinuation = toRenderBlock(currentContinuation);
if (nextContinuation->isAnonymousBlock())
return nextContinuation->blockElementContinuation();
return nextContinuation;
}
static ContinuationOutlineTableMap* continuationOutlineTable()
{
DEFINE_STATIC_LOCAL(ContinuationOutlineTableMap, table, ());
return &table;
}
void RenderBlock::addContinuationWithOutline(RenderInline* flow)
{
ASSERT(!flow->layer() && !flow->isInlineElementContinuation());
ContinuationOutlineTableMap* table = continuationOutlineTable();
ListHashSet<RenderInline*>* continuations = table->get(this);
if (!continuations) {
continuations = new ListHashSet<RenderInline*>;
table->set(this, adoptPtr(continuations));
}
continuations->add(flow);
}
bool RenderBlock::paintsContinuationOutline(RenderInline* flow)
{
ContinuationOutlineTableMap* table = continuationOutlineTable();
if (table->isEmpty())
return false;
ListHashSet<RenderInline*>* continuations = table->get(this);
if (!continuations)
return false;
return continuations->contains(flow);
}
void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& paintOffset)
{
ContinuationOutlineTableMap* table = continuationOutlineTable();
if (table->isEmpty())
return;
OwnPtr<ListHashSet<RenderInline*> > continuations = table->take(this);
if (!continuations)
return;
LayoutPoint accumulatedPaintOffset = paintOffset;
ListHashSet<RenderInline*>::iterator end = continuations->end();
for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) {
RenderInline* flow = *it;
RenderBlock* block = flow->containingBlock();
for ( ; block && block != this; block = block->containingBlock())
accumulatedPaintOffset.moveBy(block->location());
ASSERT(block);
flow->paintOutline(info, accumulatedPaintOffset);
}
}
bool RenderBlock::shouldPaintSelectionGaps() const
{
return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot();
}
bool RenderBlock::isSelectionRoot() const
{
if (isPseudoElement())
return false;
ASSERT(node() || isAnonymous());
if (isTable())
return false;
if (isBody() || isRoot() || hasOverflowClip()
|| isPositioned() || isFloating()
|| isTableCell() || isInlineBlockOrInlineTable()
|| hasTransform() || hasReflection() || hasMask() || isWritingModeRoot()
|| isRenderFlowThread())
return true;
if (view() && view()->selectionStart()) {
Node* startElement = view()->selectionStart()->node();
if (startElement && startElement->rootEditableElement() == node())
return true;
}
return false;
}
GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* repaintContainer)
{
ASSERT(!needsLayout());
if (!shouldPaintSelectionGaps())
return GapRects();
TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint());
mapLocalToContainer(repaintContainer, transformState, ApplyContainerFlip | UseTransforms);
LayoutPoint offsetFromRepaintContainer = roundedLayoutPoint(transformState.mappedPoint());
if (hasOverflowClip())
offsetFromRepaintContainer -= scrolledContentOffset();
LayoutUnit lastTop = 0;
LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop);
LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop);
return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight);
}
void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) {
LayoutUnit lastTop = 0;
LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop);
LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop);
GraphicsContextStateSaver stateSaver(*paintInfo.context);
LayoutRect gapRectsBounds = selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo);
if (!gapRectsBounds.isEmpty()) {
RenderLayer* layer = enclosingLayer();
gapRectsBounds.moveBy(-paintOffset);
if (!hasLayer()) {
LayoutRect localBounds(gapRectsBounds);
flipForWritingMode(localBounds);
gapRectsBounds = localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox();
if (layer->renderer()->hasOverflowClip())
gapRectsBounds.move(layer->renderBox()->scrolledContentOffset());
}
layer->addBlockSelectionGapsBounds(gapRectsBounds);
}
}
}
static void clipOutPositionedObjects(const PaintInfo* paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* positionedObjects)
{
if (!positionedObjects)
return;
TrackedRendererListHashSet::const_iterator end = positionedObjects->end();
for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) {
RenderBox* r = *it;
paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height()));
}
}
LayoutUnit RenderBlock::blockDirectionOffset(const LayoutSize& offsetFromBlock) const
{
return isHorizontalWritingMode() ? offsetFromBlock.height() : offsetFromBlock.width();
}
LayoutUnit RenderBlock::inlineDirectionOffset(const LayoutSize& offsetFromBlock) const
{
return isHorizontalWritingMode() ? offsetFromBlock.width() : offsetFromBlock.height();
}
LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPhysicalPosition, const LayoutRect& logicalRect)
{
LayoutRect result;
if (isHorizontalWritingMode())
result = logicalRect;
else
result = LayoutRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width());
flipForWritingMode(result);
result.moveBy(rootBlockPhysicalPosition);
return result;
}
GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo)
{
if (paintInfo) {
LayoutRect flippedBlockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height());
rootBlock->flipForWritingMode(flippedBlockRect);
flippedBlockRect.moveBy(rootBlockPhysicalPosition);
clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), positionedObjects());
if (isBody() || isRoot())
for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock())
clipOutPositionedObjects(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects());
clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock);
}
GapRects result;
if (!isRenderBlockFlow() && !isFlexibleBoxIncludingDeprecated())
return result;
if (hasColumns() || hasTransform() || style()->columnSpan()) {
lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalHeight();
lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight());
lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight());
return result;
}
if (childrenInline())
result = toRenderBlockFlow(this)->inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
else
result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd))
result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
logicalHeight(), paintInfo));
return result;
}
GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo)
{
GapRects result;
RenderBox* curr;
for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { }
for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) {
SelectionState childState = curr->selectionState();
if (childState == SelectionBoth || childState == SelectionEnd)
sawSelectionEnd = true;
if (curr->isFloatingOrOutOfFlowPositioned())
continue;
if (curr->isInFlowPositioned() && curr->hasLayer()) {
LayoutSize relOffset = curr->layer()->offsetForInFlowPosition();
if (relOffset.width() || relOffset.height())
continue;
}
bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable();
bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone);
if (fillBlockGaps) {
if (childState == SelectionEnd || childState == SelectionInside)
result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
curr->logicalTop(), paintInfo));
if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd))
childState = SelectionNone;
bool leftGap, rightGap;
getSelectionGapInfo(childState, leftGap, rightGap);
if (leftGap)
result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
if (rightGap)
result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + curr->logicalBottom();
lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom());
lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom());
} else if (childState != SelectionNone)
result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()),
lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo));
}
return result;
}
LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo* paintInfo)
{
LayoutUnit logicalTop = lastLogicalTop;
LayoutUnit logicalHeight = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalBottom - logicalTop;
if (logicalHeight <= 0)
return LayoutRect();
LayoutUnit logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom));
LayoutUnit logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom));
LayoutUnit logicalWidth = logicalRight - logicalLeft;
if (logicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight));
if (paintInfo)
paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selectionBackgroundColor());
return gapRect;
}
LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo)
{
LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop;
LayoutUnit rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight));
LayoutUnit rootBlockLogicalRight = min(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + floorToInt(logicalLeft), min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)));
LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
if (rootBlockLogicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
if (paintInfo)
paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selObj->selectionBackgroundColor());
return gapRect;
}
LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo)
{
LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop;
LayoutUnit rootBlockLogicalLeft = max(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + floorToInt(logicalRight), max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)));
LayoutUnit rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight));
LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
if (rootBlockLogicalWidth <= 0)
return LayoutRect();
LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
if (paintInfo)
paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selObj->selectionBackgroundColor());
return gapRect;
}
void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap)
{
bool ltr = style()->isLeftToRightDirection();
leftGap = (state == RenderObject::SelectionInside) ||
(state == RenderObject::SelectionEnd && ltr) ||
(state == RenderObject::SelectionStart && !ltr);
rightGap = (state == RenderObject::SelectionInside) ||
(state == RenderObject::SelectionStart && ltr) ||
(state == RenderObject::SelectionEnd && !ltr);
}
LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position)
{
if (rootBlock != this)
return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop());
return logicalLeftOffsetForContent();
}
LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position)
{
if (rootBlock != this)
return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop());
return logicalRightOffsetForContent();
}
RenderBlock* RenderBlock::blockBeforeWithinSelectionRoot(LayoutSize& offset) const
{
if (isSelectionRoot())
return 0;
const RenderObject* object = this;
RenderObject* sibling;
do {
sibling = object->previousSibling();
while (sibling && (!sibling->isRenderBlock() || toRenderBlock(sibling)->isSelectionRoot()))
sibling = sibling->previousSibling();
offset -= LayoutSize(toRenderBlock(object)->logicalLeft(), toRenderBlock(object)->logicalTop());
object = object->parent();
} while (!sibling && object && object->isRenderBlock() && !toRenderBlock(object)->isSelectionRoot());
if (!sibling)
return 0;
RenderBlock* beforeBlock = toRenderBlock(sibling);
offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop());
RenderObject* child = beforeBlock->lastChild();
while (child && child->isRenderBlock()) {
beforeBlock = toRenderBlock(child);
offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop());
child = beforeBlock->lastChild();
}
return beforeBlock;
}
void RenderBlock::insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap)
{
if (!descendantsMap) {
descendantsMap = new TrackedDescendantsMap;
containerMap = new TrackedContainerMap;
}
TrackedRendererListHashSet* descendantSet = descendantsMap->get(this);
if (!descendantSet) {
descendantSet = new TrackedRendererListHashSet;
descendantsMap->set(this, adoptPtr(descendantSet));
}
bool added = descendantSet->add(descendant).isNewEntry;
if (!added) {
ASSERT(containerMap->get(descendant));
ASSERT(containerMap->get(descendant)->contains(this));
return;
}
HashSet<RenderBlock*>* containerSet = containerMap->get(descendant);
if (!containerSet) {
containerSet = new HashSet<RenderBlock*>;
containerMap->set(descendant, adoptPtr(containerSet));
}
ASSERT(!containerSet->contains(this));
containerSet->add(this);
}
void RenderBlock::removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap)
{
if (!descendantsMap)
return;
OwnPtr<HashSet<RenderBlock*> > containerSet = containerMap->take(descendant);
if (!containerSet)
return;
HashSet<RenderBlock*>::iterator end = containerSet->end();
for (HashSet<RenderBlock*>::iterator it = containerSet->begin(); it != end; ++it) {
RenderBlock* container = *it;
TrackedDescendantsMap::iterator descendantsMapIterator = descendantsMap->find(container);
ASSERT(descendantsMapIterator != descendantsMap->end());
if (descendantsMapIterator == descendantsMap->end())
continue;
TrackedRendererListHashSet* descendantSet = descendantsMapIterator->value.get();
ASSERT(descendantSet->contains(descendant));
descendantSet->remove(descendant);
if (descendantSet->isEmpty())
descendantsMap->remove(descendantsMapIterator);
}
}
TrackedRendererListHashSet* RenderBlock::positionedObjects() const
{
if (gPositionedDescendantsMap)
return gPositionedDescendantsMap->get(this);
return 0;
}
void RenderBlock::insertPositionedObject(RenderBox* o)
{
ASSERT(!isAnonymousBlock());
if (o->isRenderFlowThread())
return;
insertIntoTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap);
}
void RenderBlock::removePositionedObject(RenderBox* o)
{
removeFromTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap);
}
void RenderBlock::removePositionedObjects(RenderBlock* o, ContainingBlockState containingBlockState)
{
TrackedRendererListHashSet* positionedDescendants = positionedObjects();
if (!positionedDescendants)
return;
RenderBox* r;
TrackedRendererListHashSet::iterator end = positionedDescendants->end();
Vector<RenderBox*, 16> deadObjects;
for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) {
r = *it;
if (!o || r->isDescendantOf(o)) {
if (containingBlockState == NewContainingBlock)
r->setChildNeedsLayout(MarkOnlyThis);
RenderObject* p = r->parent();
while (p && !p->isRenderBlock())
p = p->parent();
if (p)
p->setChildNeedsLayout();
deadObjects.append(r);
}
}
for (unsigned i = 0; i < deadObjects.size(); i++)
removePositionedObject(deadObjects.at(i));
}
void RenderBlock::addPercentHeightDescendant(RenderBox* descendant)
{
insertIntoTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap);
}
void RenderBlock::removePercentHeightDescendant(RenderBox* descendant)
{
removeFromTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap);
}
TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const
{
return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0;
}
bool RenderBlock::hasPercentHeightContainerMap()
{
return gPercentHeightContainerMap;
}
bool RenderBlock::hasPercentHeightDescendant(RenderBox* descendant)
{
ASSERT(gPercentHeightContainerMap);
return gPercentHeightContainerMap->contains(descendant);
}
void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope& layoutScope)
{
if (!gPercentHeightDescendantsMap)
return;
TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this);
if (!descendants)
return;
TrackedRendererListHashSet::iterator end = descendants->end();
for (TrackedRendererListHashSet::iterator it = descendants->begin(); it != end; ++it) {
RenderBox* box = *it;
while (box != this) {
if (box->normalChildNeedsLayout())
break;
layoutScope.setChildNeedsLayout(box);
box = box->containingBlock();
ASSERT(box);
if (!box)
break;
}
}
}
void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox* descendant)
{
if (!hasPercentHeightContainerMap())
return;
if (!hasPercentHeightDescendant(descendant))
return;
removePercentHeightDescendant(descendant);
}
void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox* parent)
{
ASSERT(gPercentHeightContainerMap);
for (RenderObject* curr = parent->firstChild(); curr; curr = curr->nextInPreOrder(parent)) {
if (!curr->isBox())
continue;
RenderBox* box = toRenderBox(curr);
if (!hasPercentHeightDescendant(box))
continue;
removePercentHeightDescendant(box);
}
}
LayoutUnit RenderBlock::textIndentOffset() const
{
LayoutUnit cw = 0;
if (style()->textIndent().isPercent())
cw = containingBlock()->availableLogicalWidth();
return minimumValueForLength(style()->textIndent(), cw);
}
void RenderBlock::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest)
{
if (logicalTop >= logicalBottom)
return;
RootInlineBox* lowestDirtyLine = lastRootBox();
RootInlineBox* afterLowest = lowestDirtyLine;
while (lowestDirtyLine && lowestDirtyLine->lineBottomWithLeading() >= logicalBottom && logicalBottom < LayoutUnit::max()) {
afterLowest = lowestDirtyLine;
lowestDirtyLine = lowestDirtyLine->prevRootBox();
}
while (afterLowest && afterLowest != highest && (afterLowest->lineBottomWithLeading() >= logicalTop || afterLowest->lineBottomWithLeading() < 0)) {
afterLowest->markDirty();
afterLowest = afterLowest->prevRootBox();
}
}
bool RenderBlock::avoidsFloats() const
{
return RenderBox::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth();
}
bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset)
{
if (!scrollsOverflow())
return false;
return layer()->scrollableArea()->hitTestOverflowControls(result, roundedIntPoint(locationInContainer - toLayoutSize(accumulatedOffset)));
}
Node* RenderBlock::nodeForHitTest() const
{
return isAnonymousBlockContinuation() ? continuation()->node() : node();
}
bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
{
LayoutPoint adjustedLocation(accumulatedOffset + location());
LayoutSize localOffset = toLayoutSize(adjustedLocation);
if (!isRenderView()) {
LayoutRect overflowBox = visualOverflowRect();
flipForWritingMode(overflowBox);
overflowBox.moveBy(adjustedLocation);
if (!locationInContainer.intersects(overflowBox))
return false;
}
if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground)
&& visibleToHitTestRequest(request)
&& isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) {
updateHitTestResult(result, locationInContainer.point() - localOffset);
if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer))
return true;
}
bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer();
bool useClip = (hasControlClip() || useOverflowClip);
bool checkChildren = !useClip || (hasControlClip() ? locationInContainer.intersects(controlClipRect(adjustedLocation)) : locationInContainer.intersects(overflowClipRect(adjustedLocation, IncludeOverlayScrollbarSize)));
if (checkChildren) {
LayoutSize scrolledOffset(localOffset);
if (hasOverflowClip())
scrolledOffset -= scrolledContentOffset();
if (!hasColumns()) {
if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) {
updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
return true;
}
if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset)))
return true;
} else if (hitTestColumns(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) {
updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
return true;
}
}
if (!isRenderView() && style()->hasBorderRadius()) {
LayoutRect borderRect = borderBoxRect();
borderRect.moveBy(adjustedLocation);
RoundedRect border = style()->getRoundedBorderFor(borderRect);
if (!locationInContainer.intersects(border))
return false;
}
if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) {
LayoutRect boundsRect(adjustedLocation, size());
if (visibleToHitTestRequest(request) && locationInContainer.intersects(boundsRect)) {
updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect))
return true;
}
}
return false;
}
class ColumnRectIterator {
WTF_MAKE_NONCOPYABLE(ColumnRectIterator);
public:
ColumnRectIterator(const RenderBlock& block)
: m_block(block)
, m_colInfo(block.columnInfo())
, m_direction(m_block.style()->isFlippedBlocksWritingMode() ? 1 : -1)
, m_isHorizontal(block.isHorizontalWritingMode())
, m_logicalLeft(block.logicalLeftOffsetForContent())
{
int colCount = m_colInfo->columnCount();
m_colIndex = colCount - 1;
m_currLogicalTopOffset = colCount * m_colInfo->columnHeight() * m_direction;
update();
}
void advance()
{
ASSERT(hasMore());
m_colIndex--;
update();
}
LayoutRect columnRect() const { return m_colRect; }
bool hasMore() const { return m_colIndex >= 0; }
void adjust(LayoutSize& offset) const
{
LayoutUnit currLogicalLeftOffset = (m_isHorizontal ? m_colRect.x() : m_colRect.y()) - m_logicalLeft;
offset += m_isHorizontal ? LayoutSize(currLogicalLeftOffset, m_currLogicalTopOffset) : LayoutSize(m_currLogicalTopOffset, currLogicalLeftOffset);
if (m_colInfo->progressionAxis() == ColumnInfo::BlockAxis) {
if (m_isHorizontal)
offset.expand(0, m_colRect.y() - m_block.borderTop() - m_block.paddingTop());
else
offset.expand(m_colRect.x() - m_block.borderLeft() - m_block.paddingLeft(), 0);
}
}
private:
void update()
{
if (m_colIndex < 0)
return;
m_colRect = m_block.columnRectAt(const_cast<ColumnInfo*>(m_colInfo), m_colIndex);
m_block.flipForWritingMode(m_colRect);
m_currLogicalTopOffset -= (m_isHorizontal ? m_colRect.height() : m_colRect.width()) * m_direction;
}
const RenderBlock& m_block;
const ColumnInfo* const m_colInfo;
const int m_direction;
const bool m_isHorizontal;
const LayoutUnit m_logicalLeft;
int m_colIndex;
LayoutUnit m_currLogicalTopOffset;
LayoutRect m_colRect;
};
bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
{
if (!hasColumns())
return false;
for (ColumnRectIterator it(*this); it.hasMore(); it.advance()) {
LayoutRect hitRect = locationInContainer.boundingBox();
LayoutRect colRect = it.columnRect();
colRect.moveBy(accumulatedOffset);
if (locationInContainer.intersects(colRect)) {
LayoutSize offset;
it.adjust(offset);
LayoutPoint finalLocation = accumulatedOffset + offset;
if (!result.isRectBasedTest() || colRect.contains(hitRect))
return hitTestContents(request, result, locationInContainer, finalLocation, hitTestAction) || (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, finalLocation));
hitTestContents(request, result, locationInContainer, finalLocation, hitTestAction);
}
}
return false;
}
void RenderBlock::adjustForColumnRect(LayoutSize& offset, const LayoutPoint& locationInContainer) const
{
for (ColumnRectIterator it(*this); it.hasMore(); it.advance()) {
LayoutRect colRect = it.columnRect();
if (colRect.contains(locationInContainer)) {
it.adjust(offset);
return;
}
}
}
bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
{
if (childrenInline() && !isTable()) {
if (m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction))
return true;
} else {
HitTestAction childHitTest = hitTestAction;
if (hitTestAction == HitTestChildBlockBackgrounds)
childHitTest = HitTestChildBlockBackground;
for (RenderBox* child = lastChildBox(); child; child = child->previousSiblingBox()) {
LayoutPoint childPoint = flipForWritingModeForChild(child, accumulatedOffset);
if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest))
return true;
}
}
return false;
}
Position RenderBlock::positionForBox(InlineBox *box, bool start) const
{
if (!box)
return Position();
if (!box->renderer().nonPseudoNode())
return createLegacyEditingPosition(nonPseudoNode(), start ? caretMinOffset() : caretMaxOffset());
if (!box->isInlineTextBox())
return createLegacyEditingPosition(box->renderer().nonPseudoNode(), start ? box->renderer().caretMinOffset() : box->renderer().caretMaxOffset());
InlineTextBox* textBox = toInlineTextBox(box);
return createLegacyEditingPosition(box->renderer().nonPseudoNode(), start ? textBox->start() : textBox->start() + textBox->len());
}
static inline bool isEditingBoundary(RenderObject* ancestor, RenderObject* child)
{
ASSERT(!ancestor || ancestor->nonPseudoNode());
ASSERT(child && child->nonPseudoNode());
return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView())
|| ancestor->nonPseudoNode()->rendererIsEditable() == child->nonPseudoNode()->rendererIsEditable();
}
static PositionWithAffinity positionForPointRespectingEditingBoundaries(RenderBlock* parent, RenderBox* child, const LayoutPoint& pointInParentCoordinates)
{
LayoutPoint childLocation = child->location();
if (child->isInFlowPositioned())
childLocation += child->offsetForInFlowPosition();
LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation));
Node* childNode = child->nonPseudoNode();
if (!childNode)
return child->positionForPoint(pointInChildCoordinates);
RenderObject* ancestor = parent;
while (ancestor && !ancestor->nonPseudoNode())
ancestor = ancestor->parent();
if (isEditingBoundary(ancestor, child))
return child->positionForPoint(pointInChildCoordinates);
LayoutUnit childMiddle = parent->logicalWidthForChild(child) / 2;
LayoutUnit logicalLeft = parent->isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y();
if (logicalLeft < childMiddle)
return ancestor->createPositionWithAffinity(childNode->nodeIndex(), DOWNSTREAM);
return ancestor->createPositionWithAffinity(childNode->nodeIndex() + 1, UPSTREAM);
}
PositionWithAffinity RenderBlock::positionForPointWithInlineChildren(const LayoutPoint& pointInLogicalContents)
{
ASSERT(childrenInline());
if (!firstRootBox())
return createPositionWithAffinity(0, DOWNSTREAM);
bool linesAreFlipped = style()->isFlippedLinesWritingMode();
bool blocksAreFlipped = style()->isFlippedBlocksWritingMode();
InlineBox* closestBox = 0;
RootInlineBox* firstRootBoxWithChildren = 0;
RootInlineBox* lastRootBoxWithChildren = 0;
for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) {
if (!root->firstLeafChild())
continue;
if (!firstRootBoxWithChildren)
firstRootBoxWithChildren = root;
if (!linesAreFlipped && root->isFirstAfterPageBreak() && (pointInLogicalContents.y() < root->lineTopWithLeading()
|| (blocksAreFlipped && pointInLogicalContents.y() == root->lineTopWithLeading())))
break;
lastRootBoxWithChildren = root;
if (pointInLogicalContents.y() < root->selectionBottom() || (blocksAreFlipped && pointInLogicalContents.y() == root->selectionBottom())) {
if (linesAreFlipped) {
RootInlineBox* nextRootBoxWithChildren = root->nextRootBox();
while (nextRootBoxWithChildren && !nextRootBoxWithChildren->firstLeafChild())
nextRootBoxWithChildren = nextRootBoxWithChildren->nextRootBox();
if (nextRootBoxWithChildren && nextRootBoxWithChildren->isFirstAfterPageBreak() && (pointInLogicalContents.y() > nextRootBoxWithChildren->lineTopWithLeading()
|| (!blocksAreFlipped && pointInLogicalContents.y() == nextRootBoxWithChildren->lineTopWithLeading())))
continue;
}
closestBox = root->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x());
if (closestBox)
break;
}
}
bool moveCaretToBoundary = document().frame()->editor().behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom();
if (!moveCaretToBoundary && !closestBox && lastRootBoxWithChildren) {
closestBox = lastRootBoxWithChildren->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x());
}
if (closestBox) {
if (moveCaretToBoundary) {
LayoutUnit firstRootBoxWithChildrenTop = min<LayoutUnit>(firstRootBoxWithChildren->selectionTop(), firstRootBoxWithChildren->logicalTop());
if (pointInLogicalContents.y() < firstRootBoxWithChildrenTop
|| (blocksAreFlipped && pointInLogicalContents.y() == firstRootBoxWithChildrenTop)) {
InlineBox* box = firstRootBoxWithChildren->firstLeafChild();
if (box->isLineBreak()) {
if (InlineBox* newBox = box->nextLeafChildIgnoringLineBreak())
box = newBox;
}
return PositionWithAffinity(positionForBox(box, true), DOWNSTREAM);
}
}
LayoutPoint point(pointInLogicalContents.x(), closestBox->root().blockDirectionPointInLine());
if (!isHorizontalWritingMode())
point = point.transposedPoint();
if (closestBox->renderer().isReplaced())
return positionForPointRespectingEditingBoundaries(this, &toRenderBox(closestBox->renderer()), point);
return closestBox->renderer().positionForPoint(point);
}
if (lastRootBoxWithChildren) {
ASSERT(moveCaretToBoundary);
InlineBox* logicallyLastBox;
if (lastRootBoxWithChildren->getLogicalEndBoxWithNode(logicallyLastBox))
return PositionWithAffinity(positionForBox(logicallyLastBox, false), DOWNSTREAM);
}
return createPositionWithAffinity(0, DOWNSTREAM);
}
static inline bool isChildHitTestCandidate(RenderBox* box)
{
return box->height() && box->style()->visibility() == VISIBLE && !box->isFloatingOrOutOfFlowPositioned();
}
PositionWithAffinity RenderBlock::positionForPoint(const LayoutPoint& point)
{
if (isTable())
return RenderBox::positionForPoint(point);
if (isReplaced()) {
LayoutUnit pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.y();
LayoutUnit pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x();
if (pointLogicalLeft < 0)
return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM);
if (pointLogicalLeft >= logicalWidth())
return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM);
if (pointLogicalTop < 0)
return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM);
if (pointLogicalTop >= logicalHeight())
return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM);
}
LayoutPoint pointInContents = point;
offsetForContents(pointInContents);
LayoutPoint pointInLogicalContents(pointInContents);
if (!isHorizontalWritingMode())
pointInLogicalContents = pointInLogicalContents.transposedPoint();
if (childrenInline())
return positionForPointWithInlineChildren(pointInLogicalContents);
RenderBox* lastCandidateBox = lastChildBox();
while (lastCandidateBox && !isChildHitTestCandidate(lastCandidateBox))
lastCandidateBox = lastCandidateBox->previousSiblingBox();
bool blocksAreFlipped = style()->isFlippedBlocksWritingMode();
if (lastCandidateBox) {
if (pointInLogicalContents.y() > logicalTopForChild(lastCandidateBox)
|| (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopForChild(lastCandidateBox)))
return positionForPointRespectingEditingBoundaries(this, lastCandidateBox, pointInContents);
for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) {
if (!isChildHitTestCandidate(childBox))
continue;
LayoutUnit childLogicalBottom = logicalTopForChild(childBox) + logicalHeightForChild(childBox);
if (isChildHitTestCandidate(childBox) && (pointInLogicalContents.y() < childLogicalBottom
|| (blocksAreFlipped && pointInLogicalContents.y() == childLogicalBottom)))
return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents);
}
}
return RenderBox::positionForPoint(point);
}
void RenderBlock::offsetForContents(LayoutPoint& offset) const
{
offset = flipForWritingMode(offset);
if (hasOverflowClip())
offset += scrolledContentOffset();
if (hasColumns())
adjustPointToColumnContents(offset);
offset = flipForWritingMode(offset);
}
LayoutUnit RenderBlock::availableLogicalWidth() const
{
if (hasColumns())
return desiredColumnWidth();
return RenderBox::availableLogicalWidth();
}
int RenderBlock::columnGap() const
{
if (style()->hasNormalColumnGap())
return style()->fontDescription().computedPixelSize();
return static_cast<int>(style()->columnGap());
}
void RenderBlock::calcColumnWidth()
{
if (document().regionBasedColumnsEnabled())
return;
unsigned desiredColumnCount = 1;
LayoutUnit desiredColumnWidth = contentLogicalWidth();
if (document().paginated() || !style()->specifiesColumns()) {
setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth);
return;
}
LayoutUnit availWidth = desiredColumnWidth;
LayoutUnit colGap = columnGap();
LayoutUnit colWidth = max<LayoutUnit>(1, LayoutUnit(style()->columnWidth()));
int colCount = max<int>(1, style()->columnCount());
if (style()->hasAutoColumnWidth() && !style()->hasAutoColumnCount()) {
desiredColumnCount = colCount;
desiredColumnWidth = max<LayoutUnit>(0, (availWidth - ((desiredColumnCount - 1) * colGap)) / desiredColumnCount);
} else if (!style()->hasAutoColumnWidth() && style()->hasAutoColumnCount()) {
desiredColumnCount = max<LayoutUnit>(1, (availWidth + colGap) / (colWidth + colGap));
desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap;
} else {
desiredColumnCount = max<LayoutUnit>(min<LayoutUnit>(colCount, (availWidth + colGap) / (colWidth + colGap)), 1);
desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap;
}
setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth);
}
bool RenderBlock::requiresColumns(int desiredColumnCount) const
{
bool isPaginated = style()->isOverflowPaged() && node() != document().viewportDefiningElement();
return firstChild()
&& (desiredColumnCount != 1 || !style()->hasAutoColumnWidth() || isPaginated)
&& !firstChild()->isAnonymousColumnsBlock()
&& !firstChild()->isAnonymousColumnSpanBlock();
}
void RenderBlock::setDesiredColumnCountAndWidth(int count, LayoutUnit width)
{
bool destroyColumns = !requiresColumns(count);
if (destroyColumns) {
if (hasColumns()) {
gColumnInfoMap->take(this);
setHasColumns(false);
}
} else {
ColumnInfo* info;
if (hasColumns())
info = gColumnInfoMap->get(this);
else {
if (!gColumnInfoMap)
gColumnInfoMap = new ColumnInfoMap;
info = new ColumnInfo;
gColumnInfoMap->add(this, adoptPtr(info));
setHasColumns(true);
}
info->setDesiredColumnWidth(width);
if (style()->isOverflowPaged()) {
info->setDesiredColumnCount(1);
info->setProgressionAxis(style()->hasInlinePaginationAxis() ? ColumnInfo::InlineAxis : ColumnInfo::BlockAxis);
} else {
info->setDesiredColumnCount(count);
info->setProgressionAxis(ColumnInfo::InlineAxis);
}
}
}
LayoutUnit RenderBlock::desiredColumnWidth() const
{
if (!hasColumns())
return contentLogicalWidth();
return gColumnInfoMap->get(this)->desiredColumnWidth();
}
ColumnInfo* RenderBlock::columnInfo() const
{
if (!hasColumns())
return 0;
return gColumnInfoMap->get(this);
}
unsigned RenderBlock::columnCount(ColumnInfo* colInfo) const
{
ASSERT(hasColumns());
ASSERT(gColumnInfoMap->get(this) == colInfo);
return colInfo->columnCount();
}
LayoutRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const
{
ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo);
LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth();
LayoutUnit colLogicalHeight = colInfo->columnHeight();
LayoutUnit colLogicalTop = borderBefore() + paddingBefore();
LayoutUnit colLogicalLeft = logicalLeftOffsetForContent();
LayoutUnit colGap = columnGap();
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
if (style()->isLeftToRightDirection())
colLogicalLeft += index * (colLogicalWidth + colGap);
else
colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap);
} else {
colLogicalTop += index * (colLogicalHeight + colGap);
}
if (isHorizontalWritingMode())
return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight);
return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth);
}
void RenderBlock::adjustPointToColumnContents(LayoutPoint& point) const
{
if (!hasColumns())
return;
ColumnInfo* colInfo = columnInfo();
if (!columnCount(colInfo))
return;
LayoutUnit colGap = columnGap();
LayoutUnit halfColGap = colGap / 2;
LayoutPoint columnPoint(columnRectAt(colInfo, 0).location());
LayoutUnit logicalOffset = 0;
for (unsigned i = 0; i < colInfo->columnCount(); i++) {
LayoutRect colRect = columnRectAt(colInfo, i);
flipForWritingMode(colRect);
if (isHorizontalWritingMode() == (colInfo->progressionAxis() == ColumnInfo::InlineAxis)) {
LayoutRect gapAndColumnRect(colRect.x() - halfColGap, colRect.y(), colRect.width() + colGap, colRect.height());
if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.maxX()) {
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
if (point.y() < gapAndColumnRect.y())
point = gapAndColumnRect.location();
else if (point.y() >= gapAndColumnRect.maxY()) {
point = gapAndColumnRect.location();
point.move(0, gapAndColumnRect.height());
}
} else {
if (point.x() < colRect.x())
point.setX(colRect.x());
else if (point.x() >= colRect.maxX())
point.setX(colRect.maxX() - 1);
}
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis)
point.move(columnPoint.x() - colRect.x(), (!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset));
else
point.move((!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset) - colRect.x() + borderLeft() + paddingLeft(), 0);
return;
}
logicalOffset += colInfo->progressionAxis() == ColumnInfo::InlineAxis ? colRect.height() : colRect.width();
} else {
LayoutRect gapAndColumnRect(colRect.x(), colRect.y() - halfColGap, colRect.width(), colRect.height() + colGap);
if (point.y() >= gapAndColumnRect.y() && point.y() < gapAndColumnRect.maxY()) {
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
if (point.x() < gapAndColumnRect.x())
point = gapAndColumnRect.location();
else if (point.x() >= gapAndColumnRect.maxX()) {
point = gapAndColumnRect.location();
point.move(gapAndColumnRect.width(), 0);
}
} else {
if (point.y() < colRect.y())
point.setY(colRect.y());
else if (point.y() >= colRect.maxY())
point.setY(colRect.maxY() - 1);
}
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis)
point.move((!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset), columnPoint.y() - colRect.y());
else
point.move(0, (!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset) - colRect.y() + borderTop() + paddingTop());
return;
}
logicalOffset += colInfo->progressionAxis() == ColumnInfo::InlineAxis ? colRect.width() : colRect.height();
}
}
}
void RenderBlock::adjustRectForColumns(LayoutRect& r) const
{
if (!hasColumns())
return;
ColumnInfo* colInfo = columnInfo();
unsigned colCount = columnCount(colInfo);
if (!colCount)
return;
LayoutRect result;
bool isHorizontal = isHorizontalWritingMode();
LayoutUnit beforeBorderPadding = borderBefore() + paddingBefore();
LayoutUnit colHeight = colInfo->columnHeight();
if (!colHeight)
return;
LayoutUnit startOffset = max(isHorizontal ? r.y() : r.x(), beforeBorderPadding);
LayoutUnit endOffset = max(min<LayoutUnit>(isHorizontal ? r.maxY() : r.maxX(), beforeBorderPadding + colCount * colHeight), beforeBorderPadding);
unsigned startColumn = (startOffset - beforeBorderPadding) / colHeight;
unsigned endColumn = (endOffset - beforeBorderPadding) / colHeight;
if (startColumn == endColumn) {
LayoutUnit logicalLeftOffset = logicalLeftOffsetForContent();
LayoutRect colRect = columnRectAt(colInfo, startColumn);
LayoutRect repaintRect = r;
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
if (isHorizontal)
repaintRect.move(colRect.x() - logicalLeftOffset, - static_cast<int>(startColumn) * colHeight);
else
repaintRect.move(- static_cast<int>(startColumn) * colHeight, colRect.y() - logicalLeftOffset);
} else {
if (isHorizontal)
repaintRect.move(0, colRect.y() - startColumn * colHeight - beforeBorderPadding);
else
repaintRect.move(colRect.x() - startColumn * colHeight - beforeBorderPadding, 0);
}
repaintRect.intersect(colRect);
result.unite(repaintRect);
} else {
result.unite(columnRectAt(colInfo, startColumn));
result.unite(columnRectAt(colInfo, endColumn));
}
r = result;
}
LayoutPoint RenderBlock::flipForWritingModeIncludingColumns(const LayoutPoint& point) const
{
ASSERT(hasColumns());
if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
return point;
ColumnInfo* colInfo = columnInfo();
LayoutUnit columnLogicalHeight = colInfo->columnHeight();
LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight();
if (isHorizontalWritingMode())
return LayoutPoint(point.x(), expandedLogicalHeight - point.y());
return LayoutPoint(expandedLogicalHeight - point.x(), point.y());
}
void RenderBlock::adjustStartEdgeForWritingModeIncludingColumns(LayoutRect& rect) const
{
ASSERT(hasColumns());
if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
return;
ColumnInfo* colInfo = columnInfo();
LayoutUnit columnLogicalHeight = colInfo->columnHeight();
LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight();
if (isHorizontalWritingMode())
rect.setY(expandedLogicalHeight - rect.maxY());
else
rect.setX(expandedLogicalHeight - rect.maxX());
}
void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) const
{
if (!hasColumns())
return;
ColumnInfo* colInfo = columnInfo();
LayoutUnit logicalLeft = logicalLeftOffsetForContent();
unsigned colCount = columnCount(colInfo);
LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth();
LayoutUnit colLogicalHeight = colInfo->columnHeight();
for (unsigned i = 0; i < colCount; ++i) {
LayoutRect sliceRect = LayoutRect(logicalLeft, borderBefore() + paddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight);
if (!isHorizontalWritingMode())
sliceRect = sliceRect.transposedRect();
LayoutUnit logicalOffset = i * colLogicalHeight;
if (isHorizontalWritingMode()) {
if (point.y() >= sliceRect.y() && point.y() < sliceRect.maxY()) {
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis)
offset.expand(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset);
else
offset.expand(0, columnRectAt(colInfo, i).y() - logicalOffset - borderBefore() - paddingBefore());
return;
}
} else {
if (point.x() >= sliceRect.x() && point.x() < sliceRect.maxX()) {
if (colInfo->progressionAxis() == ColumnInfo::InlineAxis)
offset.expand(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft);
else
offset.expand(columnRectAt(colInfo, i).x() - logicalOffset - borderBefore() - paddingBefore(), 0);
return;
}
}
}
}
void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
{
if (childrenInline()) {
toRenderBlockFlow(const_cast<RenderBlock*>(this))->computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth);
} else {
computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth);
}
maxLogicalWidth = max(minLogicalWidth, maxLogicalWidth);
adjustIntrinsicLogicalWidthsForColumns(minLogicalWidth, maxLogicalWidth);
if (childrenInline() && isMarquee() && toRenderMarquee(this)->isHorizontal())
minLogicalWidth = 0;
if (isTableCell()) {
Length tableCellWidth = toRenderTableCell(this)->styleOrColLogicalWidth();
if (tableCellWidth.isFixed() && tableCellWidth.value() > 0)
maxLogicalWidth = max(minLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(tableCellWidth.value()));
}
int scrollbarWidth = instrinsicScrollbarLogicalWidth();
maxLogicalWidth += scrollbarWidth;
minLogicalWidth += scrollbarWidth;
}
void RenderBlock::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
updateFirstLetter();
m_minPreferredLogicalWidth = 0;
m_maxPreferredLogicalWidth = 0;
RenderStyle* styleToUse = style();
if (!isTableCell() && styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() >= 0
&& !(isDeprecatedFlexItem() && !styleToUse->logicalWidth().intValue()))
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value());
else
computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) {
m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
}
if (styleToUse->logicalMaxWidth().isFixed()) {
m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
}
if (isTableCell()) {
m_minPreferredLogicalWidth = m_minPreferredLogicalWidth.ceil();
m_maxPreferredLogicalWidth = m_maxPreferredLogicalWidth.ceil();
}
LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
m_minPreferredLogicalWidth += borderAndPadding;
m_maxPreferredLogicalWidth += borderAndPadding;
clearPreferredLogicalWidthsDirty();
}
void RenderBlock::adjustIntrinsicLogicalWidthsForColumns(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
{
if (!style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth()) {
int columnCount = style()->hasAutoColumnCount() ? 1 : style()->columnCount();
LayoutUnit columnWidth;
LayoutUnit gapExtra = (columnCount - 1) * columnGap();
if (style()->hasAutoColumnWidth()) {
minLogicalWidth = minLogicalWidth * columnCount + gapExtra;
} else {
columnWidth = style()->columnWidth();
minLogicalWidth = min(minLogicalWidth, columnWidth);
}
maxLogicalWidth = max(maxLogicalWidth, columnWidth) * columnCount + gapExtra;
}
}
void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
{
RenderStyle* styleToUse = style();
bool nowrap = styleToUse->whiteSpace() == NOWRAP;
RenderObject* child = firstChild();
RenderBlock* containingBlock = this->containingBlock();
LayoutUnit floatLeftWidth = 0, floatRightWidth = 0;
while (child) {
if (child->isOutOfFlowPositioned()) {
child = child->nextSibling();
continue;
}
RenderStyle* childStyle = child->style();
if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) {
LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth;
if (childStyle->clear() & CLEFT) {
maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth);
floatLeftWidth = 0;
}
if (childStyle->clear() & CRIGHT) {
maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth);
floatRightWidth = 0;
}
}
Length startMarginLength = childStyle->marginStartUsing(styleToUse);
Length endMarginLength = childStyle->marginEndUsing(styleToUse);
LayoutUnit margin = 0;
LayoutUnit marginStart = 0;
LayoutUnit marginEnd = 0;
if (startMarginLength.isFixed())
marginStart += startMarginLength.value();
if (endMarginLength.isFixed())
marginEnd += endMarginLength.value();
margin = marginStart + marginEnd;
LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth;
if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) {
RenderBox* childBox = toRenderBox(child);
LogicalExtentComputedValues computedValues;
childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues);
childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent;
} else {
childMinPreferredLogicalWidth = child->minPreferredLogicalWidth();
childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth();
}
LayoutUnit w = childMinPreferredLogicalWidth + margin;
minLogicalWidth = max(w, minLogicalWidth);
if (nowrap && !child->isTable())
maxLogicalWidth = max(w, maxLogicalWidth);
w = childMaxPreferredLogicalWidth + margin;
if (!child->isFloating()) {
if (child->isBox() && toRenderBox(child)->avoidsFloats()) {
bool ltr = containingBlock ? containingBlock->style()->isLeftToRightDirection() : styleToUse->isLeftToRightDirection();
LayoutUnit marginLogicalLeft = ltr ? marginStart : marginEnd;
LayoutUnit marginLogicalRight = ltr ? marginEnd : marginStart;
LayoutUnit maxLeft = marginLogicalLeft > 0 ? max(floatLeftWidth, marginLogicalLeft) : floatLeftWidth + marginLogicalLeft;
LayoutUnit maxRight = marginLogicalRight > 0 ? max(floatRightWidth, marginLogicalRight) : floatRightWidth + marginLogicalRight;
w = childMaxPreferredLogicalWidth + maxLeft + maxRight;
w = max(w, floatLeftWidth + floatRightWidth);
}
else
maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth);
floatLeftWidth = floatRightWidth = 0;
}
if (child->isFloating()) {
if (childStyle->floating() == LeftFloat)
floatLeftWidth += w;
else
floatRightWidth += w;
} else
maxLogicalWidth = max(w, maxLogicalWidth);
child = child->nextSibling();
}
minLogicalWidth = max<LayoutUnit>(0, minLogicalWidth);
maxLogicalWidth = max<LayoutUnit>(0, maxLogicalWidth);
maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth);
}
bool RenderBlock::hasLineIfEmpty() const
{
if (!node())
return false;
if (node()->isRootEditableElement())
return true;
if (node()->isShadowRoot() && isHTMLInputElement(*toShadowRoot(node())->host()))
return true;
return false;
}
LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
{
if (isReplaced() && linePositionMode == PositionOnContainingLine)
return RenderBox::lineHeight(firstLine, direction, linePositionMode);
if (firstLine && document().styleEngine()->usesFirstLineRules()) {
RenderStyle* s = style(firstLine);
if (s != style())
return s->computedLineHeight();
}
if (m_lineHeight == -1)
m_lineHeight = style()->computedLineHeight();
return m_lineHeight;
}
int RenderBlock::beforeMarginInLineDirection(LineDirectionMode direction) const
{
return direction == HorizontalLine ? marginTop() : marginRight();
}
int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
{
if (isInline() && linePositionMode == PositionOnContainingLine) {
if (style()->hasAppearance() && !RenderTheme::theme().isControlContainer(style()->appearance()))
return RenderTheme::theme().baselinePosition(this);
bool ignoreBaseline = (layer() && layer()->scrollableArea() && (isMarquee() || (direction == HorizontalLine ? (layer()->scrollableArea()->verticalScrollbar() || layer()->scrollableArea()->scrollYOffset())
: (layer()->scrollableArea()->horizontalScrollbar() || layer()->scrollableArea()->scrollXOffset())))) || (isWritingModeRoot() && !isRubyRun());
int baselinePos = ignoreBaseline ? -1 : inlineBlockBaseline(direction);
if (isDeprecatedFlexibleBox()) {
LayoutUnit bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth();
if (baselinePos > bottomOfContent)
baselinePos = -1;
}
if (baselinePos != -1)
return beforeMarginInLineDirection(direction) + baselinePos;
return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
}
ASSERT(linePositionMode == PositionOfInteriorLineBoxes);
const FontMetrics& fontMetrics = style(firstLine)->fontMetrics();
return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
}
LayoutUnit RenderBlock::minLineHeightForReplacedRenderer(bool isFirstLine, LayoutUnit replacedHeight) const
{
if (!document().inNoQuirksMode() && replacedHeight)
return replacedHeight;
if (!(style(isFirstLine)->lineBoxContain() & LineBoxContainBlock))
return 0;
return std::max<LayoutUnit>(replacedHeight, lineHeight(isFirstLine, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
}
int RenderBlock::firstLineBoxBaseline() const
{
if (isWritingModeRoot() && !isRubyRun())
return -1;
if (childrenInline()) {
if (firstLineBox())
return firstLineBox()->logicalTop() + style(true)->fontMetrics().ascent(firstRootBox()->baselineType());
else
return -1;
}
else {
for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) {
if (!curr->isFloatingOrOutOfFlowPositioned()) {
int result = curr->firstLineBoxBaseline();
if (result != -1)
return curr->logicalTop() + result;
}
}
}
return -1;
}
int RenderBlock::inlineBlockBaseline(LineDirectionMode direction) const
{
if (!style()->isOverflowVisible()) {
return direction == HorizontalLine ? height() + m_marginBox.bottom() : width() + m_marginBox.left();
}
return lastLineBoxBaseline(direction);
}
int RenderBlock::lastLineBoxBaseline(LineDirectionMode lineDirection) const
{
if (isWritingModeRoot() && !isRubyRun())
return -1;
if (childrenInline()) {
if (!firstLineBox() && hasLineIfEmpty()) {
const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics();
return fontMetrics.ascent()
+ (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2
+ (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight());
}
if (lastLineBox())
return lastLineBox()->logicalTop() + style(lastLineBox() == firstLineBox())->fontMetrics().ascent(lastRootBox()->baselineType());
return -1;
} else {
bool haveNormalFlowChild = false;
for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) {
if (!curr->isFloatingOrOutOfFlowPositioned()) {
haveNormalFlowChild = true;
int result = curr->inlineBlockBaseline(lineDirection);
if (result != -1)
return curr->logicalTop() + result;
}
}
if (!haveNormalFlowChild && hasLineIfEmpty()) {
const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics();
return fontMetrics.ascent()
+ (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2
+ (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight());
}
}
return -1;
}
RenderBlock* RenderBlock::firstLineBlock() const
{
RenderBlock* firstLineBlock = const_cast<RenderBlock*>(this);
bool hasPseudo = false;
while (true) {
hasPseudo = firstLineBlock->style()->hasPseudoStyle(FIRST_LINE);
if (hasPseudo)
break;
RenderObject* parentBlock = firstLineBlock->parent();
if (firstLineBlock->isReplaced() || firstLineBlock->isFloating()
|| !parentBlock || parentBlock->firstChild() != firstLineBlock
|| (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton()))
break;
ASSERT_WITH_SECURITY_IMPLICATION(parentBlock->isRenderBlock());
firstLineBlock = toRenderBlock(parentBlock);
}
if (!hasPseudo)
return 0;
return firstLineBlock;
}
static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer)
{
RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle());
pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE);
pseudoStyle->setPosition(StaticPosition);
return pseudoStyle;
}
static inline RenderObject* findFirstLetterBlock(RenderBlock* start)
{
RenderObject* firstLetterBlock = start;
while (true) {
bool canHaveFirstLetterRenderer = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER)
&& firstLetterBlock->canHaveGeneratedChildren()
&& (!firstLetterBlock->isFlexibleBox() || firstLetterBlock->isRenderButton());
if (canHaveFirstLetterRenderer)
return firstLetterBlock;
RenderObject* parentBlock = firstLetterBlock->parent();
if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock ||
(!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton()))
return 0;
firstLetterBlock = parentBlock;
}
return 0;
}
void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderObject* currentChild)
{
RenderObject* firstLetter = currentChild->parent();
RenderObject* firstLetterContainer = firstLetter->parent();
RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
ASSERT(firstLetter->isFloating() || firstLetter->isInline());
if (RenderStyle::stylePropagationDiff(firstLetter->style(), pseudoStyle) == Reattach) {
RenderBoxModelObject* newFirstLetter;
if (pseudoStyle->display() == INLINE)
newFirstLetter = RenderInline::createAnonymous(&document());
else
newFirstLetter = RenderBlockFlow::createAnonymous(&document());
newFirstLetter->setStyle(pseudoStyle);
LayoutStateDisabler layoutStateDisabler(*this);
while (RenderObject* child = firstLetter->firstChild()) {
if (child->isText())
toRenderText(child)->removeAndDestroyTextBoxes();
firstLetter->removeChild(child);
newFirstLetter->addChild(child, 0);
}
RenderObject* nextSibling = firstLetter->nextSibling();
if (RenderTextFragment* remainingText = toRenderBoxModelObject(firstLetter)->firstLetterRemainingText()) {
ASSERT(remainingText->isAnonymous() || remainingText->node()->renderer() == remainingText);
remainingText->setFirstLetter(newFirstLetter);
newFirstLetter->setFirstLetterRemainingText(remainingText);
}
firstLetterContainer->virtualChildren()->removeChildNode(firstLetterContainer, firstLetter);
firstLetter->destroy();
firstLetter = newFirstLetter;
firstLetterContainer->addChild(firstLetter, nextSibling);
} else
firstLetter->setStyle(pseudoStyle);
for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) {
if (genChild->isText())
genChild->setStyle(pseudoStyle);
}
}
void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, RenderObject* currentChild, unsigned length)
{
ASSERT(length && currentChild->isText());
RenderObject* firstLetterContainer = currentChild->parent();
RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
RenderObject* firstLetter = 0;
if (pseudoStyle->display() == INLINE)
firstLetter = RenderInline::createAnonymous(&document());
else
firstLetter = RenderBlockFlow::createAnonymous(&document());
firstLetter->setStyle(pseudoStyle);
firstLetterContainer->addChild(firstLetter, currentChild);
RenderText* textObj = toRenderText(currentChild);
String oldText = textObj->originalText();
ASSERT(oldText.impl());
RenderTextFragment* remainingText =
new RenderTextFragment(textObj->node() ? textObj->node() : &textObj->document(), oldText.impl(), length, oldText.length() - length);
remainingText->setStyle(textObj->style());
if (remainingText->node())
remainingText->node()->setRenderer(remainingText);
firstLetterContainer->addChild(remainingText, textObj);
firstLetterContainer->removeChild(textObj);
remainingText->setFirstLetter(firstLetter);
toRenderBoxModelObject(firstLetter)->setFirstLetterRemainingText(remainingText);
RenderTextFragment* letter =
new RenderTextFragment(remainingText->node() ? remainingText->node() : &remainingText->document(), oldText.impl(), 0, length);
letter->setStyle(pseudoStyle);
firstLetter->addChild(letter);
textObj->destroy();
}
static bool isRendererAllowedForFirstLetter(RenderObject* renderer)
{
return renderer->isText() && !renderer->isBR() && !toRenderText(renderer)->isWordBreak();
}
typedef Vector<std::pair<RenderObject*, unsigned> > FirstLetterRenderersList;
enum FirstLetterSearchState {
SearchLeadingSpaces,
SearchLeadingPunctuation,
SearchFirstLetterCharacter,
SearchTrailingPunctuation
};
class FirstLetterFinder {
WTF_MAKE_NONCOPYABLE(FirstLetterFinder);
public:
FirstLetterFinder(RenderObject* block)
: m_containerBlock(block)
, m_renderers()
, m_searchState(SearchLeadingSpaces)
, m_firstLetterFound(false)
{
findTextRenderers();
}
FirstLetterRenderersList& renderers() { return m_renderers; }
RenderObject* containerBlock() { return m_containerBlock; }
private:
void findTextRenderers()
{
RenderObject* currentChild = m_containerBlock->firstChild();
unsigned currentLength = 0;
while (currentChild) {
if (currentChild->isText()) {
currentLength = processTextRenderer(currentChild);
if (currentLength)
m_renderers.append(std::make_pair(currentChild, currentLength));
String text = rendererTextForFirstLetter(currentChild);
if (!currentLength || currentLength < text.length())
break;
currentChild = currentChild->nextInPreOrderAfterChildren(m_containerBlock);
} else if (currentChild->isListMarker()) {
currentChild = currentChild->nextSibling();
} else if (currentChild->isFloatingOrOutOfFlowPositioned()) {
if (currentChild->style()->styleType() == FIRST_LETTER) {
currentChild = currentChild->firstChild();
if (currentChild) {
m_firstLetterFound = true;
m_renderers.append(std::make_pair(currentChild, rendererTextForFirstLetter(currentChild).length()));
}
break;
}
currentChild = currentChild->nextSibling();
} else if (currentChild->isReplaced() || currentChild->isRenderButton() || currentChild->isMenuList()) {
break;
} else if (currentChild->style()->hasPseudoStyle(FIRST_LETTER) && currentChild->canHaveGeneratedChildren()) {
m_containerBlock = currentChild;
currentChild = currentChild->firstChild();
} else {
currentChild = currentChild->firstChild();
}
}
if (!m_firstLetterFound) {
m_renderers.clear();
}
}
String rendererTextForFirstLetter(RenderObject* renderer) const
{
ASSERT(renderer->isText());
RenderText* textRenderer = toRenderText(renderer);
String result = textRenderer->originalText();
if (!result.isNull())
return result;
if (isRendererAllowedForFirstLetter(renderer))
return textRenderer->text();
return String();
}
unsigned processTextRenderer(RenderObject* renderer)
{
ASSERT(renderer->isText());
String text = rendererTextForFirstLetter(renderer);
if (text.isEmpty()
|| (m_searchState == SearchLeadingPunctuation && isSpaceForFirstLetter(text[0]))
|| (m_firstLetterFound && !isPunctuationForFirstLetter(text[0])))
return 0;
bool doneSearching = false;
unsigned textLength = text.length();
unsigned length = 0;
while (!doneSearching && length < textLength) {
switch (m_searchState) {
case SearchLeadingSpaces:
advancePositionWhile<isSpaceForFirstLetter>(text, length);
if (length < textLength)
m_searchState = SearchLeadingPunctuation;
break;
case SearchLeadingPunctuation:
advancePositionWhile<isPunctuationForFirstLetter>(text, length);
if (length < textLength)
m_searchState = SearchFirstLetterCharacter;
break;
case SearchFirstLetterCharacter:
if (isSpaceForFirstLetter(text[length]))
return 0;
m_firstLetterFound = true;
m_searchState = SearchTrailingPunctuation;
length++;
break;
case SearchTrailingPunctuation:
for (unsigned scanLength = length; scanLength < textLength; ++scanLength) {
UChar c = text[scanLength];
if (!isPunctuationForFirstLetter(c)) {
doneSearching = true;
break;
}
length = scanLength + 1;
}
break;
}
}
ASSERT(length <= textLength);
return length;
}
template<bool characterPredicate(UChar)>
void advancePositionWhile(const String& text, unsigned& position)
{
unsigned textLength = text.length();
while (position < textLength && characterPredicate(text[position]))
position++;
}
static inline bool isPunctuationForFirstLetter(UChar c)
{
CharCategory charCategory = category(c);
return charCategory == Punctuation_Open
|| charCategory == Punctuation_Close
|| charCategory == Punctuation_InitialQuote
|| charCategory == Punctuation_FinalQuote
|| charCategory == Punctuation_Other;
}
static inline bool isSpaceForFirstLetter(UChar c)
{
return isSpaceOrNewline(c) || c == noBreakSpace;
}
RenderObject* m_containerBlock;
FirstLetterRenderersList m_renderers;
FirstLetterSearchState m_searchState;
bool m_firstLetterFound;
};
void RenderBlock::updateFirstLetter()
{
if (!document().styleEngine()->usesFirstLetterRules())
return;
if (style()->styleType() == FIRST_LETTER)
return;
RenderObject* firstLetterBlock = findFirstLetterBlock(this);
if (!firstLetterBlock)
return;
FirstLetterFinder firstLetterFinder(firstLetterBlock);
FirstLetterRenderersList& renderers = firstLetterFinder.renderers();
if (renderers.isEmpty())
return;
firstLetterBlock = firstLetterFinder.containerBlock();
for (FirstLetterRenderersList::const_iterator it = renderers.begin(); it != renderers.end(); ++it) {
RenderObject* currentRenderer = it->first;
ASSERT(currentRenderer->isText());
if (currentRenderer->parent()->style()->styleType() == FIRST_LETTER) {
updateFirstLetterStyle(firstLetterBlock, currentRenderer);
continue;
}
if (!isRendererAllowedForFirstLetter(currentRenderer))
continue;
LayoutStateDisabler layoutStateDisabler(*this);
unsigned lengthForRenderer = it->second;
if (lengthForRenderer)
createFirstLetterRenderer(firstLetterBlock, currentRenderer, lengthForRenderer);
}
}
static bool shouldCheckLines(RenderObject* obj)
{
return !obj->isFloatingOrOutOfFlowPositioned()
&& obj->isRenderBlock() && obj->style()->height().isAuto()
&& (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERTICAL);
}
static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, int& count)
{
if (block->style()->visibility() == VISIBLE) {
if (block->childrenInline()) {
for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) {
if (++count == l)
return box->lineBottom() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : LayoutUnit());
}
}
else {
RenderBox* normalFlowChildWithoutLines = 0;
for (RenderBox* obj = block->firstChildBox(); obj; obj = obj->nextSiblingBox()) {
if (shouldCheckLines(obj)) {
int result = getHeightForLineCount(toRenderBlock(obj), l, false, count);
if (result != -1)
return result + obj->y() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : LayoutUnit());
} else if (!obj->isFloatingOrOutOfFlowPositioned())
normalFlowChildWithoutLines = obj;
}
if (normalFlowChildWithoutLines && l == 0)
return normalFlowChildWithoutLines->y() + normalFlowChildWithoutLines->height();
}
}
return -1;
}
RootInlineBox* RenderBlock::lineAtIndex(int i) const
{
ASSERT(i >= 0);
if (style()->visibility() != VISIBLE)
return 0;
if (childrenInline()) {
for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox())
if (!i--)
return box;
} else {
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (!shouldCheckLines(child))
continue;
if (RootInlineBox* box = toRenderBlock(child)->lineAtIndex(i))
return box;
}
}
return 0;
}
int RenderBlock::lineCount(const RootInlineBox* stopRootInlineBox, bool* found) const
{
int count = 0;
if (style()->visibility() == VISIBLE) {
if (childrenInline())
for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) {
count++;
if (box == stopRootInlineBox) {
if (found)
*found = true;
break;
}
}
else
for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling())
if (shouldCheckLines(obj)) {
bool recursiveFound = false;
count += toRenderBlock(obj)->lineCount(stopRootInlineBox, &recursiveFound);
if (recursiveFound) {
if (found)
*found = true;
break;
}
}
}
return count;
}
int RenderBlock::heightForLineCount(int l)
{
int count = 0;
return getHeightForLineCount(this, l, true, count);
}
void RenderBlock::adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutUnit& right) const
{
if (style()->visibility() == VISIBLE) {
if (childrenInline()) {
for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) {
if (box->firstChild())
left = min(left, x + static_cast<LayoutUnit>(box->firstChild()->x()));
if (box->lastChild())
right = max(right, x + static_cast<LayoutUnit>(ceilf(box->lastChild()->logicalRight())));
}
} else {
for (RenderBox* obj = firstChildBox(); obj; obj = obj->nextSiblingBox()) {
if (!obj->isFloatingOrOutOfFlowPositioned()) {
if (obj->isRenderBlockFlow() && !obj->hasOverflowClip())
toRenderBlock(obj)->adjustForBorderFit(x + obj->x(), left, right);
else if (obj->style()->visibility() == VISIBLE) {
left = min(left, x + obj->x());
right = max(right, x + obj->x() + obj->width());
}
}
}
}
}
}
void RenderBlock::fitBorderToLinesIfNeeded()
{
if (style()->borderFit() == BorderFitBorder || hasOverrideWidth())
return;
LayoutUnit left = LayoutUnit::max();
LayoutUnit right = LayoutUnit::min();
LayoutUnit oldWidth = contentWidth();
adjustForBorderFit(0, left, right);
LayoutUnit leftEdge = borderLeft() + paddingLeft();
LayoutUnit rightEdge = leftEdge + oldWidth;
left = min(rightEdge, max(leftEdge, left));
right = max(left, min(rightEdge, right));
LayoutUnit newContentWidth = right - left;
if (newContentWidth == oldWidth)
return;
setOverrideLogicalContentWidth(newContentWidth);
layoutBlock(false);
clearOverrideLogicalContentWidth();
}
void RenderBlock::clearTruncation()
{
if (style()->visibility() == VISIBLE) {
if (childrenInline() && hasMarkupTruncation()) {
setHasMarkupTruncation(false);
for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox())
box->clearTruncation();
} else {
for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) {
if (shouldCheckLines(obj))
toRenderBlock(obj)->clearTruncation();
}
}
}
}
void RenderBlock::setPaginationStrut(LayoutUnit strut)
{
if (!m_rareData) {
if (!strut)
return;
m_rareData = adoptPtr(new RenderBlockRareData());
}
m_rareData->m_paginationStrut = strut;
}
void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset)
{
if (!m_rareData) {
if (!logicalOffset)
return;
m_rareData = adoptPtr(new RenderBlockRareData());
}
m_rareData->m_pageLogicalOffset = logicalOffset;
}
void RenderBlock::setBreakAtLineToAvoidWidow(int lineToBreak)
{
ASSERT(lineToBreak >= 0);
if (!m_rareData)
m_rareData = adoptPtr(new RenderBlockRareData());
ASSERT(!m_rareData->m_didBreakAtLineToAvoidWidow);
m_rareData->m_lineBreakToAvoidWidow = lineToBreak;
}
void RenderBlock::setDidBreakAtLineToAvoidWidow()
{
ASSERT(!shouldBreakAtLineToAvoidWidow());
ASSERT(m_rareData);
m_rareData->m_didBreakAtLineToAvoidWidow = true;
}
void RenderBlock::clearDidBreakAtLineToAvoidWidow()
{
if (!m_rareData)
return;
m_rareData->m_didBreakAtLineToAvoidWidow = false;
}
void RenderBlock::clearShouldBreakAtLineToAvoidWidow() const
{
ASSERT(shouldBreakAtLineToAvoidWidow());
if (!m_rareData)
return;
m_rareData->m_lineBreakToAvoidWidow = -1;
}
void RenderBlock::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
{
if (isAnonymousBlockContinuation()) {
rects.append(pixelSnappedIntRect(accumulatedOffset.x(), accumulatedOffset.y() - collapsedMarginBefore(),
width(), height() + collapsedMarginBefore() + collapsedMarginAfter()));
continuation()->absoluteRects(rects, accumulatedOffset - toLayoutSize(location() +
inlineElementContinuation()->containingBlock()->location()));
} else
rects.append(pixelSnappedIntRect(accumulatedOffset, size()));
}
void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
{
if (isAnonymousBlockContinuation()) {
FloatRect localRect(0, -collapsedMarginBefore().toFloat(),
width().toFloat(), (height() + collapsedMarginBefore() + collapsedMarginAfter()).toFloat());
quads.append(localToAbsoluteQuad(localRect, 0 , wasFixed));
continuation()->absoluteQuads(quads, wasFixed);
} else {
quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width().toFloat(), height().toFloat()), 0 , wasFixed));
}
}
LayoutRect RenderBlock::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
{
LayoutRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
if (isAnonymousBlockContinuation())
r.inflateY(collapsedMarginBefore());
return r;
}
RenderObject* RenderBlock::hoverAncestor() const
{
return isAnonymousBlockContinuation() ? continuation() : RenderBox::hoverAncestor();
}
void RenderBlock::updateDragState(bool dragOn)
{
RenderBox::updateDragState(dragOn);
if (continuation())
continuation()->updateDragState(dragOn);
}
RenderStyle* RenderBlock::outlineStyleForRepaint() const
{
return isAnonymousBlockContinuation() ? continuation()->style() : style();
}
void RenderBlock::childBecameNonInline(RenderObject*)
{
makeChildrenNonInline();
if (isAnonymousBlock() && parent() && parent()->isRenderBlock())
toRenderBlock(parent())->removeLeftoverAnonymousBlock(this);
}
void RenderBlock::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
{
if (result.innerNode())
return;
if (Node* n = nodeForHitTest()) {
result.setInnerNode(n);
if (!result.innerNonSharedNode())
result.setInnerNonSharedNode(n);
result.setLocalPoint(point);
}
}
LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
{
if (firstChild())
return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine);
LayoutRect caretRect = localCaretRectForEmptyElement(width(), textIndentOffset());
if (extraWidthToEndOfLine) {
if (isRenderBlock()) {
*extraWidthToEndOfLine = width() - caretRect.maxX();
} else {
LayoutUnit myRight = caretRect.maxX();
FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight.toFloat(), 0));
LayoutUnit containerRight = containingBlock()->x() + containingBlockLogicalWidthForContent();
FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight.toFloat(), 0));
*extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x();
}
}
return caretRect;
}
void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
{
if (inlineElementContinuation()) {
bool nextInlineHasLineBox = inlineElementContinuation()->firstLineBox();
bool prevInlineHasLineBox = toRenderInline(inlineElementContinuation()->node()->renderer())->firstLineBox();
LayoutUnit topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : LayoutUnit();
LayoutUnit bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : LayoutUnit();
LayoutRect rect(additionalOffset.x(), additionalOffset.y() - topMargin, width(), height() + topMargin + bottomMargin);
if (!rect.isEmpty())
rects.append(pixelSnappedIntRect(rect));
} else if (width() && height())
rects.append(pixelSnappedIntRect(additionalOffset, size()));
if (!hasOverflowClip() && !hasControlClip()) {
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
LayoutUnit top = max<LayoutUnit>(curr->lineTop(), curr->top());
LayoutUnit bottom = min<LayoutUnit>(curr->lineBottom(), curr->top() + curr->height());
LayoutRect rect(additionalOffset.x() + curr->x(), additionalOffset.y() + top, curr->width(), bottom - top);
if (!rect.isEmpty())
rects.append(pixelSnappedIntRect(rect));
}
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (!curr->isText() && !curr->isListMarker() && curr->isBox()) {
RenderBox* box = toRenderBox(curr);
FloatPoint pos;
if (box->layer())
pos = curr->localToContainerPoint(FloatPoint(), paintContainer);
else
pos = FloatPoint((additionalOffset.x() + box->x()).toFloat(), (additionalOffset.y() + box->y()).toFloat());
box->addFocusRingRects(rects, flooredLayoutPoint(pos), paintContainer);
}
}
}
if (inlineElementContinuation())
inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location()), paintContainer);
}
void RenderBlock::computeSelfHitTestRects(Vector<LayoutRect>& rects, const LayoutPoint& layerOffset) const
{
RenderBox::computeSelfHitTestRects(rects, layerOffset);
if (hasHorizontalLayoutOverflow() || hasVerticalLayoutOverflow()) {
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
LayoutUnit top = max<LayoutUnit>(curr->lineTop(), curr->top());
LayoutUnit bottom = min<LayoutUnit>(curr->lineBottom(), curr->top() + curr->height());
LayoutRect rect(layerOffset.x() + curr->x(), layerOffset.y() + top, curr->width(), bottom - top);
if (!rect.isEmpty() && (rects.isEmpty() || !rects[0].contains(rect)))
rects.append(rect);
}
}
}
RenderBox* RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const
{
if (isAnonymousColumnsBlock())
return createAnonymousColumnsWithParentRenderer(parent);
if (isAnonymousColumnSpanBlock())
return createAnonymousColumnSpanWithParentRenderer(parent);
return createAnonymousWithParentRendererAndDisplay(parent, style()->display());
}
LayoutUnit RenderBlock::nextPageLogicalTop(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const
{
LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
if (!pageLogicalHeight)
return logicalOffset;
LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset);
if (pageBoundaryRule == ExcludePageBoundary)
return logicalOffset + (remainingLogicalHeight ? remainingLogicalHeight : pageLogicalHeight);
return logicalOffset + remainingLogicalHeight;
}
LayoutUnit RenderBlock::pageLogicalTopForOffset(LayoutUnit offset) const
{
RenderView* renderView = view();
LayoutUnit firstPageLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->pageOffset().height() : renderView->layoutState()->pageOffset().width();
LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->layoutOffset().height() : renderView->layoutState()->layoutOffset().width();
LayoutUnit cumulativeOffset = offset + blockLogicalTop;
RenderFlowThread* flowThread = flowThreadContainingBlock();
if (!flowThread) {
LayoutUnit pageLogicalHeight = renderView->layoutState()->pageLogicalHeight();
if (!pageLogicalHeight)
return 0;
return cumulativeOffset - roundToInt(cumulativeOffset - firstPageLogicalTop) % roundToInt(pageLogicalHeight);
}
return flowThread->pageLogicalTopForOffset(cumulativeOffset);
}
LayoutUnit RenderBlock::pageLogicalHeightForOffset(LayoutUnit offset) const
{
RenderView* renderView = view();
RenderFlowThread* flowThread = flowThreadContainingBlock();
if (!flowThread)
return renderView->layoutState()->pageLogicalHeight();
return flowThread->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage());
}
LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) const
{
RenderView* renderView = view();
offset += offsetFromLogicalTopOfFirstPage();
RenderFlowThread* flowThread = flowThreadContainingBlock();
if (!flowThread) {
LayoutUnit pageLogicalHeight = renderView->layoutState()->pageLogicalHeight();
LayoutUnit remainingHeight = pageLogicalHeight - intMod(offset, pageLogicalHeight);
if (pageBoundaryRule == IncludePageBoundary) {
remainingHeight = intMod(remainingHeight, pageLogicalHeight);
}
return remainingHeight;
}
return flowThread->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule);
}
LayoutUnit RenderBlock::adjustForUnsplittableChild(RenderBox* child, LayoutUnit logicalOffset, bool includeMargins)
{
bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns() || flowThreadContainingBlock();
bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->pageLogicalHeight();
bool isUnsplittable = child->isUnsplittableForPagination() || (checkColumnBreaks && child->style()->columnBreakInside() == PBAVOID)
|| (checkPageBreaks && child->style()->pageBreakInside() == PBAVOID);
if (!isUnsplittable)
return logicalOffset;
LayoutUnit childLogicalHeight = logicalHeightForChild(child) + (includeMargins ? marginBeforeForChild(child) + marginAfterForChild(child) : LayoutUnit());
LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
updateMinimumPageHeight(logicalOffset, childLogicalHeight);
if (!pageLogicalHeight || childLogicalHeight > pageLogicalHeight)
return logicalOffset;
LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary);
if (remainingLogicalHeight < childLogicalHeight)
return logicalOffset + remainingLogicalHeight;
return logicalOffset;
}
bool RenderBlock::pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, LayoutUnit logicalOffset, LayoutUnit minimumLogicalHeight) const
{
return false;
}
void RenderBlock::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage)
{
if (RenderFlowThread* flowThread = flowThreadContainingBlock())
flowThread->setPageBreak(offsetFromLogicalTopOfFirstPage() + offset, spaceShortage);
}
void RenderBlock::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight)
{
if (RenderFlowThread* flowThread = flowThreadContainingBlock())
flowThread->updateMinimumPageHeight(offsetFromLogicalTopOfFirstPage() + offset, minHeight);
else if (ColumnInfo* colInfo = view()->layoutState()->columnInfo())
colInfo->updateMinimumColumnHeight(minHeight);
}
static inline LayoutUnit calculateMinimumPageHeight(RenderStyle* renderStyle, RootInlineBox* lastLine, LayoutUnit lineTop, LayoutUnit lineBottom)
{
unsigned lineCount = max<unsigned>(renderStyle->hasAutoOrphans() ? 1 : renderStyle->orphans(), renderStyle->hasAutoWidows() ? 1 : renderStyle->widows());
if (lineCount > 1) {
RootInlineBox* line = lastLine;
for (unsigned i = 1; i < lineCount && line->prevRootBox(); i++)
line = line->prevRootBox();
LayoutRect overflow = line->logicalVisualOverflowRect(line->lineTop(), line->lineBottom());
lineTop = min(line->lineTopWithLeading(), overflow.y());
}
return lineBottom - lineTop;
}
void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta, RenderFlowThread* flowThread)
{
LayoutRect logicalVisualOverflow = lineBox->logicalVisualOverflowRect(lineBox->lineTop(), lineBox->lineBottom());
LayoutUnit logicalOffset = min(lineBox->lineTopWithLeading(), logicalVisualOverflow.y());
LayoutUnit logicalBottom = max(lineBox->lineBottomWithLeading(), logicalVisualOverflow.maxY());
LayoutUnit lineHeight = logicalBottom - logicalOffset;
updateMinimumPageHeight(logicalOffset, calculateMinimumPageHeight(style(), lineBox, logicalOffset, logicalBottom));
logicalOffset += delta;
lineBox->setPaginationStrut(0);
lineBox->setIsFirstAfterPageBreak(false);
LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight();
if (!pageLogicalHeight || (hasUniformPageLogicalHeight && logicalVisualOverflow.height() > pageLogicalHeight))
return;
LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary);
int lineIndex = lineCount(lineBox);
if (remainingLogicalHeight < lineHeight || (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineIndex)) {
if (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineIndex) {
clearShouldBreakAtLineToAvoidWidow();
setDidBreakAtLineToAvoidWidow();
}
if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, lineHeight))
return;
if (lineHeight > pageLogicalHeight) {
remainingLogicalHeight -= min(lineHeight - pageLogicalHeight, max<LayoutUnit>(0, logicalVisualOverflow.y() - lineBox->lineTopWithLeading()));
}
LayoutUnit totalLogicalHeight = lineHeight + max<LayoutUnit>(0, logicalOffset);
LayoutUnit pageLogicalHeightAtNewOffset = hasUniformPageLogicalHeight ? pageLogicalHeight : pageLogicalHeightForOffset(logicalOffset + remainingLogicalHeight);
setPageBreak(logicalOffset, lineHeight - remainingLogicalHeight);
if (((lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeightAtNewOffset) || (!style()->hasAutoOrphans() && style()->orphans() >= lineIndex))
&& !isOutOfFlowPositioned() && !isTableCell())
setPaginationStrut(remainingLogicalHeight + max<LayoutUnit>(0, logicalOffset));
else {
delta += remainingLogicalHeight;
lineBox->setPaginationStrut(remainingLogicalHeight);
lineBox->setIsFirstAfterPageBreak(true);
}
} else if (remainingLogicalHeight == pageLogicalHeight) {
if (lineBox != firstRootBox())
lineBox->setIsFirstAfterPageBreak(true);
if (lineBox != firstRootBox() || offsetFromLogicalTopOfFirstPage())
setPageBreak(logicalOffset, lineHeight);
}
}
LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const
{
LayoutState* layoutState = view()->layoutState();
if (layoutState && !layoutState->isPaginated())
return 0;
RenderFlowThread* flowThread = flowThreadContainingBlock();
if (flowThread)
return flowThread->offsetFromLogicalTopOfFirstRegion(this);
if (layoutState) {
ASSERT(layoutState->renderer() == this);
LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
}
ASSERT_NOT_REACHED();
return 0;
}
RenderRegion* RenderBlock::regionAtBlockOffset(LayoutUnit blockOffset) const
{
RenderFlowThread* flowThread = flowThreadContainingBlock();
if (!flowThread || !flowThread->hasValidRegionInfo())
return 0;
return flowThread->regionAtBlockOffset(offsetFromLogicalTopOfFirstPage() + blockOffset, true);
}
LayoutUnit RenderBlock::collapsedMarginBeforeForChild(const RenderBox* child) const
{
if (!child->isWritingModeRoot())
return child->collapsedMarginBefore();
if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
return child->collapsedMarginAfter();
return marginBeforeForChild(child);
}
LayoutUnit RenderBlock::collapsedMarginAfterForChild(const RenderBox* child) const
{
if (!child->isWritingModeRoot())
return child->collapsedMarginAfter();
if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
return child->collapsedMarginBefore();
return marginAfterForChild(child);
}
bool RenderBlock::hasMarginBeforeQuirk(const RenderBox* child) const
{
if (!child->isWritingModeRoot())
return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk();
if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk();
return false;
}
bool RenderBlock::hasMarginAfterQuirk(const RenderBox* child) const
{
if (!child->isWritingModeRoot())
return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk();
if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk();
return false;
}
const char* RenderBlock::renderName() const
{
if (isBody())
return "RenderBody";
if (isFloating())
return "RenderBlock (floating)";
if (isOutOfFlowPositioned())
return "RenderBlock (positioned)";
if (isAnonymousColumnsBlock())
return "RenderBlock (anonymous multi-column)";
if (isAnonymousColumnSpanBlock())
return "RenderBlock (anonymous multi-column span)";
if (isAnonymousBlock())
return "RenderBlock (anonymous)";
if (isPseudoElement())
return "RenderBlock (generated)";
if (isAnonymous())
return "RenderBlock (generated)";
if (isRelPositioned())
return "RenderBlock (relative positioned)";
if (isStickyPositioned())
return "RenderBlock (sticky positioned)";
return "RenderBlock";
}
RenderBlock* RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderObject* parent, EDisplay display)
{
EDisplay newDisplay;
RenderBlock* newBox = 0;
if (display == BOX || display == INLINE_BOX) {
newBox = RenderDeprecatedFlexibleBox::createAnonymous(&parent->document());
newDisplay = BOX;
} else if (display == FLEX || display == INLINE_FLEX) {
newBox = RenderFlexibleBox::createAnonymous(&parent->document());
newDisplay = FLEX;
} else {
newBox = RenderBlockFlow::createAnonymous(&parent->document());
newDisplay = BLOCK;
}
RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), newDisplay);
newBox->setStyle(newStyle.release());
return newBox;
}
RenderBlockFlow* RenderBlock::createAnonymousColumnsWithParentRenderer(const RenderObject* parent)
{
RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK);
newStyle->inheritColumnPropertiesFrom(parent->style());
RenderBlockFlow* newBox = RenderBlockFlow::createAnonymous(&parent->document());
newBox->setStyle(newStyle.release());
return newBox;
}
RenderBlockFlow* RenderBlock::createAnonymousColumnSpanWithParentRenderer(const RenderObject* parent)
{
RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK);
newStyle->setColumnSpan(ColumnSpanAll);
RenderBlockFlow* newBox = RenderBlockFlow::createAnonymous(&parent->document());
newBox->setStyle(newStyle.release());
return newBox;
}
#ifndef NDEBUG
void RenderBlock::checkPositionedObjectsNeedLayout()
{
if (!gPositionedDescendantsMap)
return;
if (TrackedRendererListHashSet* positionedDescendantSet = positionedObjects()) {
TrackedRendererListHashSet::const_iterator end = positionedDescendantSet->end();
for (TrackedRendererListHashSet::const_iterator it = positionedDescendantSet->begin(); it != end; ++it) {
RenderBox* currBox = *it;
ASSERT(!currBox->needsLayout());
}
}
}
void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj) const
{
showRenderObject();
for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox())
root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, 1);
}
#endif
}