• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "config.h"
24 #include "RenderReplaced.h"
25 
26 #include "GraphicsContext.h"
27 #include "RenderBlock.h"
28 #include "RenderLayer.h"
29 #include "RenderTheme.h"
30 #include "RenderView.h"
31 #include "VisiblePosition.h"
32 
33 using namespace std;
34 
35 namespace WebCore {
36 
37 const int cDefaultWidth = 300;
38 const int cDefaultHeight = 150;
39 
RenderReplaced(Node * node)40 RenderReplaced::RenderReplaced(Node* node)
41     : RenderBox(node)
42     , m_intrinsicSize(cDefaultWidth, cDefaultHeight)
43     , m_hasIntrinsicSize(false)
44 {
45     setReplaced(true);
46 }
47 
RenderReplaced(Node * node,const IntSize & intrinsicSize)48 RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize)
49     : RenderBox(node)
50     , m_intrinsicSize(intrinsicSize)
51     , m_hasIntrinsicSize(true)
52 {
53     setReplaced(true);
54 }
55 
~RenderReplaced()56 RenderReplaced::~RenderReplaced()
57 {
58 }
59 
destroy()60 void RenderReplaced::destroy()
61 {
62     if (!documentBeingDestroyed() && parent())
63         parent()->dirtyLinesFromChangedChild(this);
64 
65     RenderBox::destroy();
66 }
67 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)68 void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
69 {
70     RenderBox::styleDidChange(diff, oldStyle);
71 
72     bool hadStyle = (oldStyle != 0);
73     float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom();
74     if (style() && style()->effectiveZoom() != oldZoom)
75         intrinsicSizeChanged();
76 }
77 
layout()78 void RenderReplaced::layout()
79 {
80     ASSERT(needsLayout());
81 
82     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
83 
84     setHeight(minimumReplacedHeight());
85 
86     computeLogicalWidth();
87     computeLogicalHeight();
88 
89     m_overflow.clear();
90     addShadowOverflow();
91     updateLayerTransform();
92 
93     repainter.repaintAfterLayout();
94     setNeedsLayout(false);
95 }
96 
intrinsicSizeChanged()97 void RenderReplaced::intrinsicSizeChanged()
98 {
99     int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom());
100     int scaledHeight = static_cast<int>(cDefaultHeight * style()->effectiveZoom());
101     m_intrinsicSize = IntSize(scaledWidth, scaledHeight);
102     setNeedsLayoutAndPrefWidthsRecalc();
103 }
104 
paint(PaintInfo & paintInfo,int tx,int ty)105 void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
106 {
107     if (!shouldPaint(paintInfo, tx, ty))
108         return;
109 
110     tx += x();
111     ty += y();
112 
113     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
114         paintBoxDecorations(paintInfo, tx, ty);
115 
116     if (paintInfo.phase == PaintPhaseMask) {
117         paintMask(paintInfo, tx, ty);
118         return;
119     }
120 
121     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
122         paintOutline(paintInfo.context, tx, ty, width(), height());
123 
124     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
125         return;
126 
127     if (!paintInfo.shouldPaintWithinRoot(this))
128         return;
129 
130     bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing();
131     if (paintInfo.phase == PaintPhaseSelection) {
132         if (selectionState() == SelectionNone)
133             return;
134         drawSelectionTint = false;
135     }
136 
137     bool completelyClippedOut = false;
138     if (style()->hasBorderRadius()) {
139         IntRect borderRect = IntRect(tx, ty, width(), height());
140 
141         if (borderRect.isEmpty())
142             completelyClippedOut = true;
143         else {
144             // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
145             paintInfo.context->save();
146             paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(borderRect));
147         }
148     }
149 
150     if (!completelyClippedOut) {
151         paintReplaced(paintInfo, tx, ty);
152 
153         if (style()->hasBorderRadius())
154             paintInfo.context->restore();
155     }
156 
157     // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
158     // surrounding content.
159     if (drawSelectionTint) {
160         IntRect selectionPaintingRect = localSelectionRect();
161         selectionPaintingRect.move(tx, ty);
162         paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor(), style()->colorSpace());
163     }
164 }
165 
shouldPaint(PaintInfo & paintInfo,int & tx,int & ty)166 bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty)
167 {
168     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline
169             && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask)
170         return false;
171 
172     if (!paintInfo.shouldPaintWithinRoot(this))
173         return false;
174 
175     // if we're invisible or haven't received a layout yet, then just bail.
176     if (style()->visibility() != VISIBLE)
177         return false;
178 
179     int currentTX = tx + x();
180     int currentTY = ty + y();
181 
182     // Early exit if the element touches the edges.
183     int top = currentTY + minYVisualOverflow();
184     int bottom = currentTY + maxYVisualOverflow();
185     if (isSelected() && m_inlineBoxWrapper) {
186         int selTop = ty + m_inlineBoxWrapper->root()->selectionTop();
187         int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
188         top = min(selTop, top);
189         bottom = max(selBottom, bottom);
190     }
191 
192     int os = 2 * maximalOutlineSize(paintInfo.phase);
193     if (currentTX + minXVisualOverflow() >= paintInfo.rect.maxX() + os || currentTX + maxXVisualOverflow() <= paintInfo.rect.x() - os)
194         return false;
195     if (top >= paintInfo.rect.maxY() + os || bottom <= paintInfo.rect.y() - os)
196         return false;
197 
198     return true;
199 }
200 
lengthIsSpecified(Length length)201 static inline bool lengthIsSpecified(Length length)
202 {
203     LengthType lengthType = length.type();
204     return lengthType == Fixed || lengthType == Percent;
205 }
206 
computeReplacedLogicalWidth(bool includeMaxWidth) const207 int RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) const
208 {
209     int logicalWidth;
210     if (lengthIsSpecified(style()->width()))
211         logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth());
212     else if (m_hasIntrinsicSize)
213         logicalWidth = calcAspectRatioLogicalWidth();
214     else
215         logicalWidth = intrinsicLogicalWidth();
216 
217     int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
218     int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
219 
220     return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
221 }
222 
computeReplacedLogicalHeight() const223 int RenderReplaced::computeReplacedLogicalHeight() const
224 {
225     int logicalHeight;
226     if (lengthIsSpecified(style()->logicalHeight()))
227         logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight());
228     else if (m_hasIntrinsicSize)
229         logicalHeight = calcAspectRatioLogicalHeight();
230     else
231         logicalHeight = intrinsicLogicalHeight();
232 
233     int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
234     int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
235 
236     return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
237 }
238 
calcAspectRatioLogicalWidth() const239 int RenderReplaced::calcAspectRatioLogicalWidth() const
240 {
241     int intrinsicWidth = intrinsicLogicalWidth();
242     int intrinsicHeight = intrinsicLogicalHeight();
243     if (!intrinsicHeight)
244         return 0;
245     return RenderBox::computeReplacedLogicalHeight() * intrinsicWidth / intrinsicHeight;
246 }
247 
calcAspectRatioLogicalHeight() const248 int RenderReplaced::calcAspectRatioLogicalHeight() const
249 {
250     int intrinsicWidth = intrinsicLogicalWidth();
251     int intrinsicHeight = intrinsicLogicalHeight();
252     if (!intrinsicWidth)
253         return 0;
254     return RenderBox::computeReplacedLogicalWidth() * intrinsicHeight / intrinsicWidth;
255 }
256 
computePreferredLogicalWidths()257 void RenderReplaced::computePreferredLogicalWidths()
258 {
259     ASSERT(preferredLogicalWidthsDirty());
260 
261     int borderAndPadding = borderAndPaddingWidth();
262     m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(false) + borderAndPadding;
263 
264     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
265         m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0));
266 
267     if (style()->width().isPercent() || style()->height().isPercent()
268         || style()->maxWidth().isPercent() || style()->maxHeight().isPercent()
269         || style()->minWidth().isPercent() || style()->minHeight().isPercent())
270         m_minPreferredLogicalWidth = 0;
271     else
272         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
273 
274     setPreferredLogicalWidthsDirty(false);
275 }
276 
caretMaxRenderedOffset() const277 unsigned RenderReplaced::caretMaxRenderedOffset() const
278 {
279     return 1;
280 }
281 
positionForPoint(const IntPoint & point)282 VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point)
283 {
284     InlineBox* box = inlineBoxWrapper();
285     if (!box)
286         return createVisiblePosition(0, DOWNSTREAM);
287 
288     // FIXME: This code is buggy if the replaced element is relative positioned.
289 
290     RootInlineBox* root = box->root();
291 
292     int top = root->selectionTop();
293     int bottom = root->selectionBottom();
294 
295     int blockDirectionPosition = box->isHorizontal() ? point.y() + y() : point.x() + x();
296     int lineDirectionPosition = box->isHorizontal() ? point.x() + x() : point.y() + y();
297 
298     if (blockDirectionPosition < top)
299         return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above
300 
301     if (blockDirectionPosition >= bottom)
302         return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below
303 
304     if (node()) {
305         if (lineDirectionPosition <= box->logicalLeft() + (box->logicalWidth() / 2))
306             return createVisiblePosition(0, DOWNSTREAM);
307         return createVisiblePosition(1, DOWNSTREAM);
308     }
309 
310     return RenderBox::positionForPoint(point);
311 }
312 
selectionRectForRepaint(RenderBoxModelObject * repaintContainer,bool clipToVisibleContent)313 IntRect RenderReplaced::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
314 {
315     ASSERT(!needsLayout());
316 
317     if (!isSelected())
318         return IntRect();
319 
320     IntRect rect = localSelectionRect();
321     if (clipToVisibleContent)
322         computeRectForRepaint(repaintContainer, rect);
323     else
324         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
325 
326     return rect;
327 }
328 
localSelectionRect(bool checkWhetherSelected) const329 IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
330 {
331     if (checkWhetherSelected && !isSelected())
332         return IntRect();
333 
334     if (!m_inlineBoxWrapper)
335         // We're a block-level replaced element.  Just return our own dimensions.
336         return IntRect(0, 0, width(), height());
337 
338     RootInlineBox* root = m_inlineBoxWrapper->root();
339     int newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
340     if (root->block()->style()->isHorizontalWritingMode())
341         return IntRect(0, newLogicalTop, width(), root->selectionHeight());
342     return IntRect(newLogicalTop, 0, root->selectionHeight(), height());
343 }
344 
setSelectionState(SelectionState s)345 void RenderReplaced::setSelectionState(SelectionState s)
346 {
347     RenderBox::setSelectionState(s); // The selection state for our containing block hierarchy is updated by the base class call.
348     if (m_inlineBoxWrapper) {
349         RootInlineBox* line = m_inlineBoxWrapper->root();
350         if (line)
351             line->setHasSelectedChildren(isSelected());
352     }
353 }
354 
isSelected() const355 bool RenderReplaced::isSelected() const
356 {
357     SelectionState s = selectionState();
358     if (s == SelectionNone)
359         return false;
360     if (s == SelectionInside)
361         return true;
362 
363     int selectionStart, selectionEnd;
364     selectionStartEnd(selectionStart, selectionEnd);
365     if (s == SelectionStart)
366         return selectionStart == 0;
367 
368     int end = node()->hasChildNodes() ? node()->childNodeCount() : 1;
369     if (s == SelectionEnd)
370         return selectionEnd == end;
371     if (s == SelectionBoth)
372         return selectionStart == 0 && selectionEnd == end;
373 
374     ASSERT(0);
375     return false;
376 }
377 
intrinsicSize() const378 IntSize RenderReplaced::intrinsicSize() const
379 {
380     return m_intrinsicSize;
381 }
382 
setIntrinsicSize(const IntSize & size)383 void RenderReplaced::setIntrinsicSize(const IntSize& size)
384 {
385     ASSERT(m_hasIntrinsicSize);
386     m_intrinsicSize = size;
387 }
388 
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)389 IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
390 {
391     if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
392         return IntRect();
393 
394     // The selectionRect can project outside of the overflowRect, so take their union
395     // for repainting to avoid selection painting glitches.
396     IntRect r = unionRect(localSelectionRect(false), visualOverflowRect());
397 
398     RenderView* v = view();
399     if (v) {
400         // FIXME: layoutDelta needs to be applied in parts before/after transforms and
401         // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
402         r.move(v->layoutDelta());
403     }
404 
405     if (style()) {
406         if (style()->hasAppearance())
407             // The theme may wish to inflate the rect used when repainting.
408             theme()->adjustRepaintRect(this, r);
409         if (v)
410             r.inflate(style()->outlineSize());
411     }
412     computeRectForRepaint(repaintContainer, r);
413     return r;
414 }
415 
416 }
417