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