• 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 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)60 void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
61 {
62     RenderBox::styleDidChange(diff, oldStyle);
63 
64     bool hadStyle = (oldStyle != 0);
65     float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom();
66     if (style() && style()->effectiveZoom() != oldZoom)
67         intrinsicSizeChanged();
68 }
69 
layout()70 void RenderReplaced::layout()
71 {
72     ASSERT(needsLayout());
73 
74     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
75 
76     setHeight(minimumReplacedHeight());
77 
78     computeLogicalWidth();
79     computeLogicalHeight();
80 
81     m_overflow.clear();
82     addShadowOverflow();
83     updateLayerTransform();
84 
85     repainter.repaintAfterLayout();
86     setNeedsLayout(false);
87 }
88 
intrinsicSizeChanged()89 void RenderReplaced::intrinsicSizeChanged()
90 {
91     int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom());
92     int scaledHeight = static_cast<int>(cDefaultHeight * style()->effectiveZoom());
93     m_intrinsicSize = IntSize(scaledWidth, scaledHeight);
94     setNeedsLayoutAndPrefWidthsRecalc();
95 }
96 
paint(PaintInfo & paintInfo,int tx,int ty)97 void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
98 {
99     if (!shouldPaint(paintInfo, tx, ty))
100         return;
101 
102     tx += x();
103     ty += y();
104 
105     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
106         paintBoxDecorations(paintInfo, tx, ty);
107 
108     if (paintInfo.phase == PaintPhaseMask) {
109         paintMask(paintInfo, tx, ty);
110         return;
111     }
112 
113     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
114         paintOutline(paintInfo.context, tx, ty, width(), height());
115 
116     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
117         return;
118 
119     if (!paintInfo.shouldPaintWithinRoot(this))
120         return;
121 
122     bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing();
123     if (paintInfo.phase == PaintPhaseSelection) {
124         if (selectionState() == SelectionNone)
125             return;
126         drawSelectionTint = false;
127     }
128 
129     bool completelyClippedOut = false;
130     if (style()->hasBorderRadius()) {
131         IntRect borderRect = IntRect(tx, ty, width(), height());
132 
133         if (borderRect.isEmpty())
134             completelyClippedOut = true;
135         else {
136             // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
137             paintInfo.context->save();
138             paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(borderRect));
139         }
140     }
141 
142     if (!completelyClippedOut) {
143         paintReplaced(paintInfo, tx, ty);
144 
145         if (style()->hasBorderRadius())
146             paintInfo.context->restore();
147     }
148 
149     // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
150     // surrounding content.
151     if (drawSelectionTint) {
152         IntRect selectionPaintingRect = localSelectionRect();
153         selectionPaintingRect.move(tx, ty);
154         paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor(), style()->colorSpace());
155     }
156 }
157 
shouldPaint(PaintInfo & paintInfo,int & tx,int & ty)158 bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty)
159 {
160     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline
161             && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask)
162         return false;
163 
164     if (!paintInfo.shouldPaintWithinRoot(this))
165         return false;
166 
167     // if we're invisible or haven't received a layout yet, then just bail.
168     if (style()->visibility() != VISIBLE)
169         return false;
170 
171     int currentTX = tx + x();
172     int currentTY = ty + y();
173 
174     // Early exit if the element touches the edges.
175     int top = currentTY + minYVisualOverflow();
176     int bottom = currentTY + maxYVisualOverflow();
177     if (isSelected() && m_inlineBoxWrapper) {
178         int selTop = ty + m_inlineBoxWrapper->root()->selectionTop();
179         int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
180         top = min(selTop, top);
181         bottom = max(selBottom, bottom);
182     }
183 
184     int os = 2 * maximalOutlineSize(paintInfo.phase);
185     if (currentTX + minXVisualOverflow() >= paintInfo.rect.maxX() + os || currentTX + maxXVisualOverflow() <= paintInfo.rect.x() - os)
186         return false;
187     if (top >= paintInfo.rect.maxY() + os || bottom <= paintInfo.rect.y() - os)
188         return false;
189 
190     return true;
191 }
192 
lengthIsSpecified(Length length)193 static inline bool lengthIsSpecified(Length length)
194 {
195     LengthType lengthType = length.type();
196     return lengthType == Fixed || lengthType == Percent;
197 }
198 
computeReplacedLogicalWidth(bool includeMaxWidth) const199 int RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) const
200 {
201     int logicalWidth;
202     if (lengthIsSpecified(style()->width()))
203         logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth());
204     else if (m_hasIntrinsicSize)
205         logicalWidth = calcAspectRatioLogicalWidth();
206     else
207         logicalWidth = intrinsicLogicalWidth();
208 
209     int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
210     int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
211 
212     return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
213 }
214 
computeReplacedLogicalHeight() const215 int RenderReplaced::computeReplacedLogicalHeight() const
216 {
217     int logicalHeight;
218     if (lengthIsSpecified(style()->logicalHeight()))
219         logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight());
220     else if (m_hasIntrinsicSize)
221         logicalHeight = calcAspectRatioLogicalHeight();
222     else
223         logicalHeight = intrinsicLogicalHeight();
224 
225     int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
226     int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
227 
228     return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
229 }
230 
calcAspectRatioLogicalWidth() const231 int RenderReplaced::calcAspectRatioLogicalWidth() const
232 {
233     int intrinsicWidth = intrinsicLogicalWidth();
234     int intrinsicHeight = intrinsicLogicalHeight();
235     if (!intrinsicHeight)
236         return 0;
237     return RenderBox::computeReplacedLogicalHeight() * intrinsicWidth / intrinsicHeight;
238 }
239 
calcAspectRatioLogicalHeight() const240 int RenderReplaced::calcAspectRatioLogicalHeight() const
241 {
242     int intrinsicWidth = intrinsicLogicalWidth();
243     int intrinsicHeight = intrinsicLogicalHeight();
244     if (!intrinsicWidth)
245         return 0;
246     return RenderBox::computeReplacedLogicalWidth() * intrinsicHeight / intrinsicWidth;
247 }
248 
computePreferredLogicalWidths()249 void RenderReplaced::computePreferredLogicalWidths()
250 {
251     ASSERT(preferredLogicalWidthsDirty());
252 
253     int borderAndPadding = borderAndPaddingWidth();
254     m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(false) + borderAndPadding;
255 
256     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
257         m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0));
258 
259     if (style()->width().isPercent() || style()->height().isPercent()
260         || style()->maxWidth().isPercent() || style()->maxHeight().isPercent()
261         || style()->minWidth().isPercent() || style()->minHeight().isPercent())
262         m_minPreferredLogicalWidth = 0;
263     else
264         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
265 
266     setPreferredLogicalWidthsDirty(false);
267 }
268 
caretMaxRenderedOffset() const269 unsigned RenderReplaced::caretMaxRenderedOffset() const
270 {
271     return 1;
272 }
273 
positionForPoint(const IntPoint & point)274 VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point)
275 {
276     InlineBox* box = inlineBoxWrapper();
277     if (!box)
278         return createVisiblePosition(0, DOWNSTREAM);
279 
280     // FIXME: This code is buggy if the replaced element is relative positioned.
281 
282     RootInlineBox* root = box->root();
283 
284     int top = root->selectionTop();
285     int bottom = root->selectionBottom();
286 
287     int blockDirectionPosition = box->isHorizontal() ? point.y() + y() : point.x() + x();
288     int lineDirectionPosition = box->isHorizontal() ? point.x() + x() : point.y() + y();
289 
290     if (blockDirectionPosition < top)
291         return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above
292 
293     if (blockDirectionPosition >= bottom)
294         return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below
295 
296     if (node()) {
297         if (lineDirectionPosition <= box->logicalLeft() + (box->logicalWidth() / 2))
298             return createVisiblePosition(0, DOWNSTREAM);
299         return createVisiblePosition(1, DOWNSTREAM);
300     }
301 
302     return RenderBox::positionForPoint(point);
303 }
304 
selectionRectForRepaint(RenderBoxModelObject * repaintContainer,bool clipToVisibleContent)305 IntRect RenderReplaced::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
306 {
307     ASSERT(!needsLayout());
308 
309     if (!isSelected())
310         return IntRect();
311 
312     IntRect rect = localSelectionRect();
313     if (clipToVisibleContent)
314         computeRectForRepaint(repaintContainer, rect);
315     else
316         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
317 
318     return rect;
319 }
320 
localSelectionRect(bool checkWhetherSelected) const321 IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
322 {
323     if (checkWhetherSelected && !isSelected())
324         return IntRect();
325 
326     if (!m_inlineBoxWrapper)
327         // We're a block-level replaced element.  Just return our own dimensions.
328         return IntRect(0, 0, width(), height());
329 
330     RootInlineBox* root = m_inlineBoxWrapper->root();
331     int newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
332     if (root->block()->style()->isHorizontalWritingMode())
333         return IntRect(0, newLogicalTop, width(), root->selectionHeight());
334     return IntRect(newLogicalTop, 0, root->selectionHeight(), height());
335 }
336 
setSelectionState(SelectionState s)337 void RenderReplaced::setSelectionState(SelectionState s)
338 {
339     RenderBox::setSelectionState(s); // The selection state for our containing block hierarchy is updated by the base class call.
340     if (m_inlineBoxWrapper) {
341         RootInlineBox* line = m_inlineBoxWrapper->root();
342         if (line)
343             line->setHasSelectedChildren(isSelected());
344     }
345 }
346 
isSelected() const347 bool RenderReplaced::isSelected() const
348 {
349     SelectionState s = selectionState();
350     if (s == SelectionNone)
351         return false;
352     if (s == SelectionInside)
353         return true;
354 
355     int selectionStart, selectionEnd;
356     selectionStartEnd(selectionStart, selectionEnd);
357     if (s == SelectionStart)
358         return selectionStart == 0;
359 
360     int end = node()->hasChildNodes() ? node()->childNodeCount() : 1;
361     if (s == SelectionEnd)
362         return selectionEnd == end;
363     if (s == SelectionBoth)
364         return selectionStart == 0 && selectionEnd == end;
365 
366     ASSERT(0);
367     return false;
368 }
369 
intrinsicSize() const370 IntSize RenderReplaced::intrinsicSize() const
371 {
372     return m_intrinsicSize;
373 }
374 
setIntrinsicSize(const IntSize & size)375 void RenderReplaced::setIntrinsicSize(const IntSize& size)
376 {
377     ASSERT(m_hasIntrinsicSize);
378     m_intrinsicSize = size;
379 }
380 
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)381 IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
382 {
383     if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
384         return IntRect();
385 
386     // The selectionRect can project outside of the overflowRect, so take their union
387     // for repainting to avoid selection painting glitches.
388     IntRect r = unionRect(localSelectionRect(false), visualOverflowRect());
389 
390     RenderView* v = view();
391     if (v) {
392         // FIXME: layoutDelta needs to be applied in parts before/after transforms and
393         // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
394         r.move(v->layoutDelta());
395     }
396 
397     if (style()) {
398         if (style()->hasAppearance())
399             // The theme may wish to inflate the rect used when repainting.
400             theme()->adjustRepaintRect(this, r);
401         if (v)
402             r.inflate(style()->outlineSize());
403     }
404     computeRectForRepaint(repaintContainer, r);
405     return r;
406 }
407 
408 }
409