1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * Copyright (C) 2000 Dirk Mueller (mueller@kde.org) 4 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. 5 * Copyright (C) Research In Motion Limited 2011-2012. All rights reserved. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24#include "config.h" 25#include "RenderReplaced.h" 26 27#include "FloatRoundedRect.h" 28#include "Frame.h" 29#include "GraphicsContext.h" 30#include "HTMLElement.h" 31#include "InlineElementBox.h" 32#include "LayoutRepainter.h" 33#include "Page.h" 34#include "RenderBlock.h" 35#include "RenderFlowThread.h" 36#include "RenderImage.h" 37#include "RenderLayer.h" 38#include "RenderNamedFlowFragment.h" 39#include "RenderTheme.h" 40#include "RenderView.h" 41#include "VisiblePosition.h" 42#include <wtf/StackStats.h> 43 44namespace WebCore { 45 46const int cDefaultWidth = 300; 47const int cDefaultHeight = 150; 48 49RenderReplaced::RenderReplaced(Element& element, PassRef<RenderStyle> style) 50 : RenderBox(element, WTF::move(style), RenderReplacedFlag) 51 , m_intrinsicSize(cDefaultWidth, cDefaultHeight) 52{ 53 setReplaced(true); 54} 55 56RenderReplaced::RenderReplaced(Element& element, PassRef<RenderStyle> style, const LayoutSize& intrinsicSize) 57 : RenderBox(element, WTF::move(style), RenderReplacedFlag) 58 , m_intrinsicSize(intrinsicSize) 59{ 60 setReplaced(true); 61} 62 63RenderReplaced::RenderReplaced(Document& document, PassRef<RenderStyle> style, const LayoutSize& intrinsicSize) 64 : RenderBox(document, WTF::move(style), RenderReplacedFlag) 65 , m_intrinsicSize(intrinsicSize) 66{ 67 setReplaced(true); 68} 69 70RenderReplaced::~RenderReplaced() 71{ 72} 73 74void RenderReplaced::willBeDestroyed() 75{ 76 if (!documentBeingDestroyed() && parent()) 77 parent()->dirtyLinesFromChangedChild(this); 78 79 RenderBox::willBeDestroyed(); 80} 81 82void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 83{ 84 RenderBox::styleDidChange(diff, oldStyle); 85 86 bool hadStyle = (oldStyle != 0); 87 float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom(); 88 if (style().effectiveZoom() != oldZoom) 89 intrinsicSizeChanged(); 90} 91 92void RenderReplaced::layout() 93{ 94 StackStats::LayoutCheckPoint layoutCheckPoint; 95 ASSERT(needsLayout()); 96 97 LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); 98 99 setHeight(minimumReplacedHeight()); 100 101 updateLogicalWidth(); 102 updateLogicalHeight(); 103 104 // Now that we've calculated our preferred layout, we check to see 105 // if we should further constrain sizing to the intrinsic aspect ratio. 106 if (style().aspectRatioType() == AspectRatioFromIntrinsic && !m_intrinsicSize.isEmpty()) { 107 float aspectRatio = m_intrinsicSize.aspectRatio(); 108 LayoutSize frameSize = size(); 109 float frameAspectRatio = frameSize.aspectRatio(); 110 if (frameAspectRatio < aspectRatio) 111 setHeight(computeReplacedLogicalHeightRespectingMinMaxHeight(frameSize.height() * frameAspectRatio / aspectRatio)); 112 else if (frameAspectRatio > aspectRatio) 113 setWidth(computeReplacedLogicalWidthRespectingMinMaxWidth(frameSize.width() * aspectRatio / frameAspectRatio, ComputePreferred)); 114 } 115 116 clearOverflow(); 117 addVisualEffectOverflow(); 118 updateLayerTransform(); 119 invalidateBackgroundObscurationStatus(); 120 121 repainter.repaintAfterLayout(); 122 clearNeedsLayout(); 123} 124 125void RenderReplaced::intrinsicSizeChanged() 126{ 127 int scaledWidth = static_cast<int>(cDefaultWidth * style().effectiveZoom()); 128 int scaledHeight = static_cast<int>(cDefaultHeight * style().effectiveZoom()); 129 m_intrinsicSize = IntSize(scaledWidth, scaledHeight); 130 setNeedsLayoutAndPrefWidthsRecalc(); 131} 132 133void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 134{ 135 if (!shouldPaint(paintInfo, paintOffset)) 136 return; 137 138 LayoutPoint adjustedPaintOffset = paintOffset + location(); 139 140 if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 141 paintBoxDecorations(paintInfo, adjustedPaintOffset); 142 143 if (paintInfo.phase == PaintPhaseMask) { 144 paintMask(paintInfo, adjustedPaintOffset); 145 return; 146 } 147 148 LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size()); 149 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style().outlineWidth()) 150 paintOutline(paintInfo, paintRect); 151 152 if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && !canHaveChildren()) 153 return; 154 155 if (!paintInfo.shouldPaintWithinRoot(*this)) 156 return; 157 158 bool drawSelectionTint = selectionState() != SelectionNone && !document().printing(); 159 if (paintInfo.phase == PaintPhaseSelection) { 160 if (selectionState() == SelectionNone) 161 return; 162 drawSelectionTint = false; 163 } 164 165 bool completelyClippedOut = false; 166 if (style().hasBorderRadius()) { 167 LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size()); 168 169 if (borderRect.isEmpty()) 170 completelyClippedOut = true; 171 else { 172 // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. 173 paintInfo.context->save(); 174 FloatRoundedRect roundedInnerRect = FloatRoundedRect(style().getRoundedInnerBorderFor(paintRect, 175 paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true)); 176 clipRoundedInnerRect(paintInfo.context, paintRect, roundedInnerRect); 177 } 178 } 179 180 if (!completelyClippedOut) { 181 paintReplaced(paintInfo, adjustedPaintOffset); 182 183 if (style().hasBorderRadius()) 184 paintInfo.context->restore(); 185 } 186 187 // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of 188 // surrounding content. 189 if (drawSelectionTint) { 190 LayoutRect selectionPaintingRect = localSelectionRect(); 191 selectionPaintingRect.moveBy(adjustedPaintOffset); 192 paintInfo.context->fillRect(pixelSnappedIntRect(selectionPaintingRect), selectionBackgroundColor(), style().colorSpace()); 193 } 194} 195 196bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 197{ 198 if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline 199 && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask) 200 return false; 201 202 if (!paintInfo.shouldPaintWithinRoot(*this)) 203 return false; 204 205 // if we're invisible or haven't received a layout yet, then just bail. 206 if (style().visibility() != VISIBLE) 207 return false; 208 209 RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); 210 // Check our region range to make sure we need to be painting in this region. 211 if (namedFlowFragment && !namedFlowFragment->flowThread()->objectShouldFragmentInFlowRegion(this, namedFlowFragment)) 212 return false; 213 214 LayoutPoint adjustedPaintOffset = paintOffset + location(); 215 216 // Early exit if the element touches the edges. 217 LayoutUnit top = adjustedPaintOffset.y() + visualOverflowRect().y(); 218 LayoutUnit bottom = adjustedPaintOffset.y() + visualOverflowRect().maxY(); 219 if (isSelected() && m_inlineBoxWrapper) { 220 const RootInlineBox& rootBox = m_inlineBoxWrapper->root(); 221 LayoutUnit selTop = paintOffset.y() + rootBox.selectionTop(); 222 LayoutUnit selBottom = paintOffset.y() + selTop + rootBox.selectionHeight(); 223 top = std::min(selTop, top); 224 bottom = std::max(selBottom, bottom); 225 } 226 227 LayoutRect localRepaintRect = paintInfo.rect; 228 localRepaintRect.inflate(maximalOutlineSize(paintInfo.phase)); 229 if (adjustedPaintOffset.x() + visualOverflowRect().x() >= localRepaintRect.maxX() || adjustedPaintOffset.x() + visualOverflowRect().maxX() <= localRepaintRect.x()) 230 return false; 231 232 if (top >= localRepaintRect.maxY() || bottom <= localRepaintRect.y()) 233 return false; 234 235 return true; 236} 237 238static inline RenderBlock* firstContainingBlockWithLogicalWidth(const RenderReplaced* replaced) 239{ 240 // We have to lookup the containing block, which has an explicit width, which must not be equal to our direct containing block. 241 // If the embedded document appears _after_ we performed the initial layout, our intrinsic size is 300x150. If our containing 242 // block doesn't provide an explicit width, it's set to the 300 default, coming from the initial layout run. 243 RenderBlock* containingBlock = replaced->containingBlock(); 244 if (!containingBlock) 245 return 0; 246 247 for (; !containingBlock->isRenderView() && !containingBlock->isBody(); containingBlock = containingBlock->containingBlock()) { 248 if (containingBlock->style().logicalWidth().isSpecified()) 249 return containingBlock; 250 } 251 252 return 0; 253} 254 255bool RenderReplaced::hasReplacedLogicalWidth() const 256{ 257 if (style().logicalWidth().isSpecified()) 258 return true; 259 260 if (style().logicalWidth().isAuto()) 261 return false; 262 263 return firstContainingBlockWithLogicalWidth(this); 264} 265 266bool RenderReplaced::hasReplacedLogicalHeight() const 267{ 268 if (style().logicalHeight().isAuto()) 269 return false; 270 271 if (style().logicalHeight().isSpecified()) { 272 if (hasAutoHeightOrContainingBlockWithAutoHeight()) 273 return false; 274 return true; 275 } 276 277 return false; 278} 279 280void RenderReplaced::computeAspectRatioInformationForRenderBox(RenderBox* contentRenderer, FloatSize& constrainedSize, double& intrinsicRatio) const 281{ 282 FloatSize intrinsicSize; 283 if (contentRenderer) { 284 contentRenderer->computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio); 285 286 // Handle zoom & vertical writing modes here, as the embedded document doesn't know about them. 287 intrinsicSize.scale(style().effectiveZoom()); 288 289 if (isRenderImage()) 290 intrinsicSize.scale(toRenderImage(this)->imageDevicePixelRatio()); 291 292 // Update our intrinsic size to match what the content renderer has computed, so that when we 293 // constrain the size below, the correct intrinsic size will be obtained for comparison against 294 // min and max widths. 295 if (intrinsicRatio && !intrinsicSize.isEmpty()) 296 m_intrinsicSize = LayoutSize(intrinsicSize); 297 298 if (!isHorizontalWritingMode()) { 299 if (intrinsicRatio) 300 intrinsicRatio = 1 / intrinsicRatio; 301 intrinsicSize = intrinsicSize.transposedSize(); 302 } 303 } else { 304 computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio); 305 if (intrinsicRatio && !intrinsicSize.isEmpty()) 306 m_intrinsicSize = LayoutSize(isHorizontalWritingMode() ? intrinsicSize : intrinsicSize.transposedSize()); 307 } 308 309 // Now constrain the intrinsic size along each axis according to minimum and maximum width/heights along the 310 // opposite axis. So for example a maximum width that shrinks our width will result in the height we compute here 311 // having to shrink in order to preserve the aspect ratio. Because we compute these values independently along 312 // each axis, the final returned size may in fact not preserve the aspect ratio. 313 // FIXME: In the long term, it might be better to just return this code more to the way it used to be before this 314 // function was added, since all it has done is make the code more unclear. 315 constrainedSize = intrinsicSize; 316 if (intrinsicRatio && !intrinsicSize.isEmpty() && style().logicalWidth().isAuto() && style().logicalHeight().isAuto()) { 317 // We can't multiply or divide by 'intrinsicRatio' here, it breaks tests, like fast/images/zoomed-img-size.html, which 318 // can only be fixed once subpixel precision is available for things like intrinsicWidth/Height - which include zoom! 319 constrainedSize.setWidth(RenderBox::computeReplacedLogicalHeight() * intrinsicSize.width() / intrinsicSize.height()); 320 constrainedSize.setHeight(RenderBox::computeReplacedLogicalWidth() * intrinsicSize.height() / intrinsicSize.width()); 321 } 322} 323 324LayoutRect RenderReplaced::replacedContentRect(const LayoutSize& intrinsicSize) const 325{ 326 LayoutRect contentRect = contentBoxRect(); 327 ObjectFit objectFit = style().objectFit(); 328 if (objectFit == ObjectFitFill) 329 return contentRect; 330 331 if (!intrinsicSize.width() || !intrinsicSize.height()) 332 return contentRect; 333 334 LayoutRect finalRect = contentRect; 335 switch (objectFit) { 336 case ObjectFitContain: 337 case ObjectFitScaleDown: 338 case ObjectFitCover: 339 finalRect.setSize(finalRect.size().fitToAspectRatio(intrinsicSize, objectFit == ObjectFitCover ? AspectRatioFitGrow : AspectRatioFitShrink)); 340 if (objectFit != ObjectFitScaleDown || finalRect.width() <= intrinsicSize.width()) 341 break; 342 FALLTHROUGH; 343 case ObjectFitNone: 344 finalRect.setSize(intrinsicSize); 345 break; 346 case ObjectFitFill: 347 ASSERT_NOT_REACHED(); 348 } 349 350 // FIXME: This is where object-position should be taken into account, but since it's not 351 // implemented yet, assume the initial value of "50% 50%". 352 LayoutUnit xOffset = (contentRect.width() - finalRect.width()) / 2; 353 LayoutUnit yOffset = (contentRect.height() - finalRect.height()) / 2; 354 finalRect.move(xOffset, yOffset); 355 356 return finalRect; 357} 358 359void RenderReplaced::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio) const 360{ 361 // If there's an embeddedContentBox() of a remote, referenced document available, this code-path should never be used. 362 ASSERT(!embeddedContentBox()); 363 intrinsicSize = FloatSize(intrinsicLogicalWidth(), intrinsicLogicalHeight()); 364 365 // Figure out if we need to compute an intrinsic ratio. 366 if (intrinsicSize.isEmpty() || !hasAspectRatio()) 367 return; 368 369 intrinsicRatio = intrinsicSize.width() / intrinsicSize.height(); 370} 371 372LayoutUnit RenderReplaced::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const 373{ 374 if (style().logicalWidth().isSpecified() || style().logicalWidth().isIntrinsic()) 375 return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style().logicalWidth()), shouldComputePreferred); 376 377 RenderBox* contentRenderer = embeddedContentBox(); 378 379 // 10.3.2 Inline, replaced elements: http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width 380 double intrinsicRatio = 0; 381 FloatSize constrainedSize; 382 computeAspectRatioInformationForRenderBox(contentRenderer, constrainedSize, intrinsicRatio); 383 384 if (style().logicalWidth().isAuto()) { 385 bool computedHeightIsAuto = hasAutoHeightOrContainingBlockWithAutoHeight(); 386 bool hasIntrinsicWidth = constrainedSize.width() > 0; 387 388 // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width, then that intrinsic width is the used value of 'width'. 389 if (computedHeightIsAuto && hasIntrinsicWidth) 390 return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred); 391 392 bool hasIntrinsicHeight = constrainedSize.height() > 0; 393 if (intrinsicRatio) { 394 // If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width, but does have an intrinsic height and intrinsic ratio; 395 // or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value 396 // of 'width' is: (used height) * (intrinsic ratio) 397 if (intrinsicRatio && ((computedHeightIsAuto && !hasIntrinsicWidth && hasIntrinsicHeight) || !computedHeightIsAuto)) { 398 LayoutUnit logicalHeight = computeReplacedLogicalHeight(); 399 return computeReplacedLogicalWidthRespectingMinMaxWidth(roundToInt(round(logicalHeight * intrinsicRatio)), shouldComputePreferred); 400 } 401 402 // If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width, then the used value of 403 // 'width' is undefined in CSS 2.1. However, it is suggested that, if the containing block's width does not itself depend on the replaced element's width, then 404 // the used value of 'width' is calculated from the constraint equation used for block-level, non-replaced elements in normal flow. 405 if (computedHeightIsAuto && !hasIntrinsicWidth && !hasIntrinsicHeight) { 406 // The aforementioned 'constraint equation' used for block-level, non-replaced elements in normal flow: 407 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block 408 LayoutUnit logicalWidth; 409 if (RenderBlock* blockWithWidth = firstContainingBlockWithLogicalWidth(this)) 410 logicalWidth = blockWithWidth->computeReplacedLogicalWidthRespectingMinMaxWidth(blockWithWidth->computeReplacedLogicalWidthUsing(blockWithWidth->style().logicalWidth()), shouldComputePreferred); 411 else 412 logicalWidth = containingBlock()->availableLogicalWidth(); 413 414 // This solves above equation for 'width' (== logicalWidth). 415 LayoutUnit marginStart = minimumValueForLength(style().marginStart(), logicalWidth); 416 LayoutUnit marginEnd = minimumValueForLength(style().marginEnd(), logicalWidth); 417 logicalWidth = std::max<LayoutUnit>(0, logicalWidth - (marginStart + marginEnd + (width() - clientWidth()))); 418 return computeReplacedLogicalWidthRespectingMinMaxWidth(logicalWidth, shouldComputePreferred); 419 } 420 } 421 422 // Otherwise, if 'width' has a computed value of 'auto', and the element has an intrinsic width, then that intrinsic width is the used value of 'width'. 423 if (hasIntrinsicWidth) 424 return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred); 425 426 // Otherwise, if 'width' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'width' becomes 300px. If 300px is too 427 // wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead. 428 // Note: We fall through and instead return intrinsicLogicalWidth() here - to preserve existing WebKit behavior, which might or might not be correct, or desired. 429 // Changing this to return cDefaultWidth, will affect lots of test results. Eg. some tests assume that a blank <img> tag (which implies width/height=auto) 430 // has no intrinsic size, which is wrong per CSS 2.1, but matches our behavior since a long time. 431 } 432 433 return computeReplacedLogicalWidthRespectingMinMaxWidth(intrinsicLogicalWidth(), shouldComputePreferred); 434} 435 436LayoutUnit RenderReplaced::computeReplacedLogicalHeight() const 437{ 438 // 10.5 Content height: the 'height' property: http://www.w3.org/TR/CSS21/visudet.html#propdef-height 439 if (hasReplacedLogicalHeight()) 440 return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style().logicalHeight())); 441 442 RenderBox* contentRenderer = embeddedContentBox(); 443 444 // 10.6.2 Inline, replaced elements: http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height 445 double intrinsicRatio = 0; 446 FloatSize constrainedSize; 447 computeAspectRatioInformationForRenderBox(contentRenderer, constrainedSize, intrinsicRatio); 448 449 bool widthIsAuto = style().logicalWidth().isAuto(); 450 bool hasIntrinsicHeight = constrainedSize.height() > 0; 451 452 // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic height, then that intrinsic height is the used value of 'height'. 453 if (widthIsAuto && hasIntrinsicHeight) 454 return computeReplacedLogicalHeightRespectingMinMaxHeight(constrainedSize.height()); 455 456 // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is: 457 // (used width) / (intrinsic ratio) 458 if (intrinsicRatio) 459 return computeReplacedLogicalHeightRespectingMinMaxHeight(roundToInt(round(availableLogicalWidth() / intrinsicRatio))); 460 461 // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic height, then that intrinsic height is the used value of 'height'. 462 if (hasIntrinsicHeight) 463 return computeReplacedLogicalHeightRespectingMinMaxHeight(constrainedSize.height()); 464 465 // Otherwise, if 'height' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'height' must be set to the height 466 // of the largest rectangle that has a 2:1 ratio, has a height not greater than 150px, and has a width not greater than the device width. 467 return computeReplacedLogicalHeightRespectingMinMaxHeight(intrinsicLogicalHeight()); 468} 469 470void RenderReplaced::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 471{ 472 minLogicalWidth = maxLogicalWidth = intrinsicLogicalWidth(); 473} 474 475void RenderReplaced::computePreferredLogicalWidths() 476{ 477 ASSERT(preferredLogicalWidthsDirty()); 478 479 // We cannot resolve any percent logical width here as the available logical 480 // width may not be set on our containing block. 481 if (style().logicalWidth().isPercent()) 482 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 483 else 484 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(ComputePreferred); 485 486 const RenderStyle& styleToUse = style(); 487 if (styleToUse.logicalWidth().isPercent() || styleToUse.logicalMaxWidth().isPercent()) 488 m_minPreferredLogicalWidth = 0; 489 490 if (styleToUse.logicalMinWidth().isFixed() && styleToUse.logicalMinWidth().value() > 0) { 491 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value())); 492 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value())); 493 } 494 495 if (styleToUse.logicalMaxWidth().isFixed()) { 496 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); 497 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); 498 } 499 500 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); 501 m_minPreferredLogicalWidth += borderAndPadding; 502 m_maxPreferredLogicalWidth += borderAndPadding; 503 504 setPreferredLogicalWidthsDirty(false); 505} 506 507VisiblePosition RenderReplaced::positionForPoint(const LayoutPoint& point, const RenderRegion* region) 508{ 509 // FIXME: This code is buggy if the replaced element is relative positioned. 510 InlineBox* box = inlineBoxWrapper(); 511 const RootInlineBox* rootBox = box ? &box->root() : 0; 512 513 LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); 514 LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); 515 516 LayoutUnit blockDirectionPosition = isHorizontalWritingMode() ? point.y() + y() : point.x() + x(); 517 LayoutUnit lineDirectionPosition = isHorizontalWritingMode() ? point.x() + x() : point.y() + y(); 518 519 if (blockDirectionPosition < top) 520 return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above 521 522 if (blockDirectionPosition >= bottom) 523 return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below 524 525 if (element()) { 526 if (lineDirectionPosition <= logicalLeft() + (logicalWidth() / 2)) 527 return createVisiblePosition(0, DOWNSTREAM); 528 return createVisiblePosition(1, DOWNSTREAM); 529 } 530 531 return RenderBox::positionForPoint(point, region); 532} 533 534LayoutRect RenderReplaced::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent) 535{ 536 ASSERT(!needsLayout()); 537 538 if (!isSelected()) 539 return LayoutRect(); 540 541 LayoutRect rect = localSelectionRect(); 542 if (clipToVisibleContent) 543 computeRectForRepaint(repaintContainer, rect); 544 else 545 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox(); 546 547 return rect; 548} 549 550LayoutRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const 551{ 552 if (checkWhetherSelected && !isSelected()) 553 return LayoutRect(); 554 555 if (!m_inlineBoxWrapper) 556 // We're a block-level replaced element. Just return our own dimensions. 557 return LayoutRect(LayoutPoint(), size()); 558 559 const RootInlineBox& rootBox = m_inlineBoxWrapper->root(); 560 LayoutUnit newLogicalTop = rootBox.blockFlow().style().isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - rootBox.selectionBottom() : rootBox.selectionTop() - m_inlineBoxWrapper->logicalTop(); 561 if (rootBox.blockFlow().style().isHorizontalWritingMode()) 562 return LayoutRect(0, newLogicalTop, width(), rootBox.selectionHeight()); 563 return LayoutRect(newLogicalTop, 0, rootBox.selectionHeight(), height()); 564} 565 566void RenderReplaced::setSelectionState(SelectionState state) 567{ 568 // The selection state for our containing block hierarchy is updated by the base class call. 569 RenderBox::setSelectionState(state); 570 571 if (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes()) 572 m_inlineBoxWrapper->root().setHasSelectedChildren(isSelected()); 573} 574 575bool RenderReplaced::isSelected() const 576{ 577 SelectionState s = selectionState(); 578 if (s == SelectionNone) 579 return false; 580 if (s == SelectionInside) 581 return true; 582 583 int selectionStart, selectionEnd; 584 selectionStartEnd(selectionStart, selectionEnd); 585 if (s == SelectionStart) 586 return selectionStart == 0; 587 588 int end = element()->hasChildNodes() ? element()->childNodeCount() : 1; 589 if (s == SelectionEnd) 590 return selectionEnd == end; 591 if (s == SelectionBoth) 592 return selectionStart == 0 && selectionEnd == end; 593 594 ASSERT(0); 595 return false; 596} 597 598LayoutRect RenderReplaced::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const 599{ 600 if (style().visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) 601 return LayoutRect(); 602 603 // The selectionRect can project outside of the overflowRect, so take their union 604 // for repainting to avoid selection painting glitches. 605 LayoutRect r = unionRect(localSelectionRect(false), visualOverflowRect()); 606 607 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 608 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 609 r.move(view().layoutDelta()); 610 611 r.inflate(style().outlineSize()); 612 613 computeRectForRepaint(repaintContainer, r); 614 return r; 615} 616 617} 618