blob: 6be08232c9bb0679330690aa70b600bf16b31dc7 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "core/rendering/RenderBlockFlow.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
#include "core/rendering/HitTestLocation.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderText.h"
#include "core/rendering/RenderView.h"
#include "core/rendering/line/LineWidth.h"
#include "platform/text/BidiTextRun.h"
namespace blink {
RenderBlockFlow::RenderBlockFlow(ContainerNode* node)
: RenderBlock(node)
{
setChildrenInline(true);
}
RenderBlockFlow::~RenderBlockFlow()
{
}
RenderBlockFlow* RenderBlockFlow::createAnonymous(Document* document)
{
RenderBlockFlow* renderer = new RenderBlockFlow(0);
renderer->setDocumentForAnonymous(document);
return renderer;
}
bool RenderBlockFlow::updateLogicalWidthAndColumnWidth()
{
return RenderBlock::updateLogicalWidthAndColumnWidth();
}
void RenderBlockFlow::layoutBlock(bool relayoutChildren)
{
ASSERT(needsLayout());
ASSERT(isInlineBlock() || !isInline());
if (!relayoutChildren && simplifiedLayout())
return;
SubtreeLayoutScope layoutScope(*this);
layoutBlockFlow(relayoutChildren, layoutScope);
updateLayerTransformAfterLayout();
// Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
// we overflow or not.
updateScrollInfoAfterLayout();
if (m_paintInvalidationLogicalTop != m_paintInvalidationLogicalBottom)
setShouldInvalidateOverflowForPaint(true);
clearNeedsLayout();
}
inline void RenderBlockFlow::layoutBlockFlow(bool relayoutChildren, SubtreeLayoutScope& layoutScope)
{
LayoutUnit oldLeft = logicalLeft();
bool logicalWidthChanged = updateLogicalWidthAndColumnWidth();
relayoutChildren |= logicalWidthChanged;
LayoutState state(*this, locationOffset(), logicalWidthChanged);
LayoutUnit beforeEdge = borderBefore() + paddingBefore();
LayoutUnit afterEdge = borderAfter() + paddingAfter();
LayoutUnit previousHeight = logicalHeight();
setLogicalHeight(beforeEdge);
m_paintInvalidationLogicalTop = 0;
m_paintInvalidationLogicalBottom = 0;
if (!firstChild() && !isAnonymousBlock())
setChildrenInline(true);
if (childrenInline())
layoutInlineChildren(relayoutChildren, m_paintInvalidationLogicalTop, m_paintInvalidationLogicalBottom, afterEdge);
else
layoutBlockChildren(relayoutChildren, layoutScope, beforeEdge, afterEdge);
LayoutUnit oldClientAfterEdge = clientLogicalBottom();
updateLogicalHeight();
if (previousHeight != logicalHeight())
relayoutChildren = true;
layoutPositionedObjects(relayoutChildren || isDocumentElement(), oldLeft != logicalLeft() ? ForcedLayoutAfterContainingBlockMoved : DefaultLayout);
// Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway).
computeOverflow(oldClientAfterEdge);
}
void RenderBlockFlow::determineLogicalLeftPositionForChild(RenderBox* child)
{
LayoutUnit startPosition = borderStart() + paddingStart();
LayoutUnit totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + availableLogicalWidth();
LayoutUnit childMarginStart = marginStartForChild(child);
LayoutUnit newPosition = startPosition + childMarginStart;
// If the child has an offset from the content edge to avoid floats then use that, otherwise let any negative
// margin pull it back over the content edge or any positive margin push it out.
if (child->style()->marginStartUsing(style()).isAuto())
newPosition = std::max(newPosition, childMarginStart);
child->setX(style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child));
}
void RenderBlockFlow::layoutBlockChild(RenderBox* child)
{
child->computeAndSetBlockDirectionMargins(this);
LayoutUnit marginBefore = marginBeforeForChild(child);
child->setY(logicalHeight() + marginBefore);
child->layoutIfNeeded();
determineLogicalLeftPositionForChild(child);
setLogicalHeight(logicalHeight() + marginBefore + logicalHeightForChild(child) + marginAfterForChild(child));
}
void RenderBlockFlow::layoutBlockChildren(bool relayoutChildren, SubtreeLayoutScope& layoutScope, LayoutUnit beforeEdge, LayoutUnit afterEdge)
{
dirtyForLayoutFromPercentageHeightDescendants(layoutScope);
RenderBox* next = firstChildBox();
RenderBox* lastNormalFlowChild = 0;
while (next) {
RenderBox* child = next;
next = child->nextSiblingBox();
// FIXME: this should only be set from clearNeedsLayout crbug.com/361250
child->setLayoutDidGetCalled(true);
updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child);
if (child->isOutOfFlowPositioned()) {
child->containingBlock()->insertPositionedObject(child);
adjustPositionedBlock(child);
continue;
}
// Lay out the child.
layoutBlockChild(child);
lastNormalFlowChild = child;
}
// Negative margins can cause our height to shrink below our minimal height (border/padding).
// If this happens, ensure that the computed height is increased to the minimal height.
setLogicalHeight(std::max(logicalHeight() + afterEdge, beforeEdge + afterEdge));
}
void RenderBlockFlow::adjustPositionedBlock(RenderBox* child)
{
bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition();
LayoutUnit logicalTop = logicalHeight();
updateStaticInlinePositionForChild(child);
RenderLayer* childLayer = child->layer();
if (childLayer->staticBlockPosition() != logicalTop) {
childLayer->setStaticBlockPosition(logicalTop);
if (hasStaticBlockPosition)
child->setChildNeedsLayout(MarkOnlyThis);
}
}
RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox()
{
RootInlineBox* rootBox = createRootInlineBox();
m_lineBoxes.appendLineBox(rootBox);
return rootBox;
}
void RenderBlockFlow::deleteLineBoxTree()
{
m_lineBoxes.deleteLineBoxTree();
}
void RenderBlockFlow::updateStaticInlinePositionForChild(RenderBox* child)
{
if (child->style()->isOriginalDisplayInlineType())
setStaticInlinePositionForChild(child, startAlignedOffsetForLine(false));
else
setStaticInlinePositionForChild(child, startOffsetForContent());
}
void RenderBlockFlow::setStaticInlinePositionForChild(RenderBox* child, LayoutUnit inlinePosition)
{
child->layer()->setStaticInlinePosition(inlinePosition);
}
void RenderBlockFlow::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
RenderBlock::addChild(newChild, beforeChild);
}
void RenderBlockFlow::invalidatePaintForOverflow()
{
// FIXME: We could tighten up the left and right invalidation points if we let layoutInlineChildren fill them in based off the particular lines
// it had to lay out. We wouldn't need the hasOverflowClip() hack in that case either.
LayoutUnit paintInvalidationLogicalLeft = logicalLeftVisualOverflow();
LayoutUnit paintInvalidationLogicalRight = logicalRightVisualOverflow();
if (hasOverflowClip()) {
// If we have clipped overflow, we should use layout overflow as well, since visual overflow from lines didn't propagate to our block's overflow.
// Note the old code did this as well but even for overflow:visible. The addition of hasOverflowClip() at least tightens up the hack a bit.
// layoutInlineChildren should be patched to compute the entire paint invalidation rect.
paintInvalidationLogicalLeft = std::min(paintInvalidationLogicalLeft, logicalLeftLayoutOverflow());
paintInvalidationLogicalRight = std::max(paintInvalidationLogicalRight, logicalRightLayoutOverflow());
}
LayoutRect paintInvalidationRect = LayoutRect(paintInvalidationLogicalLeft, m_paintInvalidationLogicalTop, paintInvalidationLogicalRight - paintInvalidationLogicalLeft, m_paintInvalidationLogicalBottom - m_paintInvalidationLogicalTop);
if (hasOverflowClip()) {
// Adjust the paint invalidation rect for scroll offset
paintInvalidationRect.move(-scrolledContentOffset());
// Don't allow this rect to spill out of our overflow box.
paintInvalidationRect.intersect(LayoutRect(LayoutPoint(), size()));
}
// Make sure the rect is still non-empty after intersecting for overflow above
if (!paintInvalidationRect.isEmpty()) {
// Hits in media/event-attributes.html
DisableCompositingQueryAsserts disabler;
invalidatePaintRectangle(paintInvalidationRect); // We need to do a partial paint invalidation of our content.
}
m_paintInvalidationLogicalTop = 0;
m_paintInvalidationLogicalBottom = 0;
}
GapRects RenderBlockFlow::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo)
{
GapRects result;
bool containsStart = selectionState() == SelectionStart || selectionState() == SelectionBoth;
if (!firstLineBox()) {
if (containsStart) {
// Go ahead and update our lastLogicalTop to be the bottom of the block. <hr>s or empty blocks with height can trip this
// case.
lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalHeight();
lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight());
lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight());
}
return result;
}
RootInlineBox* lastSelectedLine = 0;
RootInlineBox* curr;
for (curr = firstRootBox(); curr && !curr->hasSelectedChildren(); curr = curr->nextRootBox()) { }
// Now paint the gaps for the lines.
for (; curr && curr->hasSelectedChildren(); curr = curr->nextRootBox()) {
LayoutUnit selTop = curr->selectionTopAdjustedForPrecedingBlock();
LayoutUnit selHeight = curr->selectionHeightAdjustedForPrecedingBlock();
if (!containsStart && !lastSelectedLine && selectionState() != SelectionStart && selectionState() != SelectionBoth) {
result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop,
lastLogicalLeft, lastLogicalRight, selTop, paintInfo));
}
LayoutRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), selTop + selHeight);
logicalRect.move(offsetFromRootBlock);
LayoutRect physicalRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect);
if (!paintInfo || (physicalRect.y() < paintInfo->rect.maxY() && physicalRect.maxY() > paintInfo->rect.y()))
result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, paintInfo));
lastSelectedLine = curr;
}
if (containsStart && !lastSelectedLine) {
// VisibleSelection must start just after our last line.
lastSelectedLine = lastRootBox();
}
if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) {
// Go ahead and update our lastY to be the bottom of the last selected line.
lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + lastSelectedLine->selectionBottom();
lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom());
lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom());
}
return result;
}
LayoutUnit RenderBlockFlow::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position)
{
LayoutUnit logicalLeft = logicalLeftOffsetForLine(false);
if (logicalLeft == logicalLeftOffsetForContent())
return RenderBlock::logicalLeftSelectionOffset(rootBlock, position);
RenderBlock* cb = this;
while (cb != rootBlock) {
logicalLeft += cb->logicalLeft();
cb = cb->containingBlock();
}
return logicalLeft;
}
LayoutUnit RenderBlockFlow::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position)
{
LayoutUnit logicalRight = logicalRightOffsetForLine(false);
if (logicalRight == logicalRightOffsetForContent())
return RenderBlock::logicalRightSelectionOffset(rootBlock, position);
RenderBlock* cb = this;
while (cb != rootBlock) {
logicalRight += cb->logicalLeft();
cb = cb->containingBlock();
}
return logicalRight;
}
RootInlineBox* RenderBlockFlow::createRootInlineBox()
{
return new RootInlineBox(*this);
}
} // namespace blink