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/platform/PODIntervalTree.h" 36 #include "core/platform/graphics/transforms/TransformState.h" 37 #include "core/rendering/FlowThreadController.h" 38 #include "core/rendering/HitTestRequest.h" 39 #include "core/rendering/HitTestResult.h" 40 #include "core/rendering/PaintInfo.h" 41 #include "core/rendering/RenderBoxRegionInfo.h" 42 #include "core/rendering/RenderInline.h" 43 #include "core/rendering/RenderLayer.h" 44 #include "core/rendering/RenderRegion.h" 45 #include "core/rendering/RenderView.h" 46 47 namespace WebCore { 48 49 RenderFlowThread::RenderFlowThread() 50 : RenderBlock(0) 51 , m_previousRegionCount(0) 52 , m_autoLogicalHeightRegionsCount(0) 53 , m_regionsInvalidated(false) 54 , m_regionsHaveUniformLogicalWidth(true) 55 , m_regionsHaveUniformLogicalHeight(true) 56 , m_hasRegionsWithStyling(false) 57 , m_dispatchRegionLayoutUpdateEvent(false) 58 , m_dispatchRegionOversetChangeEvent(false) 59 , m_pageLogicalSizeChanged(false) 60 , m_inConstrainedLayoutPhase(false) 61 , m_needsTwoPhasesLayout(false) 62 { 63 setFlowThreadState(InsideOutOfFlowThread); 64 } 65 66 PassRefPtr<RenderStyle> RenderFlowThread::createFlowThreadStyle(RenderStyle* parentStyle) 67 { 68 RefPtr<RenderStyle> newStyle(RenderStyle::create()); 69 newStyle->inheritFrom(parentStyle); 70 newStyle->setDisplay(BLOCK); 71 newStyle->setPosition(AbsolutePosition); 72 newStyle->setZIndex(0); 73 newStyle->setLeft(Length(0, Fixed)); 74 newStyle->setTop(Length(0, Fixed)); 75 newStyle->setWidth(Length(100, Percent)); 76 newStyle->setHeight(Length(100, Percent)); 77 newStyle->font().update(0); 78 79 return newStyle.release(); 80 } 81 82 void RenderFlowThread::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 83 { 84 RenderBlock::styleDidChange(diff, oldStyle); 85 86 if (oldStyle && oldStyle->writingMode() != style()->writingMode()) 87 invalidateRegions(); 88 } 89 90 void RenderFlowThread::removeFlowChildInfo(RenderObject* child) 91 { 92 if (child->isBox()) 93 removeRenderBoxRegionInfo(toRenderBox(child)); 94 clearRenderObjectCustomStyle(child); 95 } 96 97 void RenderFlowThread::addRegionToThread(RenderRegion* renderRegion) 98 { 99 ASSERT(renderRegion); 100 m_regionList.add(renderRegion); 101 renderRegion->setIsValid(true); 102 } 103 104 void RenderFlowThread::removeRegionFromThread(RenderRegion* renderRegion) 105 { 106 ASSERT(renderRegion); 107 m_regionList.remove(renderRegion); 108 } 109 110 void RenderFlowThread::invalidateRegions() 111 { 112 if (m_regionsInvalidated) { 113 ASSERT(selfNeedsLayout()); 114 return; 115 } 116 117 m_regionRangeMap.clear(); 118 m_breakBeforeToRegionMap.clear(); 119 m_breakAfterToRegionMap.clear(); 120 setNeedsLayout(); 121 122 m_regionsInvalidated = true; 123 } 124 125 class CurrentRenderFlowThreadDisabler { 126 WTF_MAKE_NONCOPYABLE(CurrentRenderFlowThreadDisabler); 127 public: 128 CurrentRenderFlowThreadDisabler(RenderView* view) 129 : m_view(view) 130 , m_renderFlowThread(0) 131 { 132 m_renderFlowThread = m_view->flowThreadController()->currentRenderFlowThread(); 133 if (m_renderFlowThread) 134 view->flowThreadController()->setCurrentRenderFlowThread(0); 135 } 136 ~CurrentRenderFlowThreadDisabler() 137 { 138 if (m_renderFlowThread) 139 m_view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread); 140 } 141 private: 142 RenderView* m_view; 143 RenderFlowThread* m_renderFlowThread; 144 }; 145 146 void RenderFlowThread::validateRegions() 147 { 148 if (m_regionsInvalidated) { 149 m_regionsInvalidated = false; 150 m_regionsHaveUniformLogicalWidth = true; 151 m_regionsHaveUniformLogicalHeight = true; 152 153 if (hasRegions()) { 154 LayoutUnit previousRegionLogicalWidth = 0; 155 LayoutUnit previousRegionLogicalHeight = 0; 156 bool firstRegionVisited = false; 157 158 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 159 RenderRegion* region = *iter; 160 ASSERT(!region->needsLayout() || region->isRenderRegionSet()); 161 162 region->deleteAllRenderBoxRegionInfo(); 163 164 // In the normal layout phase we need to initialize the computedAutoHeight for auto-height regions. 165 // See initializeRegionsComputedAutoHeight for the explanation. 166 // Also, if we have auto-height regions we can't assume m_regionsHaveUniformLogicalHeight to be true in the first phase 167 // because the auto-height regions don't have their height computed yet. 168 if (!inConstrainedLayoutPhase() && region->hasAutoLogicalHeight()) { 169 region->setComputedAutoHeight(region->maxPageLogicalHeight()); 170 m_regionsHaveUniformLogicalHeight = false; 171 } 172 173 LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); 174 LayoutUnit regionLogicalHeight = region->pageLogicalHeight(); 175 176 if (!firstRegionVisited) 177 firstRegionVisited = true; 178 else { 179 if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth) 180 m_regionsHaveUniformLogicalWidth = false; 181 if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight) 182 m_regionsHaveUniformLogicalHeight = false; 183 } 184 185 previousRegionLogicalWidth = regionLogicalWidth; 186 } 187 } 188 } 189 190 updateLogicalWidth(); // Called to get the maximum logical width for the region. 191 updateRegionsFlowThreadPortionRect(); 192 } 193 194 void RenderFlowThread::layout() 195 { 196 StackStats::LayoutCheckPoint layoutCheckPoint; 197 198 m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout(); 199 200 // In case this is the second pass of the normal phase we need to update the auto-height regions to their initial value. 201 // If the region chain was invalidated this will happen anyway. 202 if (!m_regionsInvalidated && !inConstrainedLayoutPhase()) 203 initializeRegionsComputedAutoHeight(); 204 205 validateRegions(); 206 207 // This is the first phase of the layout and because we have auto-height regions we'll need a second 208 // pass to update the flow with the computed auto-height regions. 209 m_needsTwoPhasesLayout = !inConstrainedLayoutPhase() && hasAutoLogicalHeightRegions(); 210 211 CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this); 212 RenderBlock::layout(); 213 214 m_pageLogicalSizeChanged = false; 215 216 if (lastRegion()) 217 lastRegion()->expandToEncompassFlowThreadContentsIfNeeded(); 218 219 if (shouldDispatchRegionLayoutUpdateEvent()) 220 dispatchRegionLayoutUpdateEvent(); 221 222 if (shouldDispatchRegionOversetChangeEvent()) 223 dispatchRegionOversetChangeEvent(); 224 } 225 226 void RenderFlowThread::updateLogicalWidth() 227 { 228 LayoutUnit logicalWidth = initialLogicalWidth(); 229 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 230 RenderRegion* region = *iter; 231 ASSERT(!region->needsLayout() || region->isRenderRegionSet()); 232 logicalWidth = max(region->pageLogicalWidth(), logicalWidth); 233 } 234 setLogicalWidth(logicalWidth); 235 236 // If the regions have non-uniform logical widths, then insert inset information for the RenderFlowThread. 237 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 238 RenderRegion* region = *iter; 239 LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); 240 if (regionLogicalWidth != logicalWidth) { 241 LayoutUnit logicalLeft = style()->direction() == LTR ? LayoutUnit() : logicalWidth - regionLogicalWidth; 242 region->setRenderBoxRegionInfo(this, logicalLeft, regionLogicalWidth, false); 243 } 244 } 245 } 246 247 void RenderFlowThread::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const 248 { 249 computedValues.m_position = logicalTop; 250 computedValues.m_extent = 0; 251 252 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 253 RenderRegion* region = *iter; 254 ASSERT(!region->needsLayout() || region->isRenderRegionSet()); 255 256 computedValues.m_extent += region->logicalHeightOfAllFlowThreadContent(); 257 } 258 } 259 260 LayoutRect RenderFlowThread::computeRegionClippingRect(const LayoutPoint& offset, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect) const 261 { 262 LayoutRect regionClippingRect(offset + (flowThreadPortionOverflowRect.location() - flowThreadPortionRect.location()), flowThreadPortionOverflowRect.size()); 263 if (style()->isFlippedBlocksWritingMode()) 264 regionClippingRect.move(flowThreadPortionRect.size() - flowThreadPortionOverflowRect.size()); 265 return regionClippingRect; 266 } 267 268 void RenderFlowThread::paintFlowThreadPortionInRegion(PaintInfo& paintInfo, RenderRegion* region, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect, const LayoutPoint& paintOffset) const 269 { 270 GraphicsContext* context = paintInfo.context; 271 if (!context) 272 return; 273 274 // RenderFlowThread should start painting its content in a position that is offset 275 // from the region rect's current position. The amount of offset is equal to the location of 276 // the flow thread portion in the flow thread's local coordinates. 277 // Note that we have to pixel snap the location at which we're going to paint, since this is necessary 278 // to minimize the amount of incorrect snapping that would otherwise occur. 279 // If we tried to paint by applying a non-integral translation, then all the 280 // layout code that attempted to pixel snap would be incorrect. 281 IntPoint adjustedPaintOffset; 282 LayoutPoint portionLocation; 283 if (style()->isFlippedBlocksWritingMode()) { 284 LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect); 285 flipForWritingMode(flippedFlowThreadPortionRect); 286 portionLocation = flippedFlowThreadPortionRect.location(); 287 } else 288 portionLocation = flowThreadPortionRect.location(); 289 adjustedPaintOffset = roundedIntPoint(paintOffset - portionLocation); 290 291 // The clipping rect for the region is set up by assuming the flowThreadPortionRect is going to paint offset from adjustedPaintOffset. 292 // Remember that we pixel snapped and moved the paintOffset and stored the snapped result in adjustedPaintOffset. Now we add back in 293 // the flowThreadPortionRect's location to get the spot where we expect the portion to actually paint. This can be non-integral and 294 // that's ok. We then pixel snap the resulting clipping rect to account for snapping that will occur when the flow thread paints. 295 IntRect regionClippingRect = pixelSnappedIntRect(computeRegionClippingRect(adjustedPaintOffset + portionLocation, flowThreadPortionRect, flowThreadPortionOverflowRect)); 296 297 PaintInfo info(paintInfo); 298 info.rect.intersect(regionClippingRect); 299 300 if (!info.rect.isEmpty()) { 301 context->save(); 302 303 context->clip(regionClippingRect); 304 305 context->translate(adjustedPaintOffset.x(), adjustedPaintOffset.y()); 306 info.rect.moveBy(-adjustedPaintOffset); 307 308 layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects); 309 310 context->restore(); 311 } 312 } 313 314 bool RenderFlowThread::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) 315 { 316 if (hitTestAction == HitTestBlockBackground) 317 return false; 318 return RenderBlock::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction); 319 } 320 321 bool RenderFlowThread::hitTestFlowThreadPortionInRegion(RenderRegion* region, const LayoutRect& flowThreadPortionRect, const LayoutRect& flowThreadPortionOverflowRect, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const 322 { 323 LayoutRect regionClippingRect = computeRegionClippingRect(accumulatedOffset, flowThreadPortionRect, flowThreadPortionOverflowRect); 324 if (!regionClippingRect.contains(locationInContainer.point())) 325 return false; 326 327 LayoutSize renderFlowThreadOffset; 328 if (style()->isFlippedBlocksWritingMode()) { 329 LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect); 330 flipForWritingMode(flippedFlowThreadPortionRect); 331 renderFlowThreadOffset = accumulatedOffset - flippedFlowThreadPortionRect.location(); 332 } else 333 renderFlowThreadOffset = accumulatedOffset - flowThreadPortionRect.location(); 334 335 // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView. 336 HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowShadowContent); 337 338 // Make a new temporary HitTestLocation in the new region. 339 HitTestLocation newHitTestLocation(locationInContainer, -renderFlowThreadOffset, region); 340 341 bool isPointInsideFlowThread = layer()->hitTest(newRequest, newHitTestLocation, result); 342 343 // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate 344 // space? Right now it's staying in the RenderFlowThread's coordinate space, which may end up being ok. We will know more when we get around to 345 // patching positionForPoint. 346 return isPointInsideFlowThread; 347 } 348 349 bool RenderFlowThread::shouldRepaint(const LayoutRect& r) const 350 { 351 if (view()->printing() || r.isEmpty()) 352 return false; 353 354 return true; 355 } 356 357 void RenderFlowThread::repaintRectangleInRegions(const LayoutRect& repaintRect) const 358 { 359 if (!shouldRepaint(repaintRect) || !hasValidRegionInfo()) 360 return; 361 362 LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the regions are somewhere else. 363 364 // We can't use currentFlowThread as it is possible to have interleaved flow threads and the wrong one could be used. 365 // Let each region figure out the proper enclosing flow thread. 366 CurrentRenderFlowThreadDisabler disabler(view()); 367 368 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 369 RenderRegion* region = *iter; 370 371 region->repaintFlowThreadContent(repaintRect); 372 } 373 } 374 375 RenderRegion* RenderFlowThread::regionAtBlockOffset(LayoutUnit offset, bool extendLastRegion, RegionAutoGenerationPolicy autoGenerationPolicy) 376 { 377 ASSERT(!m_regionsInvalidated); 378 379 if (autoGenerationPolicy == AllowRegionAutoGeneration) 380 autoGenerateRegionsToBlockOffset(offset); 381 382 if (offset <= 0) 383 return m_regionList.isEmpty() ? 0 : m_regionList.first(); 384 385 RegionSearchAdapter adapter(offset); 386 m_regionIntervalTree.allOverlapsWithAdapter<RegionSearchAdapter>(adapter); 387 388 // If no region was found, the offset is in the flow thread overflow. 389 // The last region will contain the offset if extendLastRegion is set or if the last region is a set. 390 if (!adapter.result() && !m_regionList.isEmpty() && (extendLastRegion || m_regionList.last()->isRenderRegionSet())) 391 return m_regionList.last(); 392 393 return adapter.result(); 394 } 395 396 LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint) 397 { 398 LayoutPoint referencePoint = startPoint; 399 400 // FIXME: This needs to be adapted for different writing modes inside the flow thread. 401 RenderRegion* startRegion = regionAtBlockOffset(referencePoint.y()); 402 if (startRegion) { 403 // Take into account the offset coordinates of the region. 404 RenderObject* currObject = startRegion; 405 RenderObject* currOffsetParentRenderer; 406 Element* currOffsetParentElement; 407 while ((currOffsetParentElement = currObject->offsetParent()) && (currOffsetParentRenderer = currOffsetParentElement->renderer())) { 408 if (currObject->isBoxModelObject()) 409 referencePoint.move(toRenderBoxModelObject(currObject)->offsetLeft(), toRenderBoxModelObject(currObject)->offsetTop()); 410 411 // Since we're looking for the offset relative to the body, we must also 412 // take into consideration the borders of the region's offsetParent. 413 if (currOffsetParentRenderer->isBox() && !currOffsetParentRenderer->isBody()) 414 referencePoint.move(toRenderBox(currOffsetParentRenderer)->borderLeft(), toRenderBox(currOffsetParentRenderer)->borderTop()); 415 416 currObject = currOffsetParentRenderer; 417 } 418 419 // We need to check if any of this box's containing blocks start in a different region 420 // and if so, drop the object's top position (which was computed relative to its containing block 421 // and is no longer valid) and recompute it using the region in which it flows as reference. 422 bool wasComputedRelativeToOtherRegion = false; 423 const RenderBlock* objContainingBlock = boxModelObject.containingBlock(); 424 while (objContainingBlock && !objContainingBlock->isRenderNamedFlowThread()) { 425 // Check if this object is in a different region. 426 RenderRegion* parentStartRegion = 0; 427 RenderRegion* parentEndRegion = 0; 428 getRegionRangeForBox(objContainingBlock, parentStartRegion, parentEndRegion); 429 if (parentStartRegion && parentStartRegion != startRegion) { 430 wasComputedRelativeToOtherRegion = true; 431 break; 432 } 433 objContainingBlock = objContainingBlock->containingBlock(); 434 } 435 436 if (wasComputedRelativeToOtherRegion) { 437 if (boxModelObject.isBox()) { 438 // Use borderBoxRectInRegion to account for variations such as percentage margins. 439 LayoutRect borderBoxRect = toRenderBox(&boxModelObject)->borderBoxRectInRegion(startRegion, 0, RenderBox::DoNotCacheRenderBoxRegionInfo); 440 referencePoint.move(borderBoxRect.location().x(), 0); 441 } 442 443 // Get the logical top coordinate of the current object. 444 LayoutUnit top = 0; 445 if (boxModelObject.isRenderBlock()) 446 top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage(); 447 else { 448 if (boxModelObject.containingBlock()) 449 top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage(); 450 451 if (boxModelObject.isBox()) 452 top += toRenderBox(&boxModelObject)->topLeftLocation().y(); 453 else if (boxModelObject.isRenderInline()) 454 top -= toRenderInline(&boxModelObject)->borderTop(); 455 } 456 457 // Get the logical top of the region this object starts in 458 // and compute the object's top, relative to the region's top. 459 LayoutUnit regionLogicalTop = startRegion->pageLogicalTopForOffset(top); 460 LayoutUnit topRelativeToRegion = top - regionLogicalTop; 461 referencePoint.setY(startRegion->offsetTop() + topRelativeToRegion); 462 463 // Since the top has been overriden, check if the 464 // relative/sticky positioning must be reconsidered. 465 if (boxModelObject.isRelPositioned()) 466 referencePoint.move(0, boxModelObject.relativePositionOffset().height()); 467 else if (boxModelObject.isStickyPositioned()) 468 referencePoint.move(0, boxModelObject.stickyPositionOffset().height()); 469 } 470 471 // Since we're looking for the offset relative to the body, we must also 472 // take into consideration the borders of the region. 473 referencePoint.move(startRegion->borderLeft(), startRegion->borderTop()); 474 } 475 476 return referencePoint; 477 } 478 479 LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset) 480 { 481 RenderRegion* region = regionAtBlockOffset(offset); 482 return region ? region->pageLogicalTopForOffset(offset) : LayoutUnit(); 483 } 484 485 LayoutUnit RenderFlowThread::pageLogicalWidthForOffset(LayoutUnit offset) 486 { 487 RenderRegion* region = regionAtBlockOffset(offset, true); 488 return region ? region->pageLogicalWidth() : contentLogicalWidth(); 489 } 490 491 LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset) 492 { 493 RenderRegion* region = regionAtBlockOffset(offset); 494 if (!region) 495 return 0; 496 497 return region->pageLogicalHeight(); 498 } 499 500 LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) 501 { 502 RenderRegion* region = regionAtBlockOffset(offset); 503 if (!region) 504 return 0; 505 506 LayoutUnit pageLogicalTop = region->pageLogicalTopForOffset(offset); 507 LayoutUnit pageLogicalHeight = region->pageLogicalHeight(); 508 LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight; 509 LayoutUnit remainingHeight = pageLogicalBottom - offset; 510 if (pageBoundaryRule == IncludePageBoundary) { 511 // If IncludePageBoundary is set, the line exactly on the top edge of a 512 // region will act as being part of the previous region. 513 remainingHeight = intMod(remainingHeight, pageLogicalHeight); 514 } 515 return remainingHeight; 516 } 517 518 RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const 519 { 520 if (!hasValidRegionInfo()) 521 return 0; 522 523 LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox(); 524 flipForWritingMode(boxRect); 525 526 // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions, 527 // for now we just take the center of the mapped enclosing box and map it to a region. 528 // Note: Using the center in order to avoid rounding errors. 529 530 LayoutPoint center = boxRect.center(); 531 RenderRegion* renderRegion = const_cast<RenderFlowThread*>(this)->regionAtBlockOffset(isHorizontalWritingMode() ? center.y() : center.x(), true, DisallowRegionAutoGeneration); 532 if (!renderRegion) 533 return 0; 534 535 LayoutRect flippedRegionRect(renderRegion->flowThreadPortionRect()); 536 flipForWritingMode(flippedRegionRect); 537 538 transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location()); 539 540 return renderRegion; 541 } 542 543 void RenderFlowThread::removeRenderBoxRegionInfo(RenderBox* box) 544 { 545 if (!hasRegions()) 546 return; 547 548 // If the region chain was invalidated the next layout will clear the box information from all the regions. 549 if (m_regionsInvalidated) { 550 ASSERT(selfNeedsLayout()); 551 return; 552 } 553 554 RenderRegion* startRegion; 555 RenderRegion* endRegion; 556 getRegionRangeForBox(box, startRegion, endRegion); 557 558 for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) { 559 RenderRegion* region = *iter; 560 region->removeRenderBoxRegionInfo(box); 561 if (region == endRegion) 562 break; 563 } 564 565 #ifndef NDEBUG 566 // We have to make sure we did not leave any RenderBoxRegionInfo attached. 567 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 568 RenderRegion* region = *iter; 569 ASSERT(!region->renderBoxRegionInfo(box)); 570 } 571 #endif 572 573 m_regionRangeMap.remove(box); 574 } 575 576 bool RenderFlowThread::logicalWidthChangedInRegions(const RenderBlock* block, LayoutUnit offsetFromLogicalTopOfFirstPage) 577 { 578 if (!hasRegions()) 579 return false; 580 581 RenderRegion* startRegion; 582 RenderRegion* endRegion; 583 getRegionRangeForBox(block, startRegion, endRegion); 584 585 // When the region chain is invalidated the box information is discarded so we must assume the width has changed. 586 if (m_pageLogicalSizeChanged && !startRegion) 587 return true; 588 589 // Not necessary for the flow thread, since we already computed the correct info for it. 590 if (block == this) 591 return false; 592 593 for (RenderRegionList::iterator iter = m_regionList.find(startRegion); iter != m_regionList.end(); ++iter) { 594 RenderRegion* region = *iter; 595 ASSERT(!region->needsLayout() || region->isRenderRegionSet()); 596 597 OwnPtr<RenderBoxRegionInfo> oldInfo = region->takeRenderBoxRegionInfo(block); 598 if (!oldInfo) 599 continue; 600 601 LayoutUnit oldLogicalWidth = oldInfo->logicalWidth(); 602 RenderBoxRegionInfo* newInfo = block->renderBoxRegionInfo(region, offsetFromLogicalTopOfFirstPage); 603 if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth) 604 return true; 605 606 if (region == endRegion) 607 break; 608 } 609 610 return false; 611 } 612 613 LayoutUnit RenderFlowThread::contentLogicalWidthOfFirstRegion() const 614 { 615 RenderRegion* firstValidRegionInFlow = firstRegion(); 616 if (!firstValidRegionInFlow) 617 return 0; 618 return isHorizontalWritingMode() ? firstValidRegionInFlow->contentWidth() : firstValidRegionInFlow->contentHeight(); 619 } 620 621 LayoutUnit RenderFlowThread::contentLogicalHeightOfFirstRegion() const 622 { 623 RenderRegion* firstValidRegionInFlow = firstRegion(); 624 if (!firstValidRegionInFlow) 625 return 0; 626 return isHorizontalWritingMode() ? firstValidRegionInFlow->contentHeight() : firstValidRegionInFlow->contentWidth(); 627 } 628 629 LayoutUnit RenderFlowThread::contentLogicalLeftOfFirstRegion() const 630 { 631 RenderRegion* firstValidRegionInFlow = firstRegion(); 632 if (!firstValidRegionInFlow) 633 return 0; 634 return isHorizontalWritingMode() ? firstValidRegionInFlow->flowThreadPortionRect().x() : firstValidRegionInFlow->flowThreadPortionRect().y(); 635 } 636 637 RenderRegion* RenderFlowThread::firstRegion() const 638 { 639 if (!hasValidRegionInfo()) 640 return 0; 641 return m_regionList.first(); 642 } 643 644 RenderRegion* RenderFlowThread::lastRegion() const 645 { 646 if (!hasValidRegionInfo()) 647 return 0; 648 return m_regionList.last(); 649 } 650 651 void RenderFlowThread::clearRenderObjectCustomStyle(const RenderObject* object, 652 const RenderRegion* oldStartRegion, const RenderRegion* oldEndRegion, 653 const RenderRegion* newStartRegion, const RenderRegion* newEndRegion) 654 { 655 // Clear the styles for the object in the regions. 656 // The styles are not cleared for the regions that are contained in both ranges. 657 bool insideOldRegionRange = false; 658 bool insideNewRegionRange = false; 659 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 660 RenderRegion* region = *iter; 661 662 if (oldStartRegion == region) 663 insideOldRegionRange = true; 664 if (newStartRegion == region) 665 insideNewRegionRange = true; 666 667 if (!(insideOldRegionRange && insideNewRegionRange)) 668 region->clearObjectStyleInRegion(object); 669 670 if (oldEndRegion == region) 671 insideOldRegionRange = false; 672 if (newEndRegion == region) 673 insideNewRegionRange = false; 674 } 675 } 676 677 void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage) 678 { 679 if (!hasRegions()) 680 return; 681 682 // FIXME: Not right for differing writing-modes. 683 RenderRegion* startRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage, true); 684 RenderRegion* endRegion = regionAtBlockOffset(offsetFromLogicalTopOfFirstPage + box->logicalHeight(), true); 685 RenderRegionRangeMap::iterator it = m_regionRangeMap.find(box); 686 if (it == m_regionRangeMap.end()) { 687 m_regionRangeMap.set(box, RenderRegionRange(startRegion, endRegion)); 688 clearRenderObjectCustomStyle(box); 689 return; 690 } 691 692 // If nothing changed, just bail. 693 RenderRegionRange& range = it->value; 694 if (range.startRegion() == startRegion && range.endRegion() == endRegion) 695 return; 696 697 // Delete any info that we find before our new startRegion and after our new endRegion. 698 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 699 RenderRegion* region = *iter; 700 if (region == startRegion) { 701 iter = m_regionList.find(endRegion); 702 continue; 703 } 704 705 region->removeRenderBoxRegionInfo(box); 706 707 if (region == range.endRegion()) 708 break; 709 } 710 711 clearRenderObjectCustomStyle(box, range.startRegion(), range.endRegion(), startRegion, endRegion); 712 range.setRange(startRegion, endRegion); 713 } 714 715 void RenderFlowThread::getRegionRangeForBox(const RenderBox* box, RenderRegion*& startRegion, RenderRegion*& endRegion) const 716 { 717 startRegion = 0; 718 endRegion = 0; 719 RenderRegionRangeMap::const_iterator it = m_regionRangeMap.find(box); 720 if (it == m_regionRangeMap.end()) 721 return; 722 723 const RenderRegionRange& range = it->value; 724 startRegion = range.startRegion(); 725 endRegion = range.endRegion(); 726 ASSERT(m_regionList.contains(startRegion) && m_regionList.contains(endRegion)); 727 } 728 729 void RenderFlowThread::applyBreakAfterContent(LayoutUnit clientHeight) 730 { 731 // Simulate a region break at height. If it points inside an auto logical height region, 732 // then it may determine the region computed autoheight. 733 addForcedRegionBreak(clientHeight, this, false); 734 } 735 736 bool RenderFlowThread::regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const 737 { 738 ASSERT(targetRegion); 739 740 for (RenderRegionList::const_iterator it = m_regionList.find(const_cast<RenderRegion*>(startRegion)); it != m_regionList.end(); ++it) { 741 const RenderRegion* currRegion = *it; 742 if (targetRegion == currRegion) 743 return true; 744 if (currRegion == endRegion) 745 break; 746 } 747 748 return false; 749 } 750 751 // Check if the content is flown into at least a region with region styling rules. 752 void RenderFlowThread::checkRegionsWithStyling() 753 { 754 bool hasRegionsWithStyling = false; 755 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 756 RenderRegion* region = *iter; 757 if (region->hasCustomRegionStyle()) { 758 hasRegionsWithStyling = true; 759 break; 760 } 761 } 762 m_hasRegionsWithStyling = hasRegionsWithStyling; 763 } 764 765 bool RenderFlowThread::objectInFlowRegion(const RenderObject* object, const RenderRegion* region) const 766 { 767 ASSERT(object); 768 ASSERT(region); 769 770 RenderFlowThread* flowThread = object->flowThreadContainingBlock(); 771 if (flowThread != this) 772 return false; 773 if (!m_regionList.contains(const_cast<RenderRegion*>(region))) 774 return false; 775 776 RenderBox* enclosingBox = object->enclosingBox(); 777 RenderRegion* enclosingBoxStartRegion = 0; 778 RenderRegion* enclosingBoxEndRegion = 0; 779 getRegionRangeForBox(enclosingBox, enclosingBoxStartRegion, enclosingBoxEndRegion); 780 if (!regionInRange(region, enclosingBoxStartRegion, enclosingBoxEndRegion)) 781 return false; 782 783 if (object->isBox()) 784 return true; 785 786 LayoutRect objectABBRect = object->absoluteBoundingBoxRect(true); 787 if (!objectABBRect.width()) 788 objectABBRect.setWidth(1); 789 if (!objectABBRect.height()) 790 objectABBRect.setHeight(1); 791 if (objectABBRect.intersects(region->absoluteBoundingBoxRect(true))) 792 return true; 793 794 if (region == lastRegion()) { 795 // If the object does not intersect any of the enclosing box regions 796 // then the object is in last region. 797 for (RenderRegionList::const_iterator it = m_regionList.find(enclosingBoxStartRegion); it != m_regionList.end(); ++it) { 798 const RenderRegion* currRegion = *it; 799 if (currRegion == region) 800 break; 801 if (objectABBRect.intersects(currRegion->absoluteBoundingBoxRect(true))) 802 return false; 803 } 804 return true; 805 } 806 807 return false; 808 } 809 810 #ifndef NDEBUG 811 bool RenderFlowThread::isAutoLogicalHeightRegionsCountConsistent() const 812 { 813 unsigned autoLogicalHeightRegions = 0; 814 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 815 const RenderRegion* region = *iter; 816 if (region->hasAutoLogicalHeight()) 817 autoLogicalHeightRegions++; 818 } 819 820 return autoLogicalHeightRegions == m_autoLogicalHeightRegionsCount; 821 } 822 #endif 823 824 // During the normal layout phase of the named flow the regions are initialized with a height equal to their max-height. 825 // This way unforced breaks are automatically placed when a region is full and the content height/position correctly estimated. 826 // Also, the region where a forced break falls is exactly the region found at the forced break offset inside the flow content. 827 void RenderFlowThread::initializeRegionsComputedAutoHeight(RenderRegion* startRegion) 828 { 829 ASSERT(!inConstrainedLayoutPhase()); 830 if (!hasAutoLogicalHeightRegions()) 831 return; 832 833 RenderRegionList::iterator regionIter = startRegion ? m_regionList.find(startRegion) : m_regionList.begin(); 834 for (; regionIter != m_regionList.end(); ++regionIter) { 835 RenderRegion* region = *regionIter; 836 if (region->hasAutoLogicalHeight()) 837 region->setComputedAutoHeight(region->maxPageLogicalHeight()); 838 } 839 } 840 841 void RenderFlowThread::markAutoLogicalHeightRegionsForLayout() 842 { 843 ASSERT(hasAutoLogicalHeightRegions()); 844 845 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 846 RenderRegion* region = *iter; 847 if (!region->hasAutoLogicalHeight()) 848 continue; 849 850 // FIXME: We need to find a way to avoid marking all the regions ancestors for layout 851 // as we are already inside layout. 852 region->setNeedsLayout(); 853 } 854 } 855 856 void RenderFlowThread::updateRegionsFlowThreadPortionRect(const RenderRegion* lastRegionWithContent) 857 { 858 ASSERT(!lastRegionWithContent || (!inConstrainedLayoutPhase() && hasAutoLogicalHeightRegions())); 859 LayoutUnit logicalHeight = 0; 860 bool emptyRegionsSegment = false; 861 // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle. 862 m_regionIntervalTree.clear(); 863 m_regionIntervalTree.initIfNeeded(); 864 for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 865 RenderRegion* region = *iter; 866 867 // If we find an empty auto-height region, clear the computedAutoHeight value. 868 if (emptyRegionsSegment && region->hasAutoLogicalHeight()) 869 region->clearComputedAutoHeight(); 870 871 LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); 872 LayoutUnit regionLogicalHeight = std::min<LayoutUnit>(LayoutUnit::max() / 2 - logicalHeight, region->logicalHeightOfAllFlowThreadContent()); 873 874 LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight); 875 876 region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect()); 877 878 m_regionIntervalTree.add(RegionIntervalTree::createInterval(logicalHeight, logicalHeight + regionLogicalHeight, region)); 879 880 logicalHeight += regionLogicalHeight; 881 882 // Once we find the last region with content the next regions are considered empty. 883 if (lastRegionWithContent == region) 884 emptyRegionsSegment = true; 885 } 886 887 ASSERT(!lastRegionWithContent || emptyRegionsSegment); 888 } 889 890 // Even if we require the break to occur at offsetBreakInFlowThread, because regions may have min/max-height values, 891 // it is possible that the break will occur at a different offset than the original one required. 892 // offsetBreakAdjustment measures the different between the requested break offset and the current break offset. 893 bool RenderFlowThread::addForcedRegionBreak(LayoutUnit offsetBreakInFlowThread, RenderObject* breakChild, bool isBefore, LayoutUnit* offsetBreakAdjustment) 894 { 895 // We take breaks into account for height computation for auto logical height regions 896 // only in the layout phase in which we lay out the flows threads unconstrained 897 // and we use the content breaks to determine the computedAutoHeight for 898 // auto logical height regions. 899 if (inConstrainedLayoutPhase()) 900 return false; 901 902 // Breaks can come before or after some objects. We need to track these objects, so that if we get 903 // multiple breaks for the same object (for example because of multiple layouts on the same object), 904 // we need to invalidate every other region after the old one and start computing from fresh. 905 RenderObjectToRegionMap& mapToUse = isBefore ? m_breakBeforeToRegionMap : m_breakAfterToRegionMap; 906 RenderObjectToRegionMap::iterator iter = mapToUse.find(breakChild); 907 if (iter != mapToUse.end()) { 908 RenderRegionList::iterator regionIter = m_regionList.find(iter->value); 909 ASSERT(regionIter != m_regionList.end()); 910 ASSERT((*regionIter)->hasAutoLogicalHeight()); 911 initializeRegionsComputedAutoHeight(*regionIter); 912 913 // We need to update the regions flow thread portion rect because we are going to process 914 // a break on these regions. 915 updateRegionsFlowThreadPortionRect(); 916 } 917 918 // Simulate a region break at offsetBreakInFlowThread. If it points inside an auto logical height region, 919 // then it determines the region computed auto height. 920 RenderRegion* region = regionAtBlockOffset(offsetBreakInFlowThread); 921 if (!region) 922 return false; 923 924 bool lastBreakAfterContent = breakChild == this; 925 bool hasComputedAutoHeight = false; 926 927 LayoutUnit currentRegionOffsetInFlowThread = isHorizontalWritingMode() ? region->flowThreadPortionRect().y() : region->flowThreadPortionRect().x(); 928 LayoutUnit offsetBreakInCurrentRegion = offsetBreakInFlowThread - currentRegionOffsetInFlowThread; 929 930 if (region->hasAutoLogicalHeight()) { 931 // A forced break can appear only in an auto-height region that didn't have a forced break before. 932 // This ASSERT is a good-enough heuristic to verify the above condition. 933 ASSERT(region->maxPageLogicalHeight() == region->computedAutoHeight()); 934 935 mapToUse.set(breakChild, region); 936 937 hasComputedAutoHeight = true; 938 939 // Compute the region height pretending that the offsetBreakInCurrentRegion is the logicalHeight for the auto-height region. 940 LayoutUnit regionComputedAutoHeight = region->constrainContentBoxLogicalHeightByMinMax(offsetBreakInCurrentRegion, -1); 941 942 // The new height of this region needs to be smaller than the initial value, the max height. A forced break is the only way to change the initial 943 // height of an auto-height region besides content ending. 944 ASSERT(regionComputedAutoHeight <= region->maxPageLogicalHeight()); 945 946 region->setComputedAutoHeight(regionComputedAutoHeight); 947 948 currentRegionOffsetInFlowThread += regionComputedAutoHeight; 949 } else 950 currentRegionOffsetInFlowThread += isHorizontalWritingMode() ? region->flowThreadPortionRect().height() : region->flowThreadPortionRect().width(); 951 952 // If the break was found inside an auto-height region its size changed so we need to recompute the flow thread portion rectangles. 953 // Also, if this is the last break after the content we need to clear the computedAutoHeight value on the last empty regions. 954 if (hasAutoLogicalHeightRegions() && lastBreakAfterContent) 955 updateRegionsFlowThreadPortionRect(region); 956 else if (hasComputedAutoHeight) 957 updateRegionsFlowThreadPortionRect(); 958 959 if (offsetBreakAdjustment) 960 *offsetBreakAdjustment = max<LayoutUnit>(0, currentRegionOffsetInFlowThread - offsetBreakInFlowThread); 961 962 return hasComputedAutoHeight; 963 } 964 965 void RenderFlowThread::incrementAutoLogicalHeightRegions() 966 { 967 if (!m_autoLogicalHeightRegionsCount) 968 view()->flowThreadController()->incrementFlowThreadsWithAutoLogicalHeightRegions(); 969 ++m_autoLogicalHeightRegionsCount; 970 } 971 972 void RenderFlowThread::decrementAutoLogicalHeightRegions() 973 { 974 ASSERT(m_autoLogicalHeightRegionsCount > 0); 975 --m_autoLogicalHeightRegionsCount; 976 if (!m_autoLogicalHeightRegionsCount) 977 view()->flowThreadController()->decrementFlowThreadsWithAutoLogicalHeightRegions(); 978 } 979 980 void RenderFlowThread::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) 981 { 982 ASSERT(!m_regionsInvalidated); 983 984 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 985 RenderRegion* region = *iter; 986 region->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect); 987 } 988 } 989 990 LayoutRect RenderFlowThread::fragmentsBoundingBox(const LayoutRect& layerBoundingBox) 991 { 992 ASSERT(!m_regionsInvalidated); 993 994 LayoutRect result; 995 for (RenderRegionList::const_iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { 996 RenderRegion* region = *iter; 997 LayerFragments fragments; 998 region->collectLayerFragments(fragments, layerBoundingBox, PaintInfo::infiniteRect()); 999 for (size_t i = 0; i < fragments.size(); ++i) { 1000 const LayerFragment& fragment = fragments.at(i); 1001 LayoutRect fragmentRect(layerBoundingBox); 1002 fragmentRect.intersect(fragment.paginationClip); 1003 fragmentRect.moveBy(fragment.paginationOffset); 1004 result.unite(fragmentRect); 1005 } 1006 } 1007 1008 return result; 1009 } 1010 1011 void RenderFlowThread::RegionSearchAdapter::collectIfNeeded(const RegionInterval& interval) 1012 { 1013 if (m_result) 1014 return; 1015 if (interval.low() <= m_offset && interval.high() > m_offset) 1016 m_result = interval.data(); 1017 } 1018 1019 void RenderFlowThread::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const 1020 { 1021 if (this == repaintContainer) 1022 return; 1023 1024 if (RenderRegion* region = mapFromFlowToRegion(transformState)) { 1025 // FIXME: The cast below is probably not the best solution, we may need to find a better way. 1026 static_cast<const RenderObject*>(region)->mapLocalToContainer(region->containerForRepaint(), transformState, mode, wasFixed); 1027 } 1028 } 1029 1030 CurrentRenderFlowThreadMaintainer::CurrentRenderFlowThreadMaintainer(RenderFlowThread* renderFlowThread) 1031 : m_renderFlowThread(renderFlowThread) 1032 , m_previousRenderFlowThread(0) 1033 { 1034 if (!m_renderFlowThread) 1035 return; 1036 RenderView* view = m_renderFlowThread->view(); 1037 m_previousRenderFlowThread = view->flowThreadController()->currentRenderFlowThread(); 1038 ASSERT(!m_previousRenderFlowThread || !renderFlowThread->isRenderNamedFlowThread()); 1039 view->flowThreadController()->setCurrentRenderFlowThread(m_renderFlowThread); 1040 } 1041 1042 CurrentRenderFlowThreadMaintainer::~CurrentRenderFlowThreadMaintainer() 1043 { 1044 if (!m_renderFlowThread) 1045 return; 1046 RenderView* view = m_renderFlowThread->view(); 1047 ASSERT(view->flowThreadController()->currentRenderFlowThread() == m_renderFlowThread); 1048 view->flowThreadController()->setCurrentRenderFlowThread(m_previousRenderFlowThread); 1049 } 1050 1051 1052 } // namespace WebCore 1053