Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1. Redistributions of source code must retain the above
      9  *    copyright notice, this list of conditions and the following
     10  *    disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above
     12  *    copyright notice, this list of conditions and the following
     13  *    disclaimer in the documentation and/or other materials
     14  *    provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
     17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
     20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
     21  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
     25  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
     26  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  * SUCH DAMAGE.
     28  */
     29 
     30 #include "config.h"
     31 
     32 #include "core/rendering/RenderFlowThread.h"
     33 
     34 #include "core/dom/Node.h"
     35 #include "core/rendering/FlowThreadController.h"
     36 #include "core/rendering/HitTestRequest.h"
     37 #include "core/rendering/HitTestResult.h"
     38 #include "core/rendering/PaintInfo.h"
     39 #include "core/rendering/RenderInline.h"
     40 #include "core/rendering/RenderLayer.h"
     41 #include "core/rendering/RenderRegion.h"
     42 #include "core/rendering/RenderView.h"
     43 #include "platform/PODIntervalTree.h"
     44 #include "platform/geometry/TransformState.h"
     45 
     46 namespace WebCore {
     47 
     48 RenderFlowThread::RenderFlowThread()
     49     : RenderBlockFlow(0)
     50     , m_regionsInvalidated(false)
     51     , m_regionsHaveUniformLogicalHeight(true)
     52     , m_pageLogicalSizeChanged(false)
     53 {
     54     setFlowThreadState(InsideOutOfFlowThread);
     55 }
     56 
     57 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
     58 {
     59     ASSERT(renderRegion);
     60     m_regionList.remove(renderRegion);
     61 }
     62 
     63 void RenderFlowThread::invalidateRegions()
     64 {
     65     if (m_regionsInvalidated) {
     66         ASSERT(selfNeedsLayout());
     67         return;
     68     }
     69 
     70     m_regionRangeMap.clear();
     71     setNeedsLayoutAndFullPaintInvalidation();
     72 
     73     m_regionsInvalidated = true;
     74 }
     75 
     76 class CurrentRenderFlowThreadDisabler {
     77     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
     78 public:
     79     CurrentRenderFlowThreadDisabler(RenderView* view)
     80         : m_view(view)
     81         , m_renderFlowThread(0)
     82     {
     83         m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
     84         if (m_renderFlowThread)
     85             view->flowThreadController()->setCurrentRenderFlowThread(0);
     86     }
     87     ~CurrentRenderFlowThreadDisabler()
     88     {
     89         if (m_renderFlowThread)
     90             m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
     91     }
     92 private:
     93     RenderView* m_view;
     94     RenderFlowThread* m_renderFlowThread;
     95 };
     96 
     97 void RenderFlowThread::validateRegions()
     98 {
     99     if (m_regionsInvalidated) {
    100         m_regionsInvalidated = false;
    101         m_regionsHaveUniformLogicalHeight = true;
    102 
    103         if (hasRegions()) {
    104             LayoutUnit previousRegionLogicalHeight = 0;
    105             bool firstRegionVisited = false;
    106 
    107             for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
    108                 RenderRegion* region = *iter;
    109                 LayoutUnit regionLogicalHeight = region->pageLogicalHeight();
    110 
    111                 if (!firstRegionVisited) {
    112                     firstRegionVisited = true;
    113                 } else {
    114                     if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
    115                         m_regionsHaveUniformLogicalHeight = false;
    116                 }
    117 
    118                 previousRegionLogicalHeight = regionLogicalHeight;
    119             }
    120         }
    121     }
    122 
    123     updateLogicalWidth(); // Called to get the maximum logical width for the region.
    124     updateRegionsFlowThreadPortionRect();
    125 }
    126 
    127 void RenderFlowThread::layout()
    128 {
    129     m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();
    130 
    131     CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
    132     RenderBlockFlow::layout();
    133 
    134     m_pageLogicalSizeChanged = false;
    135 }
    136 
    137 void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
    138 {
    139     computedValues.m_position = logicalTop;
    140     computedValues.m_extent = 0;
    141 
    142     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
    143         RenderRegion* region = *iter;
    144         computedValues.m_extent += region->logicalHeightOfAllFlowThreadContent();
    145     }
    146 }
    147 
    148 bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
    149 {
    150     if (hitTestAction == HitTestBlockBackground)
    151         return false;
    152     return RenderBlockFlow::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction);
    153 }
    154 
    155 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const
    156 {
    157     if (view()->document().printing() || r.isEmpty())
    158         return false;
    159 
    160     return true;
    161 }
    162 
    163 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect) const
    164 {
    165     if (!shouldRepaint(repaintRect) || !hasValidRegionInfo())
    166         return;
    167 
    168     ForceHorriblySlowRectMapping slowRectMapping(*this); // We can't use layout state to repaint, since the regions are somewhere else.
    169 
    170     // We can't use currentFlowThread as it is possible to have interleaved flow threads and the wrong one could be used.
    171     // Let each region figure out the proper enclosing flow thread.
    172     CurrentRenderFlowThreadDisabler disabler(view());
    173 
    174     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
    175         RenderRegion* region = *iter;
    176 
    177         region->repaintFlowThreadContent(repaintRect);
    178     }
    179 }
    180 
    181 RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset) const
    182 {
    183     ASSERT(!m_regionsInvalidated);
    184 
    185     if (offset <= 0)
    186         return m_regionList.isEmpty() ? 0 : m_regionList.first();
    187 
    188     RegionSearchAdapter adapter(offset);
    189     m_regionIntervalTree.allOverlapsWithAdapter<RegionSearchAdapter>(adapter);
    190 
    191     // If no region was found, the offset is in the flow thread overflow.
    192     if (!adapter.result() && !m_regionList.isEmpty())
    193         return m_regionList.last();
    194 
    195     return adapter.result();
    196 }
    197 
    198 LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint)
    199 {
    200     LayoutPoint referencePoint = startPoint;
    201 
    202     // FIXME: This needs to be adapted for different writing modes inside the flow thread.
    203     RenderRegion* startRegion = regionAtBlockOffset(referencePoint.y());
    204     if (startRegion) {
    205         // Take into account the offset coordinates of the region.
    206         RenderObject* currObject = startRegion;
    207         RenderObject* currOffsetParentRenderer;
    208         Element* currOffsetParentElement;
    209         while ((currOffsetParentElement = currObject->offsetParent()) && (currOffsetParentRenderer = currOffsetParentElement->renderer())) {
    210             if (currObject->isBoxModelObject())
    211                 referencePoint.move(toRenderBoxModelObject(currObject)->offsetLeft(), toRenderBoxModelObject(currObject)->offsetTop());
    212 
    213             // Since we're looking for the offset relative to the body, we must also
    214             // take into consideration the borders of the region's offsetParent.
    215             if (currOffsetParentRenderer->isBox() && !currOffsetParentRenderer->isBody())
    216                 referencePoint.move(toRenderBox(currOffsetParentRenderer)->borderLeft(), toRenderBox(currOffsetParentRenderer)->borderTop());
    217 
    218             currObject = currOffsetParentRenderer;
    219         }
    220 
    221         // We need to check if any of this box's containing blocks start in a different region
    222         // and if so, drop the object's top position (which was computed relative to its containing block
    223         // and is no longer valid) and recompute it using the region in which it flows as reference.
    224         bool wasComputedRelativeToOtherRegion = false;
    225         const RenderBlock* objContainingBlock = boxModelObject.containingBlock();
    226         while (objContainingBlock) {
    227             // Check if this object is in a different region.
    228             RenderRegion* parentStartRegion = 0;
    229             RenderRegion* parentEndRegion = 0;
    230             getRegionRangeForBox(objContainingBlock, parentStartRegion, parentEndRegion);
    231             if (parentStartRegion && parentStartRegion != startRegion) {
    232                 wasComputedRelativeToOtherRegion = true;
    233                 break;
    234             }
    235             objContainingBlock = objContainingBlock->containingBlock();
    236         }
    237 
    238         if (wasComputedRelativeToOtherRegion) {
    239             // Get the logical top coordinate of the current object.
    240             LayoutUnit top = 0;
    241             if (boxModelObject.isRenderBlock()) {
    242                 top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage();
    243             } else {
    244                 if (boxModelObject.containingBlock())
    245                     top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage();
    246 
    247                 if (boxModelObject.isBox())
    248                     top += toRenderBox(&boxModelObject)->topLeftLocation().y();
    249                 else if (boxModelObject.isRenderInline())
    250                     top -= toRenderInline(&boxModelObject)->borderTop();
    251             }
    252 
    253             // Get the logical top of the region this object starts in
    254             // and compute the object's top, relative to the region's top.
    255             LayoutUnit regionLogicalTop = startRegion->pageLogicalTopForOffset(top);
    256             LayoutUnit topRelativeToRegion = top - regionLogicalTop;
    257             referencePoint.setY(startRegion->offsetTop() + topRelativeToRegion);
    258 
    259             // Since the top has been overriden, check if the
    260             // relative/sticky positioning must be reconsidered.
    261             if (boxModelObject.isRelPositioned())
    262                 referencePoint.move(0, boxModelObject.relativePositionOffset().height());
    263             else if (boxModelObject.isStickyPositioned())
    264                 referencePoint.move(0, boxModelObject.stickyPositionOffset().height());
    265         }
    266 
    267         // Since we're looking for the offset relative to the body, we must also
    268         // take into consideration the borders of the region.
    269         referencePoint.move(startRegion->borderLeft(), startRegion->borderTop());
    270     }
    271 
    272     return referencePoint;
    273 }
    274 
    275 LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset)
    276 {
    277     RenderRegion* region = regionAtBlockOffset(offset);
    278     return region ? region->pageLogicalTopForOffset(offset) : LayoutUnit();
    279 }
    280 
    281 LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset)
    282 {
    283     RenderRegion* region = regionAtBlockOffset(offset);
    284     if (!region)
    285         return 0;
    286 
    287     return region->pageLogicalHeight();
    288 }
    289 
    290 LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule)
    291 {
    292     RenderRegion* region = regionAtBlockOffset(offset);
    293     if (!region)
    294         return 0;
    295 
    296     LayoutUnit pageLogicalTop = region->pageLogicalTopForOffset(offset);
    297     LayoutUnit pageLogicalHeight = region->pageLogicalHeight();
    298     LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight;
    299     LayoutUnit remainingHeight = pageLogicalBottom - offset;
    300     if (pageBoundaryRule == IncludePageBoundary) {
    301         // If IncludePageBoundary is set, the line exactly on the top edge of a
    302         // region will act as being part of the previous region.
    303         remainingHeight = intMod(remainingHeight, pageLogicalHeight);
    304     }
    305     return remainingHeight;
    306 }
    307 
    308 RenderRegion* RenderFlowThread::firstRegion() const
    309 {
    310     if (!hasValidRegionInfo())
    311         return 0;
    312     return m_regionList.first();
    313 }
    314 
    315 RenderRegion* RenderFlowThread::lastRegion() const
    316 {
    317     if (!hasValidRegionInfo())
    318         return 0;
    319     return m_regionList.last();
    320 }
    321 
    322 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage)
    323 {
    324     if (!hasRegions())
    325         return;
    326 
    327     // FIXME: Not right for differing writing-modes.
    328     RenderRegion* startRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage);
    329     RenderRegion* endRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage + box->logicalHeight());
    330     RenderRegionRangeMap::iterator it = m_regionRangeMap.find(box);
    331     if (it == m_regionRangeMap.end()) {
    332         m_regionRangeMap.set(box, RenderRegionRange(startRegion, endRegion));
    333         return;
    334     }
    335 
    336     // If nothing changed, just bail.
    337     RenderRegionRange& range = it->value;
    338     if (range.startRegion() == startRegion && range.endRegion() == endRegion)
    339         return;
    340 
    341     range.setRange(startRegion, endRegion);
    342 }
    343 
    344 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const
    345 {
    346     startRegion = 0;
    347     endRegion = 0;
    348     RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box);
    349     if (it == m_regionRangeMap.end())
    350         return;
    351 
    352     const RenderRegionRange& range = it->value;
    353     startRegion = range.startRegion();
    354     endRegion = range.endRegion();
    355     ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion));
    356 }
    357 
    358 void RenderFlowThread::updateRegionsFlowThreadPortionRect()
    359 {
    360     LayoutUnit logicalHeight = 0;
    361     // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle.
    362     m_regionIntervalTree.clear();
    363     m_regionIntervalTree.initIfNeeded();
    364     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
    365         RenderRegion* region = *iter;
    366 
    367         LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
    368         LayoutUnit regionLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, region->logicalHeightOfAllFlowThreadContent());
    369 
    370         LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight);
    371 
    372         region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect());
    373 
    374         m_regionIntervalTree.add(RegionIntervalTree::createInterval(logicalHeight, logicalHeight + regionLogicalHeight, region));
    375 
    376         logicalHeight += regionLogicalHeight;
    377     }
    378 }
    379 
    380 void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
    381 {
    382     ASSERT(!m_regionsInvalidated);
    383 
    384     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
    385         RenderRegion* region = *iter;
    386         region->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
    387     }
    388 }
    389 
    390 LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
    391 {
    392     ASSERT(!m_regionsInvalidated);
    393 
    394     LayoutRect result;
    395     for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
    396         RenderRegion* region = *iter;
    397         LayerFragments fragments;
    398         region->collectLayerFragments(fragments, layerBoundingBox, PaintInfo::infiniteRect());
    399         for (size_t i = 0; i < fragments.size(); ++i) {
    400             const LayerFragment& fragment = fragments.at(i);
    401             LayoutRect fragmentRect(layerBoundingBox);
    402             fragmentRect.intersect(fragment.paginationClip);
    403             fragmentRect.moveBy(fragment.paginationOffset);
    404             result.unite(fragmentRect);
    405         }
    406     }
    407 
    408     return result;
    409 }
    410 
    411 bool RenderFlowThread::cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit& result) const
    412 {
    413     RenderBoxToOffsetMap::const_iterator offsetIterator = m_boxesToOffsetMap.find(box);
    414     if (offsetIterator == m_boxesToOffsetMap.end())
    415         return false;
    416 
    417     result = offsetIterator->value;
    418     return true;
    419 }
    420 
    421 void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset)
    422 {
    423     m_boxesToOffsetMap.set(box, offset);
    424 }
    425 
    426 void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box)
    427 {
    428     ASSERT(m_boxesToOffsetMap.contains(box));
    429     m_boxesToOffsetMap.remove(box);
    430 }
    431 
    432 const RenderBox* RenderFlowThread::currentStatePusherRenderBox() const
    433 {
    434     const RenderObject* currentObject = m_statePusherObjectsStack.isEmpty() ? 0 : m_statePusherObjectsStack.last();
    435     if (currentObject && currentObject->isBox())
    436         return toRenderBox(currentObject);
    437 
    438     return 0;
    439 }
    440 
    441 void RenderFlowThread::pushFlowThreadLayoutState(const RenderObject& object)
    442 {
    443     if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
    444         LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
    445         if (layoutState && layoutState->isPaginated()) {
    446             ASSERT(layoutState->renderer() == currentBoxDescendant);
    447             LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
    448             setOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant, currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width());
    449         }
    450     }
    451 
    452     m_statePusherObjectsStack.add(&object);
    453 }
    454 
    455 void RenderFlowThread::popFlowThreadLayoutState()
    456 {
    457     m_statePusherObjectsStack.removeLast();
    458 
    459     if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
    460         LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
    461         if (layoutState && layoutState->isPaginated())
    462             clearOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant);
    463     }
    464 }
    465 
    466 LayoutUnit RenderFlowThread::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const
    467 {
    468     // First check if we cached the offset for the block if it's an ancestor containing block of the box
    469     // being currently laid out.
    470     LayoutUnit offset;
    471     if (cachedOffsetFromLogicalTopOfFirstRegion(currentBlock, offset))
    472         return offset;
    473 
    474     // If it's the current box being laid out, use the layout state.
    475     const RenderBox* currentBoxDescendant = currentStatePusherRenderBox();
    476     if (currentBlock == currentBoxDescendant) {
    477         LayoutState* layoutState = view()->layoutState();
    478         ASSERT(layoutState->renderer() == currentBlock);
    479         ASSERT(layoutState && layoutState->isPaginated());
    480         LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
    481         return currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
    482     }
    483 
    484     // As a last resort, take the slow path.
    485     LayoutRect blockRect(0, 0, currentBlock->width(), currentBlock->height());
    486     while (currentBlock && !currentBlock->isRenderFlowThread()) {
    487         RenderBlock* containerBlock = currentBlock->containingBlock();
    488         ASSERT(containerBlock);
    489         if (!containerBlock)
    490             return 0;
    491         LayoutPoint currentBlockLocation = currentBlock->location();
    492 
    493         if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) {
    494             // We have to put the block rect in container coordinates
    495             // and we have to take into account both the container and current block flipping modes
    496             if (containerBlock->style()->isFlippedBlocksWritingMode()) {
    497                 if (containerBlock->isHorizontalWritingMode())
    498                     blockRect.setY(currentBlock->height() - blockRect.maxY());
    499                 else
    500                     blockRect.setX(currentBlock->width() - blockRect.maxX());
    501             }
    502             currentBlock->flipForWritingMode(blockRect);
    503         }
    504         blockRect.moveBy(currentBlockLocation);
    505         currentBlock = containerBlock;
    506     }
    507 
    508     return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x();
    509 }
    510 
    511 void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const RegionInterval& interval)
    512 {
    513     if (m_result)
    514         return;
    515     if (interval.low() <= m_offset && interval.high() > m_offset)
    516         m_result = interval.data();
    517 }
    518 
    519 CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
    520     : m_renderFlowThread(renderFlowThread)
    521     , m_previousRenderFlowThread(0)
    522 {
    523     if (!m_renderFlowThread)
    524         return;
    525     RenderView* view = m_renderFlowThread->view();
    526     m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread();
    527     view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
    528 }
    529 
    530 CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer()
    531 {
    532     if (!m_renderFlowThread)
    533         return;
    534     RenderView* view = m_renderFlowThread->view();
    535     ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
    536     view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread);
    537 }
    538 
    539 
    540 } // namespace WebCore
    541