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