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