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