1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2005 Allan Sandfeld Jensen (kde (at) carewolf.com) 5 * (C) 2005, 2006 Samuel Weinig (sam.weinig (at) gmail.com) 6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 7 * Copyright (C) 2010 Google Inc. All rights reserved. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 * 24 */ 25 26 #include "config.h" 27 #include "core/rendering/RenderBoxModelObject.h" 28 29 #include "core/page/scrolling/ScrollingConstraints.h" 30 #include "core/rendering/ImageQualityController.h" 31 #include "core/rendering/RenderBlock.h" 32 #include "core/rendering/RenderFlowThread.h" 33 #include "core/rendering/RenderGeometryMap.h" 34 #include "core/rendering/RenderInline.h" 35 #include "core/rendering/RenderLayer.h" 36 #include "core/rendering/RenderObjectInlines.h" 37 #include "core/rendering/RenderRegion.h" 38 #include "core/rendering/RenderTextFragment.h" 39 #include "core/rendering/RenderView.h" 40 #include "core/rendering/compositing/CompositedLayerMapping.h" 41 #include "core/rendering/compositing/RenderLayerCompositor.h" 42 #include "core/rendering/style/BorderEdge.h" 43 #include "core/rendering/style/ShadowList.h" 44 #include "platform/LengthFunctions.h" 45 #include "platform/geometry/TransformState.h" 46 #include "platform/graphics/DrawLooperBuilder.h" 47 #include "platform/graphics/GraphicsContextStateSaver.h" 48 #include "platform/graphics/Path.h" 49 #include "wtf/CurrentTime.h" 50 51 namespace blink { 52 53 // The HashMap for storing continuation pointers. 54 // An inline can be split with blocks occuring in between the inline content. 55 // When this occurs we need a pointer to the next object. We can basically be 56 // split into a sequence of inlines and blocks. The continuation will either be 57 // an anonymous block (that houses other blocks) or it will be an inline flow. 58 // <b><i><p>Hello</p></i></b>. In this example the <i> will have a block as 59 // its continuation but the <b> will just have an inline as its continuation. 60 typedef WillBeHeapHashMap<RawPtrWillBeMember<const RenderBoxModelObject>, RawPtrWillBeMember<RenderBoxModelObject> > ContinuationMap; 61 static OwnPtrWillBePersistent<ContinuationMap>* continuationMap = 0; 62 63 // This HashMap is similar to the continuation map, but connects first-letter 64 // renderers to their remaining text fragments. 65 typedef WillBeHeapHashMap<RawPtrWillBeMember<const RenderBoxModelObject>, RawPtrWillBeMember<RenderTextFragment> > FirstLetterRemainingTextMap; 66 static OwnPtrWillBePersistent<FirstLetterRemainingTextMap>* firstLetterRemainingTextMap = 0; 67 68 void RenderBoxModelObject::setSelectionState(SelectionState state) 69 { 70 if (state == SelectionInside && selectionState() != SelectionNone) 71 return; 72 73 if ((state == SelectionStart && selectionState() == SelectionEnd) 74 || (state == SelectionEnd && selectionState() == SelectionStart)) 75 RenderObject::setSelectionState(SelectionBoth); 76 else 77 RenderObject::setSelectionState(state); 78 79 // FIXME: We should consider whether it is OK propagating to ancestor RenderInlines. 80 // This is a workaround for http://webkit.org/b/32123 81 // The containing block can be null in case of an orphaned tree. 82 RenderBlock* containingBlock = this->containingBlock(); 83 if (containingBlock && !containingBlock->isRenderView()) 84 containingBlock->setSelectionState(state); 85 } 86 87 void RenderBoxModelObject::contentChanged(ContentChangeType changeType) 88 { 89 if (!hasLayer()) 90 return; 91 92 layer()->contentChanged(changeType); 93 } 94 95 bool RenderBoxModelObject::hasAcceleratedCompositing() const 96 { 97 return view()->compositor()->hasAcceleratedCompositing(); 98 } 99 100 RenderBoxModelObject::RenderBoxModelObject(ContainerNode* node) 101 : RenderLayerModelObject(node) 102 { 103 } 104 105 RenderBoxModelObject::~RenderBoxModelObject() 106 { 107 } 108 109 void RenderBoxModelObject::willBeDestroyed() 110 { 111 ImageQualityController::remove(this); 112 113 // A continuation of this RenderObject should be destroyed at subclasses. 114 ASSERT(!continuation()); 115 116 // If this is a first-letter object with a remaining text fragment then the 117 // entry needs to be cleared from the map. 118 if (firstLetterRemainingText()) 119 setFirstLetterRemainingText(0); 120 121 RenderLayerModelObject::willBeDestroyed(); 122 } 123 124 bool RenderBoxModelObject::calculateHasBoxDecorations() const 125 { 126 RenderStyle* styleToUse = style(); 127 ASSERT(styleToUse); 128 return hasBackground() || styleToUse->hasBorder() || styleToUse->hasAppearance() || styleToUse->boxShadow(); 129 } 130 131 void RenderBoxModelObject::updateFromStyle() 132 { 133 RenderLayerModelObject::updateFromStyle(); 134 135 RenderStyle* styleToUse = style(); 136 setHasBoxDecorationBackground(calculateHasBoxDecorations()); 137 setInline(styleToUse->isDisplayInlineType()); 138 setPositionState(styleToUse->position()); 139 setHorizontalWritingMode(styleToUse->isHorizontalWritingMode()); 140 } 141 142 static LayoutSize accumulateInFlowPositionOffsets(const RenderObject* child) 143 { 144 if (!child->isAnonymousBlock() || !child->isRelPositioned()) 145 return LayoutSize(); 146 LayoutSize offset; 147 RenderObject* p = toRenderBlock(child)->inlineElementContinuation(); 148 while (p && p->isRenderInline()) { 149 if (p->isRelPositioned()) { 150 RenderInline* renderInline = toRenderInline(p); 151 offset += renderInline->offsetForInFlowPosition(); 152 } 153 p = p->parent(); 154 } 155 return offset; 156 } 157 158 bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() const 159 { 160 Length logicalHeightLength = style()->logicalHeight(); 161 if (logicalHeightLength.isAuto()) 162 return true; 163 164 // For percentage heights: The percentage is calculated with respect to the height of the generated box's 165 // containing block. If the height of the containing block is not specified explicitly (i.e., it depends 166 // on content height), and this element is not absolutely positioned, the value computes to 'auto'. 167 if (!logicalHeightLength.isPercent() || isOutOfFlowPositioned() || document().inQuirksMode()) 168 return false; 169 170 // Anonymous block boxes are ignored when resolving percentage values that would refer to it: 171 // the closest non-anonymous ancestor box is used instead. 172 RenderBlock* cb = containingBlock(); 173 while (cb->isAnonymous()) 174 cb = cb->containingBlock(); 175 176 // Matching RenderBox::percentageLogicalHeightIsResolvableFromBlock() by 177 // ignoring table cell's attribute value, where it says that table cells violate 178 // what the CSS spec says to do with heights. Basically we 179 // don't care if the cell specified a height or not. 180 if (cb->isTableCell()) 181 return false; 182 183 // Match RenderBox::availableLogicalHeightUsing by special casing 184 // the render view. The available height is taken from the frame. 185 if (cb->isRenderView()) 186 return false; 187 188 if (cb->isOutOfFlowPositioned() && !cb->style()->logicalTop().isAuto() && !cb->style()->logicalBottom().isAuto()) 189 return false; 190 191 // If the height of the containing block computes to 'auto', then it hasn't been 'specified explicitly'. 192 return cb->hasAutoHeightOrContainingBlockWithAutoHeight(); 193 } 194 195 LayoutSize RenderBoxModelObject::relativePositionOffset() const 196 { 197 LayoutSize offset = accumulateInFlowPositionOffsets(this); 198 199 RenderBlock* containingBlock = this->containingBlock(); 200 201 // Objects that shrink to avoid floats normally use available line width when computing containing block width. However 202 // in the case of relative positioning using percentages, we can't do this. The offset should always be resolved using the 203 // available width of the containing block. Therefore we don't use containingBlockLogicalWidthForContent() here, but instead explicitly 204 // call availableWidth on our containing block. 205 if (!style()->left().isAuto()) { 206 if (!style()->right().isAuto() && !containingBlock->style()->isLeftToRightDirection()) 207 offset.setWidth(-valueForLength(style()->right(), containingBlock->availableWidth())); 208 else 209 offset.expand(valueForLength(style()->left(), containingBlock->availableWidth()), 0); 210 } else if (!style()->right().isAuto()) { 211 offset.expand(-valueForLength(style()->right(), containingBlock->availableWidth()), 0); 212 } 213 214 // If the containing block of a relatively positioned element does not 215 // specify a height, a percentage top or bottom offset should be resolved as 216 // auto. An exception to this is if the containing block has the WinIE quirk 217 // where <html> and <body> assume the size of the viewport. In this case, 218 // calculate the percent offset based on this height. 219 // See <https://bugs.webkit.org/show_bug.cgi?id=26396>. 220 if (!style()->top().isAuto() 221 && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() 222 || !style()->top().isPercent() 223 || containingBlock->stretchesToViewport())) 224 offset.expand(0, valueForLength(style()->top(), containingBlock->availableHeight())); 225 226 else if (!style()->bottom().isAuto() 227 && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() 228 || !style()->bottom().isPercent() 229 || containingBlock->stretchesToViewport())) 230 offset.expand(0, -valueForLength(style()->bottom(), containingBlock->availableHeight())); 231 232 return offset; 233 } 234 235 LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const LayoutPoint& startPoint) const 236 { 237 // If the element is the HTML body element or doesn't have a parent 238 // return 0 and stop this algorithm. 239 if (isBody() || !parent()) 240 return LayoutPoint(); 241 242 LayoutPoint referencePoint = startPoint; 243 referencePoint.move(parent()->columnOffset(referencePoint)); 244 245 // If the offsetParent of the element is null, or is the HTML body element, 246 // return the distance between the canvas origin and the left border edge 247 // of the element and stop this algorithm. 248 Element* element = offsetParent(); 249 if (!element) 250 return referencePoint; 251 252 if (const RenderBoxModelObject* offsetParent = element->renderBoxModelObject()) { 253 if (offsetParent->isBox() && !offsetParent->isBody()) 254 referencePoint.move(-toRenderBox(offsetParent)->borderLeft(), -toRenderBox(offsetParent)->borderTop()); 255 if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) { 256 if (isRelPositioned()) 257 referencePoint.move(relativePositionOffset()); 258 259 RenderObject* current; 260 for (current = parent(); current != offsetParent && current->parent(); current = current->parent()) { 261 // FIXME: What are we supposed to do inside SVG content? 262 if (!isOutOfFlowPositioned()) { 263 if (current->isBox() && !current->isTableRow()) 264 referencePoint.moveBy(toRenderBox(current)->topLeftLocation()); 265 referencePoint.move(current->parent()->columnOffset(referencePoint)); 266 } 267 } 268 269 if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isPositioned()) 270 referencePoint.moveBy(toRenderBox(offsetParent)->topLeftLocation()); 271 } 272 } 273 274 return referencePoint; 275 } 276 277 LayoutSize RenderBoxModelObject::offsetForInFlowPosition() const 278 { 279 return isRelPositioned() ? relativePositionOffset() : LayoutSize(); 280 } 281 282 LayoutUnit RenderBoxModelObject::offsetLeft() const 283 { 284 // Note that RenderInline and RenderBox override this to pass a different 285 // startPoint to adjustedPositionRelativeToOffsetParent. 286 return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x(); 287 } 288 289 LayoutUnit RenderBoxModelObject::offsetTop() const 290 { 291 // Note that RenderInline and RenderBox override this to pass a different 292 // startPoint to adjustedPositionRelativeToOffsetParent. 293 return adjustedPositionRelativeToOffsetParent(LayoutPoint()).y(); 294 } 295 296 int RenderBoxModelObject::pixelSnappedOffsetWidth() const 297 { 298 return snapSizeToPixel(offsetWidth(), offsetLeft()); 299 } 300 301 int RenderBoxModelObject::pixelSnappedOffsetHeight() const 302 { 303 return snapSizeToPixel(offsetHeight(), offsetTop()); 304 } 305 306 LayoutUnit RenderBoxModelObject::computedCSSPadding(const Length& padding) const 307 { 308 LayoutUnit w = 0; 309 if (padding.isPercent()) 310 w = containingBlockLogicalWidthForContent(); 311 return minimumValueForLength(padding, w); 312 } 313 314 static inline int resolveWidthForRatio(int height, const FloatSize& intrinsicRatio) 315 { 316 return ceilf(height * intrinsicRatio.width() / intrinsicRatio.height()); 317 } 318 319 static inline int resolveHeightForRatio(int width, const FloatSize& intrinsicRatio) 320 { 321 return ceilf(width * intrinsicRatio.height() / intrinsicRatio.width()); 322 } 323 324 static inline IntSize resolveAgainstIntrinsicWidthOrHeightAndRatio(const IntSize& size, const FloatSize& intrinsicRatio, int useWidth, int useHeight) 325 { 326 if (intrinsicRatio.isEmpty()) { 327 if (useWidth) 328 return IntSize(useWidth, size.height()); 329 return IntSize(size.width(), useHeight); 330 } 331 332 if (useWidth) 333 return IntSize(useWidth, resolveHeightForRatio(useWidth, intrinsicRatio)); 334 return IntSize(resolveWidthForRatio(useHeight, intrinsicRatio), useHeight); 335 } 336 337 static inline IntSize resolveAgainstIntrinsicRatio(const IntSize& size, const FloatSize& intrinsicRatio) 338 { 339 // Two possible solutions: (size.width(), solutionHeight) or (solutionWidth, size.height()) 340 // "... must be assumed to be the largest dimensions..." = easiest answer: the rect with the largest surface area. 341 342 int solutionWidth = resolveWidthForRatio(size.height(), intrinsicRatio); 343 int solutionHeight = resolveHeightForRatio(size.width(), intrinsicRatio); 344 if (solutionWidth <= size.width()) { 345 if (solutionHeight <= size.height()) { 346 // If both solutions fit, choose the one covering the larger area. 347 int areaOne = solutionWidth * size.height(); 348 int areaTwo = size.width() * solutionHeight; 349 if (areaOne < areaTwo) 350 return IntSize(size.width(), solutionHeight); 351 return IntSize(solutionWidth, size.height()); 352 } 353 354 // Only the first solution fits. 355 return IntSize(solutionWidth, size.height()); 356 } 357 358 // Only the second solution fits, assert that. 359 ASSERT(solutionHeight <= size.height()); 360 return IntSize(size.width(), solutionHeight); 361 } 362 363 IntSize RenderBoxModelObject::calculateImageIntrinsicDimensions(StyleImage* image, const IntSize& positioningAreaSize, ScaleByEffectiveZoomOrNot shouldScaleOrNot) const 364 { 365 // A generated image without a fixed size, will always return the container size as intrinsic size. 366 if (image->isGeneratedImage() && image->usesImageContainerSize()) 367 return IntSize(positioningAreaSize.width(), positioningAreaSize.height()); 368 369 Length intrinsicWidth; 370 Length intrinsicHeight; 371 FloatSize intrinsicRatio; 372 image->computeIntrinsicDimensions(this, intrinsicWidth, intrinsicHeight, intrinsicRatio); 373 374 ASSERT(!intrinsicWidth.isPercent()); 375 ASSERT(!intrinsicHeight.isPercent()); 376 377 IntSize resolvedSize(intrinsicWidth.value(), intrinsicHeight.value()); 378 IntSize minimumSize(resolvedSize.width() > 0 ? 1 : 0, resolvedSize.height() > 0 ? 1 : 0); 379 if (shouldScaleOrNot == ScaleByEffectiveZoom) 380 resolvedSize.scale(style()->effectiveZoom()); 381 resolvedSize.clampToMinimumSize(minimumSize); 382 383 if (!resolvedSize.isEmpty()) 384 return resolvedSize; 385 386 // If the image has one of either an intrinsic width or an intrinsic height: 387 // * and an intrinsic aspect ratio, then the missing dimension is calculated from the given dimension and the ratio. 388 // * and no intrinsic aspect ratio, then the missing dimension is assumed to be the size of the rectangle that 389 // establishes the coordinate system for the 'background-position' property. 390 if (resolvedSize.width() > 0 || resolvedSize.height() > 0) 391 return resolveAgainstIntrinsicWidthOrHeightAndRatio(positioningAreaSize, intrinsicRatio, resolvedSize.width(), resolvedSize.height()); 392 393 // If the image has no intrinsic dimensions and has an intrinsic ratio the dimensions must be assumed to be the 394 // largest dimensions at that ratio such that neither dimension exceeds the dimensions of the rectangle that 395 // establishes the coordinate system for the 'background-position' property. 396 if (!intrinsicRatio.isEmpty()) 397 return resolveAgainstIntrinsicRatio(positioningAreaSize, intrinsicRatio); 398 399 // If the image has no intrinsic ratio either, then the dimensions must be assumed to be the rectangle that 400 // establishes the coordinate system for the 'background-position' property. 401 return positioningAreaSize; 402 } 403 404 bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* inlineFlowBox) const 405 { 406 if (bleedAvoidance != BackgroundBleedNone) 407 return false; 408 409 if (style()->hasAppearance()) 410 return false; 411 412 const ShadowList* shadowList = style()->boxShadow(); 413 if (!shadowList) 414 return false; 415 416 bool hasOneNormalBoxShadow = false; 417 size_t shadowCount = shadowList->shadows().size(); 418 for (size_t i = 0; i < shadowCount; ++i) { 419 const ShadowData& currentShadow = shadowList->shadows()[i]; 420 if (currentShadow.style() != Normal) 421 continue; 422 423 if (hasOneNormalBoxShadow) 424 return false; 425 hasOneNormalBoxShadow = true; 426 427 if (currentShadow.spread()) 428 return false; 429 } 430 431 if (!hasOneNormalBoxShadow) 432 return false; 433 434 Color backgroundColor = resolveColor(CSSPropertyBackgroundColor); 435 if (backgroundColor.hasAlpha()) 436 return false; 437 438 const FillLayer* lastBackgroundLayer = &style()->backgroundLayers(); 439 for (const FillLayer* next = lastBackgroundLayer->next(); next; next = lastBackgroundLayer->next()) 440 lastBackgroundLayer = next; 441 442 if (lastBackgroundLayer->clip() != BorderFillBox) 443 return false; 444 445 if (lastBackgroundLayer->image() && style()->hasBorderRadius()) 446 return false; 447 448 if (inlineFlowBox && !inlineFlowBox->boxShadowCanBeAppliedToBackground(*lastBackgroundLayer)) 449 return false; 450 451 if (hasOverflowClip() && lastBackgroundLayer->attachment() == LocalBackgroundAttachment) 452 return false; 453 454 return true; 455 } 456 457 458 459 LayoutUnit RenderBoxModelObject::containingBlockLogicalWidthForContent() const 460 { 461 return containingBlock()->availableLogicalWidth(); 462 } 463 464 RenderBoxModelObject* RenderBoxModelObject::continuation() const 465 { 466 if (!continuationMap) 467 return 0; 468 return (*continuationMap)->get(this); 469 } 470 471 void RenderBoxModelObject::setContinuation(RenderBoxModelObject* continuation) 472 { 473 if (continuation) { 474 if (!continuationMap) 475 continuationMap = new OwnPtrWillBePersistent<ContinuationMap>(adoptPtrWillBeNoop(new ContinuationMap)); 476 (*continuationMap)->set(this, continuation); 477 } else { 478 if (continuationMap) 479 (*continuationMap)->remove(this); 480 } 481 } 482 483 void RenderBoxModelObject::computeLayerHitTestRects(LayerHitTestRects& rects) const 484 { 485 RenderLayerModelObject::computeLayerHitTestRects(rects); 486 487 // If there is a continuation then we need to consult it here, since this is 488 // the root of the tree walk and it wouldn't otherwise get picked up. 489 // Continuations should always be siblings in the tree, so any others should 490 // get picked up already by the tree walk. 491 if (continuation()) 492 continuation()->computeLayerHitTestRects(rects); 493 } 494 495 RenderTextFragment* RenderBoxModelObject::firstLetterRemainingText() const 496 { 497 if (!firstLetterRemainingTextMap) 498 return 0; 499 return (*firstLetterRemainingTextMap)->get(this); 500 } 501 502 void RenderBoxModelObject::setFirstLetterRemainingText(RenderTextFragment* remainingText) 503 { 504 if (remainingText) { 505 if (!firstLetterRemainingTextMap) 506 firstLetterRemainingTextMap = new OwnPtrWillBePersistent<FirstLetterRemainingTextMap>(adoptPtrWillBeNoop(new FirstLetterRemainingTextMap)); 507 (*firstLetterRemainingTextMap)->set(this, remainingText); 508 } else if (firstLetterRemainingTextMap) { 509 (*firstLetterRemainingTextMap)->remove(this); 510 } 511 } 512 513 LayoutRect RenderBoxModelObject::localCaretRectForEmptyElement(LayoutUnit width, LayoutUnit textIndentOffset) 514 { 515 ASSERT(!slowFirstChild()); 516 517 // FIXME: This does not take into account either :first-line or :first-letter 518 // However, as soon as some content is entered, the line boxes will be 519 // constructed and this kludge is not called any more. So only the caret size 520 // of an empty :first-line'd block is wrong. I think we can live with that. 521 RenderStyle* currentStyle = firstLineStyle(); 522 523 enum CaretAlignment { alignLeft, alignRight, alignCenter }; 524 525 CaretAlignment alignment = alignLeft; 526 527 switch (currentStyle->textAlign()) { 528 case LEFT: 529 case WEBKIT_LEFT: 530 break; 531 case CENTER: 532 case WEBKIT_CENTER: 533 alignment = alignCenter; 534 break; 535 case RIGHT: 536 case WEBKIT_RIGHT: 537 alignment = alignRight; 538 break; 539 case JUSTIFY: 540 case TASTART: 541 if (!currentStyle->isLeftToRightDirection()) 542 alignment = alignRight; 543 break; 544 case TAEND: 545 if (currentStyle->isLeftToRightDirection()) 546 alignment = alignRight; 547 break; 548 } 549 550 LayoutUnit x = borderLeft() + paddingLeft(); 551 LayoutUnit maxX = width - borderRight() - paddingRight(); 552 553 switch (alignment) { 554 case alignLeft: 555 if (currentStyle->isLeftToRightDirection()) 556 x += textIndentOffset; 557 break; 558 case alignCenter: 559 x = (x + maxX) / 2; 560 if (currentStyle->isLeftToRightDirection()) 561 x += textIndentOffset / 2; 562 else 563 x -= textIndentOffset / 2; 564 break; 565 case alignRight: 566 x = maxX - caretWidth; 567 if (!currentStyle->isLeftToRightDirection()) 568 x -= textIndentOffset; 569 break; 570 } 571 x = std::min(x, std::max<LayoutUnit>(maxX - caretWidth, 0)); 572 573 LayoutUnit height = style()->fontMetrics().height(); 574 LayoutUnit verticalSpace = lineHeight(true, currentStyle->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes) - height; 575 LayoutUnit y = paddingTop() + borderTop() + (verticalSpace / 2); 576 return currentStyle->isHorizontalWritingMode() ? LayoutRect(x, y, caretWidth, height) : LayoutRect(y, x, height, caretWidth); 577 } 578 579 void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const 580 { 581 RenderObject* o = container(); 582 if (!o) 583 return; 584 585 if (o->isRenderFlowThread()) 586 transformState.move(o->columnOffset(LayoutPoint(transformState.mappedPoint()))); 587 588 o->mapAbsoluteToLocalPoint(mode, transformState); 589 590 LayoutSize containerOffset = offsetFromContainer(o, LayoutPoint()); 591 592 if (!style()->hasOutOfFlowPosition() && o->hasColumns()) { 593 RenderBlock* block = toRenderBlock(o); 594 LayoutPoint point(roundedLayoutPoint(transformState.mappedPoint())); 595 point -= containerOffset; 596 block->adjustForColumnRect(containerOffset, point); 597 } 598 599 bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D()); 600 if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { 601 TransformationMatrix t; 602 getTransformFromContainer(o, containerOffset, t); 603 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 604 } else 605 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 606 } 607 608 const RenderObject* RenderBoxModelObject::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const 609 { 610 ASSERT(ancestorToStopAt != this); 611 612 bool ancestorSkipped; 613 RenderObject* container = this->container(ancestorToStopAt, &ancestorSkipped); 614 if (!container) 615 return 0; 616 617 bool isInline = isRenderInline(); 618 bool isFixedPos = !isInline && style()->position() == FixedPosition; 619 bool hasTransform = !isInline && hasLayer() && layer()->transform(); 620 621 LayoutSize adjustmentForSkippedAncestor; 622 if (ancestorSkipped) { 623 // There can't be a transform between paintInvalidationContainer and o, because transforms create containers, so it should be safe 624 // to just subtract the delta between the ancestor and o. 625 adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(container); 626 } 627 628 bool offsetDependsOnPoint = false; 629 LayoutSize containerOffset = offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint); 630 631 bool preserve3D = container->style()->preserves3D() || style()->preserves3D(); 632 if (shouldUseTransformFromContainer(container)) { 633 TransformationMatrix t; 634 getTransformFromContainer(container, containerOffset, t); 635 t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustmentForSkippedAncestor.height().toFloat()); 636 geometryMap.push(this, t, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform); 637 } else { 638 containerOffset += adjustmentForSkippedAncestor; 639 geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform); 640 } 641 642 return ancestorSkipped ? ancestorToStopAt : container; 643 } 644 645 void RenderBoxModelObject::moveChildTo(RenderBoxModelObject* toBoxModelObject, RenderObject* child, RenderObject* beforeChild, bool fullRemoveInsert) 646 { 647 // We assume that callers have cleared their positioned objects list for child moves (!fullRemoveInsert) so the 648 // positioned renderer maps don't become stale. It would be too slow to do the map lookup on each call. 649 ASSERT(!fullRemoveInsert || !isRenderBlock() || !toRenderBlock(this)->hasPositionedObjects()); 650 651 ASSERT(this == child->parent()); 652 ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); 653 if (fullRemoveInsert && (toBoxModelObject->isRenderBlock() || toBoxModelObject->isRenderInline())) { 654 // Takes care of adding the new child correctly if toBlock and fromBlock 655 // have different kind of children (block vs inline). 656 toBoxModelObject->addChild(virtualChildren()->removeChildNode(this, child), beforeChild); 657 } else 658 toBoxModelObject->virtualChildren()->insertChildNode(toBoxModelObject, virtualChildren()->removeChildNode(this, child, fullRemoveInsert), beforeChild, fullRemoveInsert); 659 } 660 661 void RenderBoxModelObject::moveChildrenTo(RenderBoxModelObject* toBoxModelObject, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, bool fullRemoveInsert) 662 { 663 // This condition is rarely hit since this function is usually called on 664 // anonymous blocks which can no longer carry positioned objects (see r120761) 665 // or when fullRemoveInsert is false. 666 if (fullRemoveInsert && isRenderBlock()) { 667 RenderBlock* block = toRenderBlock(this); 668 block->removePositionedObjects(0); 669 if (block->isRenderBlockFlow()) 670 toRenderBlockFlow(block)->removeFloatingObjects(); 671 } 672 673 ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); 674 for (RenderObject* child = startChild; child && child != endChild; ) { 675 // Save our next sibling as moveChildTo will clear it. 676 RenderObject* nextSibling = child->nextSibling(); 677 moveChildTo(toBoxModelObject, child, beforeChild, fullRemoveInsert); 678 child = nextSibling; 679 } 680 } 681 682 } // namespace blink 683