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