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