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