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