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 45namespace blink { 46 47RenderFlowThread::RenderFlowThread() 48 : RenderBlockFlow(0) 49 , m_regionsInvalidated(false) 50 , m_regionsHaveUniformLogicalHeight(true) 51 , m_pageLogicalSizeChanged(false) 52{ 53 setFlowThreadState(InsideOutOfFlowThread); 54} 55 56void RenderFlowThread::removeRegionFromThread(RenderMultiColumnSet* columnSet) 57{ 58 ASSERT(columnSet); 59 m_multiColumnSetList.remove(columnSet); 60} 61 62void RenderFlowThread::invalidateRegions() 63{ 64 if (m_regionsInvalidated) { 65 ASSERT(selfNeedsLayout()); 66 return; 67 } 68 69 setNeedsLayoutAndFullPaintInvalidation(); 70 71 m_regionsInvalidated = true; 72} 73 74class CurrentRenderFlowThreadDisabler { 75 WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler); 76public: 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 } 90private: 91 RenderView* m_view; 92 RenderFlowThread* m_renderFlowThread; 93}; 94 95void 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 125void RenderFlowThread::layout() 126{ 127 m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout(); 128 129 CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this); 130 RenderBlockFlow::layout(); 131 132 m_pageLogicalSizeChanged = false; 133} 134 135void 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 146bool 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 153bool RenderFlowThread::shouldIssuePaintInvalidations(const LayoutRect& r) const 154{ 155 if (view()->document().printing() || r.isEmpty()) 156 return false; 157 158 return true; 159} 160 161void 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 177LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset) 178{ 179 RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset); 180 if (!columnSet) 181 return 0; 182 183 return columnSet->pageLogicalHeight(); 184} 185 186LayoutUnit 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 204RenderRegion* RenderFlowThread::firstRegion() const 205{ 206 if (!hasValidRegionInfo()) 207 return 0; 208 return m_multiColumnSetList.first(); 209} 210 211RenderRegion* RenderFlowThread::lastRegion() const 212{ 213 if (!hasValidRegionInfo()) 214 return 0; 215 return m_multiColumnSetList.last(); 216} 217 218void 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 240void 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 250LayoutRect 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 271bool 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 281void RenderFlowThread::setOffsetFromLogicalTopOfFirstRegion(const RenderBox* box, LayoutUnit offset) 282{ 283 m_boxesToOffsetMap.set(box, offset); 284} 285 286void RenderFlowThread::clearOffsetFromLogicalTopOfFirstRegion(const RenderBox* box) 287{ 288 ASSERT(m_boxesToOffsetMap.contains(box)); 289 m_boxesToOffsetMap.remove(box); 290} 291 292const 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 301void 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 315void 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 326LayoutUnit 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 371void 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 379CurrentRenderFlowThreadMaintainer::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 390CurrentRenderFlowThreadMaintainer::~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