mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This caused us to lose our gn check certification. :( Turns out gn check was just ignoring all the header paths it didn't understand and so gn check passing for sky wasn't meaning much. I tried to straighten out some of the mess in this CL, but its going to take several more rounds of massaging before gn check passes again. On the bright side (almost) all of our headers are absolute now. Turns out my script (attached to the bug) didn't notice ../ includes but I'll fix that in the next patch. R=abarth@chromium.org BUG=435361 Review URL: https://codereview.chromium.org/746023002
356 lines
14 KiB
C++
356 lines
14 KiB
C++
/*
|
|
* 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 "sky/engine/config.h"
|
|
#include "sky/engine/core/rendering/RenderBlockFlow.h"
|
|
|
|
#include "sky/engine/core/frame/FrameView.h"
|
|
#include "sky/engine/core/frame/LocalFrame.h"
|
|
#include "sky/engine/core/frame/Settings.h"
|
|
#include "sky/engine/core/rendering/HitTestLocation.h"
|
|
#include "sky/engine/core/rendering/RenderLayer.h"
|
|
#include "sky/engine/core/rendering/RenderText.h"
|
|
#include "sky/engine/core/rendering/RenderView.h"
|
|
#include "sky/engine/core/rendering/line/LineWidth.h"
|
|
#include "sky/engine/platform/text/BidiTextRun.h"
|
|
|
|
namespace blink {
|
|
|
|
RenderBlockFlow::RenderBlockFlow(ContainerNode* node)
|
|
: RenderBlock(node)
|
|
{
|
|
}
|
|
|
|
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
|