• 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 typedef WTF::HashMap<const RenderReplaced*, IntRect> OverflowRectMap;
38 static OverflowRectMap* gOverflowRectMap = 0;
39 
40 const int cDefaultWidth = 300;
41 const int cDefaultHeight = 150;
42 
RenderReplaced(Node * node)43 RenderReplaced::RenderReplaced(Node* node)
44     : RenderBox(node)
45     , m_intrinsicSize(cDefaultWidth, cDefaultHeight)
46 {
47     setReplaced(true);
48 }
49 
RenderReplaced(Node * node,const IntSize & intrinsicSize)50 RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize)
51     : RenderBox(node)
52     , m_intrinsicSize(intrinsicSize)
53 {
54     setReplaced(true);
55 }
56 
~RenderReplaced()57 RenderReplaced::~RenderReplaced()
58 {
59     if (replacedHasOverflow())
60         gOverflowRectMap->remove(this);
61 }
62 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)63 void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
64 {
65     RenderBox::styleDidChange(diff, oldStyle);
66 
67     bool hadStyle = (oldStyle != 0);
68     float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom();
69     if (hadStyle && style() && style()->effectiveZoom() != oldZoom)
70         intrinsicSizeChanged();
71 }
72 
layout()73 void RenderReplaced::layout()
74 {
75     ASSERT(needsLayout());
76 
77     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
78 
79     setHeight(minimumReplacedHeight());
80 
81     calcWidth();
82     calcHeight();
83     adjustOverflowForBoxShadowAndReflect();
84 
85     repainter.repaintAfterLayout();
86 
87     setNeedsLayout(false);
88 }
89 
intrinsicSizeChanged()90 void RenderReplaced::intrinsicSizeChanged()
91 {
92     int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom());
93     int scaledHeight = static_cast<int>(cDefaultHeight * style()->effectiveZoom());
94     m_intrinsicSize = IntSize(scaledWidth, scaledHeight);
95     setNeedsLayoutAndPrefWidthsRecalc();
96 }
97 
paint(PaintInfo & paintInfo,int tx,int ty)98 void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
99 {
100     if (!shouldPaint(paintInfo, tx, ty))
101         return;
102 
103     tx += x();
104     ty += y();
105 
106     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
107         paintBoxDecorations(paintInfo, tx, ty);
108 
109     if (paintInfo.phase == PaintPhaseMask) {
110         paintMask(paintInfo, tx, ty);
111         return;
112     }
113 
114     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
115         paintOutline(paintInfo.context, tx, ty, width(), height(), style());
116 
117     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
118         return;
119 
120     if (!shouldPaintWithinRoot(paintInfo))
121         return;
122 
123     bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing();
124     if (paintInfo.phase == PaintPhaseSelection) {
125         if (selectionState() == SelectionNone)
126             return;
127         drawSelectionTint = false;
128     }
129 
130     if (style()->hasBorderRadius()) {
131         // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
132         paintInfo.context->save();
133 
134         IntSize topLeft, topRight, bottomLeft, bottomRight;
135         IntRect borderRect = IntRect(tx, ty, width(), height());
136         style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
137 
138         paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
139     }
140 
141     paintReplaced(paintInfo, tx, ty);
142 
143     if (style()->hasBorderRadius())
144         paintInfo.context->restore();
145 
146     // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
147     // surrounding content.
148     if (drawSelectionTint) {
149         IntRect selectionPaintingRect = localSelectionRect();
150         selectionPaintingRect.move(tx, ty);
151         paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor());
152     }
153 }
154 
shouldPaint(PaintInfo & paintInfo,int & tx,int & ty)155 bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty)
156 {
157     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline
158             && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask)
159         return false;
160 
161     if (!shouldPaintWithinRoot(paintInfo))
162         return false;
163 
164     // if we're invisible or haven't received a layout yet, then just bail.
165     if (style()->visibility() != VISIBLE)
166         return false;
167 
168     int currentTX = tx + x();
169     int currentTY = ty + y();
170 
171     // Early exit if the element touches the edges.
172     int top = currentTY + overflowTop();
173     int bottom = currentTY + overflowHeight();
174     if (isSelected() && m_inlineBoxWrapper) {
175         int selTop = ty + m_inlineBoxWrapper->root()->selectionTop();
176         int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
177         top = min(selTop, top);
178         bottom = max(selBottom, bottom);
179     }
180 
181     int os = 2 * maximalOutlineSize(paintInfo.phase);
182     if (currentTX + overflowLeft() >= paintInfo.rect.right() + os || currentTX + overflowWidth() <= paintInfo.rect.x() - os)
183         return false;
184     if (top >= paintInfo.rect.bottom() + os || bottom <= paintInfo.rect.y() - os)
185         return false;
186 
187     return true;
188 }
189 
calcPrefWidths()190 void RenderReplaced::calcPrefWidths()
191 {
192     ASSERT(prefWidthsDirty());
193 
194     int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight();
195     int width = calcReplacedWidth(false) + paddingAndBorders;
196 
197     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
198         width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0));
199 
200     if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) {
201         m_minPrefWidth = 0;
202         m_maxPrefWidth = width;
203     } else
204         m_minPrefWidth = m_maxPrefWidth = width;
205 
206     setPrefWidthsDirty(false);
207 }
208 
lineHeight(bool,bool) const209 int RenderReplaced::lineHeight(bool, bool) const
210 {
211     return height() + marginTop() + marginBottom();
212 }
213 
baselinePosition(bool,bool) const214 int RenderReplaced::baselinePosition(bool, bool) const
215 {
216     return height() + marginTop() + marginBottom();
217 }
218 
caretMaxRenderedOffset() const219 unsigned RenderReplaced::caretMaxRenderedOffset() const
220 {
221     return 1;
222 }
223 
positionForPoint(const IntPoint & point)224 VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point)
225 {
226     InlineBox* box = inlineBoxWrapper();
227     if (!box)
228         return createVisiblePosition(0, DOWNSTREAM);
229 
230     // FIXME: This code is buggy if the replaced element is relative positioned.
231 
232     RootInlineBox* root = box->root();
233 
234     int top = root->topOverflow();
235     int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow();
236 
237     if (point.y() + y() < top)
238         return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above
239 
240     if (point.y() + y() >= bottom)
241         return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below
242 
243     if (node()) {
244         if (point.x() <= width() / 2)
245             return createVisiblePosition(0, DOWNSTREAM);
246         return createVisiblePosition(1, DOWNSTREAM);
247     }
248 
249     return RenderBox::positionForPoint(point);
250 }
251 
selectionRectForRepaint(RenderBoxModelObject * repaintContainer,bool clipToVisibleContent)252 IntRect RenderReplaced::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
253 {
254     ASSERT(!needsLayout());
255 
256     if (!isSelected())
257         return IntRect();
258 
259     IntRect rect = localSelectionRect();
260     if (clipToVisibleContent)
261         computeRectForRepaint(repaintContainer, rect);
262     else
263         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
264 
265     return rect;
266 }
267 
localSelectionRect(bool checkWhetherSelected) const268 IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
269 {
270     if (checkWhetherSelected && !isSelected())
271         return IntRect();
272 
273     if (!m_inlineBoxWrapper)
274         // We're a block-level replaced element.  Just return our own dimensions.
275         return IntRect(0, 0, width(), height());
276 
277     RenderBlock* cb =  containingBlock();
278     if (!cb)
279         return IntRect();
280 
281     RootInlineBox* root = m_inlineBoxWrapper->root();
282     return IntRect(0, root->selectionTop() - y(), width(), root->selectionHeight());
283 }
284 
setSelectionState(SelectionState s)285 void RenderReplaced::setSelectionState(SelectionState s)
286 {
287     RenderBox::setSelectionState(s);
288     if (m_inlineBoxWrapper) {
289         RootInlineBox* line = m_inlineBoxWrapper->root();
290         if (line)
291             line->setHasSelectedChildren(isSelected());
292     }
293 
294     containingBlock()->setSelectionState(s);
295 }
296 
isSelected() const297 bool RenderReplaced::isSelected() const
298 {
299     SelectionState s = selectionState();
300     if (s == SelectionNone)
301         return false;
302     if (s == SelectionInside)
303         return true;
304 
305     int selectionStart, selectionEnd;
306     selectionStartEnd(selectionStart, selectionEnd);
307     if (s == SelectionStart)
308         return selectionStart == 0;
309 
310     int end = node()->hasChildNodes() ? node()->childNodeCount() : 1;
311     if (s == SelectionEnd)
312         return selectionEnd == end;
313     if (s == SelectionBoth)
314         return selectionStart == 0 && selectionEnd == end;
315 
316     ASSERT(0);
317     return false;
318 }
319 
intrinsicSize() const320 IntSize RenderReplaced::intrinsicSize() const
321 {
322     return m_intrinsicSize;
323 }
324 
setIntrinsicSize(const IntSize & size)325 void RenderReplaced::setIntrinsicSize(const IntSize& size)
326 {
327     m_intrinsicSize = size;
328 }
329 
adjustOverflowForBoxShadowAndReflect()330 void RenderReplaced::adjustOverflowForBoxShadowAndReflect()
331 {
332     IntRect overflow;
333     for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) {
334         if (boxShadow->style == Inset)
335             continue;
336         IntRect shadow = borderBoxRect();
337         shadow.move(boxShadow->x, boxShadow->y);
338         shadow.inflate(boxShadow->blur + boxShadow->spread);
339         overflow.unite(shadow);
340     }
341 
342     // Now that we have an overflow rect including shadow, let's make sure that
343     // the reflection (which can also include the shadow) is also included.
344     if (hasReflection()) {
345         if (overflow.isEmpty())
346             overflow = borderBoxRect();
347         overflow.unite(reflectedRect(overflow));
348     }
349 
350     if (!overflow.isEmpty()) {
351         if (!gOverflowRectMap)
352             gOverflowRectMap = new OverflowRectMap();
353         overflow.unite(borderBoxRect());
354         gOverflowRectMap->set(this, overflow);
355         setReplacedHasOverflow(true);
356     } else if (replacedHasOverflow()) {
357         gOverflowRectMap->remove(this);
358         setReplacedHasOverflow(false);
359     }
360 }
361 
overflowHeight(bool) const362 int RenderReplaced::overflowHeight(bool) const
363 {
364     if (replacedHasOverflow()) {
365         IntRect *r = &gOverflowRectMap->find(this)->second;
366         return r->height() + r->y();
367     }
368 
369     return height();
370 }
371 
overflowWidth(bool) const372 int RenderReplaced::overflowWidth(bool) const
373 {
374     if (replacedHasOverflow()) {
375         IntRect *r = &gOverflowRectMap->find(this)->second;
376         return r->width() + r->x();
377     }
378 
379     return width();
380 }
381 
overflowLeft(bool) const382 int RenderReplaced::overflowLeft(bool) const
383 {
384     if (replacedHasOverflow())
385         return gOverflowRectMap->get(this).x();
386 
387     return 0;
388 }
389 
overflowTop(bool) const390 int RenderReplaced::overflowTop(bool) const
391 {
392     if (replacedHasOverflow())
393         return gOverflowRectMap->get(this).y();
394 
395     return 0;
396 }
397 
overflowRect(bool) const398 IntRect RenderReplaced::overflowRect(bool) const
399 {
400     if (replacedHasOverflow())
401         return gOverflowRectMap->find(this)->second;
402 
403     return borderBoxRect();
404 }
405 
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)406 IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
407 {
408     if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
409         return IntRect();
410 
411     // The selectionRect can project outside of the overflowRect, so take their union
412     // for repainting to avoid selection painting glitches.
413     IntRect r = unionRect(localSelectionRect(false), overflowRect(false));
414 
415     RenderView* v = view();
416     if (v) {
417         // FIXME: layoutDelta needs to be applied in parts before/after transforms and
418         // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
419         r.move(v->layoutDelta());
420     }
421 
422     if (style()) {
423         if (style()->hasAppearance())
424             // The theme may wish to inflate the rect used when repainting.
425             theme()->adjustRepaintRect(this, r);
426         if (v)
427             r.inflate(style()->outlineSize());
428     }
429     computeRectForRepaint(repaintContainer, r);
430     return r;
431 }
432 
433 }
434