1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25 #include "config.h"
26 #include "RenderBox.h"
27
28 #include "CachedImage.h"
29 #include "ChromeClient.h"
30 #include "Document.h"
31 #include "FrameView.h"
32 #include "GraphicsContext.h"
33 #include "htmlediting.h"
34 #include "HTMLElement.h"
35 #include "HTMLNames.h"
36 #include "ImageBuffer.h"
37 #include "FloatQuad.h"
38 #include "Frame.h"
39 #include "Page.h"
40 #include "RenderArena.h"
41 #include "RenderFlexibleBox.h"
42 #include "RenderInline.h"
43 #include "RenderLayer.h"
44 #include "RenderTableCell.h"
45 #include "RenderTheme.h"
46 #ifdef ANDROID_LAYOUT
47 #include "Settings.h"
48 #endif
49 #include "RenderView.h"
50 #include "TransformState.h"
51 #include <algorithm>
52 #include <math.h>
53
54 #if ENABLE(WML)
55 #include "WMLNames.h"
56 #endif
57
58 using namespace std;
59
60 namespace WebCore {
61
62 using namespace HTMLNames;
63
64 // Used by flexible boxes when flexing this element.
65 typedef WTF::HashMap<const RenderBox*, int> OverrideSizeMap;
66 static OverrideSizeMap* gOverrideSizeMap = 0;
67
68 bool RenderBox::s_hadOverflowClip = false;
69
RenderBox(Node * node)70 RenderBox::RenderBox(Node* node)
71 : RenderBoxModelObject(node)
72 #ifdef ANDROID_LAYOUT
73 , m_visibleWidth(0)
74 #endif
75 , m_marginLeft(0)
76 , m_marginRight(0)
77 , m_marginTop(0)
78 , m_marginBottom(0)
79 , m_minPrefWidth(-1)
80 , m_maxPrefWidth(-1)
81 , m_inlineBoxWrapper(0)
82 {
83 setIsBox();
84 }
85
~RenderBox()86 RenderBox::~RenderBox()
87 {
88 }
89
destroy()90 void RenderBox::destroy()
91 {
92 // A lot of the code in this function is just pasted into
93 // RenderWidget::destroy. If anything in this function changes,
94 // be sure to fix RenderWidget::destroy() as well.
95 if (hasOverrideSize())
96 gOverrideSizeMap->remove(this);
97
98 if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent()))
99 RenderBlock::removePercentHeightDescendant(this);
100
101 RenderBoxModelObject::destroy();
102 }
103
removeFloatingOrPositionedChildFromBlockLists()104 void RenderBox::removeFloatingOrPositionedChildFromBlockLists()
105 {
106 ASSERT(isFloatingOrPositioned());
107
108 if (documentBeingDestroyed())
109 return;
110
111 if (isFloating()) {
112 RenderBlock* outermostBlock = containingBlock();
113 for (RenderBlock* p = outermostBlock; p && !p->isRenderView(); p = p->containingBlock()) {
114 if (p->containsFloat(this))
115 outermostBlock = p;
116 }
117
118 if (outermostBlock)
119 outermostBlock->markAllDescendantsWithFloatsForLayout(this, false);
120 }
121
122 if (isPositioned()) {
123 RenderObject* p;
124 for (p = parent(); p; p = p->parent()) {
125 if (p->isRenderBlock())
126 toRenderBlock(p)->removePositionedObject(this);
127 }
128 }
129 }
130
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)131 void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
132 {
133 s_hadOverflowClip = hasOverflowClip();
134
135 if (style()) {
136 // The background of the root element or the body element could propagate up to
137 // the canvas. Just dirty the entire canvas when our style changes substantially.
138 if (diff >= StyleDifferenceRepaint && node() &&
139 (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag)))
140 view()->repaint();
141
142 // When a layout hint happens and an object's position style changes, we have to do a layout
143 // to dirty the render tree using the old position value now.
144 if (diff == StyleDifferenceLayout && parent() && style()->position() != newStyle->position()) {
145 markContainingBlocksForLayout();
146 if (style()->position() == StaticPosition)
147 repaint();
148 if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition))
149 removeFloatingOrPositionedChildFromBlockLists();
150 }
151 }
152
153 RenderBoxModelObject::styleWillChange(diff, newStyle);
154 }
155
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)156 void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
157 {
158 RenderBoxModelObject::styleDidChange(diff, oldStyle);
159
160 if (needsLayout() && oldStyle && (oldStyle->height().isPercent() || oldStyle->minHeight().isPercent() || oldStyle->maxHeight().isPercent()))
161 RenderBlock::removePercentHeightDescendant(this);
162
163 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the
164 // new zoomed coordinate space.
165 if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) {
166 int left = scrollLeft();
167 if (left) {
168 left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom();
169 setScrollLeft(left);
170 }
171 int top = scrollTop();
172 if (top) {
173 top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom();
174 setScrollTop(top);
175 }
176 }
177
178 // Set the text color if we're the body.
179 if (isBody())
180 document()->setTextColor(style()->color());
181 }
182
updateBoxModelInfoFromStyle()183 void RenderBox::updateBoxModelInfoFromStyle()
184 {
185 RenderBoxModelObject::updateBoxModelInfoFromStyle();
186
187 bool isRootObject = isRoot();
188 bool isViewObject = isRenderView();
189
190 // The root and the RenderView always paint their backgrounds/borders.
191 if (isRootObject || isViewObject)
192 setHasBoxDecorations(true);
193
194 setPositioned(style()->position() == AbsolutePosition || style()->position() == FixedPosition);
195 setFloating(!isPositioned() && style()->isFloating());
196
197 // We also handle <body> and <html>, whose overflow applies to the viewport.
198 if (style()->overflowX() != OVISIBLE && !isRootObject && (isRenderBlock() || isTableRow() || isTableSection())) {
199 bool boxHasOverflowClip = true;
200 if (isBody()) {
201 // Overflow on the body can propagate to the viewport under the following conditions.
202 // (1) The root element is <html>.
203 // (2) We are the primary <body> (can be checked by looking at document.body).
204 // (3) The root element has visible overflow.
205 if (document()->documentElement()->hasTagName(htmlTag) &&
206 document()->body() == node() &&
207 document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE)
208 boxHasOverflowClip = false;
209 }
210
211 // Check for overflow clip.
212 // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value.
213 if (boxHasOverflowClip) {
214 if (!s_hadOverflowClip)
215 // Erase the overflow
216 repaint();
217 setHasOverflowClip();
218 }
219 }
220
221 setHasTransform(style()->hasTransformRelatedProperty());
222 setHasReflection(style()->boxReflect());
223 }
224
layout()225 void RenderBox::layout()
226 {
227 ASSERT(needsLayout());
228
229 RenderObject* child = firstChild();
230 if (!child) {
231 setNeedsLayout(false);
232 return;
233 }
234
235 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()));
236 while (child) {
237 child->layoutIfNeeded();
238 ASSERT(!child->needsLayout());
239 child = child->nextSibling();
240 }
241 statePusher.pop();
242 setNeedsLayout(false);
243 }
244
245 // More IE extensions. clientWidth and clientHeight represent the interior of an object
246 // excluding border and scrollbar.
clientWidth() const247 int RenderBox::clientWidth() const
248 {
249 return width() - borderLeft() - borderRight() - verticalScrollbarWidth();
250 }
251
clientHeight() const252 int RenderBox::clientHeight() const
253 {
254 return height() - borderTop() - borderBottom() - horizontalScrollbarHeight();
255 }
256
scrollWidth() const257 int RenderBox::scrollWidth() const
258 {
259 if (hasOverflowClip())
260 return layer()->scrollWidth();
261 // For objects with visible overflow, this matches IE.
262 if (style()->direction() == LTR)
263 return max(clientWidth(), rightmostPosition(true, false) - borderLeft());
264 return clientWidth() - min(0, leftmostPosition(true, false) - borderLeft());
265 }
266
scrollHeight() const267 int RenderBox::scrollHeight() const
268 {
269 if (hasOverflowClip())
270 return layer()->scrollHeight();
271 // For objects with visible overflow, this matches IE.
272 return max(clientHeight(), lowestPosition(true, false) - borderTop());
273 }
274
scrollLeft() const275 int RenderBox::scrollLeft() const
276 {
277 return hasOverflowClip() ? layer()->scrollXOffset() : 0;
278 }
279
scrollTop() const280 int RenderBox::scrollTop() const
281 {
282 return hasOverflowClip() ? layer()->scrollYOffset() : 0;
283 }
284
setScrollLeft(int newLeft)285 void RenderBox::setScrollLeft(int newLeft)
286 {
287 if (hasOverflowClip())
288 layer()->scrollToXOffset(newLeft);
289 }
290
setScrollTop(int newTop)291 void RenderBox::setScrollTop(int newTop)
292 {
293 if (hasOverflowClip())
294 layer()->scrollToYOffset(newTop);
295 }
296
absoluteRects(Vector<IntRect> & rects,int tx,int ty)297 void RenderBox::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
298 {
299 rects.append(IntRect(tx, ty, width(), height()));
300 }
301
absoluteQuads(Vector<FloatQuad> & quads)302 void RenderBox::absoluteQuads(Vector<FloatQuad>& quads)
303 {
304 quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height())));
305 }
306
absoluteContentBox() const307 IntRect RenderBox::absoluteContentBox() const
308 {
309 IntRect rect = contentBoxRect();
310 FloatPoint absPos = localToAbsolute(FloatPoint());
311 rect.move(absPos.x(), absPos.y());
312 return rect;
313 }
314
absoluteContentQuad() const315 FloatQuad RenderBox::absoluteContentQuad() const
316 {
317 IntRect rect = contentBoxRect();
318 return localToAbsoluteQuad(FloatRect(rect));
319 }
320
outlineBoundsForRepaint(RenderBoxModelObject * repaintContainer) const321 IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer) const
322 {
323 IntRect box = borderBoundingBox();
324 adjustRectForOutlineAndShadow(box);
325
326 FloatQuad containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer);
327 box = containerRelativeQuad.enclosingBoundingBox();
328
329 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
330 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
331 box.move(view()->layoutDelta());
332
333 return box;
334 }
335
addFocusRingRects(GraphicsContext * graphicsContext,int tx,int ty)336 void RenderBox::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty)
337 {
338 graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height()));
339 }
340
341
reflectionBox() const342 IntRect RenderBox::reflectionBox() const
343 {
344 IntRect result;
345 if (!style()->boxReflect())
346 return result;
347 IntRect box = borderBoxRect();
348 result = box;
349 switch (style()->boxReflect()->direction()) {
350 case ReflectionBelow:
351 result.move(0, box.height() + reflectionOffset());
352 break;
353 case ReflectionAbove:
354 result.move(0, -box.height() - reflectionOffset());
355 break;
356 case ReflectionLeft:
357 result.move(-box.width() - reflectionOffset(), 0);
358 break;
359 case ReflectionRight:
360 result.move(box.width() + reflectionOffset(), 0);
361 break;
362 }
363 return result;
364 }
365
reflectionOffset() const366 int RenderBox::reflectionOffset() const
367 {
368 if (!style()->boxReflect())
369 return 0;
370 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight)
371 return style()->boxReflect()->offset().calcValue(borderBoxRect().width());
372 return style()->boxReflect()->offset().calcValue(borderBoxRect().height());
373 }
374
reflectedRect(const IntRect & r) const375 IntRect RenderBox::reflectedRect(const IntRect& r) const
376 {
377 if (!style()->boxReflect())
378 return IntRect();
379
380 IntRect box = borderBoxRect();
381 IntRect result = r;
382 switch (style()->boxReflect()->direction()) {
383 case ReflectionBelow:
384 result.setY(box.bottom() + reflectionOffset() + (box.bottom() - r.bottom()));
385 break;
386 case ReflectionAbove:
387 result.setY(box.y() - reflectionOffset() - box.height() + (box.bottom() - r.bottom()));
388 break;
389 case ReflectionLeft:
390 result.setX(box.x() - reflectionOffset() - box.width() + (box.right() - r.right()));
391 break;
392 case ReflectionRight:
393 result.setX(box.right() + reflectionOffset() + (box.right() - r.right()));
394 break;
395 }
396 return result;
397 }
398
verticalScrollbarWidth() const399 int RenderBox::verticalScrollbarWidth() const
400 {
401 return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0;
402 }
403
horizontalScrollbarHeight() const404 int RenderBox::horizontalScrollbarHeight() const
405 {
406 return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0;
407 }
408
scroll(ScrollDirection direction,ScrollGranularity granularity,float multiplier)409 bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
410 {
411 RenderLayer* l = layer();
412 if (l && l->scroll(direction, granularity, multiplier))
413 return true;
414 RenderBlock* b = containingBlock();
415 if (b && !b->isRenderView())
416 return b->scroll(direction, granularity, multiplier);
417 return false;
418 }
419
canBeProgramaticallyScrolled(bool) const420 bool RenderBox::canBeProgramaticallyScrolled(bool) const
421 {
422 return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->isContentEditable()))) || (node() && node()->isDocumentNode());
423 }
424
autoscroll()425 void RenderBox::autoscroll()
426 {
427 if (layer())
428 layer()->autoscroll();
429 }
430
panScroll(const IntPoint & source)431 void RenderBox::panScroll(const IntPoint& source)
432 {
433 if (layer())
434 layer()->panScrollFromPoint(source);
435 }
436
minPrefWidth() const437 int RenderBox::minPrefWidth() const
438 {
439 if (prefWidthsDirty())
440 const_cast<RenderBox*>(this)->calcPrefWidths();
441
442 return m_minPrefWidth;
443 }
444
maxPrefWidth() const445 int RenderBox::maxPrefWidth() const
446 {
447 if (prefWidthsDirty())
448 const_cast<RenderBox*>(this)->calcPrefWidths();
449
450 return m_maxPrefWidth;
451 }
452
overrideSize() const453 int RenderBox::overrideSize() const
454 {
455 if (!hasOverrideSize())
456 return -1;
457 return gOverrideSizeMap->get(this);
458 }
459
setOverrideSize(int s)460 void RenderBox::setOverrideSize(int s)
461 {
462 if (s == -1) {
463 if (hasOverrideSize()) {
464 setHasOverrideSize(false);
465 gOverrideSizeMap->remove(this);
466 }
467 } else {
468 if (!gOverrideSizeMap)
469 gOverrideSizeMap = new OverrideSizeMap();
470 setHasOverrideSize(true);
471 gOverrideSizeMap->set(this, s);
472 }
473 }
474
overrideWidth() const475 int RenderBox::overrideWidth() const
476 {
477 return hasOverrideSize() ? overrideSize() : width();
478 }
479
overrideHeight() const480 int RenderBox::overrideHeight() const
481 {
482 return hasOverrideSize() ? overrideSize() : height();
483 }
484
calcBorderBoxWidth(int width) const485 int RenderBox::calcBorderBoxWidth(int width) const
486 {
487 int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight();
488 if (style()->boxSizing() == CONTENT_BOX)
489 return width + bordersPlusPadding;
490 return max(width, bordersPlusPadding);
491 }
492
calcBorderBoxHeight(int height) const493 int RenderBox::calcBorderBoxHeight(int height) const
494 {
495 int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom();
496 if (style()->boxSizing() == CONTENT_BOX)
497 return height + bordersPlusPadding;
498 return max(height, bordersPlusPadding);
499 }
500
calcContentBoxWidth(int width) const501 int RenderBox::calcContentBoxWidth(int width) const
502 {
503 if (style()->boxSizing() == BORDER_BOX)
504 width -= (borderLeft() + borderRight() + paddingLeft() + paddingRight());
505 return max(0, width);
506 }
507
calcContentBoxHeight(int height) const508 int RenderBox::calcContentBoxHeight(int height) const
509 {
510 if (style()->boxSizing() == BORDER_BOX)
511 height -= (borderTop() + borderBottom() + paddingTop() + paddingBottom());
512 return max(0, height);
513 }
514
515 // Hit Testing
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int xPos,int yPos,int tx,int ty,HitTestAction action)516 bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
517 {
518 tx += x();
519 ty += y();
520
521 // Check kids first.
522 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
523 if (!child->hasLayer() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) {
524 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
525 return true;
526 }
527 }
528
529 // Check our bounds next. For this purpose always assume that we can only be hit in the
530 // foreground phase (which is true for replaced elements like images).
531 if (visibleToHitTesting() && action == HitTestForeground && IntRect(tx, ty, width(), height()).contains(xPos, yPos)) {
532 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
533 return true;
534 }
535
536 return false;
537 }
538
539 // --------------------- painting stuff -------------------------------
540
paint(PaintInfo & paintInfo,int tx,int ty)541 void RenderBox::paint(PaintInfo& paintInfo, int tx, int ty)
542 {
543 tx += x();
544 ty += y();
545
546 // default implementation. Just pass paint through to the children
547 PaintInfo childInfo(paintInfo);
548 childInfo.paintingRoot = paintingRootForChildren(paintInfo);
549 for (RenderObject* child = firstChild(); child; child = child->nextSibling())
550 child->paint(childInfo, tx, ty);
551 }
552
paintRootBoxDecorations(PaintInfo & paintInfo,int tx,int ty)553 void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
554 {
555 const FillLayer* bgLayer = style()->backgroundLayers();
556 Color bgColor = style()->backgroundColor();
557 if (!style()->hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) {
558 // Locate the <body> element using the DOM. This is easier than trying
559 // to crawl around a render tree with potential :before/:after content and
560 // anonymous blocks created by inline <body> tags etc. We can locate the <body>
561 // render object very easily via the DOM.
562 HTMLElement* body = document()->body();
563 RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0;
564 if (bodyObject) {
565 bgLayer = bodyObject->style()->backgroundLayers();
566 bgColor = bodyObject->style()->backgroundColor();
567 }
568 }
569
570 int w = width();
571 int h = height();
572
573 int rw;
574 int rh;
575 if (view()->frameView()) {
576 rw = view()->frameView()->contentsWidth();
577 rh = view()->frameView()->contentsHeight();
578 } else {
579 rw = view()->width();
580 rh = view()->height();
581 }
582
583 // CSS2 14.2:
584 // The background of the box generated by the root element covers the entire canvas including
585 // its margins.
586 int bx = tx - marginLeft();
587 int by = ty - marginTop();
588 int bw = max(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw);
589 int bh = max(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh);
590
591 paintFillLayers(paintInfo, bgColor, bgLayer, bx, by, bw, bh);
592
593 if (style()->hasBorder() && style()->display() != INLINE)
594 paintBorder(paintInfo.context, tx, ty, w, h, style());
595 }
596
paintBoxDecorations(PaintInfo & paintInfo,int tx,int ty)597 void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
598 {
599 if (!shouldPaintWithinRoot(paintInfo))
600 return;
601
602 if (isRoot()) {
603 paintRootBoxDecorations(paintInfo, tx, ty);
604 return;
605 }
606
607 int w = width();
608 int h = height();
609
610 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat
611 // balloon layout is an example of this).
612 borderFitAdjust(tx, w);
613
614 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
615 // custom shadows of their own.
616 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal);
617
618 // If we have a native theme appearance, paint that before painting our background.
619 // The theme will tell us whether or not we should also paint the CSS background.
620 bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, IntRect(tx, ty, w, h));
621 if (!themePainted) {
622 // The <body> only paints its background if the root element has defined a background
623 // independent of the body. Go through the DOM to get to the root element's render object,
624 // since the root could be inline and wrapped in an anonymous block.
625 if (!isBody() || document()->documentElement()->renderer()->style()->hasBackground())
626 paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), tx, ty, w, h);
627 if (style()->hasAppearance())
628 theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, w, h));
629 }
630 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset);
631
632 // The theme will tell us whether or not we should also paint the CSS border.
633 if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, IntRect(tx, ty, w, h)))) && style()->hasBorder())
634 paintBorder(paintInfo.context, tx, ty, w, h, style());
635 }
636
paintMask(PaintInfo & paintInfo,int tx,int ty)637 void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty)
638 {
639 if (!shouldPaintWithinRoot(paintInfo) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
640 return;
641
642 int w = width();
643 int h = height();
644
645 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat
646 // balloon layout is an example of this).
647 borderFitAdjust(tx, w);
648
649 paintMaskImages(paintInfo, tx, ty, w, h);
650 }
651
paintMaskImages(const PaintInfo & paintInfo,int tx,int ty,int w,int h)652 void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int w, int h)
653 {
654 // Figure out if we need to push a transparency layer to render our mask.
655 bool pushTransparencyLayer = false;
656 StyleImage* maskBoxImage = style()->maskBoxImage().image();
657 if (maskBoxImage && style()->maskLayers()->hasImage()) {
658 pushTransparencyLayer = true;
659 } else {
660 // We have to use an extra image buffer to hold the mask. Multiple mask images need
661 // to composite together using source-over so that they can then combine into a single unified mask that
662 // can be composited with the content using destination-in. SVG images need to be able to set compositing modes
663 // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer
664 // and composite that buffer as the mask.
665 // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering
666 // before pushing the transparency layer.
667 for (const FillLayer* fillLayer = style()->maskLayers()->next(); fillLayer; fillLayer = fillLayer->next()) {
668 if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) {
669 pushTransparencyLayer = true;
670 // We found one image that can be used in rendering, exit the loop
671 break;
672 }
673 }
674 }
675
676 CompositeOperator compositeOp = CompositeDestinationIn;
677 if (pushTransparencyLayer) {
678 paintInfo.context->setCompositeOperation(CompositeDestinationIn);
679 paintInfo.context->beginTransparencyLayer(1.0f);
680 compositeOp = CompositeSourceOver;
681 }
682
683 paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp);
684 paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp);
685
686 if (pushTransparencyLayer)
687 paintInfo.context->endTransparencyLayer();
688 }
689
maskClipRect()690 IntRect RenderBox::maskClipRect()
691 {
692 IntRect bbox = borderBoxRect();
693 if (style()->maskBoxImage().image())
694 return bbox;
695
696 IntRect result;
697 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
698 if (maskLayer->image()) {
699 IntRect maskRect;
700 IntPoint phase;
701 IntSize tileSize;
702 calculateBackgroundImageGeometry(maskLayer, bbox.x(), bbox.y(), bbox.width(), bbox.height(), maskRect, phase, tileSize);
703 result.unite(maskRect);
704 }
705 }
706 return result;
707 }
708
paintFillLayers(const PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,int tx,int ty,int width,int height,CompositeOperator op)709 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op)
710 {
711 if (!fillLayer)
712 return;
713
714 paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op);
715 paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op);
716 }
717
paintFillLayer(const PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,int tx,int ty,int width,int height,CompositeOperator op)718 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op)
719 {
720 paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, op);
721 }
722
imageChanged(WrappedImagePtr image,const IntRect *)723 void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*)
724 {
725 if (!parent())
726 return;
727
728 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) ||
729 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) {
730 repaint();
731 return;
732 }
733
734 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true);
735 if (!didFullRepaint)
736 repaintLayerRectsForImage(image, style()->maskLayers(), false);
737 }
738
repaintLayerRectsForImage(WrappedImagePtr image,const FillLayer * layers,bool drawingBackground)739 bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground)
740 {
741 IntRect rendererRect;
742 RenderBox* layerRenderer = 0;
743
744 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
745 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(style()->effectiveZoom())) {
746 // Now that we know this image is being used, compute the renderer and the rect
747 // if we haven't already
748 if (!layerRenderer) {
749 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->style()->hasBackground()));
750 if (drawingRootBackground) {
751 layerRenderer = view();
752
753 int rw;
754 int rh;
755
756 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) {
757 rw = frameView->contentsWidth();
758 rh = frameView->contentsHeight();
759 } else {
760 rw = layerRenderer->width();
761 rh = layerRenderer->height();
762 }
763 rendererRect = IntRect(-layerRenderer->marginLeft(),
764 -layerRenderer->marginTop(),
765 max(layerRenderer->width() + layerRenderer->marginLeft() + layerRenderer->marginRight() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw),
766 max(layerRenderer->height() + layerRenderer->marginTop() + layerRenderer->marginBottom() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh));
767 } else {
768 layerRenderer = this;
769 rendererRect = borderBoxRect();
770 }
771 }
772
773 IntRect repaintRect;
774 IntPoint phase;
775 IntSize tileSize;
776 layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect.x(), rendererRect.y(), rendererRect.width(), rendererRect.height(), repaintRect, phase, tileSize);
777 layerRenderer->repaintRectangle(repaintRect);
778 if (repaintRect == rendererRect)
779 return true;
780 }
781 }
782 return false;
783 }
784
785 #if PLATFORM(MAC)
786
paintCustomHighlight(int tx,int ty,const AtomicString & type,bool behindText)787 void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText)
788 {
789 Frame* frame = document()->frame();
790 if (!frame)
791 return;
792 Page* page = frame->page();
793 if (!page)
794 return;
795
796 InlineBox* boxWrap = inlineBoxWrapper();
797 RootInlineBox* r = boxWrap ? boxWrap->root() : 0;
798 if (r) {
799 FloatRect rootRect(tx + r->x(), ty + r->selectionTop(), r->width(), r->selectionHeight());
800 FloatRect imageRect(tx + x(), rootRect.y(), width(), rootRect.height());
801 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false);
802 } else {
803 FloatRect imageRect(tx + x(), ty + y(), width(), height());
804 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false);
805 }
806 }
807
808 #endif
809
pushContentsClip(PaintInfo & paintInfo,int tx,int ty)810 bool RenderBox::pushContentsClip(PaintInfo& paintInfo, int tx, int ty)
811 {
812 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask)
813 return false;
814
815 bool isControlClip = hasControlClip();
816 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer();
817
818 if (!isControlClip && !isOverflowClip)
819 return false;
820
821 if (paintInfo.phase == PaintPhaseOutline)
822 paintInfo.phase = PaintPhaseChildOutlines;
823 else if (paintInfo.phase == PaintPhaseChildBlockBackground) {
824 paintInfo.phase = PaintPhaseBlockBackground;
825 paintObject(paintInfo, tx, ty);
826 paintInfo.phase = PaintPhaseChildBlockBackgrounds;
827 }
828 IntRect clipRect(isControlClip ? controlClipRect(tx, ty) : overflowClipRect(tx, ty));
829 paintInfo.context->save();
830 if (style()->hasBorderRadius()) {
831 IntSize topLeft, topRight, bottomLeft, bottomRight;
832 IntRect borderRect = IntRect(tx, ty, width(), height());
833 style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
834
835 paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
836 }
837
838 paintInfo.context->clip(clipRect);
839 return true;
840 }
841
popContentsClip(PaintInfo & paintInfo,PaintPhase originalPhase,int tx,int ty)842 void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, int tx, int ty)
843 {
844 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer()));
845
846 paintInfo.context->restore();
847 if (originalPhase == PaintPhaseOutline) {
848 paintInfo.phase = PaintPhaseSelfOutline;
849 paintObject(paintInfo, tx, ty);
850 paintInfo.phase = originalPhase;
851 } else if (originalPhase == PaintPhaseChildBlockBackground)
852 paintInfo.phase = originalPhase;
853 }
854
overflowClipRect(int tx,int ty)855 IntRect RenderBox::overflowClipRect(int tx, int ty)
856 {
857 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property
858 // here.
859
860 int bLeft = borderLeft();
861 int bTop = borderTop();
862
863 int clipX = tx + bLeft;
864 int clipY = ty + bTop;
865 int clipWidth = width() - bLeft - borderRight();
866 int clipHeight = height() - bTop - borderBottom();
867
868 // Subtract out scrollbars if we have them.
869 if (layer()) {
870 clipWidth -= layer()->verticalScrollbarWidth();
871 clipHeight -= layer()->horizontalScrollbarHeight();
872 }
873
874 return IntRect(clipX, clipY, clipWidth, clipHeight);
875 }
876
clipRect(int tx,int ty)877 IntRect RenderBox::clipRect(int tx, int ty)
878 {
879 int clipX = tx;
880 int clipY = ty;
881 int clipWidth = width();
882 int clipHeight = height();
883
884 if (!style()->clipLeft().isAuto()) {
885 int c = style()->clipLeft().calcValue(width());
886 clipX += c;
887 clipWidth -= c;
888 }
889
890 if (!style()->clipRight().isAuto())
891 clipWidth -= width() - style()->clipRight().calcValue(width());
892
893 if (!style()->clipTop().isAuto()) {
894 int c = style()->clipTop().calcValue(height());
895 clipY += c;
896 clipHeight -= c;
897 }
898
899 if (!style()->clipBottom().isAuto())
900 clipHeight -= height() - style()->clipBottom().calcValue(height());
901
902 return IntRect(clipX, clipY, clipWidth, clipHeight);
903 }
904
containingBlockWidthForContent() const905 int RenderBox::containingBlockWidthForContent() const
906 {
907 RenderBlock* cb = containingBlock();
908 if (shrinkToAvoidFloats())
909 return cb->lineWidth(y(), false);
910 return cb->availableWidth();
911 }
912
mapLocalToContainer(RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState) const913 void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
914 {
915 if (repaintContainer == this)
916 return;
917
918 if (RenderView* v = view()) {
919 if (v->layoutStateEnabled() && !repaintContainer) {
920 LayoutState* layoutState = v->layoutState();
921 IntSize offset = layoutState->m_offset;
922 offset.expand(x(), y());
923 if (style()->position() == RelativePosition && layer())
924 offset += layer()->relativePositionOffset();
925 transformState.move(offset);
926 return;
927 }
928 }
929
930 if (style()->position() == FixedPosition)
931 fixed = true;
932
933 RenderObject* o = container();
934 if (!o)
935 return;
936
937 bool hasTransform = hasLayer() && layer()->transform();
938 if (hasTransform)
939 fixed = false; // Elements with transforms act as a containing block for fixed position descendants
940
941 IntSize containerOffset = offsetFromContainer(o);
942
943 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
944 if (useTransforms && shouldUseTransformFromContainer(o)) {
945 TransformationMatrix t;
946 getTransformFromContainer(o, containerOffset, t);
947 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
948 } else
949 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
950
951 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
952 }
953
mapAbsoluteToLocalPoint(bool fixed,bool useTransforms,TransformState & transformState) const954 void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
955 {
956 // We don't expect absoluteToLocal() to be called during layout (yet)
957 ASSERT(!view() || !view()->layoutStateEnabled());
958
959 if (style()->position() == FixedPosition)
960 fixed = true;
961
962 bool hasTransform = hasLayer() && layer()->transform();
963 if (hasTransform)
964 fixed = false; // Elements with transforms act as a containing block for fixed position descendants
965
966 RenderObject* o = container();
967 if (!o)
968 return;
969
970 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
971
972 IntSize containerOffset = offsetFromContainer(o);
973
974 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
975 if (useTransforms && shouldUseTransformFromContainer(o)) {
976 TransformationMatrix t;
977 getTransformFromContainer(o, containerOffset, t);
978 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
979 } else
980 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
981 }
982
offsetFromContainer(RenderObject * o) const983 IntSize RenderBox::offsetFromContainer(RenderObject* o) const
984 {
985 ASSERT(o == container());
986
987 IntSize offset;
988 if (isRelPositioned())
989 offset += relativePositionOffset();
990
991 if (!isInline() || isReplaced()) {
992 RenderBlock* cb;
993 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition
994 && (cb = toRenderBlock(o))->hasColumns()) {
995 IntRect rect(x(), y(), 1, 1);
996 cb->adjustRectForColumns(rect);
997 offset.expand(rect.x(), rect.y());
998 } else
999 offset.expand(x(), y());
1000 }
1001
1002 if (o->hasOverflowClip())
1003 offset -= toRenderBox(o)->layer()->scrolledContentOffset();
1004
1005 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
1006 offset += toRenderInline(o)->relativePositionedInlineOffset(this);
1007
1008 return offset;
1009 }
1010
createInlineBox()1011 InlineBox* RenderBox::createInlineBox()
1012 {
1013 return new (renderArena()) InlineBox(this);
1014 }
1015
dirtyLineBoxes(bool fullLayout)1016 void RenderBox::dirtyLineBoxes(bool fullLayout)
1017 {
1018 if (m_inlineBoxWrapper) {
1019 if (fullLayout) {
1020 m_inlineBoxWrapper->destroy(renderArena());
1021 m_inlineBoxWrapper = 0;
1022 } else
1023 m_inlineBoxWrapper->dirtyLineBoxes();
1024 }
1025 }
1026
positionLineBox(InlineBox * box)1027 void RenderBox::positionLineBox(InlineBox* box)
1028 {
1029 if (isPositioned()) {
1030 // Cache the x position only if we were an INLINE type originally.
1031 bool wasInline = style()->isOriginalDisplayInlineType();
1032 if (wasInline && style()->hasStaticX()) {
1033 // The value is cached in the xPos of the box. We only need this value if
1034 // our object was inline originally, since otherwise it would have ended up underneath
1035 // the inlines.
1036 layer()->setStaticX(box->x());
1037 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
1038 } else if (!wasInline && style()->hasStaticY()) {
1039 // Our object was a block originally, so we make our normal flow position be
1040 // just below the line box (as though all the inlines that came before us got
1041 // wrapped in an anonymous block, which is what would have happened had we been
1042 // in flow). This value was cached in the y() of the box.
1043 layer()->setStaticY(box->y());
1044 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
1045 }
1046
1047 // Nuke the box.
1048 box->remove();
1049 box->destroy(renderArena());
1050 } else if (isReplaced()) {
1051 setLocation(box->x(), box->y());
1052 m_inlineBoxWrapper = box;
1053 }
1054 }
1055
deleteLineBoxWrapper()1056 void RenderBox::deleteLineBoxWrapper()
1057 {
1058 if (m_inlineBoxWrapper) {
1059 if (!documentBeingDestroyed())
1060 m_inlineBoxWrapper->remove();
1061 m_inlineBoxWrapper->destroy(renderArena());
1062 m_inlineBoxWrapper = 0;
1063 }
1064 }
1065
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)1066 IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
1067 {
1068 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
1069 return IntRect();
1070
1071 IntRect r = overflowRect(false);
1072
1073 RenderView* v = view();
1074 if (v) {
1075 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
1076 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
1077 r.move(v->layoutDelta());
1078 }
1079
1080 if (style()) {
1081 if (style()->hasAppearance())
1082 // The theme may wish to inflate the rect used when repainting.
1083 theme()->adjustRepaintRect(this, r);
1084
1085 // We have to use maximalOutlineSize() because a child might have an outline
1086 // that projects outside of our overflowRect.
1087 if (v) {
1088 ASSERT(style()->outlineSize() <= v->maximalOutlineSize());
1089 r.inflate(v->maximalOutlineSize());
1090 }
1091 }
1092 computeRectForRepaint(repaintContainer, r);
1093 return r;
1094 }
1095
computeRectForRepaint(RenderBoxModelObject * repaintContainer,IntRect & rect,bool fixed)1096 void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
1097 {
1098 if (RenderView* v = view()) {
1099 // LayoutState is only valid for root-relative repainting
1100 if (v->layoutStateEnabled() && !repaintContainer) {
1101 LayoutState* layoutState = v->layoutState();
1102
1103 if (layer() && layer()->transform())
1104 rect = layer()->transform()->mapRect(rect);
1105
1106 if (style()->position() == RelativePosition && layer())
1107 rect.move(layer()->relativePositionOffset());
1108
1109 rect.move(x(), y());
1110 rect.move(layoutState->m_offset);
1111 if (layoutState->m_clipped)
1112 rect.intersect(layoutState->m_clipRect);
1113 return;
1114 }
1115 }
1116
1117 if (hasReflection())
1118 rect.unite(reflectedRect(rect));
1119
1120 if (repaintContainer == this)
1121 return;
1122
1123 RenderObject* o = container();
1124 if (!o)
1125 return;
1126
1127 IntPoint topLeft = rect.location();
1128 topLeft.move(x(), y());
1129
1130 if (style()->position() == FixedPosition)
1131 fixed = true;
1132
1133 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) {
1134 RenderBlock* cb = toRenderBlock(o);
1135 if (cb->hasColumns()) {
1136 IntRect repaintRect(topLeft, rect.size());
1137 cb->adjustRectForColumns(repaintRect);
1138 topLeft = repaintRect.location();
1139 rect = repaintRect;
1140 }
1141 }
1142
1143 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box
1144 // in the parent's coordinate space that encloses us.
1145 if (layer() && layer()->transform()) {
1146 fixed = false;
1147 rect = layer()->transform()->mapRect(rect);
1148 // FIXME: this clobbers topLeft adjustment done for multicol above
1149 topLeft = rect.location();
1150 topLeft.move(x(), y());
1151 }
1152
1153 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
1154 topLeft += toRenderInline(o)->relativePositionedInlineOffset(this);
1155 else if (style()->position() == RelativePosition && layer()) {
1156 // Apply the relative position offset when invalidating a rectangle. The layer
1157 // is translated, but the render box isn't, so we need to do this to get the
1158 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
1159 // flag on the RenderObject has been cleared, so use the one on the style().
1160 topLeft += layer()->relativePositionOffset();
1161 }
1162
1163 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1164 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1165 if (o->hasOverflowClip()) {
1166 RenderBox* containerBox = toRenderBox(o);
1167
1168 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
1169 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
1170 // anyway if its size does change.
1171 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden.
1172
1173 IntRect repaintRect(topLeft, rect.size());
1174 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height());
1175 rect = intersection(repaintRect, boxRect);
1176 if (rect.isEmpty())
1177 return;
1178 } else
1179 rect.setLocation(topLeft);
1180
1181 o->computeRectForRepaint(repaintContainer, rect, fixed);
1182 }
1183
repaintDuringLayoutIfMoved(const IntRect & rect)1184 void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect)
1185 {
1186 int newX = x();
1187 int newY = y();
1188 int newWidth = width();
1189 int newHeight = height();
1190 if (rect.x() != newX || rect.y() != newY) {
1191 // The child moved. Invalidate the object's old and new positions. We have to do this
1192 // since the object may not have gotten a layout.
1193 m_frameRect = rect;
1194 repaint();
1195 repaintOverhangingFloats(true);
1196 m_frameRect = IntRect(newX, newY, newWidth, newHeight);
1197 repaint();
1198 repaintOverhangingFloats(true);
1199 }
1200 }
1201
calcWidth()1202 void RenderBox::calcWidth()
1203 {
1204 #ifdef ANDROID_LAYOUT
1205 if (view()->frameView()) {
1206 const Settings* settings = document()->settings();
1207 ASSERT(settings);
1208 if (settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) {
1209 m_visibleWidth = view()->frameView()->screenWidth();
1210 }
1211 }
1212 #endif
1213
1214 if (isPositioned()) {
1215 calcAbsoluteHorizontal();
1216 return;
1217 }
1218
1219 // If layout is limited to a subtree, the subtree root's width does not change.
1220 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this)
1221 return;
1222
1223 // The parent box is flexing us, so it has increased or decreased our
1224 // width. Use the width from the style context.
1225 if (hasOverrideSize() && parent()->style()->boxOrient() == HORIZONTAL
1226 && parent()->isFlexibleBox() && parent()->isFlexingChildren()) {
1227 setWidth(overrideSize());
1228 return;
1229 }
1230
1231 bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL);
1232 bool stretching = (parent()->style()->boxAlign() == BSTRETCH);
1233 bool treatAsReplaced = shouldCalculateSizeAsReplaced() && (!inVerticalBox || !stretching);
1234
1235 Length w = (treatAsReplaced) ? Length(calcReplacedWidth(), Fixed) : style()->width();
1236
1237 RenderBlock* cb = containingBlock();
1238 int containerWidth = max(0, containingBlockWidthForContent());
1239
1240 Length marginLeft = style()->marginLeft();
1241 Length marginRight = style()->marginRight();
1242
1243 if (isInline() && !isInlineBlockOrInlineTable()) {
1244 // just calculate margins
1245 m_marginLeft = marginLeft.calcMinValue(containerWidth);
1246 m_marginRight = marginRight.calcMinValue(containerWidth);
1247 #ifdef ANDROID_LAYOUT
1248 if (treatAsReplaced) {
1249 #else
1250 if (treatAsReplaced)
1251 #endif
1252 setWidth(max(w.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight(), minPrefWidth()));
1253 #ifdef ANDROID_LAYOUT
1254 // in SSR mode with replaced box, if the box width is wider than the container width,
1255 // it will be shrinked to fit to the container.
1256 if (containerWidth && (width() + m_marginLeft + m_marginRight) > containerWidth &&
1257 document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
1258 m_marginLeft = m_marginRight = 0;
1259 setWidth(containerWidth);
1260 m_minPrefWidth = m_maxPrefWidth = containerWidth;
1261 }
1262 }
1263 #endif
1264 return;
1265 }
1266
1267 // Width calculations
1268 if (treatAsReplaced)
1269 setWidth(w.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight());
1270 else {
1271 // Calculate Width
1272 setWidth(calcWidthUsing(Width, containerWidth));
1273
1274 // Calculate MaxWidth
1275 if (!style()->maxWidth().isUndefined()) {
1276 int maxW = calcWidthUsing(MaxWidth, containerWidth);
1277 if (width() > maxW) {
1278 setWidth(maxW);
1279 w = style()->maxWidth();
1280 }
1281 }
1282
1283 // Calculate MinWidth
1284 int minW = calcWidthUsing(MinWidth, containerWidth);
1285 if (width() < minW) {
1286 setWidth(minW);
1287 w = style()->minWidth();
1288 }
1289 }
1290
1291 if (stretchesToMinIntrinsicWidth()) {
1292 setWidth(max(width(), minPrefWidth()));
1293 w = Length(width(), Fixed);
1294 }
1295
1296 // Margin calculations
1297 if (w.isAuto()) {
1298 m_marginLeft = marginLeft.calcMinValue(containerWidth);
1299 m_marginRight = marginRight.calcMinValue(containerWidth);
1300 } else {
1301 m_marginLeft = 0;
1302 m_marginRight = 0;
1303 calcHorizontalMargins(marginLeft, marginRight, containerWidth);
1304 }
1305 #ifdef ANDROID_LAYOUT
1306 // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin.
1307 // If the box width is wider than the container width, it will be shrinked to fit to the container.
1308 if (containerWidth && !treatAsReplaced &&
1309 document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
1310 setWidth(width() + m_marginLeft + m_marginRight);
1311 m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft;
1312 m_marginRight = m_marginRight > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginRight;
1313 if (width() > containerWidth) {
1314 m_minPrefWidth = m_maxPrefWidth = containerWidth-(m_marginLeft + m_marginRight);
1315 setWidth(m_minPrefWidth);
1316 } else
1317 setWidth(width() -(m_marginLeft + m_marginRight));
1318 }
1319 #endif
1320
1321 if (containerWidth && containerWidth != (width() + m_marginLeft + m_marginRight)
1322 && !isFloating() && !isInline() && !cb->isFlexibleBox()) {
1323 if (cb->style()->direction() == LTR)
1324 m_marginRight = containerWidth - width() - m_marginLeft;
1325 else
1326 m_marginLeft = containerWidth - width() - m_marginRight;
1327 }
1328 }
1329
calcWidthUsing(WidthType widthType,int cw)1330 int RenderBox::calcWidthUsing(WidthType widthType, int cw)
1331 {
1332 int widthResult = width();
1333 Length w;
1334 if (widthType == Width)
1335 w = style()->width();
1336 else if (widthType == MinWidth)
1337 w = style()->minWidth();
1338 else
1339 w = style()->maxWidth();
1340
1341 if (w.isIntrinsicOrAuto()) {
1342 int marginLeft = style()->marginLeft().calcMinValue(cw);
1343 int marginRight = style()->marginRight().calcMinValue(cw);
1344 if (cw)
1345 widthResult = cw - marginLeft - marginRight;
1346
1347 if (sizesToIntrinsicWidth(widthType)) {
1348 widthResult = max(widthResult, minPrefWidth());
1349 widthResult = min(widthResult, maxPrefWidth());
1350 }
1351 } else
1352 widthResult = calcBorderBoxWidth(w.calcValue(cw));
1353
1354 return widthResult;
1355 }
1356
sizesToIntrinsicWidth(WidthType widthType) const1357 bool RenderBox::sizesToIntrinsicWidth(WidthType widthType) const
1358 {
1359 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
1360 // but they allow text to sit on the same line as the marquee.
1361 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee()))
1362 return true;
1363
1364 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both
1365 // min-width and width. max-width is only clamped if it is also intrinsic.
1366 Length width = (widthType == MaxWidth) ? style()->maxWidth() : style()->width();
1367 if (width.type() == Intrinsic)
1368 return true;
1369
1370 // Children of a horizontal marquee do not fill the container by default.
1371 // FIXME: Need to deal with MAUTO value properly. It could be vertical.
1372 if (parent()->style()->overflowX() == OMARQUEE) {
1373 EMarqueeDirection dir = parent()->style()->marqueeDirection();
1374 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
1375 return true;
1376 }
1377
1378 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes
1379 // that don't stretch their kids lay out their children at their intrinsic widths.
1380 if (parent()->isFlexibleBox()
1381 && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
1382 return true;
1383
1384 return false;
1385 }
1386
calcHorizontalMargins(const Length & marginLeft,const Length & marginRight,int containerWidth)1387 void RenderBox::calcHorizontalMargins(const Length& marginLeft, const Length& marginRight, int containerWidth)
1388 {
1389 if (isFloating() || isInline()) {
1390 // Inline blocks/tables and floats don't have their margins increased.
1391 m_marginLeft = marginLeft.calcMinValue(containerWidth);
1392 m_marginRight = marginRight.calcMinValue(containerWidth);
1393 return;
1394 }
1395
1396 if ((marginLeft.isAuto() && marginRight.isAuto() && width() < containerWidth)
1397 || (!marginLeft.isAuto() && !marginRight.isAuto() && containingBlock()->style()->textAlign() == WEBKIT_CENTER)) {
1398 m_marginLeft = max(0, (containerWidth - width()) / 2);
1399 m_marginRight = containerWidth - width() - m_marginLeft;
1400 } else if ((marginRight.isAuto() && width() < containerWidth)
1401 || (!marginLeft.isAuto() && containingBlock()->style()->direction() == RTL && containingBlock()->style()->textAlign() == WEBKIT_LEFT)) {
1402 m_marginLeft = marginLeft.calcValue(containerWidth);
1403 m_marginRight = containerWidth - width() - m_marginLeft;
1404 } else if ((marginLeft.isAuto() && width() < containerWidth)
1405 || (!marginRight.isAuto() && containingBlock()->style()->direction() == LTR && containingBlock()->style()->textAlign() == WEBKIT_RIGHT)) {
1406 m_marginRight = marginRight.calcValue(containerWidth);
1407 m_marginLeft = containerWidth - width() - m_marginRight;
1408 } else {
1409 // This makes auto margins 0 if we failed a width() < containerWidth test above (css2.1, 10.3.3).
1410 m_marginLeft = marginLeft.calcMinValue(containerWidth);
1411 m_marginRight = marginRight.calcMinValue(containerWidth);
1412 }
1413 }
1414
calcHeight()1415 void RenderBox::calcHeight()
1416 {
1417 // Cell height is managed by the table and inline non-replaced elements do not support a height property.
1418 if (isTableCell() || (isInline() && !isReplaced()))
1419 return;
1420
1421 Length h;
1422 if (isPositioned())
1423 calcAbsoluteVertical();
1424 else {
1425 calcVerticalMargins();
1426
1427 // For tables, calculate margins only.
1428 if (isTable())
1429 return;
1430
1431 bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
1432 bool stretching = parent()->style()->boxAlign() == BSTRETCH;
1433 bool treatAsReplaced = shouldCalculateSizeAsReplaced() && (!inHorizontalBox || !stretching);
1434 bool checkMinMaxHeight = false;
1435
1436 // The parent box is flexing us, so it has increased or decreased our height. We have to
1437 // grab our cached flexible height.
1438 if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL
1439 && parent()->isFlexingChildren())
1440 h = Length(overrideSize() - borderTop() - borderBottom() - paddingTop() - paddingBottom(), Fixed);
1441 else if (treatAsReplaced)
1442 h = Length(calcReplacedHeight(), Fixed);
1443 else {
1444 h = style()->height();
1445 checkMinMaxHeight = true;
1446 }
1447
1448 // Block children of horizontal flexible boxes fill the height of the box.
1449 if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
1450 && parent()->isStretchingChildren()) {
1451 h = Length(parentBox()->contentHeight() - marginTop() - marginBottom() -
1452 borderTop() - paddingTop() - borderBottom() - paddingBottom(), Fixed);
1453 checkMinMaxHeight = false;
1454 }
1455
1456 int heightResult;
1457 if (checkMinMaxHeight) {
1458 #ifdef ANDROID_LAYOUT
1459 // in SSR mode, ignore CSS height as layout is so different
1460 if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR)
1461 heightResult = -1;
1462 else
1463 #endif
1464 heightResult = calcHeightUsing(style()->height());
1465 if (heightResult == -1)
1466 heightResult = height();
1467 int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset.
1468 int maxH = style()->maxHeight().isUndefined() ? heightResult : calcHeightUsing(style()->maxHeight());
1469 if (maxH == -1)
1470 maxH = heightResult;
1471 heightResult = min(maxH, heightResult);
1472 heightResult = max(minH, heightResult);
1473 } else {
1474 // The only times we don't check min/max height are when a fixed length has
1475 // been given as an override. Just use that. The value has already been adjusted
1476 // for box-sizing.
1477 heightResult = h.value() + borderTop() + borderBottom() + paddingTop() + paddingBottom();
1478 }
1479
1480 setHeight(heightResult);
1481 }
1482
1483 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
1484 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
1485 // is specified. When we're printing, we also need this quirk if the body or root has a percentage
1486 // height since we don't set a height in RenderView when we're printing. So without this quirk, the
1487 // height has nothing to be a percentage of, and it ends up being 0. That is bad.
1488 bool printingNeedsBaseHeight = document()->printing() && h.isPercent()
1489 && (isRoot() || isBody() && document()->documentElement()->renderer()->style()->height().isPercent());
1490 if (stretchesToViewHeight() || printingNeedsBaseHeight) {
1491 int margins = collapsedMarginTop() + collapsedMarginBottom();
1492 int visHeight = document()->printing() ? view()->frameView()->visibleHeight() : view()->viewHeight();
1493 if (isRoot())
1494 setHeight(max(height(), visHeight - margins));
1495 else {
1496 int marginsBordersPadding = margins + parentBox()->marginTop() + parentBox()->marginBottom()
1497 + parentBox()->borderTop() + parentBox()->borderBottom()
1498 + parentBox()->paddingTop() + parentBox()->paddingBottom();
1499 setHeight(max(height(), visHeight - marginsBordersPadding));
1500 }
1501 }
1502 }
1503
calcHeightUsing(const Length & h)1504 int RenderBox::calcHeightUsing(const Length& h)
1505 {
1506 int height = -1;
1507 if (!h.isAuto()) {
1508 if (h.isFixed())
1509 height = h.value();
1510 else if (h.isPercent())
1511 height = calcPercentageHeight(h);
1512 if (height != -1) {
1513 height = calcBorderBoxHeight(height);
1514 return height;
1515 }
1516 }
1517 return height;
1518 }
1519
calcPercentageHeight(const Length & height)1520 int RenderBox::calcPercentageHeight(const Length& height)
1521 {
1522 int result = -1;
1523 bool includeBorderPadding = isTable();
1524 RenderBlock* cb = containingBlock();
1525 if (style()->htmlHacks()) {
1526 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing
1527 // block that may have a specified height and then use it. In strict mode, this violates the
1528 // specification, which states that percentage heights just revert to auto if the containing
1529 // block has an auto height.
1530 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->height().isAuto()) {
1531 cb = cb->containingBlock();
1532 cb->addPercentHeightDescendant(this);
1533 }
1534 }
1535
1536 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
1537 // explicitly specified that can be used for any percentage computations.
1538 bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->height().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto()));
1539
1540 // Table cells violate what the CSS spec says to do with heights. Basically we
1541 // don't care if the cell specified a height or not. We just always make ourselves
1542 // be a percentage of the cell's current content height.
1543 if (cb->isTableCell()) {
1544 result = cb->overrideSize();
1545 if (result == -1) {
1546 // Normally we would let the cell size intrinsically, but scrolling overflow has to be
1547 // treated differently, since WinIE lets scrolled overflow regions shrink as needed.
1548 // While we can't get all cases right, we can at least detect when the cell has a specified
1549 // height or when the table has a specified height. In these cases we want to initially have
1550 // no size and allow the flexing of the table or the cell to its specified height to cause us
1551 // to grow to fill the space. This could end up being wrong in some cases, but it is
1552 // preferable to the alternative (sizing intrinsically and making the row end up too big).
1553 RenderTableCell* cell = toRenderTableCell(cb);
1554 if (scrollsOverflowY() && (!cell->style()->height().isAuto() || !cell->table()->style()->height().isAuto()))
1555 return 0;
1556 return -1;
1557 }
1558 includeBorderPadding = true;
1559 }
1560 // Otherwise we only use our percentage height if our containing block had a specified
1561 // height.
1562 else if (cb->style()->height().isFixed())
1563 result = cb->calcContentBoxHeight(cb->style()->height().value());
1564 else if (cb->style()->height().isPercent() && !isPositionedWithSpecifiedHeight) {
1565 // We need to recur and compute the percentage height for our containing block.
1566 result = cb->calcPercentageHeight(cb->style()->height());
1567 if (result != -1)
1568 result = cb->calcContentBoxHeight(result);
1569 } else if (cb->isRenderView() || (cb->isBody() && style()->htmlHacks()) || isPositionedWithSpecifiedHeight) {
1570 // Don't allow this to affect the block' height() member variable, since this
1571 // can get called while the block is still laying out its kids.
1572 int oldHeight = cb->height();
1573 cb->calcHeight();
1574 result = cb->contentHeight();
1575 cb->setHeight(oldHeight);
1576 } else if (cb->isRoot() && isPositioned())
1577 // Match the positioned objects behavior, which is that positioned objects will fill their viewport
1578 // always. Note we could only hit this case by recurring into calcPercentageHeight on a positioned containing block.
1579 result = cb->calcContentBoxHeight(cb->availableHeight());
1580
1581 if (result != -1) {
1582 result = height.calcValue(result);
1583 if (includeBorderPadding) {
1584 // It is necessary to use the border-box to match WinIE's broken
1585 // box model. This is essential for sizing inside
1586 // table cells using percentage heights.
1587 result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom());
1588 result = max(0, result);
1589 }
1590 }
1591 return result;
1592 }
1593
calcReplacedWidth(bool includeMaxWidth) const1594 int RenderBox::calcReplacedWidth(bool includeMaxWidth) const
1595 {
1596 int width = calcReplacedWidthUsing(style()->width());
1597 int minW = calcReplacedWidthUsing(style()->minWidth());
1598 int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());
1599
1600 return max(minW, min(width, maxW));
1601 }
1602
calcReplacedWidthUsing(Length width) const1603 int RenderBox::calcReplacedWidthUsing(Length width) const
1604 {
1605 switch (width.type()) {
1606 case Fixed:
1607 return calcContentBoxWidth(width.value());
1608 case Percent: {
1609 const int cw = isPositioned() ? containingBlockWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockWidthForContent();
1610 if (cw > 0)
1611 return calcContentBoxWidth(width.calcMinValue(cw));
1612 }
1613 // fall through
1614 default:
1615 return intrinsicSize().width();
1616 }
1617 }
1618
calcReplacedHeight() const1619 int RenderBox::calcReplacedHeight() const
1620 {
1621 int height = calcReplacedHeightUsing(style()->height());
1622 int minH = calcReplacedHeightUsing(style()->minHeight());
1623 int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());
1624
1625 return max(minH, min(height, maxH));
1626 }
1627
calcReplacedHeightUsing(Length height) const1628 int RenderBox::calcReplacedHeightUsing(Length height) const
1629 {
1630 switch (height.type()) {
1631 case Fixed:
1632 return calcContentBoxHeight(height.value());
1633 case Percent:
1634 {
1635 RenderObject* cb = isPositioned() ? container() : containingBlock();
1636 while (cb->isAnonymous()) {
1637 cb = cb->containingBlock();
1638 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this));
1639 }
1640
1641 if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) {
1642 ASSERT(cb->isRenderBlock());
1643 RenderBlock* block = toRenderBlock(cb);
1644 int oldHeight = block->height();
1645 block->calcHeight();
1646 int newHeight = block->calcContentBoxHeight(block->contentHeight());
1647 block->setHeight(oldHeight);
1648 return calcContentBoxHeight(height.calcValue(newHeight));
1649 }
1650
1651 int availableHeight = isPositioned() ? containingBlockHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableHeight();
1652
1653 // It is necessary to use the border-box to match WinIE's broken
1654 // box model. This is essential for sizing inside
1655 // table cells using percentage heights.
1656 if (cb->isTableCell() && (cb->style()->height().isAuto() || cb->style()->height().isPercent())) {
1657 // Don't let table cells squeeze percent-height replaced elements
1658 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
1659 availableHeight = max(availableHeight, intrinsicSize().height());
1660 return height.calcValue(availableHeight - (borderTop() + borderBottom()
1661 + paddingTop() + paddingBottom()));
1662 }
1663
1664 return calcContentBoxHeight(height.calcValue(availableHeight));
1665 }
1666 default:
1667 return intrinsicSize().height();
1668 }
1669 }
1670
availableHeight() const1671 int RenderBox::availableHeight() const
1672 {
1673 return availableHeightUsing(style()->height());
1674 }
1675
availableHeightUsing(const Length & h) const1676 int RenderBox::availableHeightUsing(const Length& h) const
1677 {
1678 if (h.isFixed())
1679 return calcContentBoxHeight(h.value());
1680
1681 if (isRenderView())
1682 return toRenderView(this)->frameView()->visibleHeight();
1683
1684 // We need to stop here, since we don't want to increase the height of the table
1685 // artificially. We're going to rely on this cell getting expanded to some new
1686 // height, and then when we lay out again we'll use the calculation below.
1687 if (isTableCell() && (h.isAuto() || h.isPercent()))
1688 return overrideSize() - (borderLeft() + borderRight() + paddingLeft() + paddingRight());
1689
1690 if (h.isPercent())
1691 return calcContentBoxHeight(h.calcValue(containingBlock()->availableHeight()));
1692
1693 if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) {
1694 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this));
1695 int oldHeight = block->height();
1696 block->calcHeight();
1697 int newHeight = block->calcContentBoxHeight(block->contentHeight());
1698 block->setHeight(oldHeight);
1699 return calcContentBoxHeight(newHeight);
1700 }
1701
1702 return containingBlock()->availableHeight();
1703 }
1704
calcVerticalMargins()1705 void RenderBox::calcVerticalMargins()
1706 {
1707 if (isTableCell()) {
1708 m_marginTop = 0;
1709 m_marginBottom = 0;
1710 return;
1711 }
1712
1713 // margins are calculated with respect to the _width_ of
1714 // the containing block (8.3)
1715 int cw = containingBlock()->contentWidth();
1716
1717 m_marginTop = style()->marginTop().calcMinValue(cw);
1718 m_marginBottom = style()->marginBottom().calcMinValue(cw);
1719 }
1720
containingBlockWidthForPositioned(const RenderBoxModelObject * containingBlock) const1721 int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const
1722 {
1723 if (containingBlock->isBox()) {
1724 const RenderBox* containingBlockBox = toRenderBox(containingBlock);
1725 return containingBlockBox->width() - containingBlockBox->borderLeft() - containingBlockBox->borderRight() - containingBlockBox->verticalScrollbarWidth();
1726 }
1727
1728 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned());
1729
1730 const RenderInline* flow = toRenderInline(containingBlock);
1731 InlineFlowBox* first = flow->firstLineBox();
1732 InlineFlowBox* last = flow->lastLineBox();
1733
1734 // If the containing block is empty, return a width of 0.
1735 if (!first || !last)
1736 return 0;
1737
1738 int fromLeft;
1739 int fromRight;
1740 if (containingBlock->style()->direction() == LTR) {
1741 fromLeft = first->x() + first->borderLeft();
1742 fromRight = last->x() + last->width() - last->borderRight();
1743 } else {
1744 fromRight = first->x() + first->width() - first->borderRight();
1745 fromLeft = last->x() + last->borderLeft();
1746 }
1747
1748 return max(0, (fromRight - fromLeft));
1749 }
1750
containingBlockHeightForPositioned(const RenderBoxModelObject * containingBlock) const1751 int RenderBox::containingBlockHeightForPositioned(const RenderBoxModelObject* containingBlock) const
1752 {
1753 int heightResult = 0;
1754 if (containingBlock->isBox())
1755 heightResult = toRenderBox(containingBlock)->height();
1756 else if (containingBlock->isRenderInline()) {
1757 ASSERT(containingBlock->isRelPositioned());
1758 heightResult = toRenderInline(containingBlock)->linesBoundingBox().height();
1759 }
1760 return heightResult - containingBlock->borderTop() - containingBlock->borderBottom();
1761 }
1762
calcAbsoluteHorizontal()1763 void RenderBox::calcAbsoluteHorizontal()
1764 {
1765 if (isReplaced()) {
1766 calcAbsoluteHorizontalReplaced();
1767 return;
1768 }
1769
1770 // QUESTIONS
1771 // FIXME 1: Which RenderObject's 'direction' property should used: the
1772 // containing block (cb) as the spec seems to imply, the parent (parent()) as
1773 // was previously done in calculating the static distances, or ourself, which
1774 // was also previously done for deciding what to override when you had
1775 // over-constrained margins? Also note that the container block is used
1776 // in similar situations in other parts of the RenderBox class (see calcWidth()
1777 // and calcHorizontalMargins()). For now we are using the parent for quirks
1778 // mode and the containing block for strict mode.
1779
1780 // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having
1781 // the type 'static' in determining whether to calculate the static distance?
1782 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
1783
1784 // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater
1785 // than or less than the computed width(). Be careful of box-sizing and
1786 // percentage issues.
1787
1788 // The following is based off of the W3C Working Draft from April 11, 2006 of
1789 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
1790 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
1791 // (block-style-comments in this function and in calcAbsoluteHorizontalValues()
1792 // correspond to text from the spec)
1793
1794
1795 // We don't use containingBlock(), since we may be positioned by an enclosing
1796 // relative positioned inline.
1797 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
1798
1799 const int containerWidth = containingBlockWidthForPositioned(containerBlock);
1800
1801 // To match WinIE, in quirks mode use the parent's 'direction' property
1802 // instead of the the container block's.
1803 TextDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
1804
1805 const int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight();
1806 const Length marginLeft = style()->marginLeft();
1807 const Length marginRight = style()->marginRight();
1808 Length left = style()->left();
1809 Length right = style()->right();
1810
1811 /*---------------------------------------------------------------------------*\
1812 * For the purposes of this section and the next, the term "static position"
1813 * (of an element) refers, roughly, to the position an element would have had
1814 * in the normal flow. More precisely:
1815 *
1816 * * The static position for 'left' is the distance from the left edge of the
1817 * containing block to the left margin edge of a hypothetical box that would
1818 * have been the first box of the element if its 'position' property had
1819 * been 'static' and 'float' had been 'none'. The value is negative if the
1820 * hypothetical box is to the left of the containing block.
1821 * * The static position for 'right' is the distance from the right edge of the
1822 * containing block to the right margin edge of the same hypothetical box as
1823 * above. The value is positive if the hypothetical box is to the left of the
1824 * containing block's edge.
1825 *
1826 * But rather than actually calculating the dimensions of that hypothetical box,
1827 * user agents are free to make a guess at its probable position.
1828 *
1829 * For the purposes of calculating the static position, the containing block of
1830 * fixed positioned elements is the initial containing block instead of the
1831 * viewport, and all scrollable boxes should be assumed to be scrolled to their
1832 * origin.
1833 \*---------------------------------------------------------------------------*/
1834
1835 // see FIXME 2
1836 // Calculate the static distance if needed.
1837 if (left.isAuto() && right.isAuto()) {
1838 if (containerDirection == LTR) {
1839 // 'staticX' should already have been set through layout of the parent.
1840 int staticPosition = layer()->staticX() - containerBlock->borderLeft();
1841 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
1842 if (po->isBox())
1843 staticPosition += toRenderBox(po)->x();
1844 }
1845 left.setValue(Fixed, staticPosition);
1846 } else {
1847 RenderObject* po = parent();
1848 // 'staticX' should already have been set through layout of the parent.
1849 int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight();
1850 if (po->isBox())
1851 staticPosition -= toRenderBox(po)->width();
1852 for (; po && po != containerBlock; po = po->parent()) {
1853 if (po->isBox())
1854 staticPosition -= toRenderBox(po)->x();
1855 }
1856 right.setValue(Fixed, staticPosition);
1857 }
1858 }
1859
1860 // Calculate constraint equation values for 'width' case.
1861 int widthResult;
1862 int xResult;
1863 calcAbsoluteHorizontalValues(style()->width(), containerBlock, containerDirection,
1864 containerWidth, bordersPlusPadding,
1865 left, right, marginLeft, marginRight,
1866 widthResult, m_marginLeft, m_marginRight, xResult);
1867 setWidth(widthResult);
1868 setX(xResult);
1869
1870 // Calculate constraint equation values for 'max-width' case.
1871 if (!style()->maxWidth().isUndefined()) {
1872 int maxWidth;
1873 int maxMarginLeft;
1874 int maxMarginRight;
1875 int maxXPos;
1876
1877 calcAbsoluteHorizontalValues(style()->maxWidth(), containerBlock, containerDirection,
1878 containerWidth, bordersPlusPadding,
1879 left, right, marginLeft, marginRight,
1880 maxWidth, maxMarginLeft, maxMarginRight, maxXPos);
1881
1882 if (width() > maxWidth) {
1883 setWidth(maxWidth);
1884 m_marginLeft = maxMarginLeft;
1885 m_marginRight = maxMarginRight;
1886 m_frameRect.setX(maxXPos);
1887 }
1888 }
1889
1890 // Calculate constraint equation values for 'min-width' case.
1891 if (!style()->minWidth().isZero()) {
1892 int minWidth;
1893 int minMarginLeft;
1894 int minMarginRight;
1895 int minXPos;
1896
1897 calcAbsoluteHorizontalValues(style()->minWidth(), containerBlock, containerDirection,
1898 containerWidth, bordersPlusPadding,
1899 left, right, marginLeft, marginRight,
1900 minWidth, minMarginLeft, minMarginRight, minXPos);
1901
1902 if (width() < minWidth) {
1903 setWidth(minWidth);
1904 m_marginLeft = minMarginLeft;
1905 m_marginRight = minMarginRight;
1906 m_frameRect.setX(minXPos);
1907 }
1908 }
1909
1910 if (stretchesToMinIntrinsicWidth() && width() < minPrefWidth() - bordersPlusPadding) {
1911 calcAbsoluteHorizontalValues(Length(minPrefWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection,
1912 containerWidth, bordersPlusPadding,
1913 left, right, marginLeft, marginRight,
1914 widthResult, m_marginLeft, m_marginRight, xResult);
1915 setWidth(widthResult);
1916 setX(xResult);
1917 }
1918
1919 // Put width() into correct form.
1920 setWidth(width() + bordersPlusPadding);
1921 }
1922
calcAbsoluteHorizontalValues(Length width,const RenderBoxModelObject * containerBlock,TextDirection containerDirection,const int containerWidth,const int bordersPlusPadding,const Length left,const Length right,const Length marginLeft,const Length marginRight,int & widthValue,int & marginLeftValue,int & marginRightValue,int & xPos)1923 void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBoxModelObject* containerBlock, TextDirection containerDirection,
1924 const int containerWidth, const int bordersPlusPadding,
1925 const Length left, const Length right, const Length marginLeft, const Length marginRight,
1926 int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos)
1927 {
1928 // 'left' and 'right' cannot both be 'auto' because one would of been
1929 // converted to the static postion already
1930 ASSERT(!(left.isAuto() && right.isAuto()));
1931
1932 int leftValue = 0;
1933
1934 bool widthIsAuto = width.isIntrinsicOrAuto();
1935 bool leftIsAuto = left.isAuto();
1936 bool rightIsAuto = right.isAuto();
1937
1938 if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
1939 /*-----------------------------------------------------------------------*\
1940 * If none of the three is 'auto': If both 'margin-left' and 'margin-
1941 * right' are 'auto', solve the equation under the extra constraint that
1942 * the two margins get equal values, unless this would make them negative,
1943 * in which case when direction of the containing block is 'ltr' ('rtl'),
1944 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
1945 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
1946 * solve the equation for that value. If the values are over-constrained,
1947 * ignore the value for 'left' (in case the 'direction' property of the
1948 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
1949 * and solve for that value.
1950 \*-----------------------------------------------------------------------*/
1951 // NOTE: It is not necessary to solve for 'right' in the over constrained
1952 // case because the value is not used for any further calculations.
1953
1954 leftValue = left.calcValue(containerWidth);
1955 widthValue = calcContentBoxWidth(width.calcValue(containerWidth));
1956
1957 const int availableSpace = containerWidth - (leftValue + widthValue + right.calcValue(containerWidth) + bordersPlusPadding);
1958
1959 // Margins are now the only unknown
1960 if (marginLeft.isAuto() && marginRight.isAuto()) {
1961 // Both margins auto, solve for equality
1962 if (availableSpace >= 0) {
1963 marginLeftValue = availableSpace / 2; // split the diference
1964 marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences
1965 } else {
1966 // see FIXME 1
1967 if (containerDirection == LTR) {
1968 marginLeftValue = 0;
1969 marginRightValue = availableSpace; // will be negative
1970 } else {
1971 marginLeftValue = availableSpace; // will be negative
1972 marginRightValue = 0;
1973 }
1974 }
1975 } else if (marginLeft.isAuto()) {
1976 // Solve for left margin
1977 marginRightValue = marginRight.calcValue(containerWidth);
1978 marginLeftValue = availableSpace - marginRightValue;
1979 } else if (marginRight.isAuto()) {
1980 // Solve for right margin
1981 marginLeftValue = marginLeft.calcValue(containerWidth);
1982 marginRightValue = availableSpace - marginLeftValue;
1983 } else {
1984 // Over-constrained, solve for left if direction is RTL
1985 marginLeftValue = marginLeft.calcValue(containerWidth);
1986 marginRightValue = marginRight.calcValue(containerWidth);
1987
1988 // see FIXME 1 -- used to be "this->style()->direction()"
1989 if (containerDirection == RTL)
1990 leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue;
1991 }
1992 } else {
1993 /*--------------------------------------------------------------------*\
1994 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
1995 * to 0, and pick the one of the following six rules that applies.
1996 *
1997 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
1998 * width is shrink-to-fit. Then solve for 'left'
1999 *
2000 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
2001 * ------------------------------------------------------------------
2002 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
2003 * the 'direction' property of the containing block is 'ltr' set
2004 * 'left' to the static position, otherwise set 'right' to the
2005 * static position. Then solve for 'left' (if 'direction is 'rtl')
2006 * or 'right' (if 'direction' is 'ltr').
2007 * ------------------------------------------------------------------
2008 *
2009 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
2010 * width is shrink-to-fit . Then solve for 'right'
2011 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
2012 * for 'left'
2013 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
2014 * for 'width'
2015 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
2016 * for 'right'
2017 *
2018 * Calculation of the shrink-to-fit width is similar to calculating the
2019 * width of a table cell using the automatic table layout algorithm.
2020 * Roughly: calculate the preferred width by formatting the content
2021 * without breaking lines other than where explicit line breaks occur,
2022 * and also calculate the preferred minimum width, e.g., by trying all
2023 * possible line breaks. CSS 2.1 does not define the exact algorithm.
2024 * Thirdly, calculate the available width: this is found by solving
2025 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
2026 * to 0.
2027 *
2028 * Then the shrink-to-fit width is:
2029 * min(max(preferred minimum width, available width), preferred width).
2030 \*--------------------------------------------------------------------*/
2031 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
2032 // because the value is not used for any further calculations.
2033
2034 // Calculate margins, 'auto' margins are ignored.
2035 marginLeftValue = marginLeft.calcMinValue(containerWidth);
2036 marginRightValue = marginRight.calcMinValue(containerWidth);
2037
2038 const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding);
2039
2040 // FIXME: Is there a faster way to find the correct case?
2041 // Use rule/case that applies.
2042 if (leftIsAuto && widthIsAuto && !rightIsAuto) {
2043 // RULE 1: (use shrink-to-fit for width, and solve of left)
2044 int rightValue = right.calcValue(containerWidth);
2045
2046 // FIXME: would it be better to have shrink-to-fit in one step?
2047 int preferredWidth = maxPrefWidth() - bordersPlusPadding;
2048 int preferredMinWidth = minPrefWidth() - bordersPlusPadding;
2049 int availableWidth = availableSpace - rightValue;
2050 widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth);
2051 leftValue = availableSpace - (widthValue + rightValue);
2052 } else if (!leftIsAuto && widthIsAuto && rightIsAuto) {
2053 // RULE 3: (use shrink-to-fit for width, and no need solve of right)
2054 leftValue = left.calcValue(containerWidth);
2055
2056 // FIXME: would it be better to have shrink-to-fit in one step?
2057 int preferredWidth = maxPrefWidth() - bordersPlusPadding;
2058 int preferredMinWidth = minPrefWidth() - bordersPlusPadding;
2059 int availableWidth = availableSpace - leftValue;
2060 widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth);
2061 } else if (leftIsAuto && !width.isAuto() && !rightIsAuto) {
2062 // RULE 4: (solve for left)
2063 widthValue = calcContentBoxWidth(width.calcValue(containerWidth));
2064 leftValue = availableSpace - (widthValue + right.calcValue(containerWidth));
2065 } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) {
2066 // RULE 5: (solve for width)
2067 leftValue = left.calcValue(containerWidth);
2068 widthValue = availableSpace - (leftValue + right.calcValue(containerWidth));
2069 } else if (!leftIsAuto&& !widthIsAuto && rightIsAuto) {
2070 // RULE 6: (no need solve for right)
2071 leftValue = left.calcValue(containerWidth);
2072 widthValue = calcContentBoxWidth(width.calcValue(containerWidth));
2073 }
2074 }
2075
2076 // Use computed values to calculate the horizontal position.
2077
2078 // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively
2079 // positioned, inline because right now, it is using the xPos
2080 // of the first line box when really it should use the last line box. When
2081 // this is fixed elsewhere, this block should be removed.
2082 if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) {
2083 const RenderInline* flow = toRenderInline(containerBlock);
2084 InlineFlowBox* firstLine = flow->firstLineBox();
2085 InlineFlowBox* lastLine = flow->lastLineBox();
2086 if (firstLine && lastLine && firstLine != lastLine) {
2087 xPos = leftValue + marginLeftValue + lastLine->borderLeft() + (lastLine->x() - firstLine->x());
2088 return;
2089 }
2090 }
2091
2092 xPos = leftValue + marginLeftValue + containerBlock->borderLeft();
2093 }
2094
calcAbsoluteVertical()2095 void RenderBox::calcAbsoluteVertical()
2096 {
2097 if (isReplaced()) {
2098 calcAbsoluteVerticalReplaced();
2099 return;
2100 }
2101
2102 // The following is based off of the W3C Working Draft from April 11, 2006 of
2103 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
2104 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
2105 // (block-style-comments in this function and in calcAbsoluteVerticalValues()
2106 // correspond to text from the spec)
2107
2108
2109 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2110 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2111
2112 const int containerHeight = containingBlockHeightForPositioned(containerBlock);
2113
2114 const int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom();
2115 const Length marginTop = style()->marginTop();
2116 const Length marginBottom = style()->marginBottom();
2117 Length top = style()->top();
2118 Length bottom = style()->bottom();
2119
2120 /*---------------------------------------------------------------------------*\
2121 * For the purposes of this section and the next, the term "static position"
2122 * (of an element) refers, roughly, to the position an element would have had
2123 * in the normal flow. More precisely, the static position for 'top' is the
2124 * distance from the top edge of the containing block to the top margin edge
2125 * of a hypothetical box that would have been the first box of the element if
2126 * its 'position' property had been 'static' and 'float' had been 'none'. The
2127 * value is negative if the hypothetical box is above the containing block.
2128 *
2129 * But rather than actually calculating the dimensions of that hypothetical
2130 * box, user agents are free to make a guess at its probable position.
2131 *
2132 * For the purposes of calculating the static position, the containing block
2133 * of fixed positioned elements is the initial containing block instead of
2134 * the viewport.
2135 \*---------------------------------------------------------------------------*/
2136
2137 // see FIXME 2
2138 // Calculate the static distance if needed.
2139 if (top.isAuto() && bottom.isAuto()) {
2140 // staticY should already have been set through layout of the parent()
2141 int staticTop = layer()->staticY() - containerBlock->borderTop();
2142 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
2143 if (po->isBox() && !po->isTableRow())
2144 staticTop += toRenderBox(po)->y();
2145 }
2146 top.setValue(Fixed, staticTop);
2147 }
2148
2149
2150 int h; // Needed to compute overflow.
2151 int y;
2152
2153 // Calculate constraint equation values for 'height' case.
2154 calcAbsoluteVerticalValues(style()->height(), containerBlock, containerHeight, bordersPlusPadding,
2155 top, bottom, marginTop, marginBottom,
2156 h, m_marginTop, m_marginBottom, y);
2157 setY(y);
2158
2159 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
2160 // see FIXME 3
2161
2162 // Calculate constraint equation values for 'max-height' case.
2163 if (!style()->maxHeight().isUndefined()) {
2164 int maxHeight;
2165 int maxMarginTop;
2166 int maxMarginBottom;
2167 int maxYPos;
2168
2169 calcAbsoluteVerticalValues(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding,
2170 top, bottom, marginTop, marginBottom,
2171 maxHeight, maxMarginTop, maxMarginBottom, maxYPos);
2172
2173 if (h > maxHeight) {
2174 h = maxHeight;
2175 m_marginTop = maxMarginTop;
2176 m_marginBottom = maxMarginBottom;
2177 m_frameRect.setY(maxYPos);
2178 }
2179 }
2180
2181 // Calculate constraint equation values for 'min-height' case.
2182 if (!style()->minHeight().isZero()) {
2183 int minHeight;
2184 int minMarginTop;
2185 int minMarginBottom;
2186 int minYPos;
2187
2188 calcAbsoluteVerticalValues(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding,
2189 top, bottom, marginTop, marginBottom,
2190 minHeight, minMarginTop, minMarginBottom, minYPos);
2191
2192 if (h < minHeight) {
2193 h = minHeight;
2194 m_marginTop = minMarginTop;
2195 m_marginBottom = minMarginBottom;
2196 m_frameRect.setY(minYPos);
2197 }
2198 }
2199
2200 // Set final height value.
2201 setHeight(h + bordersPlusPadding);
2202 }
2203
calcAbsoluteVerticalValues(Length h,const RenderBoxModelObject * containerBlock,const int containerHeight,const int bordersPlusPadding,const Length top,const Length bottom,const Length marginTop,const Length marginBottom,int & heightValue,int & marginTopValue,int & marginBottomValue,int & yPos)2204 void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBoxModelObject* containerBlock,
2205 const int containerHeight, const int bordersPlusPadding,
2206 const Length top, const Length bottom, const Length marginTop, const Length marginBottom,
2207 int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos)
2208 {
2209 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
2210 // converted to the static position in calcAbsoluteVertical()
2211 ASSERT(!(top.isAuto() && bottom.isAuto()));
2212
2213 int contentHeight = height() - bordersPlusPadding;
2214
2215 int topValue = 0;
2216
2217 bool heightIsAuto = h.isAuto();
2218 bool topIsAuto = top.isAuto();
2219 bool bottomIsAuto = bottom.isAuto();
2220
2221 // Height is never unsolved for tables.
2222 if (isTable()) {
2223 h.setValue(Fixed, contentHeight);
2224 heightIsAuto = false;
2225 }
2226
2227 if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
2228 /*-----------------------------------------------------------------------*\
2229 * If none of the three are 'auto': If both 'margin-top' and 'margin-
2230 * bottom' are 'auto', solve the equation under the extra constraint that
2231 * the two margins get equal values. If one of 'margin-top' or 'margin-
2232 * bottom' is 'auto', solve the equation for that value. If the values
2233 * are over-constrained, ignore the value for 'bottom' and solve for that
2234 * value.
2235 \*-----------------------------------------------------------------------*/
2236 // NOTE: It is not necessary to solve for 'bottom' in the over constrained
2237 // case because the value is not used for any further calculations.
2238
2239 heightValue = calcContentBoxHeight(h.calcValue(containerHeight));
2240 topValue = top.calcValue(containerHeight);
2241
2242 const int availableSpace = containerHeight - (topValue + heightValue + bottom.calcValue(containerHeight) + bordersPlusPadding);
2243
2244 // Margins are now the only unknown
2245 if (marginTop.isAuto() && marginBottom.isAuto()) {
2246 // Both margins auto, solve for equality
2247 // NOTE: This may result in negative values.
2248 marginTopValue = availableSpace / 2; // split the diference
2249 marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences
2250 } else if (marginTop.isAuto()) {
2251 // Solve for top margin
2252 marginBottomValue = marginBottom.calcValue(containerHeight);
2253 marginTopValue = availableSpace - marginBottomValue;
2254 } else if (marginBottom.isAuto()) {
2255 // Solve for bottom margin
2256 marginTopValue = marginTop.calcValue(containerHeight);
2257 marginBottomValue = availableSpace - marginTopValue;
2258 } else {
2259 // Over-constrained, (no need solve for bottom)
2260 marginTopValue = marginTop.calcValue(containerHeight);
2261 marginBottomValue = marginBottom.calcValue(containerHeight);
2262 }
2263 } else {
2264 /*--------------------------------------------------------------------*\
2265 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
2266 * to 0, and pick the one of the following six rules that applies.
2267 *
2268 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
2269 * the height is based on the content, and solve for 'top'.
2270 *
2271 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
2272 * ------------------------------------------------------------------
2273 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
2274 * set 'top' to the static position, and solve for 'bottom'.
2275 * ------------------------------------------------------------------
2276 *
2277 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
2278 * the height is based on the content, and solve for 'bottom'.
2279 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
2280 * solve for 'top'.
2281 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
2282 * solve for 'height'.
2283 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
2284 * solve for 'bottom'.
2285 \*--------------------------------------------------------------------*/
2286 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
2287 // because the value is not used for any further calculations.
2288
2289 // Calculate margins, 'auto' margins are ignored.
2290 marginTopValue = marginTop.calcMinValue(containerHeight);
2291 marginBottomValue = marginBottom.calcMinValue(containerHeight);
2292
2293 const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding);
2294
2295 // Use rule/case that applies.
2296 if (topIsAuto && heightIsAuto && !bottomIsAuto) {
2297 // RULE 1: (height is content based, solve of top)
2298 heightValue = contentHeight;
2299 topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight));
2300 } else if (!topIsAuto && heightIsAuto && bottomIsAuto) {
2301 // RULE 3: (height is content based, no need solve of bottom)
2302 topValue = top.calcValue(containerHeight);
2303 heightValue = contentHeight;
2304 } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) {
2305 // RULE 4: (solve of top)
2306 heightValue = calcContentBoxHeight(h.calcValue(containerHeight));
2307 topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight));
2308 } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) {
2309 // RULE 5: (solve of height)
2310 topValue = top.calcValue(containerHeight);
2311 heightValue = max(0, availableSpace - (topValue + bottom.calcValue(containerHeight)));
2312 } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) {
2313 // RULE 6: (no need solve of bottom)
2314 heightValue = calcContentBoxHeight(h.calcValue(containerHeight));
2315 topValue = top.calcValue(containerHeight);
2316 }
2317 }
2318
2319 // Use computed values to calculate the vertical position.
2320 yPos = topValue + marginTopValue + containerBlock->borderTop();
2321 }
2322
calcAbsoluteHorizontalReplaced()2323 void RenderBox::calcAbsoluteHorizontalReplaced()
2324 {
2325 // The following is based off of the W3C Working Draft from April 11, 2006 of
2326 // CSS 2.1: Section 10.3.8 "Absolutly positioned, replaced elements"
2327 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
2328 // (block-style-comments in this function correspond to text from the spec and
2329 // the numbers correspond to numbers in spec)
2330
2331 // We don't use containingBlock(), since we may be positioned by an enclosing
2332 // relative positioned inline.
2333 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2334
2335 const int containerWidth = containingBlockWidthForPositioned(containerBlock);
2336
2337 // To match WinIE, in quirks mode use the parent's 'direction' property
2338 // instead of the the container block's.
2339 TextDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
2340
2341 // Variables to solve.
2342 Length left = style()->left();
2343 Length right = style()->right();
2344 Length marginLeft = style()->marginLeft();
2345 Length marginRight = style()->marginRight();
2346
2347
2348 /*-----------------------------------------------------------------------*\
2349 * 1. The used value of 'width' is determined as for inline replaced
2350 * elements.
2351 \*-----------------------------------------------------------------------*/
2352 // NOTE: This value of width is FINAL in that the min/max width calculations
2353 // are dealt with in calcReplacedWidth(). This means that the steps to produce
2354 // correct max/min in the non-replaced version, are not necessary.
2355 setWidth(calcReplacedWidth() + borderLeft() + borderRight() + paddingLeft() + paddingRight());
2356 const int availableSpace = containerWidth - width();
2357
2358 /*-----------------------------------------------------------------------*\
2359 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
2360 * of the containing block is 'ltr', set 'left' to the static position;
2361 * else if 'direction' is 'rtl', set 'right' to the static position.
2362 \*-----------------------------------------------------------------------*/
2363 // see FIXME 2
2364 if (left.isAuto() && right.isAuto()) {
2365 // see FIXME 1
2366 if (containerDirection == LTR) {
2367 // 'staticX' should already have been set through layout of the parent.
2368 int staticPosition = layer()->staticX() - containerBlock->borderLeft();
2369 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
2370 if (po->isBox())
2371 staticPosition += toRenderBox(po)->x();
2372 }
2373 left.setValue(Fixed, staticPosition);
2374 } else {
2375 RenderObject* po = parent();
2376 // 'staticX' should already have been set through layout of the parent.
2377 int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight();
2378 for ( ; po && po != containerBlock; po = po->parent()) {
2379 if (po->isBox())
2380 staticPosition += toRenderBox(po)->x();
2381 }
2382 right.setValue(Fixed, staticPosition);
2383 }
2384 }
2385
2386 /*-----------------------------------------------------------------------*\
2387 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
2388 * or 'margin-right' with '0'.
2389 \*-----------------------------------------------------------------------*/
2390 if (left.isAuto() || right.isAuto()) {
2391 if (marginLeft.isAuto())
2392 marginLeft.setValue(Fixed, 0);
2393 if (marginRight.isAuto())
2394 marginRight.setValue(Fixed, 0);
2395 }
2396
2397 /*-----------------------------------------------------------------------*\
2398 * 4. If at this point both 'margin-left' and 'margin-right' are still
2399 * 'auto', solve the equation under the extra constraint that the two
2400 * margins must get equal values, unless this would make them negative,
2401 * in which case when the direction of the containing block is 'ltr'
2402 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
2403 * 'margin-right' ('margin-left').
2404 \*-----------------------------------------------------------------------*/
2405 int leftValue = 0;
2406 int rightValue = 0;
2407
2408 if (marginLeft.isAuto() && marginRight.isAuto()) {
2409 // 'left' and 'right' cannot be 'auto' due to step 3
2410 ASSERT(!(left.isAuto() && right.isAuto()));
2411
2412 leftValue = left.calcValue(containerWidth);
2413 rightValue = right.calcValue(containerWidth);
2414
2415 int difference = availableSpace - (leftValue + rightValue);
2416 if (difference > 0) {
2417 m_marginLeft = difference / 2; // split the diference
2418 m_marginRight = difference - m_marginLeft; // account for odd valued differences
2419 } else {
2420 // see FIXME 1
2421 if (containerDirection == LTR) {
2422 m_marginLeft = 0;
2423 m_marginRight = difference; // will be negative
2424 } else {
2425 m_marginLeft = difference; // will be negative
2426 m_marginRight = 0;
2427 }
2428 }
2429
2430 /*-----------------------------------------------------------------------*\
2431 * 5. If at this point there is an 'auto' left, solve the equation for
2432 * that value.
2433 \*-----------------------------------------------------------------------*/
2434 } else if (left.isAuto()) {
2435 m_marginLeft = marginLeft.calcValue(containerWidth);
2436 m_marginRight = marginRight.calcValue(containerWidth);
2437 rightValue = right.calcValue(containerWidth);
2438
2439 // Solve for 'left'
2440 leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
2441 } else if (right.isAuto()) {
2442 m_marginLeft = marginLeft.calcValue(containerWidth);
2443 m_marginRight = marginRight.calcValue(containerWidth);
2444 leftValue = left.calcValue(containerWidth);
2445
2446 // Solve for 'right'
2447 rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
2448 } else if (marginLeft.isAuto()) {
2449 m_marginRight = marginRight.calcValue(containerWidth);
2450 leftValue = left.calcValue(containerWidth);
2451 rightValue = right.calcValue(containerWidth);
2452
2453 // Solve for 'margin-left'
2454 m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight);
2455 } else if (marginRight.isAuto()) {
2456 m_marginLeft = marginLeft.calcValue(containerWidth);
2457 leftValue = left.calcValue(containerWidth);
2458 rightValue = right.calcValue(containerWidth);
2459
2460 // Solve for 'margin-right'
2461 m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft);
2462 } else {
2463 // Nothing is 'auto', just calculate the values.
2464 m_marginLeft = marginLeft.calcValue(containerWidth);
2465 m_marginRight = marginRight.calcValue(containerWidth);
2466 rightValue = right.calcValue(containerWidth);
2467 leftValue = left.calcValue(containerWidth);
2468 }
2469
2470 /*-----------------------------------------------------------------------*\
2471 * 6. If at this point the values are over-constrained, ignore the value
2472 * for either 'left' (in case the 'direction' property of the
2473 * containing block is 'rtl') or 'right' (in case 'direction' is
2474 * 'ltr') and solve for that value.
2475 \*-----------------------------------------------------------------------*/
2476 // NOTE: It is not necessary to solve for 'right' when the direction is
2477 // LTR because the value is not used.
2478 int totalWidth = width() + leftValue + rightValue + m_marginLeft + m_marginRight;
2479 if (totalWidth > containerWidth && (containerDirection == RTL))
2480 leftValue = containerWidth - (totalWidth - leftValue);
2481
2482 // Use computed values to calculate the horizontal position.
2483
2484 // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively
2485 // positioned, inline containing block because right now, it is using the xPos
2486 // of the first line box when really it should use the last line box. When
2487 // this is fixed elsewhere, this block should be removed.
2488 if (containerBlock->isInline() && containerBlock->style()->direction() == RTL) {
2489 const RenderInline* flow = toRenderInline(containerBlock);
2490 InlineFlowBox* firstLine = flow->firstLineBox();
2491 InlineFlowBox* lastLine = flow->lastLineBox();
2492 if (firstLine && lastLine && firstLine != lastLine) {
2493 m_frameRect.setX(leftValue + m_marginLeft + lastLine->borderLeft() + (lastLine->x() - firstLine->x()));
2494 return;
2495 }
2496 }
2497
2498 m_frameRect.setX(leftValue + m_marginLeft + containerBlock->borderLeft());
2499 }
2500
calcAbsoluteVerticalReplaced()2501 void RenderBox::calcAbsoluteVerticalReplaced()
2502 {
2503 // The following is based off of the W3C Working Draft from April 11, 2006 of
2504 // CSS 2.1: Section 10.6.5 "Absolutly positioned, replaced elements"
2505 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
2506 // (block-style-comments in this function correspond to text from the spec and
2507 // the numbers correspond to numbers in spec)
2508
2509 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2510 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2511
2512 const int containerHeight = containingBlockHeightForPositioned(containerBlock);
2513
2514 // Variables to solve.
2515 Length top = style()->top();
2516 Length bottom = style()->bottom();
2517 Length marginTop = style()->marginTop();
2518 Length marginBottom = style()->marginBottom();
2519
2520
2521 /*-----------------------------------------------------------------------*\
2522 * 1. The used value of 'height' is determined as for inline replaced
2523 * elements.
2524 \*-----------------------------------------------------------------------*/
2525 // NOTE: This value of height is FINAL in that the min/max height calculations
2526 // are dealt with in calcReplacedHeight(). This means that the steps to produce
2527 // correct max/min in the non-replaced version, are not necessary.
2528 setHeight(calcReplacedHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom());
2529 const int availableSpace = containerHeight - height();
2530
2531 /*-----------------------------------------------------------------------*\
2532 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
2533 * with the element's static position.
2534 \*-----------------------------------------------------------------------*/
2535 // see FIXME 2
2536 if (top.isAuto() && bottom.isAuto()) {
2537 // staticY should already have been set through layout of the parent().
2538 int staticTop = layer()->staticY() - containerBlock->borderTop();
2539 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
2540 if (po->isBox() && !po->isTableRow())
2541 staticTop += toRenderBox(po)->y();
2542 }
2543 top.setValue(Fixed, staticTop);
2544 }
2545
2546 /*-----------------------------------------------------------------------*\
2547 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
2548 * 'margin-bottom' with '0'.
2549 \*-----------------------------------------------------------------------*/
2550 // FIXME: The spec. says that this step should only be taken when bottom is
2551 // auto, but if only top is auto, this makes step 4 impossible.
2552 if (top.isAuto() || bottom.isAuto()) {
2553 if (marginTop.isAuto())
2554 marginTop.setValue(Fixed, 0);
2555 if (marginBottom.isAuto())
2556 marginBottom.setValue(Fixed, 0);
2557 }
2558
2559 /*-----------------------------------------------------------------------*\
2560 * 4. If at this point both 'margin-top' and 'margin-bottom' are still
2561 * 'auto', solve the equation under the extra constraint that the two
2562 * margins must get equal values.
2563 \*-----------------------------------------------------------------------*/
2564 int topValue = 0;
2565 int bottomValue = 0;
2566
2567 if (marginTop.isAuto() && marginBottom.isAuto()) {
2568 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combinded.
2569 ASSERT(!(top.isAuto() || bottom.isAuto()));
2570
2571 topValue = top.calcValue(containerHeight);
2572 bottomValue = bottom.calcValue(containerHeight);
2573
2574 int difference = availableSpace - (topValue + bottomValue);
2575 // NOTE: This may result in negative values.
2576 m_marginTop = difference / 2; // split the difference
2577 m_marginBottom = difference - m_marginTop; // account for odd valued differences
2578
2579 /*-----------------------------------------------------------------------*\
2580 * 5. If at this point there is only one 'auto' left, solve the equation
2581 * for that value.
2582 \*-----------------------------------------------------------------------*/
2583 } else if (top.isAuto()) {
2584 m_marginTop = marginTop.calcValue(containerHeight);
2585 m_marginBottom = marginBottom.calcValue(containerHeight);
2586 bottomValue = bottom.calcValue(containerHeight);
2587
2588 // Solve for 'top'
2589 topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom);
2590 } else if (bottom.isAuto()) {
2591 m_marginTop = marginTop.calcValue(containerHeight);
2592 m_marginBottom = marginBottom.calcValue(containerHeight);
2593 topValue = top.calcValue(containerHeight);
2594
2595 // Solve for 'bottom'
2596 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
2597 // use the value.
2598 } else if (marginTop.isAuto()) {
2599 m_marginBottom = marginBottom.calcValue(containerHeight);
2600 topValue = top.calcValue(containerHeight);
2601 bottomValue = bottom.calcValue(containerHeight);
2602
2603 // Solve for 'margin-top'
2604 m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom);
2605 } else if (marginBottom.isAuto()) {
2606 m_marginTop = marginTop.calcValue(containerHeight);
2607 topValue = top.calcValue(containerHeight);
2608 bottomValue = bottom.calcValue(containerHeight);
2609
2610 // Solve for 'margin-bottom'
2611 m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop);
2612 } else {
2613 // Nothing is 'auto', just calculate the values.
2614 m_marginTop = marginTop.calcValue(containerHeight);
2615 m_marginBottom = marginBottom.calcValue(containerHeight);
2616 topValue = top.calcValue(containerHeight);
2617 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
2618 // use the value.
2619 }
2620
2621 /*-----------------------------------------------------------------------*\
2622 * 6. If at this point the values are over-constrained, ignore the value
2623 * for 'bottom' and solve for that value.
2624 \*-----------------------------------------------------------------------*/
2625 // NOTE: It is not necessary to do this step because we don't end up using
2626 // the value of 'bottom' regardless of whether the values are over-constrained
2627 // or not.
2628
2629 // Use computed values to calculate the vertical position.
2630 m_frameRect.setY(topValue + m_marginTop + containerBlock->borderTop());
2631 }
2632
localCaretRect(InlineBox * box,int caretOffset,int * extraWidthToEndOfLine)2633 IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine)
2634 {
2635 // VisiblePositions at offsets inside containers either a) refer to the positions before/after
2636 // those containers (tables and select elements) or b) refer to the position inside an empty block.
2637 // They never refer to children.
2638 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements.
2639
2640 // FIXME: What about border and padding?
2641 IntRect rect(x(), y(), caretWidth, height());
2642 TextDirection direction = box ? box->direction() : style()->direction();
2643
2644 if ((!caretOffset) ^ (direction == LTR))
2645 rect.move(IntSize(width() - caretWidth, 0));
2646
2647 if (box) {
2648 RootInlineBox* rootBox = box->root();
2649 int top = rootBox->topOverflow();
2650 rect.setY(top);
2651 rect.setHeight(rootBox->bottomOverflow() - top);
2652 }
2653
2654 // If height of box is smaller than font height, use the latter one,
2655 // otherwise the caret might become invisible.
2656 //
2657 // Also, if the box is not a replaced element, always use the font height.
2658 // This prevents the "big caret" bug described in:
2659 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point
2660 //
2661 // FIXME: ignoring :first-line, missing good reason to take care of
2662 int fontHeight = style()->font().height();
2663 if (fontHeight > rect.height() || (!isReplaced() && !isTable()))
2664 rect.setHeight(fontHeight);
2665
2666 if (extraWidthToEndOfLine)
2667 *extraWidthToEndOfLine = x() + width() - rect.right();
2668
2669 // Move to local coords
2670 rect.move(-x(), -y());
2671 return rect;
2672 }
2673
lowestPosition(bool,bool includeSelf) const2674 int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2675 {
2676 if (!includeSelf || !width())
2677 return 0;
2678 int bottom = height();
2679 if (isRelPositioned())
2680 bottom += relativePositionOffsetY();
2681 return bottom;
2682 }
2683
rightmostPosition(bool,bool includeSelf) const2684 int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2685 {
2686 if (!includeSelf || !height())
2687 return 0;
2688 int right = width();
2689 if (isRelPositioned())
2690 right += relativePositionOffsetX();
2691 return right;
2692 }
2693
leftmostPosition(bool,bool includeSelf) const2694 int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2695 {
2696 if (!includeSelf || !height())
2697 return width();
2698 int left = 0;
2699 if (isRelPositioned())
2700 left += relativePositionOffsetX();
2701 return left;
2702 }
2703
isAfterContent(RenderObject * child) const2704 bool RenderBox::isAfterContent(RenderObject* child) const
2705 {
2706 return (child && child->style()->styleType() == AFTER && (!child->isText() || child->isBR()));
2707 }
2708
positionForPoint(const IntPoint & point)2709 VisiblePosition RenderBox::positionForPoint(const IntPoint& point)
2710 {
2711 // no children...return this render object's element, if there is one, and offset 0
2712 if (!firstChild())
2713 return createVisiblePosition(firstDeepEditingPositionForNode(node()));
2714
2715 int xPos = point.x();
2716 int yPos = point.y();
2717
2718 if (isTable() && node()) {
2719 int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft();
2720 int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom();
2721
2722 if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) {
2723 if (xPos <= right / 2)
2724 return createVisiblePosition(firstDeepEditingPositionForNode(node()));
2725 return createVisiblePosition(lastDeepEditingPositionForNode(node()));
2726 }
2727 }
2728
2729 // Pass off to the closest child.
2730 int minDist = INT_MAX;
2731 RenderBox* closestRenderer = 0;
2732 int newX = xPos;
2733 int newY = yPos;
2734 if (isTableRow()) {
2735 newX += x();
2736 newY += y();
2737 }
2738 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) {
2739 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() )
2740 || renderObject->style()->visibility() != VISIBLE)
2741 continue;
2742
2743 if (!renderObject->isBox())
2744 continue;
2745
2746 RenderBox* renderer = toRenderBox(renderObject);
2747
2748 int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y());
2749 int bottom = top + renderer->contentHeight();
2750 int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x());
2751 int right = left + renderer->contentWidth();
2752
2753 if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) {
2754 if (renderer->isTableRow())
2755 return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y());
2756 return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y());
2757 }
2758
2759 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces
2760 // and use a different compare depending on which piece (x, y) is in.
2761 IntPoint cmp;
2762 if (xPos > right) {
2763 if (yPos < top)
2764 cmp = IntPoint(right, top);
2765 else if (yPos > bottom)
2766 cmp = IntPoint(right, bottom);
2767 else
2768 cmp = IntPoint(right, yPos);
2769 } else if (xPos < left) {
2770 if (yPos < top)
2771 cmp = IntPoint(left, top);
2772 else if (yPos > bottom)
2773 cmp = IntPoint(left, bottom);
2774 else
2775 cmp = IntPoint(left, yPos);
2776 } else {
2777 if (yPos < top)
2778 cmp = IntPoint(xPos, top);
2779 else
2780 cmp = IntPoint(xPos, bottom);
2781 }
2782
2783 int x1minusx2 = cmp.x() - xPos;
2784 int y1minusy2 = cmp.y() - yPos;
2785
2786 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2;
2787 if (dist < minDist) {
2788 closestRenderer = renderer;
2789 minDist = dist;
2790 }
2791 }
2792
2793 if (closestRenderer)
2794 return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y());
2795
2796 return createVisiblePosition(firstDeepEditingPositionForNode(node()));
2797 }
2798
shrinkToAvoidFloats() const2799 bool RenderBox::shrinkToAvoidFloats() const
2800 {
2801 // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this
2802 // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the
2803 // current remaining width on a line.
2804 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats())
2805 return false;
2806
2807 // All auto-width objects that avoid floats should always use lineWidth.
2808 return style()->width().isAuto();
2809 }
2810
avoidsFloats() const2811 bool RenderBox::avoidsFloats() const
2812 {
2813 return isReplaced() || hasOverflowClip() || isHR();
2814 }
2815
2816 #if ENABLE(SVG)
2817
localTransform() const2818 TransformationMatrix RenderBox::localTransform() const
2819 {
2820 return TransformationMatrix(1, 0, 0, 1, x(), y());
2821 }
2822
2823 #endif
2824
2825 } // namespace WebCore
2826