• 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 
32 using namespace std;
33 
34 namespace WebCore {
35 
36 typedef WTF::HashMap<const RenderReplaced*, IntRect> OverflowRectMap;
37 static OverflowRectMap* gOverflowRectMap = 0;
38 
39 const int cDefaultWidth = 300;
40 const int cDefaultHeight = 150;
41 
RenderReplaced(Node * node)42 RenderReplaced::RenderReplaced(Node* node)
43     : RenderBox(node)
44     , m_intrinsicSize(cDefaultWidth, cDefaultHeight)
45     , m_selectionState(SelectionNone)
46     , m_hasOverflow(false)
47 {
48     setReplaced(true);
49 }
50 
RenderReplaced(Node * node,const IntSize & intrinsicSize)51 RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize)
52     : RenderBox(node)
53     , m_intrinsicSize(intrinsicSize)
54     , m_selectionState(SelectionNone)
55     , m_hasOverflow(false)
56 {
57     setReplaced(true);
58 }
59 
~RenderReplaced()60 RenderReplaced::~RenderReplaced()
61 {
62     if (m_hasOverflow)
63         gOverflowRectMap->remove(this);
64 }
65 
styleDidChange(RenderStyle::Diff diff,const RenderStyle * oldStyle)66 void RenderReplaced::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle)
67 {
68     RenderBox::styleDidChange(diff, oldStyle);
69 
70     bool hadStyle = (oldStyle != 0);
71     float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom();
72     if (hadStyle && style() && style()->effectiveZoom() != oldZoom)
73         intrinsicSizeChanged();
74 }
75 
layout()76 void RenderReplaced::layout()
77 {
78     ASSERT(needsLayout());
79 
80     IntRect oldBounds;
81     IntRect oldOutlineBox;
82     bool checkForRepaint = checkForRepaintDuringLayout();
83     if (checkForRepaint) {
84         oldBounds = absoluteClippedOverflowRect();
85         oldOutlineBox = absoluteOutlineBounds();
86     }
87 
88     setHeight(minimumReplacedHeight());
89 
90     calcWidth();
91     calcHeight();
92     adjustOverflowForBoxShadow();
93 
94     if (checkForRepaint)
95         repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox);
96 
97     setNeedsLayout(false);
98 }
99 
intrinsicSizeChanged()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 
paint(PaintInfo & paintInfo,int tx,int ty)108 void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
109 {
110     if (!shouldPaint(paintInfo, tx, ty))
111         return;
112 
113     tx += x();
114     ty += y();
115 
116     if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
117         paintBoxDecorations(paintInfo, tx, ty);
118 
119     if (paintInfo.phase == PaintPhaseMask) {
120         paintMask(paintInfo, tx, ty);
121         return;
122     }
123 
124     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
125         paintOutline(paintInfo.context, tx, ty, width(), height(), style());
126 
127     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
128         return;
129 
130     if (!shouldPaintWithinRoot(paintInfo))
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     paintReplaced(paintInfo, tx, ty);
141 
142     if (drawSelectionTint) {
143         IntRect selectionPaintingRect = localSelectionRect();
144         selectionPaintingRect.move(tx, ty);
145         paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor());
146     }
147 }
148 
shouldPaint(PaintInfo & paintInfo,int & tx,int & ty)149 bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty)
150 {
151     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline
152             && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask)
153         return false;
154 
155     if (!shouldPaintWithinRoot(paintInfo))
156         return false;
157 
158     // if we're invisible or haven't received a layout yet, then just bail.
159     if (style()->visibility() != VISIBLE)
160         return false;
161 
162     int currentTX = tx + x();
163     int currentTY = ty + y();
164 
165     // Early exit if the element touches the edges.
166     int top = currentTY + overflowTop();
167     int bottom = currentTY + overflowHeight();
168     if (isSelected() && m_inlineBoxWrapper) {
169         int selTop = ty + m_inlineBoxWrapper->root()->selectionTop();
170         int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
171         top = min(selTop, top);
172         bottom = max(selBottom, bottom);
173     }
174 
175     int os = 2 * maximalOutlineSize(paintInfo.phase);
176     if (currentTX + overflowLeft() >= paintInfo.rect.right() + os || currentTX + overflowWidth() <= paintInfo.rect.x() - os)
177         return false;
178     if (top >= paintInfo.rect.bottom() + os || bottom <= paintInfo.rect.y() - os)
179         return false;
180 
181     return true;
182 }
183 
calcPrefWidths()184 void RenderReplaced::calcPrefWidths()
185 {
186     ASSERT(prefWidthsDirty());
187 
188     int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight();
189     int width = calcReplacedWidth(false) + paddingAndBorders;
190 
191     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
192         width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0));
193 
194     if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) {
195         m_minPrefWidth = 0;
196         m_maxPrefWidth = width;
197     } else
198         m_minPrefWidth = m_maxPrefWidth = width;
199 
200     setPrefWidthsDirty(false);
201 }
202 
lineHeight(bool,bool) const203 int RenderReplaced::lineHeight(bool, bool) const
204 {
205     return height() + marginTop() + marginBottom();
206 }
207 
baselinePosition(bool,bool) const208 int RenderReplaced::baselinePosition(bool, bool) const
209 {
210     return height() + marginTop() + marginBottom();
211 }
212 
caretMaxRenderedOffset() const213 unsigned RenderReplaced::caretMaxRenderedOffset() const
214 {
215     return 1;
216 }
217 
positionForCoordinates(int xPos,int yPos)218 VisiblePosition RenderReplaced::positionForCoordinates(int xPos, int yPos)
219 {
220     InlineBox* box = inlineBoxWrapper();
221     if (!box)
222         return VisiblePosition(element(), 0, DOWNSTREAM);
223 
224     // FIXME: This code is buggy if the replaced element is relative positioned.
225 
226     RootInlineBox* root = box->root();
227 
228     int top = root->topOverflow();
229     int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow();
230 
231     if (yPos + y() < top)
232         return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); // coordinates are above
233 
234     if (yPos + y() >= bottom)
235         return VisiblePosition(element(), caretMaxOffset(), DOWNSTREAM); // coordinates are below
236 
237     if (element()) {
238         if (xPos <= width() / 2)
239             return VisiblePosition(element(), 0, DOWNSTREAM);
240         return VisiblePosition(element(), 1, DOWNSTREAM);
241     }
242 
243     return RenderBox::positionForCoordinates(xPos, yPos);
244 }
245 
selectionRect(bool clipToVisibleContent)246 IntRect RenderReplaced::selectionRect(bool clipToVisibleContent)
247 {
248     ASSERT(!needsLayout());
249 
250     if (!isSelected())
251         return IntRect();
252 
253     IntRect rect = localSelectionRect();
254     if (clipToVisibleContent)
255         computeAbsoluteRepaintRect(rect);
256     else {
257         FloatPoint absPos = localToAbsolute(FloatPoint());
258         rect.move(absPos.x(), absPos.y());
259     }
260 
261     return rect;
262 }
263 
localSelectionRect(bool checkWhetherSelected) const264 IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
265 {
266     if (checkWhetherSelected && !isSelected())
267         return IntRect();
268 
269     if (!m_inlineBoxWrapper)
270         // We're a block-level replaced element.  Just return our own dimensions.
271         return IntRect(0, 0, width(), height());
272 
273     RenderBlock* cb =  containingBlock();
274     if (!cb)
275         return IntRect();
276 
277     RootInlineBox* root = m_inlineBoxWrapper->root();
278     return IntRect(0, root->selectionTop() - y(), width(), root->selectionHeight());
279 }
280 
setSelectionState(SelectionState s)281 void RenderReplaced::setSelectionState(SelectionState s)
282 {
283     m_selectionState = s;
284     if (m_inlineBoxWrapper) {
285         RootInlineBox* line = m_inlineBoxWrapper->root();
286         if (line)
287             line->setHasSelectedChildren(isSelected());
288     }
289 
290     containingBlock()->setSelectionState(s);
291 }
292 
isSelected() const293 bool RenderReplaced::isSelected() const
294 {
295     SelectionState s = selectionState();
296     if (s == SelectionNone)
297         return false;
298     if (s == SelectionInside)
299         return true;
300 
301     int selectionStart, selectionEnd;
302     selectionStartEnd(selectionStart, selectionEnd);
303     if (s == SelectionStart)
304         return selectionStart == 0;
305 
306     int end = element()->hasChildNodes() ? element()->childNodeCount() : 1;
307     if (s == SelectionEnd)
308         return selectionEnd == end;
309     if (s == SelectionBoth)
310         return selectionStart == 0 && selectionEnd == end;
311 
312     ASSERT(0);
313     return false;
314 }
315 
intrinsicSize() const316 IntSize RenderReplaced::intrinsicSize() const
317 {
318     return m_intrinsicSize;
319 }
320 
setIntrinsicSize(const IntSize & size)321 void RenderReplaced::setIntrinsicSize(const IntSize& size)
322 {
323     m_intrinsicSize = size;
324 }
325 
adjustOverflowForBoxShadow()326 void RenderReplaced::adjustOverflowForBoxShadow()
327 {
328     IntRect overflow;
329     for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) {
330         IntRect shadow = borderBoxRect();
331         shadow.move(boxShadow->x, boxShadow->y);
332         shadow.inflate(boxShadow->blur);
333         overflow.unite(shadow);
334     }
335 
336     if (!overflow.isEmpty()) {
337         if (!gOverflowRectMap)
338             gOverflowRectMap = new OverflowRectMap();
339         overflow.unite(borderBoxRect());
340         gOverflowRectMap->set(this, overflow);
341         m_hasOverflow = true;
342     } else if (m_hasOverflow) {
343         gOverflowRectMap->remove(this);
344         m_hasOverflow = false;
345     }
346 }
347 
overflowHeight(bool) const348 int RenderReplaced::overflowHeight(bool) const
349 {
350     if (m_hasOverflow) {
351         IntRect *r = &gOverflowRectMap->find(this)->second;
352         return r->height() + r->y();
353     }
354 
355     return height();
356 }
357 
overflowWidth(bool) const358 int RenderReplaced::overflowWidth(bool) const
359 {
360     if (m_hasOverflow) {
361         IntRect *r = &gOverflowRectMap->find(this)->second;
362         return r->width() + r->x();
363     }
364 
365     return width();
366 }
367 
overflowLeft(bool) const368 int RenderReplaced::overflowLeft(bool) const
369 {
370     if (m_hasOverflow)
371         return gOverflowRectMap->get(this).x();
372 
373     return 0;
374 }
375 
overflowTop(bool) const376 int RenderReplaced::overflowTop(bool) const
377 {
378     if (m_hasOverflow)
379         return gOverflowRectMap->get(this).y();
380 
381     return 0;
382 }
383 
overflowRect(bool) const384 IntRect RenderReplaced::overflowRect(bool) const
385 {
386     if (m_hasOverflow)
387         return gOverflowRectMap->find(this)->second;
388 
389     return borderBoxRect();
390 }
391 
clippedOverflowRectForRepaint(RenderBox * repaintContainer)392 IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBox* repaintContainer)
393 {
394     if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
395         return IntRect();
396 
397     // The selectionRect can project outside of the overflowRect, so use
398     // that for repainting to avoid selection painting glitches
399     IntRect r = localSelectionRect(false);
400 
401     RenderView* v = view();
402     if (v) {
403         // FIXME: layoutDelta needs to be applied in parts before/after transforms and
404         // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
405         r.move(v->layoutDelta());
406     }
407 
408     if (style()) {
409         if (style()->hasAppearance())
410             // The theme may wish to inflate the rect used when repainting.
411             theme()->adjustRepaintRect(this, r);
412         if (v)
413             r.inflate(style()->outlineSize());
414     }
415     computeRectForRepaint(r, repaintContainer);
416     return r;
417 }
418 
419 }
420