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/RenderLayer.h"
     40 #include "core/rendering/RenderMultiColumnSet.h"
     41 #include "core/rendering/RenderView.h"
     42 #include "platform/PODIntervalTree.h"
     43 #include "platform/geometry/TransformState.h"
     44 
     45 namespace blink {
     46 
     47 RenderFlowThread::RenderFlowThread()
     48     : RenderBlockFlow(0)
     49     , m_regionsInvalidated(false)
     50     , m_regionsHaveUniformLogicalHeight(true)
     51     , m_pageLogicalSizeChanged(false)
     52 {
     53     setFlowThreadState(InsideOutOfFlowThread);
     54 }
     55 
     56 void RenderFlowThread::removeRegionFromThread(RenderMultiColumnSet* columnSet)
     57 {
     58     ASSERT(columnSet);
     59     m_multiColumnSetList.remove(columnSet);
     60 }
     61 
     62 void RenderFlowThread::invalidateRegions()
     63 {
     64     if (m_regionsInvalidated) {
     65         ASSERT(selfNeedsLayout());
     66         return;
     67     }
     68 
     69     setNeedsLayoutAndFullPaintInvalidation();
     70 
     71     m_regionsInvalidated = true;
     72 }
     73 
     74 class CurrentRenderFlowThreadDisabler {
     75     WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler);
     76 public:
     77     CurrentRenderFlowThreadDisabler(RenderView* view)
     78         : m_view(view)
     79         , m_renderFlowThread(0)
     80     {
     81         m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread();
     82         if (m_renderFlowThread)
     83             view->flowThreadController()->setCurrentRenderFlowThread(0);
     84     }
     85     ~CurrentRenderFlowThreadDisabler()
     86     {
     87         if (m_renderFlowThread)
     88             m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
     89     }
     90 private:
     91     RenderView* m_view;
     92     RenderFlowThread* m_renderFlowThread;
     93 };
     94 
     95 void RenderFlowThread::validateRegions()
     96 {
     97     if (m_regionsInvalidated) {
     98         m_regionsInvalidated = false;
     99         m_regionsHaveUniformLogicalHeight = true;
    100 
    101         if (hasRegions()) {
    102             LayoutUnit previousRegionLogicalHeight = 0;
    103             bool firstRegionVisited = false;
    104 
    105             for (RenderMultiColumnSetList::iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
    106                 RenderMultiColumnSet* columnSet = *iter;
    107                 LayoutUnit regionLogicalHeight = columnSet->pageLogicalHeight();
    108 
    109                 if (!firstRegionVisited) {
    110                     firstRegionVisited = true;
    111                 } else {
    112                     if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
    113                         m_regionsHaveUniformLogicalHeight = false;
    114                 }
    115 
    116                 previousRegionLogicalHeight = regionLogicalHeight;
    117             }
    118         }
    119     }
    120 
    121     updateLogicalWidth(); // Called to get the maximum logical width for the columnSet.
    122     updateRegionsFlowThreadPortionRect();
    123 }
    124 
    125 void RenderFlowThread::layout()
    126 {
    127     m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();
    128 
    129     CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
    130     RenderBlockFlow::layout();
    131 
    132     m_pageLogicalSizeChanged = false;
    133 }
    134 
    135 void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
    136 {
    137     computedValues.m_position = logicalTop;
    138     computedValues.m_extent = 0;
    139 
    140     for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
    141         RenderMultiColumnSet* columnSet = *iter;
    142         computedValues.m_extent += columnSet->logicalHeightOfAllFlowThreadContent();
    143     }
    144 }
    145 
    146 bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
    147 {
    148     if (hitTestAction == HitTestBlockBackground)
    149         return false;
    150     return RenderBlockFlow::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction);
    151 }
    152 
    153 bool RenderFlowThread::shouldIssuePaintInvalidations(const LayoutRect& r) const
    154 {
    155     if (view()->document().printing() || r.isEmpty())
    156         return false;
    157 
    158     return true;
    159 }
    160 
    161 void RenderFlowThread::paintInvalidationRectangleInRegions(const LayoutRect& paintInvalidationRect) const
    162 {
    163     if (!shouldIssuePaintInvalidations(paintInvalidationRect) || !hasValidRegionInfo())
    164         return;
    165 
    166     // We can't use currentFlowThread as it is possible to have interleaved flow threads and the wrong one could be used.
    167     // Let each columnSet figure out the proper enclosing flow thread.
    168     CurrentRenderFlowThreadDisabler disabler(view());
    169 
    170     for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
    171         RenderMultiColumnSet* columnSet = *iter;
    172 
    173         columnSet->paintInvalidationForFlowThreadContent(paintInvalidationRect);
    174     }
    175 }
    176 
    177 LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset)
    178 {
    179     RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
    180     if (!columnSet)
    181         return 0;
    182 
    183     return columnSet->pageLogicalHeight();
    184 }
    185 
    186 LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule)
    187 {
    188     RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset);
    189     if (!columnSet)
    190         return 0;
    191 
    192     LayoutUnit pageLogicalTop = columnSet->pageLogicalTopForOffset(offset);
    193     LayoutUnit pageLogicalHeight = columnSet->pageLogicalHeight();
    194     LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight;
    195     LayoutUnit remainingHeight = pageLogicalBottom - offset;
    196     if (pageBoundaryRule == IncludePageBoundary) {
    197         // If IncludePageBoundary is set, the line exactly on the top edge of a
    198         // columnSet will act as being part of the previous columnSet.
    199         remainingHeight = intMod(remainingHeight, pageLogicalHeight);
    200     }
    201     return remainingHeight;
    202 }
    203 
    204 RenderRegion* RenderFlowThread::firstRegion() const
    205 {
    206     if (!hasValidRegionInfo())
    207         return 0;
    208     return m_multiColumnSetList.first();
    209 }
    210 
    211 RenderRegion* RenderFlowThread::lastRegion() const
    212 {
    213     if (!hasValidRegionInfo())
    214         return 0;
    215     return m_multiColumnSetList.last();
    216 }
    217 
    218 void RenderFlowThread::updateRegionsFlowThreadPortionRect()
    219 {
    220     LayoutUnit logicalHeight = 0;
    221     // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle.
    222     m_multiColumnSetIntervalTree.clear();
    223     m_multiColumnSetIntervalTree.initIfNeeded();
    224     for (RenderMultiColumnSetList::iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
    225         RenderMultiColumnSet* columnSet = *iter;
    226 
    227         LayoutUnit columnSetLogicalWidth = columnSet->pageLogicalWidth();
    228         LayoutUnit columnSetLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, columnSet->logicalHeightOfAllFlowThreadContent());
    229 
    230         LayoutRect columnSetRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - columnSetLogicalWidth, logicalHeight, columnSetLogicalWidth, columnSetLogicalHeight);
    231 
    232         columnSet->setFlowThreadPortionRect(isHorizontalWritingMode() ? columnSetRect : columnSetRect.transposedRect());
    233 
    234         m_multiColumnSetIntervalTree.add(MultiColumnSetIntervalTree::createInterval(logicalHeight, logicalHeight + columnSetLogicalHeight, columnSet));
    235 
    236         logicalHeight += columnSetLogicalHeight;
    237     }
    238 }
    239 
    240 void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
    241 {
    242     ASSERT(!m_regionsInvalidated);
    243 
    244     for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
    245         RenderMultiColumnSet* columnSet = *iter;
    246         columnSet->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
    247     }
    248 }
    249 
    250 LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
    251 {
    252     ASSERT(!m_regionsInvalidated);
    253 
    254     LayoutRect result;
    255     for (RenderMultiColumnSetList::const_iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) {
    256         RenderMultiColumnSet* columnSet = *iter;
    257         LayerFragments fragments;
    258         columnSet->collectLayerFragments(fragments, layerBoundingBox, PaintInfo::infiniteRect());
    259         for (size_t i = 0; i < fragments.size(); ++i) {
    260             const LayerFragment& fragment = fragments.at(i);
    261             LayoutRect fragmentRect(layerBoundingBox);
    262             fragmentRect.intersect(fragment.paginationClip);
    263             fragmentRect.moveBy(fragment.paginationOffset);
    264             result.unite(fragmentRect);
    265         }
    266     }
    267 
    268     return result;
    269 }
    270 
    271 bool RenderFlowThread::cachedOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit& result) const
    272 {
    273     RenderBoxToOffsetMap::const_iterator offsetIterator = m_boxesToOffsetMap.find(box);
    274     if (offsetIterator == m_boxesToOffsetMap.end())
    275         return false;
    276 
    277     result = offsetIterator->value;
    278     return true;
    279 }
    280 
    281 void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset)
    282 {
    283     m_boxesToOffsetMap.set(box, offset);
    284 }
    285 
    286 void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box)
    287 {
    288     ASSERT(m_boxesToOffsetMap.contains(box));
    289     m_boxesToOffsetMap.remove(box);
    290 }
    291 
    292 const RenderBox* RenderFlowThread::currentStatePusherRenderBox() const
    293 {
    294     const RenderObject* currentObject = m_statePusherObjectsStack.isEmpty() ? 0 : m_statePusherObjectsStack.last();
    295     if (currentObject && currentObject->isBox())
    296         return toRenderBox(currentObject);
    297 
    298     return 0;
    299 }
    300 
    301 void RenderFlowThread::pushFlowThreadLayoutState(const RenderObject& object)
    302 {
    303     if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
    304         LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
    305         if (layoutState && layoutState->isPaginated()) {
    306             ASSERT(layoutState->renderer() == currentBoxDescendant);
    307             LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
    308             setOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant, currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width());
    309         }
    310     }
    311 
    312     m_statePusherObjectsStack.add(&object);
    313 }
    314 
    315 void RenderFlowThread::popFlowThreadLayoutState()
    316 {
    317     m_statePusherObjectsStack.removeLast();
    318 
    319     if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) {
    320         LayoutState* layoutState = currentBoxDescendant->view()->layoutState();
    321         if (layoutState && layoutState->isPaginated())
    322             clearOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant);
    323     }
    324 }
    325 
    326 LayoutUnit RenderFlowThread::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const
    327 {
    328     // First check if we cached the offset for the block if it's an ancestor containing block of the box
    329     // being currently laid out.
    330     LayoutUnit offset;
    331     if (cachedOffsetFromLogicalTopOfFirstRegion(currentBlock, offset))
    332         return offset;
    333 
    334     // If it's the current box being laid out, use the layout state.
    335     const RenderBox* currentBoxDescendant = currentStatePusherRenderBox();
    336     if (currentBlock == currentBoxDescendant) {
    337         LayoutState* layoutState = view()->layoutState();
    338         ASSERT(layoutState->renderer() == currentBlock);
    339         ASSERT(layoutState && layoutState->isPaginated());
    340         LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
    341         return currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
    342     }
    343 
    344     // As a last resort, take the slow path.
    345     LayoutRect blockRect(0, 0, currentBlock->width(), currentBlock->height());
    346     while (currentBlock && !currentBlock->isRenderFlowThread()) {
    347         RenderBlock* containerBlock = currentBlock->containingBlock();
    348         ASSERT(containerBlock);
    349         if (!containerBlock)
    350             return 0;
    351         LayoutPoint currentBlockLocation = currentBlock->location();
    352 
    353         if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) {
    354             // We have to put the block rect in container coordinates
    355             // and we have to take into account both the container and current block flipping modes
    356             if (containerBlock->style()->isFlippedBlocksWritingMode()) {
    357                 if (containerBlock->isHorizontalWritingMode())
    358                     blockRect.setY(currentBlock->height() - blockRect.maxY());
    359                 else
    360                     blockRect.setX(currentBlock->width() - blockRect.maxX());
    361             }
    362             currentBlock->flipForWritingMode(blockRect);
    363         }
    364         blockRect.moveBy(currentBlockLocation);
    365         currentBlock = containerBlock;
    366     }
    367 
    368     return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x();
    369 }
    370 
    371 void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const MultiColumnSetInterval& interval)
    372 {
    373     if (m_result)
    374         return;
    375     if (interval.low() <= m_offset && interval.high() > m_offset)
    376         m_result = interval.data();
    377 }
    378 
    379 CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread)
    380     : m_renderFlowThread(renderFlowThread)
    381     , m_previousRenderFlowThread(0)
    382 {
    383     if (!m_renderFlowThread)
    384         return;
    385     RenderView* view = m_renderFlowThread->view();
    386     m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread();
    387     view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread);
    388 }
    389 
    390 CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer()
    391 {
    392     if (!m_renderFlowThread)
    393         return;
    394     RenderView* view = m_renderFlowThread->view();
    395     ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread);
    396     view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread);
    397 }
    398 
    399 
    400 } // namespace blink
    401