| /* |
| * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "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 HOLDER 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/layout/LayoutFlowThread.h" |
| |
| #include "core/layout/LayoutMultiColumnSet.h" |
| #include "core/layout/LayoutView.h" |
| |
| namespace blink { |
| |
| LayoutFlowThread::LayoutFlowThread() |
| : LayoutBlockFlow(nullptr) |
| , m_columnSetsInvalidated(false) |
| , m_pageLogicalSizeChanged(false) |
| { |
| } |
| |
| LayoutFlowThread* LayoutFlowThread::locateFlowThreadContainingBlockOf(const LayoutObject& descendant) |
| { |
| ASSERT(descendant.isInsideFlowThread()); |
| LayoutObject* curr = const_cast<LayoutObject*>(&descendant); |
| while (curr) { |
| if (curr->isSVG() && !curr->isSVGRoot()) |
| return nullptr; |
| if (curr->isLayoutFlowThread()) |
| return toLayoutFlowThread(curr); |
| LayoutObject* container = curr->container(); |
| curr = curr->parent(); |
| while (curr != container) { |
| if (curr->isLayoutFlowThread()) { |
| // The nearest ancestor flow thread isn't in our containing block chain. Then we |
| // aren't really part of any flow thread, and we should stop looking. This happens |
| // when there are out-of-flow objects or column spanners. |
| return nullptr; |
| } |
| curr = curr->parent(); |
| } |
| } |
| return nullptr; |
| } |
| |
| void LayoutFlowThread::removeColumnSetFromThread(LayoutMultiColumnSet* columnSet) |
| { |
| ASSERT(columnSet); |
| m_multiColumnSetList.remove(columnSet); |
| invalidateColumnSets(); |
| // Clear the interval tree right away, instead of leaving it around with dead objects. Not that |
| // anyone _should_ try to access the interval tree when the column sets are marked as invalid, |
| // but this is actually possible if other parts of the engine has bugs that cause us to not lay |
| // out everything that was marked for layout, so that LayoutObject::assertLaidOut() (and a LOT |
| // of other assertions) fails. |
| m_multiColumnSetIntervalTree.clear(); |
| } |
| |
| void LayoutFlowThread::invalidateColumnSets() |
| { |
| if (m_columnSetsInvalidated) { |
| ASSERT(selfNeedsLayout()); |
| return; |
| } |
| |
| setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::ColumnsChanged); |
| |
| m_columnSetsInvalidated = true; |
| } |
| |
| void LayoutFlowThread::validateColumnSets() |
| { |
| m_columnSetsInvalidated = false; |
| updateLogicalWidth(); // Called to get the maximum logical width for the columnSet. |
| generateColumnSetIntervalTree(); |
| } |
| |
| void LayoutFlowThread::mapToVisibleRectInContainerSpace(const LayoutBoxModelObject* paintInvalidationContainer, LayoutRect& rect, const PaintInvalidationState* paintInvalidationState) const |
| { |
| ASSERT(paintInvalidationContainer != this); // A flow thread should never be an invalidation container. |
| // |rect| is a layout rectangle, where the block direction coordinate is flipped for writing |
| // mode. fragmentsBoundingBox(), on the other hand, works on physical rectangles, so we need to |
| // flip the rectangle before and after calling it. |
| flipForWritingMode(rect); |
| rect = fragmentsBoundingBox(rect); |
| flipForWritingMode(rect); |
| LayoutBlockFlow::mapToVisibleRectInContainerSpace(paintInvalidationContainer, rect, paintInvalidationState); |
| } |
| |
| void LayoutFlowThread::layout() |
| { |
| m_pageLogicalSizeChanged = m_columnSetsInvalidated && everHadLayout(); |
| LayoutBlockFlow::layout(); |
| m_pageLogicalSizeChanged = false; |
| } |
| |
| void LayoutFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
| { |
| computedValues.m_position = logicalTop; |
| computedValues.m_extent = 0; |
| |
| for (LayoutMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) { |
| LayoutMultiColumnSet* columnSet = *iter; |
| computedValues.m_extent += columnSet->logicalHeightInFlowThread(); |
| } |
| } |
| |
| bool LayoutFlowThread::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) |
| { |
| if (hitTestAction == HitTestBlockBackground) |
| return false; |
| return LayoutBlockFlow::nodeAtPoint(result, locationInContainer, accumulatedOffset, hitTestAction); |
| } |
| |
| LayoutUnit LayoutFlowThread::pageLogicalHeightForOffset(LayoutUnit offset) |
| { |
| LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(offset); |
| if (!columnSet) |
| return LayoutUnit(); |
| |
| return columnSet->pageLogicalHeightForOffset(offset); |
| } |
| |
| LayoutUnit LayoutFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) |
| { |
| LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(offset); |
| if (!columnSet) |
| return LayoutUnit(); |
| |
| return columnSet->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule); |
| } |
| |
| void LayoutFlowThread::generateColumnSetIntervalTree() |
| { |
| // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle. |
| m_multiColumnSetIntervalTree.clear(); |
| m_multiColumnSetIntervalTree.initIfNeeded(); |
| for (auto columnSet : m_multiColumnSetList) |
| m_multiColumnSetIntervalTree.add(MultiColumnSetIntervalTree::createInterval(columnSet->logicalTopInFlowThread(), columnSet->logicalBottomInFlowThread(), columnSet)); |
| } |
| |
| LayoutUnit LayoutFlowThread::nextLogicalTopForUnbreakableContent(LayoutUnit flowThreadOffset, LayoutUnit contentLogicalHeight) const |
| { |
| LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(flowThreadOffset); |
| if (!columnSet) |
| return flowThreadOffset; |
| return columnSet->nextLogicalTopForUnbreakableContent(flowThreadOffset, contentLogicalHeight); |
| } |
| |
| void LayoutFlowThread::collectLayerFragments(PaintLayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRectInFlowThread) |
| { |
| ASSERT(!m_columnSetsInvalidated); |
| |
| LayoutRect dirtyRectInMulticolContainer(dirtyRectInFlowThread); |
| dirtyRectInMulticolContainer.moveBy(location()); |
| |
| for (LayoutMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) { |
| LayoutMultiColumnSet* columnSet = *iter; |
| columnSet->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRectInMulticolContainer); |
| } |
| } |
| |
| LayoutRect LayoutFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox) const |
| { |
| ASSERT(!RuntimeEnabledFeatures::slimmingPaintV2Enabled() || !m_columnSetsInvalidated); |
| |
| LayoutRect result; |
| for (auto* columnSet : m_multiColumnSetList) |
| result.unite(columnSet->fragmentsBoundingBox(layerBoundingBox)); |
| |
| return result; |
| } |
| |
| void LayoutFlowThread::MultiColumnSetSearchAdapter::collectIfNeeded(const MultiColumnSetInterval& interval) |
| { |
| if (m_result) |
| return; |
| if (interval.low() <= m_offset && interval.high() > m_offset) |
| m_result = interval.data(); |
| } |
| |
| } // namespace blink |