| /* |
| * Copyright (C) 2012 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 "FlowThreadController.h" |
| |
| #include "NamedFlowCollection.h" |
| #include "RenderFlowThread.h" |
| #include "RenderLayer.h" |
| #include "RenderNamedFlowThread.h" |
| #include "StyleInheritedData.h" |
| #include "WebKitNamedFlow.h" |
| #include <wtf/text/AtomicString.h> |
| |
| namespace WebCore { |
| |
| FlowThreadController::FlowThreadController(RenderView* view) |
| : m_view(view) |
| , m_isRenderNamedFlowThreadOrderDirty(false) |
| , m_flowThreadsWithAutoLogicalHeightRegions(0) |
| { |
| } |
| |
| FlowThreadController::~FlowThreadController() |
| { |
| } |
| |
| RenderNamedFlowThread& FlowThreadController::ensureRenderFlowThreadWithName(const AtomicString& name) |
| { |
| if (!m_renderNamedFlowThreadList) |
| m_renderNamedFlowThreadList = std::make_unique<RenderNamedFlowThreadList>(); |
| else { |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| if (flowRenderer->flowThreadName() == name) |
| return *flowRenderer; |
| } |
| } |
| |
| NamedFlowCollection& namedFlows = m_view->document().namedFlows(); |
| |
| // Sanity check for the absence of a named flow in the "CREATED" state with the same name. |
| ASSERT(!namedFlows.flowByName(name)); |
| |
| auto flowRenderer = new RenderNamedFlowThread(m_view->document(), RenderFlowThread::createFlowThreadStyle(&m_view->style()), namedFlows.ensureFlowWithName(name)); |
| flowRenderer->initializeStyle(); |
| m_renderNamedFlowThreadList->add(flowRenderer); |
| |
| // Keep the flow renderer as a child of RenderView. |
| m_view->addChild(flowRenderer); |
| |
| setIsRenderNamedFlowThreadOrderDirty(true); |
| |
| return *flowRenderer; |
| } |
| |
| void FlowThreadController::styleDidChange() |
| { |
| RenderStyle& viewStyle = m_view->style(); |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) |
| flowRenderer->setStyle(RenderFlowThread::createFlowThreadStyle(&viewStyle)); |
| } |
| |
| void FlowThreadController::layoutRenderNamedFlowThreads() |
| { |
| updateFlowThreadsChainIfNecessary(); |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) |
| flowRenderer->layoutIfNeeded(); |
| } |
| |
| void FlowThreadController::registerNamedFlowContentElement(Element& contentElement, RenderNamedFlowThread& namedFlow) |
| { |
| ASSERT(!m_mapNamedFlowContentElement.contains(&contentElement)); |
| ASSERT(!namedFlow.hasContentElement(contentElement)); |
| m_mapNamedFlowContentElement.add(&contentElement, &namedFlow); |
| namedFlow.registerNamedFlowContentElement(contentElement); |
| } |
| |
| void FlowThreadController::unregisterNamedFlowContentElement(Element& contentElement) |
| { |
| auto it = m_mapNamedFlowContentElement.find(&contentElement); |
| ASSERT(it != m_mapNamedFlowContentElement.end()); |
| ASSERT(it->value); |
| ASSERT(it->value->hasContentElement(contentElement)); |
| it->value->unregisterNamedFlowContentElement(contentElement); |
| m_mapNamedFlowContentElement.remove(&contentElement); |
| } |
| |
| void FlowThreadController::updateFlowThreadsChainIfNecessary() |
| { |
| ASSERT(m_renderNamedFlowThreadList); |
| ASSERT(isAutoLogicalHeightRegionsCountConsistent()); |
| |
| // Remove the left-over flow threads. |
| RenderNamedFlowThreadList toRemoveList; |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| if (flowRenderer->isMarkedForDestruction()) |
| toRemoveList.add(flowRenderer); |
| } |
| |
| if (toRemoveList.size() > 0) |
| setIsRenderNamedFlowThreadOrderDirty(true); |
| |
| for (auto& flowRenderer : toRemoveList) { |
| m_renderNamedFlowThreadList->remove(flowRenderer); |
| flowRenderer->destroy(); |
| } |
| |
| if (isRenderNamedFlowThreadOrderDirty()) { |
| // Arrange the thread list according to dependencies. |
| RenderNamedFlowThreadList sortedList; |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| if (sortedList.contains(flowRenderer)) |
| continue; |
| flowRenderer->pushDependencies(sortedList); |
| sortedList.add(flowRenderer); |
| } |
| m_renderNamedFlowThreadList->swap(sortedList); |
| setIsRenderNamedFlowThreadOrderDirty(false); |
| } |
| } |
| |
| bool FlowThreadController::updateFlowThreadsNeedingLayout() |
| { |
| bool needsTwoPassLayout = false; |
| |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| ASSERT(!flowRenderer->needsTwoPhasesLayout()); |
| ASSERT(flowRenderer->inMeasureContentLayoutPhase()); |
| if (flowRenderer->needsLayout() && flowRenderer->hasAutoLogicalHeightRegions()) |
| needsTwoPassLayout = true; |
| } |
| |
| if (needsTwoPassLayout) |
| resetFlowThreadsWithAutoHeightRegions(); |
| |
| return needsTwoPassLayout; |
| } |
| |
| bool FlowThreadController::updateFlowThreadsNeedingTwoStepLayout() |
| { |
| bool needsTwoPassLayout = false; |
| |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| if (flowRenderer->needsTwoPhasesLayout()) { |
| needsTwoPassLayout = true; |
| break; |
| } |
| } |
| |
| if (needsTwoPassLayout) |
| resetFlowThreadsWithAutoHeightRegions(); |
| |
| return needsTwoPassLayout; |
| } |
| |
| void FlowThreadController::resetFlowThreadsWithAutoHeightRegions() |
| { |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| if (flowRenderer->hasAutoLogicalHeightRegions()) { |
| flowRenderer->markAutoLogicalHeightRegionsForLayout(); |
| flowRenderer->invalidateRegions(); |
| } |
| } |
| } |
| |
| void FlowThreadController::updateFlowThreadsIntoConstrainedPhase() |
| { |
| // Walk the flow chain in reverse order to update the auto-height regions and compute correct sizes for the containing regions. Only after this we can |
| // set the flow in the constrained layout phase. |
| for (auto iter = m_renderNamedFlowThreadList->rbegin(), end = m_renderNamedFlowThreadList->rend(); iter != end; ++iter) { |
| RenderNamedFlowThread* flowRenderer = *iter; |
| ASSERT(!flowRenderer->hasRegions() || flowRenderer->hasValidRegionInfo()); |
| flowRenderer->layoutIfNeeded(); |
| if (flowRenderer->hasAutoLogicalHeightRegions()) { |
| ASSERT(flowRenderer->needsTwoPhasesLayout()); |
| flowRenderer->markAutoLogicalHeightRegionsForLayout(); |
| } |
| flowRenderer->setLayoutPhase(RenderFlowThread::LayoutPhaseConstrained); |
| flowRenderer->clearNeedsTwoPhasesLayout(); |
| } |
| } |
| |
| void FlowThreadController::updateFlowThreadsIntoOverflowPhase() |
| { |
| for (auto iter = m_renderNamedFlowThreadList->rbegin(), end = m_renderNamedFlowThreadList->rend(); iter != end; ++iter) { |
| RenderNamedFlowThread* flowRenderer = *iter; |
| ASSERT(!flowRenderer->hasRegions() || flowRenderer->hasValidRegionInfo()); |
| ASSERT(!flowRenderer->needsTwoPhasesLayout()); |
| |
| // In the overflow computation phase the flow threads start in the constrained phase even though optimizations didn't set the state before. |
| flowRenderer->setLayoutPhase(RenderFlowThread::LayoutPhaseConstrained); |
| |
| flowRenderer->layoutIfNeeded(); |
| flowRenderer->markRegionsForOverflowLayoutIfNeeded(); |
| flowRenderer->setLayoutPhase(RenderFlowThread::LayoutPhaseOverflow); |
| } |
| } |
| |
| void FlowThreadController::updateFlowThreadsIntoMeasureContentPhase() |
| { |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| ASSERT(flowRenderer->inFinalLayoutPhase()); |
| |
| flowRenderer->dispatchNamedFlowEvents(); |
| flowRenderer->setLayoutPhase(RenderFlowThread::LayoutPhaseMeasureContent); |
| } |
| } |
| |
| void FlowThreadController::updateFlowThreadsIntoFinalPhase() |
| { |
| for (auto iter = m_renderNamedFlowThreadList->rbegin(), end = m_renderNamedFlowThreadList->rend(); iter != end; ++iter) { |
| RenderNamedFlowThread* flowRenderer = *iter; |
| flowRenderer->layoutIfNeeded(); |
| if (flowRenderer->needsTwoPhasesLayout()) { |
| flowRenderer->markRegionsForOverflowLayoutIfNeeded(); |
| flowRenderer->clearNeedsTwoPhasesLayout(); |
| } |
| flowRenderer->setLayoutPhase(RenderFlowThread::LayoutPhaseFinal); |
| } |
| } |
| |
| void FlowThreadController::updateFlowThreadsLayerToRegionMappingsIfNeeded() |
| { |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) |
| flowRenderer->updateAllLayerToRegionMappingsIfNeeded(); |
| } |
| |
| void FlowThreadController::updateNamedFlowsLayerListsIfNeeded() |
| { |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) |
| flowRenderer->layer()->updateLayerListsIfNeeded(); |
| } |
| |
| static inline bool compareZIndexFlowThreadController(RenderLayer* first, RenderLayer* second) |
| { |
| return first->zIndex() < second->zIndex(); |
| } |
| |
| // Collect the fixed positioned layers that have the named flows as containing block |
| // These layers are painted and hit-tested starting from RenderView not from regions. |
| void FlowThreadController::collectFixedPositionedLayers(Vector<RenderLayer*>& fixedPosLayers) const |
| { |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| // If the named flow does not have any regions attached, a fixed element should not be |
| // displayed even if the fixed element is positioned/sized by the viewport. |
| if (!flowRenderer->hasRegions()) |
| continue; |
| |
| RenderLayer* flowThreadLayer = flowRenderer->layer(); |
| if (Vector<RenderLayer*>* negZOrderList = flowThreadLayer->negZOrderList()) { |
| for (size_t i = 0, size = negZOrderList->size(); i < size; ++i) { |
| RenderLayer* currLayer = negZOrderList->at(i); |
| if (currLayer->renderer().style().position() != FixedPosition) |
| continue; |
| fixedPosLayers.append(currLayer); |
| } |
| } |
| |
| if (Vector<RenderLayer*>* posZOrderList = flowThreadLayer->posZOrderList()) { |
| for (size_t i = 0, size = posZOrderList->size(); i < size; ++i) { |
| RenderLayer* currLayer = posZOrderList->at(i); |
| if (currLayer->renderer().style().position() != FixedPosition) |
| continue; |
| fixedPosLayers.append(currLayer); |
| } |
| } |
| } |
| |
| std::stable_sort(fixedPosLayers.begin(), fixedPosLayers.end(), compareZIndexFlowThreadController); |
| } |
| |
| #ifndef NDEBUG |
| bool FlowThreadController::isAutoLogicalHeightRegionsCountConsistent() const |
| { |
| if (!hasRenderNamedFlowThreads()) |
| return !hasFlowThreadsWithAutoLogicalHeightRegions(); |
| |
| for (auto& flowRenderer : *m_renderNamedFlowThreadList) { |
| if (!flowRenderer->isAutoLogicalHeightRegionsCountConsistent()) |
| return false; |
| } |
| |
| return true; |
| } |
| #endif |
| |
| } // namespace WebCore |