diff options
| author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
|---|---|---|
| committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
| commit | 40736c5763bf61337c8c14e16d8587db021a87d4 (patch) | |
| tree | b17a9c00042ad89cb1308e2484491799aa14e9f8 /Source/WebCore/rendering/RenderFlowThread.cpp | |
| download | qtwebkit-40736c5763bf61337c8c14e16d8587db021a87d4.tar.gz | |
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Source/WebCore/rendering/RenderFlowThread.cpp')
| -rw-r--r-- | Source/WebCore/rendering/RenderFlowThread.cpp | 803 |
1 files changed, 803 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/RenderFlowThread.cpp b/Source/WebCore/rendering/RenderFlowThread.cpp new file mode 100644 index 000000000..967c5d16b --- /dev/null +++ b/Source/WebCore/rendering/RenderFlowThread.cpp @@ -0,0 +1,803 @@ +/* + * Copyright 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 "RenderFlowThread.h" + +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "Node.h" +#include "PaintInfo.h" +#include "RenderBoxRegionInfo.h" +#include "RenderLayer.h" +#include "RenderRegion.h" +#include "RenderView.h" +#include "TransformState.h" +#include "WebKitNamedFlow.h" + +namespace WebCore { + +RenderFlowThread::RenderFlowThread(Node* node, const AtomicString& flowThread) + : RenderBlock(node) + , m_flowThread(flowThread) + , m_hasValidRegions(false) + , m_regionsInvalidated(false) + , m_regionsHaveUniformLogicalWidth(true) + , m_regionsHaveUniformLogicalHeight(true) +{ + setIsAnonymous(false); + setInRenderFlowThread(); +} + +void RenderFlowThread::clearRenderRegionRangeMap() +{ + deleteAllValues(m_regionRangeMap); + m_regionRangeMap.clear(); +} + +RenderFlowThread::~RenderFlowThread() +{ + clearRenderRegionRangeMap(); +} + +PassRefPtr<RenderStyle> RenderFlowThread::createFlowThreadStyle(RenderStyle* parentStyle) +{ + RefPtr<RenderStyle> newStyle(RenderStyle::create()); + newStyle->inheritFrom(parentStyle); + newStyle->setDisplay(BLOCK); + newStyle->setPosition(AbsolutePosition); + newStyle->setZIndex(0); + newStyle->setLeft(Length(0, Fixed)); + newStyle->setTop(Length(0, Fixed)); + newStyle->setWidth(Length(100, Percent)); + newStyle->setHeight(Length(100, Percent)); + newStyle->font().update(0); + + return newStyle.release(); +} + +void RenderFlowThread::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (oldStyle && oldStyle->writingMode() != style()->writingMode()) + m_regionsInvalidated = true; +} + +RenderObject* RenderFlowThread::nextRendererForNode(Node* node) const +{ + FlowThreadChildList::const_iterator it = m_flowThreadChildList.begin(); + FlowThreadChildList::const_iterator end = m_flowThreadChildList.end(); + + for (; it != end; ++it) { + RenderObject* child = *it; + ASSERT(child->node()); + unsigned short position = node->compareDocumentPosition(child->node()); + if (position & Node::DOCUMENT_POSITION_FOLLOWING) + return child; + } + + return 0; +} + +RenderObject* RenderFlowThread::previousRendererForNode(Node* node) const +{ + if (m_flowThreadChildList.isEmpty()) + return 0; + + FlowThreadChildList::const_iterator begin = m_flowThreadChildList.begin(); + FlowThreadChildList::const_iterator end = m_flowThreadChildList.end(); + FlowThreadChildList::const_iterator it = end; + + do { + --it; + RenderObject* child = *it; + ASSERT(child->node()); + unsigned short position = node->compareDocumentPosition(child->node()); + if (position & Node::DOCUMENT_POSITION_PRECEDING) + return child; + } while (it != begin); + + return 0; +} + +void RenderFlowThread::addFlowChild(RenderObject* newChild, RenderObject* beforeChild) +{ + // The child list is used to sort the flow thread's children render objects + // based on their corresponding nodes DOM order. The list is needed to avoid searching the whole DOM. + + // Do not add anonymous objects. + if (!newChild->node()) + return; + + if (beforeChild) + m_flowThreadChildList.insertBefore(beforeChild, newChild); + else + m_flowThreadChildList.add(newChild); +} + +void RenderFlowThread::removeFlowChild(RenderObject* child) +{ + m_flowThreadChildList.remove(child); +} + +// Compare two regions to determine in which one the content should flow first. +// The function returns true if the first passed region is "less" than the second passed region. +// If the first region appears before second region in DOM, +// the first region is "less" than the second region. +// If the first region is "less" than the second region, the first region receives content before second region. +static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRegion* secondRegion) +{ + ASSERT(firstRegion); + ASSERT(secondRegion); + + // If the regions have the same region-index, compare their position in dom. + ASSERT(firstRegion->node()); + ASSERT(secondRegion->node()); + + unsigned short position = firstRegion->node()->compareDocumentPosition(secondRegion->node()); + return (position & Node::DOCUMENT_POSITION_FOLLOWING); +} + +bool RenderFlowThread::dependsOn(RenderFlowThread* otherRenderFlowThread) const +{ + if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread)) + return true; + + // Recursively traverse the m_layoutBeforeThreadsSet. + RenderFlowThreadCountedSet::const_iterator iterator = m_layoutBeforeThreadsSet.begin(); + RenderFlowThreadCountedSet::const_iterator end = m_layoutBeforeThreadsSet.end(); + for (; iterator != end; ++iterator) { + const RenderFlowThread* beforeFlowThread = (*iterator).first; + if (beforeFlowThread->dependsOn(otherRenderFlowThread)) + return true; + } + + return false; +} + +void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion) +{ + ASSERT(renderRegion); + if (m_regionList.isEmpty()) + m_regionList.add(renderRegion); + else { + // Find the first region "greater" than renderRegion. + RenderRegionList::iterator it = m_regionList.begin(); + while (it != m_regionList.end() && !compareRenderRegions(renderRegion, *it)) + ++it; + m_regionList.insertBefore(it, renderRegion); + } + + ASSERT(!renderRegion->isValid()); + if (renderRegion->parentFlowThread()) { + if (renderRegion->parentFlowThread()->dependsOn(this)) { + // Register ourself to get a notification when the state changes. + renderRegion->parentFlowThread()->m_observerThreadsSet.add(this); + return; + } + + addDependencyOnFlowThread(renderRegion->parentFlowThread()); + } + + renderRegion->setIsValid(true); + + invalidateRegions(); +} + +void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion) +{ + ASSERT(renderRegion); + clearRenderRegionRangeMap(); + m_regionList.remove(renderRegion); + + if (renderRegion->parentFlowThread()) { + if (!renderRegion->isValid()) { + renderRegion->parentFlowThread()->m_observerThreadsSet.remove(this); + // No need to invalidate the regions rectangles. The removed region + // was not taken into account. Just return here. + return; + } + removeDependencyOnFlowThread(renderRegion->parentFlowThread()); + } + + invalidateRegions(); +} + +void RenderFlowThread::checkInvalidRegions() +{ + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + // The only reason a region would be invalid is because it has a parent flow thread. + ASSERT(region->isValid() || region->parentFlowThread()); + if (region->isValid() || region->parentFlowThread()->dependsOn(this)) + continue; + + region->parentFlowThread()->m_observerThreadsSet.remove(this); + addDependencyOnFlowThread(region->parentFlowThread()); + region->setIsValid(true); + invalidateRegions(); + } + + if (m_observerThreadsSet.isEmpty()) + return; + + // Notify all the flow threads that were dependent on this flow. + + // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions. + Vector<RenderFlowThread*> observers; + copyToVector(m_observerThreadsSet, observers); + + for (size_t i = 0; i < observers.size(); ++i) { + RenderFlowThread* flowThread = observers.at(i); + flowThread->checkInvalidRegions(); + } +} + +void RenderFlowThread::addDependencyOnFlowThread(RenderFlowThread* otherFlowThread) +{ + std::pair<RenderFlowThreadCountedSet::iterator, bool> result = m_layoutBeforeThreadsSet.add(otherFlowThread); + if (result.second) { + // This is the first time we see this dependency. Make sure we recalculate all the dependencies. + view()->setIsRenderFlowThreadOrderDirty(true); + } +} + +void RenderFlowThread::removeDependencyOnFlowThread(RenderFlowThread* otherFlowThread) +{ + bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread); + if (removed) { + checkInvalidRegions(); + view()->setIsRenderFlowThreadOrderDirty(true); + } +} + +void RenderFlowThread::pushDependencies(RenderFlowThreadList& list) +{ + for (RenderFlowThreadCountedSet::iterator iter = m_layoutBeforeThreadsSet.begin(); iter != m_layoutBeforeThreadsSet.end(); ++iter) { + RenderFlowThread* flowThread = (*iter).first; + if (list.contains(flowThread)) + continue; + flowThread->pushDependencies(list); + list.add(flowThread); + } +} + +class CurrentRenderFlowThreadMaintainer { + WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadMaintainer); +public: + CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread) + : m_renderFlowThread(renderFlowThread) + { + RenderView* view = m_renderFlowThread->view(); + ASSERT(!view->currentRenderFlowThread()); + view->setCurrentRenderFlowThread(m_renderFlowThread); + } + ~CurrentRenderFlowThreadMaintainer() + { + RenderView* view = m_renderFlowThread->view(); + ASSERT(view->currentRenderFlowThread() == m_renderFlowThread); + view->setCurrentRenderFlowThread(0); + } +private: + RenderFlowThread* m_renderFlowThread; +}; + +void RenderFlowThread::layout() +{ + bool regionsChanged = m_regionsInvalidated && everHadLayout(); + if (m_regionsInvalidated) { + m_regionsInvalidated = false; + m_hasValidRegions = false; + m_regionsHaveUniformLogicalWidth = true; + m_regionsHaveUniformLogicalHeight = true; + clearRenderRegionRangeMap(); + LayoutUnit previousRegionLogicalWidth = 0; + LayoutUnit previousRegionLogicalHeight = 0; + if (hasRegions()) { + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + ASSERT(!region->needsLayout()); + + region->deleteAllRenderBoxRegionInfo(); + + LayoutUnit regionLogicalWidth; + LayoutUnit regionLogicalHeight; + + if (isHorizontalWritingMode()) { + regionLogicalWidth = region->contentWidth(); + regionLogicalHeight = region->contentHeight(); + } else { + regionLogicalWidth = region->contentHeight(); + regionLogicalHeight = region->contentWidth(); + } + + if (!m_hasValidRegions) + m_hasValidRegions = true; + else { + if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth) + m_regionsHaveUniformLogicalWidth = false; + if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight) + m_regionsHaveUniformLogicalHeight = false; + } + + previousRegionLogicalWidth = regionLogicalWidth; + } + + computeLogicalWidth(); // Called to get the maximum logical width for the region. + + LayoutUnit logicalHeight = 0; + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + LayoutRect regionRect; + if (isHorizontalWritingMode()) { + regionRect = LayoutRect(style()->direction() == LTR ? 0 : logicalWidth() - region->contentWidth(), logicalHeight, region->contentWidth(), region->contentHeight()); + logicalHeight += regionRect.height(); + } else { + regionRect = LayoutRect(logicalHeight, style()->direction() == LTR ? 0 : logicalWidth() - region->contentHeight(), region->contentWidth(), region->contentHeight()); + logicalHeight += regionRect.width(); + } + region->setRegionRect(regionRect); + } + } + } + + CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this); + LayoutStateMaintainer statePusher(view(), this, regionsChanged); + RenderBlock::layout(); + statePusher.pop(); +} + +void RenderFlowThread::computeLogicalWidth() +{ + LayoutUnit logicalWidth = 0; + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + ASSERT(!region->needsLayout()); + logicalWidth = max(isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(), logicalWidth); + } + setLogicalWidth(logicalWidth); + + // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread. + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + + LayoutUnit regionLogicalWidth = isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(); + if (regionLogicalWidth != logicalWidth) { + LayoutUnit logicalLeft = style()->direction() == LTR ? 0 : logicalWidth - regionLogicalWidth; + region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false); + } + } +} + +void RenderFlowThread::computeLogicalHeight() +{ + int logicalHeight = 0; + + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + ASSERT(!region->needsLayout()); + logicalHeight += isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth(); + } + + setLogicalHeight(logicalHeight); +} + +void RenderFlowThread::paintIntoRegion(PaintInfo& paintInfo, RenderRegion* region, const LayoutPoint& paintOffset) +{ + GraphicsContext* context = paintInfo.context; + if (!context) + return; + + // Adjust the clipping rect for the region. + // paintOffset contains the offset where the painting should occur + // adjusted with the region padding and border. + LayoutRect regionRect(region->regionRect()); + LayoutRect regionOverflowRect(region->regionOverflowRect()); + LayoutRect regionClippingRect(paintOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size()); + + PaintInfo info(paintInfo); + info.rect.intersect(regionClippingRect); + + if (!info.rect.isEmpty()) { + context->save(); + + context->clip(regionClippingRect); + + // RenderFlowThread should start painting its content in a position that is offset + // from the region rect's current position. The amount of offset is equal to the location of + // region in flow coordinates. + LayoutPoint renderFlowThreadOffset; + if (style()->isFlippedBlocksWritingMode()) { + LayoutRect flippedRegionRect(regionRect); + flipForWritingMode(flippedRegionRect); + renderFlowThreadOffset = LayoutPoint(paintOffset - flippedRegionRect.location()); + } else + renderFlowThreadOffset = LayoutPoint(paintOffset - regionRect.location()); + + context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y()); + info.rect.moveBy(-renderFlowThreadOffset); + + layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects); + + context->restore(); + } +} + +bool RenderFlowThread::hitTestRegion(RenderRegion* region, const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset) +{ + LayoutRect regionRect(region->regionRect()); + LayoutRect regionOverflowRect = region->regionOverflowRect(); + LayoutRect regionClippingRect(accumulatedOffset + (regionOverflowRect.location() - regionRect.location()), regionOverflowRect.size()); + if (!regionClippingRect.contains(pointInContainer)) + return false; + + LayoutPoint renderFlowThreadOffset; + if (style()->isFlippedBlocksWritingMode()) { + LayoutRect flippedRegionRect(regionRect); + flipForWritingMode(flippedRegionRect); + renderFlowThreadOffset = LayoutPoint(accumulatedOffset - flippedRegionRect.location()); + } else + renderFlowThreadOffset = LayoutPoint(accumulatedOffset - regionRect.location()); + + LayoutPoint transformedPoint(pointInContainer.x() - renderFlowThreadOffset.x(), pointInContainer.y() - renderFlowThreadOffset.y()); + + // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView. + HitTestRequest newRequest(request.type() & HitTestRequest::IgnoreClipping); + + RenderRegion* oldRegion = result.region(); + result.setRegion(region); + LayoutPoint oldPoint = result.point(); + result.setPoint(transformedPoint); + bool isPointInsideFlowThread = layer()->hitTest(newRequest, result); + result.setPoint(oldPoint); + result.setRegion(oldRegion); + + // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate + // space? Right now it's staying in the RenderFlowThread's coordinate space, which may end up being ok. We will know more when we get around to + // patching positionForPoint. + return isPointInsideFlowThread; +} + +bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const +{ + if (view()->printing() || r.isEmpty()) + return false; + + return true; +} + +void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect, bool immediate) +{ + if (!shouldRepaint(repaintRect) || !hasValidRegionInfo()) + return; + + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + + // We only have to issue a repaint in this region if the region rect intersects the repaint rect. + LayoutRect flippedRegionRect(region->regionRect()); + LayoutRect flippedRegionOverflowRect(region->regionOverflowRect()); + flipForWritingMode(flippedRegionRect); // Put the region rects into physical coordinates. + flipForWritingMode(flippedRegionOverflowRect); + + LayoutRect clippedRect(flippedRegionOverflowRect); + clippedRect.intersect(repaintRect); + if (clippedRect.isEmpty()) + continue; + + // Put the region rect into the region's physical coordinate space. + clippedRect.setLocation(region->contentBoxRect().location() + (repaintRect.location() - flippedRegionRect.location())); + + // Now switch to the region's writing mode coordinate space and let it repaint itself. + region->flipForWritingMode(clippedRect); + LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the region is somewhere else. + region->repaintRectangle(clippedRect, immediate); + } +} + +RenderRegion* RenderFlowThread::renderRegionForLine(LayoutUnit position, bool extendLastRegion) const +{ + ASSERT(!m_regionsInvalidated); + + // If no region matches the position and extendLastRegion is true, it will return + // the last valid region. It is similar to auto extending the size of the last region. + RenderRegion* lastValidRegion = 0; + + // FIXME: The regions are always in order, optimize this search. + bool useHorizontalWritingMode = isHorizontalWritingMode(); + for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + + if (position <= 0) + return region; + + LayoutRect regionRect = region->regionRect(); + + if ((useHorizontalWritingMode && position < regionRect.maxY()) || (!useHorizontalWritingMode && position < regionRect.maxX())) + return region; + + if (extendLastRegion) + lastValidRegion = region; + } + + return lastValidRegion; +} + +LayoutUnit RenderFlowThread::regionLogicalWidthForLine(LayoutUnit position) const +{ + RenderRegion* region = renderRegionForLine(position, true); + if (!region) + return contentLogicalWidth(); + return isHorizontalWritingMode() ? region->regionRect().width() : region->regionRect().height(); +} + +LayoutUnit RenderFlowThread::regionLogicalHeightForLine(LayoutUnit position) const +{ + RenderRegion* region = renderRegionForLine(position); + if (!region) + return 0; + return isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width(); +} + +LayoutUnit RenderFlowThread::regionRemainingLogicalHeightForLine(LayoutUnit position, PageBoundaryRule pageBoundaryRule) const +{ + RenderRegion* region = renderRegionForLine(position); + if (!region) + return 0; + + LayoutUnit regionLogicalBottom = isHorizontalWritingMode() ? region->regionRect().maxY() : region->regionRect().maxX(); + LayoutUnit remainingHeight = regionLogicalBottom - position; + if (pageBoundaryRule == IncludePageBoundary) { + // If IncludePageBoundary is set, the line exactly on the top edge of a + // region will act as being part of the previous region. + LayoutUnit regionHeight = isHorizontalWritingMode() ? region->regionRect().height() : region->regionRect().width(); + remainingHeight = layoutMod(remainingHeight, regionHeight); + } + return remainingHeight; +} + +RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const +{ + if (!hasValidRegionInfo()) + return 0; + + LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox(); + flipForWritingMode(boxRect); + + // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions, + // for now we just take the center of the mapped enclosing box and map it to a region. + // Note: Using the center in order to avoid rounding errors. + + LayoutPoint center = boxRect.center(); + RenderRegion* renderRegion = renderRegionForLine(isHorizontalWritingMode() ? center.y() : center.x(), true); + if (!renderRegion) + return 0; + + LayoutRect flippedRegionRect(renderRegion->regionRect()); + flipForWritingMode(flippedRegionRect); + + transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location()); + + return renderRegion; +} + +void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box) +{ + if (!hasRegions()) + return; + + RenderRegion* startRegion; + RenderRegion* endRegion; + getRegionRangeForBox(box, startRegion, endRegion); + + for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + region->removeRenderBoxRegionInfo(box); + if (region == endRegion) + break; + } + + delete m_regionRangeMap.take(box); +} + +bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage) +{ + if (!hasRegions() || block == this) // Not necessary, since if any region changes, we do a full pagination relayout anyway. + return false; + + RenderRegion* startRegion; + RenderRegion* endRegion; + getRegionRangeForBox(block, startRegion, endRegion); + + for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + + if (!region->isValid()) + continue; + + ASSERT(!region->needsLayout()); + + RenderBoxRegionInfo* oldInfo = region->takeRenderBoxRegionInfo(block); + if (!oldInfo) + continue; + + LayoutUnit oldLogicalWidth = oldInfo->logicalWidth(); + delete oldInfo; + + RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage); + if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth) + return true; + + if (region == endRegion) + break; + } + + return false; +} + +LayoutUnit RenderFlowThread::contentLogicalWidthOfFirstRegion() const +{ + if (!hasValidRegionInfo()) + return 0; + for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + return isHorizontalWritingMode() ? region->contentWidth() : region->contentHeight(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +LayoutUnit RenderFlowThread::contentLogicalHeightOfFirstRegion() const +{ + if (!hasValidRegionInfo()) + return 0; + for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + return isHorizontalWritingMode() ? region->contentHeight() : region->contentWidth(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const +{ + if (!hasValidRegionInfo()) + return 0; + for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + return isHorizontalWritingMode() ? region->regionRect().x() : region->regionRect().y(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +RenderRegion* RenderFlowThread::firstRegion() const +{ + if (!hasValidRegionInfo()) + return 0; + for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + return region; + } + return 0; +} + +RenderRegion* RenderFlowThread::lastRegion() const +{ + if (!hasValidRegionInfo()) + return 0; + for (RenderRegionList::const_reverse_iterator iter = m_regionList.rbegin(); iter != m_regionList.rend(); ++iter) { + RenderRegion* region = *iter; + if (!region->isValid()) + continue; + return region; + } + return 0; +} + +void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage) +{ + // FIXME: Not right for differing writing-modes. + RenderRegion* startRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage, true); + RenderRegion* endRegion = renderRegionForLine(offsetFromLogicalTopOfFirstPage + box->logicalHeight(), true); + RenderRegionRange* range = m_regionRangeMap.get(box); + if (range) { + // If nothing changed, just bail. + if (range->startRegion() == startRegion && range->endRegion() == endRegion) + return; + + // Delete any info that we find before our new startRegion and after our new endRegion. + for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { + RenderRegion* region = *iter; + if (region == startRegion) { + iter = m_regionList.find(endRegion); + continue; + } + + region->removeRenderBoxRegionInfo(box); + + if (region == range->endRegion()) + break; + } + + range->setRange(startRegion, endRegion); + return; + } + range = new RenderRegionRange(startRegion, endRegion); + m_regionRangeMap.set(box, range); +} + +void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const +{ + startRegion = 0; + endRegion = 0; + RenderRegionRange* range = m_regionRangeMap.get(box); + if (!range) + return; + + startRegion = range->startRegion(); + endRegion = range->endRegion(); + ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion)); +} + +WebKitNamedFlow* RenderFlowThread::ensureNamedFlow() +{ + if (!m_namedFlow) + m_namedFlow = WebKitNamedFlow::create(); + + return m_namedFlow.get(); +} + +} // namespace WebCore |
