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