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, 2010 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 "Chrome.h"
30 #include "ChromeClient.h"
31 #include "Document.h"
32 #include "FrameView.h"
33 #include "GraphicsContext.h"
34 #include "HitTestResult.h"
35 #include "htmlediting.h"
36 #include "HTMLElement.h"
37 #include "HTMLNames.h"
38 #include "ImageBuffer.h"
39 #include "FloatQuad.h"
40 #include "Frame.h"
41 #include "Page.h"
42 #include "PaintInfo.h"
43 #include "RenderArena.h"
44 #include "RenderFlexibleBox.h"
45 #include "RenderInline.h"
46 #include "RenderLayer.h"
47 #include "RenderTableCell.h"
48 #include "RenderTheme.h"
49 #ifdef ANDROID_LAYOUT
50 #include "Settings.h"
51 #endif
52 #include "RenderView.h"
53 #include "ScrollbarTheme.h"
54 #include "TransformState.h"
55 #include <algorithm>
56 #include <math.h>
57
58 #if ENABLE(WML)
59 #include "WMLNames.h"
60 #endif
61
62 #if PLATFORM(ANDROID)
63 #include "PlatformBridge.h"
64 #endif
65
66 using namespace std;
67
68 namespace WebCore {
69
70 using namespace HTMLNames;
71
72 // Used by flexible boxes when flexing this element.
73 typedef WTF::HashMap<const RenderBox*, int> OverrideSizeMap;
74 static OverrideSizeMap* gOverrideSizeMap = 0;
75
76 bool RenderBox::s_hadOverflowClip = false;
77
RenderBox(Node * node)78 RenderBox::RenderBox(Node* node)
79 : RenderBoxModelObject(node)
80 , m_marginLeft(0)
81 , m_marginRight(0)
82 , m_marginTop(0)
83 , m_marginBottom(0)
84 , m_minPreferredLogicalWidth(-1)
85 , m_maxPreferredLogicalWidth(-1)
86 , m_inlineBoxWrapper(0)
87 #ifdef ANDROID_LAYOUT
88 , m_visibleWidth(0)
89 , m_isVisibleWidthChangedBeforeLayout(false)
90 #endif
91 {
92 setIsBox();
93 }
94
~RenderBox()95 RenderBox::~RenderBox()
96 {
97 }
98
marginBefore() const99 int RenderBox::marginBefore() const
100 {
101 switch (style()->writingMode()) {
102 case TopToBottomWritingMode:
103 return m_marginTop;
104 case BottomToTopWritingMode:
105 return m_marginBottom;
106 case LeftToRightWritingMode:
107 return m_marginLeft;
108 case RightToLeftWritingMode:
109 return m_marginRight;
110 }
111 ASSERT_NOT_REACHED();
112 return m_marginTop;
113 }
114
marginAfter() const115 int RenderBox::marginAfter() const
116 {
117 switch (style()->writingMode()) {
118 case TopToBottomWritingMode:
119 return m_marginBottom;
120 case BottomToTopWritingMode:
121 return m_marginTop;
122 case LeftToRightWritingMode:
123 return m_marginRight;
124 case RightToLeftWritingMode:
125 return m_marginLeft;
126 }
127 ASSERT_NOT_REACHED();
128 return m_marginBottom;
129 }
130
marginStart() const131 int RenderBox::marginStart() const
132 {
133 if (isHorizontalWritingMode())
134 return style()->isLeftToRightDirection() ? m_marginLeft : m_marginRight;
135 return style()->isLeftToRightDirection() ? m_marginTop : m_marginBottom;
136 }
137
marginEnd() const138 int RenderBox::marginEnd() const
139 {
140 if (isHorizontalWritingMode())
141 return style()->isLeftToRightDirection() ? m_marginRight : m_marginLeft;
142 return style()->isLeftToRightDirection() ? m_marginBottom : m_marginTop;
143 }
144
setMarginStart(int margin)145 void RenderBox::setMarginStart(int margin)
146 {
147 if (isHorizontalWritingMode()) {
148 if (style()->isLeftToRightDirection())
149 m_marginLeft = margin;
150 else
151 m_marginRight = margin;
152 } else {
153 if (style()->isLeftToRightDirection())
154 m_marginTop = margin;
155 else
156 m_marginBottom = margin;
157 }
158 }
159
setMarginEnd(int margin)160 void RenderBox::setMarginEnd(int margin)
161 {
162 if (isHorizontalWritingMode()) {
163 if (style()->isLeftToRightDirection())
164 m_marginRight = margin;
165 else
166 m_marginLeft = margin;
167 } else {
168 if (style()->isLeftToRightDirection())
169 m_marginBottom = margin;
170 else
171 m_marginTop = margin;
172 }
173 }
174
setMarginBefore(int margin)175 void RenderBox::setMarginBefore(int margin)
176 {
177 switch (style()->writingMode()) {
178 case TopToBottomWritingMode:
179 m_marginTop = margin;
180 break;
181 case BottomToTopWritingMode:
182 m_marginBottom = margin;
183 break;
184 case LeftToRightWritingMode:
185 m_marginLeft = margin;
186 break;
187 case RightToLeftWritingMode:
188 m_marginRight = margin;
189 break;
190 }
191 }
192
setMarginAfter(int margin)193 void RenderBox::setMarginAfter(int margin)
194 {
195 switch (style()->writingMode()) {
196 case TopToBottomWritingMode:
197 m_marginBottom = margin;
198 break;
199 case BottomToTopWritingMode:
200 m_marginTop = margin;
201 break;
202 case LeftToRightWritingMode:
203 m_marginRight = margin;
204 break;
205 case RightToLeftWritingMode:
206 m_marginLeft = margin;
207 break;
208 }
209 }
210
destroy()211 void RenderBox::destroy()
212 {
213 // A lot of the code in this function is just pasted into
214 // RenderWidget::destroy. If anything in this function changes,
215 // be sure to fix RenderWidget::destroy() as well.
216 if (hasOverrideSize())
217 gOverrideSizeMap->remove(this);
218
219 if (style() && (style()->logicalHeight().isPercent() || style()->logicalMinHeight().isPercent() || style()->logicalMaxHeight().isPercent()))
220 RenderBlock::removePercentHeightDescendant(this);
221
222 RenderBoxModelObject::destroy();
223 }
224
removeFloatingOrPositionedChildFromBlockLists()225 void RenderBox::removeFloatingOrPositionedChildFromBlockLists()
226 {
227 ASSERT(isFloatingOrPositioned());
228
229 if (documentBeingDestroyed())
230 return;
231
232 if (isFloating()) {
233 RenderBlock* parentBlock = 0;
234 for (RenderObject* curr = parent(); curr && !curr->isRenderView(); curr = curr->parent()) {
235 if (curr->isRenderBlock()) {
236 RenderBlock* currBlock = toRenderBlock(curr);
237 if (!parentBlock || currBlock->containsFloat(this))
238 parentBlock = currBlock;
239 }
240 }
241
242 if (parentBlock) {
243 RenderObject* parent = parentBlock->parent();
244 if (parent && parent->isFlexibleBox())
245 parentBlock = toRenderBlock(parent);
246
247 parentBlock->markAllDescendantsWithFloatsForLayout(this, false);
248 }
249 }
250
251 if (isPositioned()) {
252 for (RenderObject* curr = parent(); curr; curr = curr->parent()) {
253 if (curr->isRenderBlock())
254 toRenderBlock(curr)->removePositionedObject(this);
255 }
256 }
257 }
258
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)259 void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
260 {
261 s_hadOverflowClip = hasOverflowClip();
262
263 if (style()) {
264 // The background of the root element or the body element could propagate up to
265 // the canvas. Just dirty the entire canvas when our style changes substantially.
266 if (diff >= StyleDifferenceRepaint && node() &&
267 (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag)))
268 view()->repaint();
269
270 // When a layout hint happens and an object's position style changes, we have to do a layout
271 // to dirty the render tree using the old position value now.
272 if (diff == StyleDifferenceLayout && parent() && style()->position() != newStyle->position()) {
273 markContainingBlocksForLayout();
274 if (style()->position() == StaticPosition)
275 repaint();
276 else if (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition)
277 parent()->setChildNeedsLayout(true);
278 if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition))
279 removeFloatingOrPositionedChildFromBlockLists();
280 }
281 } else if (newStyle && isBody())
282 view()->repaint();
283
284 if (FrameView *frameView = view()->frameView()) {
285 bool newStyleIsFixed = newStyle && newStyle->position() == FixedPosition;
286 bool oldStyleIsFixed = style() && style()->position() == FixedPosition;
287 if (newStyleIsFixed != oldStyleIsFixed) {
288 if (newStyleIsFixed)
289 frameView->addFixedObject();
290 else
291 frameView->removeFixedObject();
292 }
293 }
294
295 RenderBoxModelObject::styleWillChange(diff, newStyle);
296 }
297
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)298 void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
299 {
300 RenderBoxModelObject::styleDidChange(diff, oldStyle);
301
302 if (needsLayout() && oldStyle) {
303 if (oldStyle && (oldStyle->logicalHeight().isPercent() || oldStyle->logicalMinHeight().isPercent() || oldStyle->logicalMaxHeight().isPercent()))
304 RenderBlock::removePercentHeightDescendant(this);
305
306 // Normally we can do optimized positioning layout for absolute/fixed positioned objects. There is one special case, however, which is
307 // when the positioned object's margin-before is changed. In this case the parent has to get a layout in order to run margin collapsing
308 // to determine the new static position.
309 if (isPositioned() && style()->hasStaticBlockPosition(isHorizontalWritingMode()) && oldStyle->marginBefore() != style()->marginBefore()
310 && parent() && !parent()->normalChildNeedsLayout())
311 parent()->setChildNeedsLayout(true);
312 }
313
314 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the
315 // new zoomed coordinate space.
316 if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) {
317 if (int left = layer()->scrollXOffset()) {
318 left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom();
319 layer()->scrollToXOffset(left);
320 }
321 if (int top = layer()->scrollYOffset()) {
322 top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom();
323 layer()->scrollToYOffset(top);
324 }
325 }
326
327 bool isBodyRenderer = isBody();
328 bool isRootRenderer = isRoot();
329
330 // Set the text color if we're the body.
331 if (isBodyRenderer)
332 document()->setTextColor(style()->visitedDependentColor(CSSPropertyColor));
333
334 if (isRootRenderer || isBodyRenderer) {
335 // Propagate the new writing mode and direction up to the RenderView.
336 RenderView* viewRenderer = view();
337 RenderStyle* viewStyle = viewRenderer->style();
338 if (viewStyle->direction() != style()->direction() && (isRootRenderer || !document()->directionSetOnDocumentElement())) {
339 viewStyle->setDirection(style()->direction());
340 if (isBodyRenderer)
341 document()->documentElement()->renderer()->style()->setDirection(style()->direction());
342 setNeedsLayoutAndPrefWidthsRecalc();
343 }
344
345 if (viewStyle->writingMode() != style()->writingMode() && (isRootRenderer || !document()->writingModeSetOnDocumentElement())) {
346 viewStyle->setWritingMode(style()->writingMode());
347 viewRenderer->setHorizontalWritingMode(style()->isHorizontalWritingMode());
348 if (isBodyRenderer) {
349 document()->documentElement()->renderer()->style()->setWritingMode(style()->writingMode());
350 document()->documentElement()->renderer()->setHorizontalWritingMode(style()->isHorizontalWritingMode());
351 }
352 setNeedsLayoutAndPrefWidthsRecalc();
353 }
354 }
355 }
356
updateBoxModelInfoFromStyle()357 void RenderBox::updateBoxModelInfoFromStyle()
358 {
359 RenderBoxModelObject::updateBoxModelInfoFromStyle();
360
361 bool isRootObject = isRoot();
362 bool isViewObject = isRenderView();
363
364 // The root and the RenderView always paint their backgrounds/borders.
365 if (isRootObject || isViewObject)
366 setHasBoxDecorations(true);
367
368 setPositioned(style()->position() == AbsolutePosition || style()->position() == FixedPosition);
369 setFloating(!isPositioned() && style()->isFloating());
370
371 // We also handle <body> and <html>, whose overflow applies to the viewport.
372 if (style()->overflowX() != OVISIBLE && !isRootObject && (isRenderBlock() || isTableRow() || isTableSection())) {
373 bool boxHasOverflowClip = true;
374 if (isBody()) {
375 // Overflow on the body can propagate to the viewport under the following conditions.
376 // (1) The root element is <html>.
377 // (2) We are the primary <body> (can be checked by looking at document.body).
378 // (3) The root element has visible overflow.
379 if (document()->documentElement()->hasTagName(htmlTag) &&
380 document()->body() == node() &&
381 document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE)
382 boxHasOverflowClip = false;
383 }
384
385 // Check for overflow clip.
386 // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value.
387 if (boxHasOverflowClip) {
388 if (!s_hadOverflowClip)
389 // Erase the overflow
390 repaint();
391 setHasOverflowClip();
392 }
393 }
394
395 setHasTransform(style()->hasTransformRelatedProperty());
396 setHasReflection(style()->boxReflect());
397 }
398
layout()399 void RenderBox::layout()
400 {
401 ASSERT(needsLayout());
402
403 RenderObject* child = firstChild();
404 if (!child) {
405 setNeedsLayout(false);
406 return;
407 }
408
409 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode());
410 while (child) {
411 child->layoutIfNeeded();
412 ASSERT(!child->needsLayout());
413 child = child->nextSibling();
414 }
415 statePusher.pop();
416 setNeedsLayout(false);
417 }
418
419 // More IE extensions. clientWidth and clientHeight represent the interior of an object
420 // excluding border and scrollbar.
clientWidth() const421 int RenderBox::clientWidth() const
422 {
423 return width() - borderLeft() - borderRight() - verticalScrollbarWidth();
424 }
425
clientHeight() const426 int RenderBox::clientHeight() const
427 {
428 return height() - borderTop() - borderBottom() - horizontalScrollbarHeight();
429 }
430
scrollWidth() const431 int RenderBox::scrollWidth() const
432 {
433 if (hasOverflowClip())
434 return layer()->scrollWidth();
435 // For objects with visible overflow, this matches IE.
436 // FIXME: Need to work right with writing modes.
437 if (style()->isLeftToRightDirection())
438 return max(clientWidth(), maxXLayoutOverflow() - borderLeft());
439 return clientWidth() - min(0, minXLayoutOverflow() - borderLeft());
440 }
441
scrollHeight() const442 int RenderBox::scrollHeight() const
443 {
444 if (hasOverflowClip())
445 return layer()->scrollHeight();
446 // For objects with visible overflow, this matches IE.
447 // FIXME: Need to work right with writing modes.
448 return max(clientHeight(), maxYLayoutOverflow() - borderTop());
449 }
450
scrollLeft() const451 int RenderBox::scrollLeft() const
452 {
453 return hasOverflowClip() ? layer()->scrollXOffset() : 0;
454 }
455
scrollTop() const456 int RenderBox::scrollTop() const
457 {
458 return hasOverflowClip() ? layer()->scrollYOffset() : 0;
459 }
460
setScrollLeft(int newLeft)461 void RenderBox::setScrollLeft(int newLeft)
462 {
463 if (hasOverflowClip())
464 layer()->scrollToXOffset(newLeft);
465 }
466
setScrollTop(int newTop)467 void RenderBox::setScrollTop(int newTop)
468 {
469 if (hasOverflowClip())
470 layer()->scrollToYOffset(newTop);
471 }
472
absoluteRects(Vector<IntRect> & rects,int tx,int ty)473 void RenderBox::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
474 {
475 rects.append(IntRect(tx, ty, width(), height()));
476 }
477
absoluteQuads(Vector<FloatQuad> & quads)478 void RenderBox::absoluteQuads(Vector<FloatQuad>& quads)
479 {
480 quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height())));
481 }
482
updateLayerTransform()483 void RenderBox::updateLayerTransform()
484 {
485 // Transform-origin depends on box size, so we need to update the layer transform after layout.
486 if (hasLayer())
487 layer()->updateTransform();
488 }
489
absoluteContentBox() const490 IntRect RenderBox::absoluteContentBox() const
491 {
492 IntRect rect = contentBoxRect();
493 FloatPoint absPos = localToAbsolute(FloatPoint());
494 rect.move(absPos.x(), absPos.y());
495 return rect;
496 }
497
absoluteContentQuad() const498 FloatQuad RenderBox::absoluteContentQuad() const
499 {
500 IntRect rect = contentBoxRect();
501 return localToAbsoluteQuad(FloatRect(rect));
502 }
503
outlineBoundsForRepaint(RenderBoxModelObject * repaintContainer,IntPoint * cachedOffsetToRepaintContainer) const504 IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer, IntPoint* cachedOffsetToRepaintContainer) const
505 {
506 IntRect box = borderBoundingBox();
507 adjustRectForOutlineAndShadow(box);
508
509 FloatQuad containerRelativeQuad = FloatRect(box);
510 if (cachedOffsetToRepaintContainer)
511 containerRelativeQuad.move(cachedOffsetToRepaintContainer->x(), cachedOffsetToRepaintContainer->y());
512 else
513 containerRelativeQuad = localToContainerQuad(containerRelativeQuad, repaintContainer);
514
515 box = containerRelativeQuad.enclosingBoundingBox();
516
517 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
518 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
519 box.move(view()->layoutDelta());
520
521 return box;
522 }
523
addFocusRingRects(Vector<IntRect> & rects,int tx,int ty)524 void RenderBox::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
525 {
526 if (width() && height())
527 rects.append(IntRect(tx, ty, width(), height()));
528 }
529
reflectionBox() const530 IntRect RenderBox::reflectionBox() const
531 {
532 IntRect result;
533 if (!style()->boxReflect())
534 return result;
535 IntRect box = borderBoxRect();
536 result = box;
537 switch (style()->boxReflect()->direction()) {
538 case ReflectionBelow:
539 result.move(0, box.height() + reflectionOffset());
540 break;
541 case ReflectionAbove:
542 result.move(0, -box.height() - reflectionOffset());
543 break;
544 case ReflectionLeft:
545 result.move(-box.width() - reflectionOffset(), 0);
546 break;
547 case ReflectionRight:
548 result.move(box.width() + reflectionOffset(), 0);
549 break;
550 }
551 return result;
552 }
553
reflectionOffset() const554 int RenderBox::reflectionOffset() const
555 {
556 if (!style()->boxReflect())
557 return 0;
558 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight)
559 return style()->boxReflect()->offset().calcValue(borderBoxRect().width());
560 return style()->boxReflect()->offset().calcValue(borderBoxRect().height());
561 }
562
reflectedRect(const IntRect & r) const563 IntRect RenderBox::reflectedRect(const IntRect& r) const
564 {
565 if (!style()->boxReflect())
566 return IntRect();
567
568 IntRect box = borderBoxRect();
569 IntRect result = r;
570 switch (style()->boxReflect()->direction()) {
571 case ReflectionBelow:
572 result.setY(box.maxY() + reflectionOffset() + (box.maxY() - r.maxY()));
573 break;
574 case ReflectionAbove:
575 result.setY(box.y() - reflectionOffset() - box.height() + (box.maxY() - r.maxY()));
576 break;
577 case ReflectionLeft:
578 result.setX(box.x() - reflectionOffset() - box.width() + (box.maxX() - r.maxX()));
579 break;
580 case ReflectionRight:
581 result.setX(box.maxX() + reflectionOffset() + (box.maxX() - r.maxX()));
582 break;
583 }
584 return result;
585 }
586
includeVerticalScrollbarSize() const587 bool RenderBox::includeVerticalScrollbarSize() const
588 {
589 return hasOverflowClip() && !layer()->hasOverlayScrollbars()
590 && (style()->overflowY() == OSCROLL || style()->overflowY() == OAUTO);
591 }
592
includeHorizontalScrollbarSize() const593 bool RenderBox::includeHorizontalScrollbarSize() const
594 {
595 return hasOverflowClip() && !layer()->hasOverlayScrollbars()
596 && (style()->overflowX() == OSCROLL || style()->overflowX() == OAUTO);
597 }
598
verticalScrollbarWidth() const599 int RenderBox::verticalScrollbarWidth() const
600 {
601 return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0;
602 }
603
horizontalScrollbarHeight() const604 int RenderBox::horizontalScrollbarHeight() const
605 {
606 return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0;
607 }
608
scroll(ScrollDirection direction,ScrollGranularity granularity,float multiplier,Node ** stopNode)609 bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
610 {
611 RenderLayer* l = layer();
612 if (l && l->scroll(direction, granularity, multiplier)) {
613 if (stopNode)
614 *stopNode = node();
615 return true;
616 }
617
618 if (stopNode && *stopNode && *stopNode == node())
619 return true;
620
621 RenderBlock* b = containingBlock();
622 if (b && !b->isRenderView())
623 return b->scroll(direction, granularity, multiplier, stopNode);
624 return false;
625 }
626
logicalScroll(ScrollLogicalDirection direction,ScrollGranularity granularity,float multiplier,Node ** stopNode)627 bool RenderBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
628 {
629 bool scrolled = false;
630
631 RenderLayer* l = layer();
632 if (l) {
633 #if PLATFORM(MAC)
634 // On Mac only we reset the inline direction position when doing a document scroll (e.g., hitting Home/End).
635 if (granularity == ScrollByDocument)
636 scrolled = l->scroll(logicalToPhysical(ScrollInlineDirectionBackward, isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), ScrollByDocument, multiplier);
637 #endif
638 if (l->scroll(logicalToPhysical(direction, isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier))
639 scrolled = true;
640
641 if (scrolled) {
642 if (stopNode)
643 *stopNode = node();
644 return true;
645 }
646 }
647
648 if (stopNode && *stopNode && *stopNode == node())
649 return true;
650
651 RenderBlock* b = containingBlock();
652 if (b && !b->isRenderView())
653 return b->logicalScroll(direction, granularity, multiplier, stopNode);
654 return false;
655 }
656
canBeScrolledAndHasScrollableArea() const657 bool RenderBox::canBeScrolledAndHasScrollableArea() const
658 {
659 return canBeProgramaticallyScrolled(false) && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth());
660 }
661
canBeProgramaticallyScrolled(bool) const662 bool RenderBox::canBeProgramaticallyScrolled(bool) const
663 {
664 return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->rendererIsEditable()))) || (node() && node()->isDocumentNode());
665 }
666
autoscroll()667 void RenderBox::autoscroll()
668 {
669 if (layer())
670 layer()->autoscroll();
671 }
672
panScroll(const IntPoint & source)673 void RenderBox::panScroll(const IntPoint& source)
674 {
675 if (layer())
676 layer()->panScrollFromPoint(source);
677 }
678
minPreferredLogicalWidth() const679 int RenderBox::minPreferredLogicalWidth() const
680 {
681 if (preferredLogicalWidthsDirty())
682 const_cast<RenderBox*>(this)->computePreferredLogicalWidths();
683
684 return m_minPreferredLogicalWidth;
685 }
686
maxPreferredLogicalWidth() const687 int RenderBox::maxPreferredLogicalWidth() const
688 {
689 if (preferredLogicalWidthsDirty())
690 const_cast<RenderBox*>(this)->computePreferredLogicalWidths();
691
692 return m_maxPreferredLogicalWidth;
693 }
694
overrideSize() const695 int RenderBox::overrideSize() const
696 {
697 if (!hasOverrideSize())
698 return -1;
699 return gOverrideSizeMap->get(this);
700 }
701
setOverrideSize(int s)702 void RenderBox::setOverrideSize(int s)
703 {
704 if (s == -1) {
705 if (hasOverrideSize()) {
706 setHasOverrideSize(false);
707 gOverrideSizeMap->remove(this);
708 }
709 } else {
710 if (!gOverrideSizeMap)
711 gOverrideSizeMap = new OverrideSizeMap();
712 setHasOverrideSize(true);
713 gOverrideSizeMap->set(this, s);
714 }
715 }
716
overrideWidth() const717 int RenderBox::overrideWidth() const
718 {
719 return hasOverrideSize() ? overrideSize() : width();
720 }
721
overrideHeight() const722 int RenderBox::overrideHeight() const
723 {
724 return hasOverrideSize() ? overrideSize() : height();
725 }
726
computeBorderBoxLogicalWidth(int width) const727 int RenderBox::computeBorderBoxLogicalWidth(int width) const
728 {
729 int bordersPlusPadding = borderAndPaddingLogicalWidth();
730 if (style()->boxSizing() == CONTENT_BOX)
731 return width + bordersPlusPadding;
732 return max(width, bordersPlusPadding);
733 }
734
computeBorderBoxLogicalHeight(int height) const735 int RenderBox::computeBorderBoxLogicalHeight(int height) const
736 {
737 int bordersPlusPadding = borderAndPaddingLogicalHeight();
738 if (style()->boxSizing() == CONTENT_BOX)
739 return height + bordersPlusPadding;
740 return max(height, bordersPlusPadding);
741 }
742
computeContentBoxLogicalWidth(int width) const743 int RenderBox::computeContentBoxLogicalWidth(int width) const
744 {
745 if (style()->boxSizing() == BORDER_BOX)
746 width -= borderAndPaddingLogicalWidth();
747 return max(0, width);
748 }
749
computeContentBoxLogicalHeight(int height) const750 int RenderBox::computeContentBoxLogicalHeight(int height) const
751 {
752 if (style()->boxSizing() == BORDER_BOX)
753 height -= borderAndPaddingLogicalHeight();
754 return max(0, height);
755 }
756
757 // Hit Testing
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int xPos,int yPos,int tx,int ty,HitTestAction action)758 bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
759 {
760 tx += x();
761 ty += y();
762
763 // Check kids first.
764 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
765 if (!child->hasLayer() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) {
766 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
767 return true;
768 }
769 }
770
771 // Check our bounds next. For this purpose always assume that we can only be hit in the
772 // foreground phase (which is true for replaced elements like images).
773 IntRect boundsRect = IntRect(tx, ty, width(), height());
774 if (visibleToHitTesting() && action == HitTestForeground && boundsRect.intersects(result.rectForPoint(xPos, yPos))) {
775 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
776 if (!result.addNodeToRectBasedTestResult(node(), xPos, yPos, boundsRect))
777 return true;
778 }
779
780 return false;
781 }
782
783 // --------------------- painting stuff -------------------------------
784
paint(PaintInfo & paintInfo,int tx,int ty)785 void RenderBox::paint(PaintInfo& paintInfo, int tx, int ty)
786 {
787 tx += x();
788 ty += y();
789
790 // default implementation. Just pass paint through to the children
791 PaintInfo childInfo(paintInfo);
792 childInfo.updatePaintingRootForChildren(this);
793 for (RenderObject* child = firstChild(); child; child = child->nextSibling())
794 child->paint(childInfo, tx, ty);
795 }
796
paintRootBoxFillLayers(const PaintInfo & paintInfo)797 void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo)
798 {
799 const FillLayer* bgLayer = style()->backgroundLayers();
800 Color bgColor = style()->visitedDependentColor(CSSPropertyBackgroundColor);
801 RenderObject* bodyObject = 0;
802 if (!hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) {
803 // Locate the <body> element using the DOM. This is easier than trying
804 // to crawl around a render tree with potential :before/:after content and
805 // anonymous blocks created by inline <body> tags etc. We can locate the <body>
806 // render object very easily via the DOM.
807 HTMLElement* body = document()->body();
808 bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0;
809 if (bodyObject) {
810 bgLayer = bodyObject->style()->backgroundLayers();
811 bgColor = bodyObject->style()->visitedDependentColor(CSSPropertyBackgroundColor);
812 }
813 }
814
815 // The background of the box generated by the root element covers the entire canvas, so just use
816 // the RenderView's docTop/Left/Width/Height accessors.
817 paintFillLayers(paintInfo, bgColor, bgLayer, view()->docLeft(), view()->docTop(), view()->docWidth(), view()->docHeight(), CompositeSourceOver, bodyObject);
818 }
819
paintBoxDecorations(PaintInfo & paintInfo,int tx,int ty)820 void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
821 {
822 if (!paintInfo.shouldPaintWithinRoot(this))
823 return;
824 return paintBoxDecorationsWithSize(paintInfo, tx, ty, width(), height());
825 }
826
paintBoxDecorationsWithSize(PaintInfo & paintInfo,int tx,int ty,int width,int height)827 void RenderBox::paintBoxDecorationsWithSize(PaintInfo& paintInfo, int tx, int ty, int width, int height)
828 {
829 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat
830 // balloon layout is an example of this).
831 borderFitAdjust(tx, width);
832
833 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
834 // custom shadows of their own.
835 paintBoxShadow(paintInfo.context, tx, ty, width, height, style(), Normal);
836
837 // If we have a native theme appearance, paint that before painting our background.
838 // The theme will tell us whether or not we should also paint the CSS background.
839 bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, IntRect(tx, ty, width, height));
840 if (!themePainted) {
841 if (isRoot())
842 paintRootBoxFillLayers(paintInfo);
843 else if (!isBody() || document()->documentElement()->renderer()->hasBackground()) {
844 // The <body> only paints its background if the root element has defined a background
845 // independent of the body.
846 paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), tx, ty, width, height);
847 }
848 if (style()->hasAppearance())
849 theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, width, height));
850 }
851 paintBoxShadow(paintInfo.context, tx, ty, width, height, style(), Inset);
852
853 // The theme will tell us whether or not we should also paint the CSS border.
854 if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, IntRect(tx, ty, width, height)))) && style()->hasBorder())
855 paintBorder(paintInfo.context, tx, ty, width, height, style());
856 }
857
paintMask(PaintInfo & paintInfo,int tx,int ty)858 void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty)
859 {
860 if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled())
861 return;
862
863 int w = width();
864 int h = height();
865
866 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat
867 // balloon layout is an example of this).
868 borderFitAdjust(tx, w);
869
870 paintMaskImages(paintInfo, tx, ty, w, h);
871 }
872
paintMaskImages(const PaintInfo & paintInfo,int tx,int ty,int w,int h)873 void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int w, int h)
874 {
875 // Figure out if we need to push a transparency layer to render our mask.
876 bool pushTransparencyLayer = false;
877 bool compositedMask = hasLayer() && layer()->hasCompositedMask();
878 CompositeOperator compositeOp = CompositeSourceOver;
879
880 bool allMaskImagesLoaded = true;
881
882 if (!compositedMask) {
883 // If the context has a rotation, scale or skew, then use a transparency layer to avoid
884 // pixel cruft around the edge of the mask.
885 const AffineTransform& currentCTM = paintInfo.context->getCTM();
886 pushTransparencyLayer = !currentCTM.isIdentityOrTranslationOrFlipped();
887
888 StyleImage* maskBoxImage = style()->maskBoxImage().image();
889 const FillLayer* maskLayers = style()->maskLayers();
890
891 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content.
892 if (maskBoxImage)
893 allMaskImagesLoaded &= maskBoxImage->isLoaded();
894
895 if (maskLayers)
896 allMaskImagesLoaded &= maskLayers->imagesAreLoaded();
897
898 // Before all images have loaded, just use an empty transparency layer as the mask.
899 if (!allMaskImagesLoaded)
900 pushTransparencyLayer = true;
901
902 if (maskBoxImage && maskLayers->hasImage()) {
903 // We have a mask-box-image and mask-image, so need to composite them together before using the result as a mask.
904 pushTransparencyLayer = true;
905 } else {
906 // We have to use an extra image buffer to hold the mask. Multiple mask images need
907 // to composite together using source-over so that they can then combine into a single unified mask that
908 // can be composited with the content using destination-in. SVG images need to be able to set compositing modes
909 // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer
910 // and composite that buffer as the mask.
911 // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering
912 // before pushing the transparency layer.
913 for (const FillLayer* fillLayer = maskLayers->next(); fillLayer; fillLayer = fillLayer->next()) {
914 if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) {
915 pushTransparencyLayer = true;
916 // We found one image that can be used in rendering, exit the loop
917 break;
918 }
919 }
920 }
921
922 compositeOp = CompositeDestinationIn;
923 if (pushTransparencyLayer) {
924 paintInfo.context->setCompositeOperation(CompositeDestinationIn);
925 paintInfo.context->beginTransparencyLayer(1.0f);
926 compositeOp = CompositeSourceOver;
927 }
928 }
929
930 if (allMaskImagesLoaded) {
931 paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp);
932 paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp);
933 }
934
935 if (pushTransparencyLayer)
936 paintInfo.context->endTransparencyLayer();
937 }
938
maskClipRect()939 IntRect RenderBox::maskClipRect()
940 {
941 IntRect bbox = borderBoxRect();
942 if (style()->maskBoxImage().image())
943 return bbox;
944
945 IntRect result;
946 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
947 if (maskLayer->image()) {
948 IntRect maskRect;
949 IntPoint phase;
950 IntSize tileSize;
951 calculateBackgroundImageGeometry(maskLayer, bbox.x(), bbox.y(), bbox.width(), bbox.height(), maskRect, phase, tileSize);
952 result.unite(maskRect);
953 }
954 }
955 return result;
956 }
957
paintFillLayers(const PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,int tx,int ty,int width,int height,CompositeOperator op,RenderObject * backgroundObject)958 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject)
959 {
960 if (!fillLayer)
961 return;
962
963 paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op, backgroundObject);
964 paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op, backgroundObject);
965 }
966
paintFillLayer(const PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,int tx,int ty,int width,int height,CompositeOperator op,RenderObject * backgroundObject)967 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject)
968 {
969 paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, 0, 0, op, backgroundObject);
970 }
971
972 #if USE(ACCELERATED_COMPOSITING)
layersUseImage(WrappedImagePtr image,const FillLayer * layers)973 static bool layersUseImage(WrappedImagePtr image, const FillLayer* layers)
974 {
975 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
976 if (curLayer->image() && image == curLayer->image()->data())
977 return true;
978 }
979
980 return false;
981 }
982 #endif
983
imageChanged(WrappedImagePtr image,const IntRect *)984 void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*)
985 {
986 if (!parent())
987 return;
988
989 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) ||
990 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) {
991 repaint();
992 return;
993 }
994
995 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true);
996 if (!didFullRepaint)
997 repaintLayerRectsForImage(image, style()->maskLayers(), false);
998
999
1000 #if USE(ACCELERATED_COMPOSITING)
1001 if (hasLayer() && layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers()))
1002 layer()->contentChanged(RenderLayer::MaskImageChanged);
1003 #endif
1004 }
1005
repaintLayerRectsForImage(WrappedImagePtr image,const FillLayer * layers,bool drawingBackground)1006 bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground)
1007 {
1008 IntRect rendererRect;
1009 RenderBox* layerRenderer = 0;
1010
1011 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
1012 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(style()->effectiveZoom())) {
1013 // Now that we know this image is being used, compute the renderer and the rect
1014 // if we haven't already
1015 if (!layerRenderer) {
1016 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->hasBackground()));
1017 if (drawingRootBackground) {
1018 layerRenderer = view();
1019
1020 int rw;
1021 int rh;
1022
1023 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) {
1024 rw = frameView->contentsWidth();
1025 rh = frameView->contentsHeight();
1026 } else {
1027 rw = layerRenderer->width();
1028 rh = layerRenderer->height();
1029 }
1030 rendererRect = IntRect(-layerRenderer->marginLeft(),
1031 -layerRenderer->marginTop(),
1032 max(layerRenderer->width() + layerRenderer->marginLeft() + layerRenderer->marginRight() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw),
1033 max(layerRenderer->height() + layerRenderer->marginTop() + layerRenderer->marginBottom() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh));
1034 } else {
1035 layerRenderer = this;
1036 rendererRect = borderBoxRect();
1037 }
1038 }
1039
1040 IntRect repaintRect;
1041 IntPoint phase;
1042 IntSize tileSize;
1043 layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect.x(), rendererRect.y(), rendererRect.width(), rendererRect.height(), repaintRect, phase, tileSize);
1044 layerRenderer->repaintRectangle(repaintRect);
1045 if (repaintRect == rendererRect)
1046 return true;
1047 }
1048 }
1049 return false;
1050 }
1051
1052 #if PLATFORM(MAC)
1053
paintCustomHighlight(int tx,int ty,const AtomicString & type,bool behindText)1054 void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText)
1055 {
1056 Frame* frame = this->frame();
1057 if (!frame)
1058 return;
1059 Page* page = frame->page();
1060 if (!page)
1061 return;
1062
1063 InlineBox* boxWrap = inlineBoxWrapper();
1064 RootInlineBox* r = boxWrap ? boxWrap->root() : 0;
1065 if (r) {
1066 FloatRect rootRect(tx + r->x(), ty + r->selectionTop(), r->logicalWidth(), r->selectionHeight());
1067 FloatRect imageRect(tx + x(), rootRect.y(), width(), rootRect.height());
1068 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false);
1069 } else {
1070 FloatRect imageRect(tx + x(), ty + y(), width(), height());
1071 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false);
1072 }
1073 }
1074
1075 #endif
1076
pushContentsClip(PaintInfo & paintInfo,int tx,int ty)1077 bool RenderBox::pushContentsClip(PaintInfo& paintInfo, int tx, int ty)
1078 {
1079 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask)
1080 return false;
1081
1082 bool isControlClip = hasControlClip();
1083 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer();
1084
1085 if (!isControlClip && !isOverflowClip)
1086 return false;
1087
1088 if (paintInfo.phase == PaintPhaseOutline)
1089 paintInfo.phase = PaintPhaseChildOutlines;
1090 else if (paintInfo.phase == PaintPhaseChildBlockBackground) {
1091 paintInfo.phase = PaintPhaseBlockBackground;
1092 paintObject(paintInfo, tx, ty);
1093 paintInfo.phase = PaintPhaseChildBlockBackgrounds;
1094 }
1095 IntRect clipRect(isControlClip ? controlClipRect(tx, ty) : overflowClipRect(tx, ty));
1096 paintInfo.context->save();
1097 if (style()->hasBorderRadius())
1098 paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(IntRect(tx, ty, width(), height())));
1099 paintInfo.context->clip(clipRect);
1100 return true;
1101 }
1102
popContentsClip(PaintInfo & paintInfo,PaintPhase originalPhase,int tx,int ty)1103 void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, int tx, int ty)
1104 {
1105 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer()));
1106
1107 paintInfo.context->restore();
1108 if (originalPhase == PaintPhaseOutline) {
1109 paintInfo.phase = PaintPhaseSelfOutline;
1110 paintObject(paintInfo, tx, ty);
1111 paintInfo.phase = originalPhase;
1112 } else if (originalPhase == PaintPhaseChildBlockBackground)
1113 paintInfo.phase = originalPhase;
1114 }
1115
overflowClipRect(int tx,int ty,OverlayScrollbarSizeRelevancy relevancy)1116 IntRect RenderBox::overflowClipRect(int tx, int ty, OverlayScrollbarSizeRelevancy relevancy)
1117 {
1118 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property
1119 // here.
1120
1121 int bLeft = borderLeft();
1122 int bTop = borderTop();
1123
1124 int clipX = tx + bLeft;
1125 int clipY = ty + bTop;
1126 int clipWidth = width() - bLeft - borderRight();
1127 int clipHeight = height() - bTop - borderBottom();
1128
1129 // Subtract out scrollbars if we have them.
1130 if (layer()) {
1131 clipWidth -= layer()->verticalScrollbarWidth(relevancy);
1132 clipHeight -= layer()->horizontalScrollbarHeight(relevancy);
1133 }
1134
1135 return IntRect(clipX, clipY, clipWidth, clipHeight);
1136 }
1137
clipRect(int tx,int ty)1138 IntRect RenderBox::clipRect(int tx, int ty)
1139 {
1140 int clipX = tx;
1141 int clipY = ty;
1142 int clipWidth = width();
1143 int clipHeight = height();
1144
1145 if (!style()->clipLeft().isAuto()) {
1146 int c = style()->clipLeft().calcValue(width());
1147 clipX += c;
1148 clipWidth -= c;
1149 }
1150
1151 if (!style()->clipRight().isAuto())
1152 clipWidth -= width() - style()->clipRight().calcValue(width());
1153
1154 if (!style()->clipTop().isAuto()) {
1155 int c = style()->clipTop().calcValue(height());
1156 clipY += c;
1157 clipHeight -= c;
1158 }
1159
1160 if (!style()->clipBottom().isAuto())
1161 clipHeight -= height() - style()->clipBottom().calcValue(height());
1162
1163 return IntRect(clipX, clipY, clipWidth, clipHeight);
1164 }
1165
containingBlockLogicalWidthForContent() const1166 int RenderBox::containingBlockLogicalWidthForContent() const
1167 {
1168 RenderBlock* cb = containingBlock();
1169 if (shrinkToAvoidFloats())
1170 return cb->availableLogicalWidthForLine(y(), false);
1171 return cb->availableLogicalWidth();
1172 }
1173
perpendicularContainingBlockLogicalHeight() const1174 int RenderBox::perpendicularContainingBlockLogicalHeight() const
1175 {
1176 RenderBlock* cb = containingBlock();
1177 RenderStyle* containingBlockStyle = cb->style();
1178 Length logicalHeightLength = containingBlockStyle->logicalHeight();
1179
1180 // FIXME: For now just support fixed heights. Eventually should support percentage heights as well.
1181 if (!logicalHeightLength.isFixed()) {
1182 // Rather than making the child be completely unconstrained, WinIE uses the viewport width and height
1183 // as a constraint. We do that for now as well even though it's likely being unconstrained is what the spec
1184 // will decide.
1185 return containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth();
1186 }
1187
1188 // Use the content box logical height as specified by the style.
1189 return cb->computeContentBoxLogicalHeight(logicalHeightLength.value());
1190 }
1191
mapLocalToContainer(RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState) const1192 void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
1193 {
1194 if (repaintContainer == this)
1195 return;
1196
1197 if (RenderView* v = view()) {
1198 if (v->layoutStateEnabled() && !repaintContainer) {
1199 LayoutState* layoutState = v->layoutState();
1200 IntSize offset = layoutState->m_paintOffset;
1201 offset.expand(x(), y());
1202 if (style()->position() == RelativePosition && layer())
1203 offset += layer()->relativePositionOffset();
1204 transformState.move(offset);
1205 return;
1206 }
1207 }
1208
1209 bool containerSkipped;
1210 RenderObject* o = container(repaintContainer, &containerSkipped);
1211 if (!o)
1212 return;
1213
1214 bool isFixedPos = style()->position() == FixedPosition;
1215 bool hasTransform = hasLayer() && layer()->transform();
1216 if (hasTransform) {
1217 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1218 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1219 fixed &= isFixedPos;
1220 } else
1221 fixed |= isFixedPos;
1222
1223 IntSize containerOffset = offsetFromContainer(o, roundedIntPoint(transformState.mappedPoint()));
1224
1225 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
1226 if (useTransforms && shouldUseTransformFromContainer(o)) {
1227 TransformationMatrix t;
1228 getTransformFromContainer(o, containerOffset, t);
1229 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1230 } else
1231 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1232
1233 if (containerSkipped) {
1234 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1235 // to just subtract the delta between the repaintContainer and o.
1236 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1237 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1238 return;
1239 }
1240
1241 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
1242 }
1243
mapAbsoluteToLocalPoint(bool fixed,bool useTransforms,TransformState & transformState) const1244 void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
1245 {
1246 // We don't expect absoluteToLocal() to be called during layout (yet)
1247 ASSERT(!view() || !view()->layoutStateEnabled());
1248
1249 bool isFixedPos = style()->position() == FixedPosition;
1250 bool hasTransform = hasLayer() && layer()->transform();
1251 if (hasTransform) {
1252 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1253 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1254 fixed &= isFixedPos;
1255 } else
1256 fixed |= isFixedPos;
1257
1258 RenderObject* o = container();
1259 if (!o)
1260 return;
1261
1262 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
1263
1264 IntSize containerOffset = offsetFromContainer(o, IntPoint());
1265
1266 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
1267 if (useTransforms && shouldUseTransformFromContainer(o)) {
1268 TransformationMatrix t;
1269 getTransformFromContainer(o, containerOffset, t);
1270 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1271 } else
1272 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1273 }
1274
offsetFromContainer(RenderObject * o,const IntPoint & point) const1275 IntSize RenderBox::offsetFromContainer(RenderObject* o, const IntPoint& point) const
1276 {
1277 ASSERT(o == container());
1278
1279 IntSize offset;
1280 if (isRelPositioned())
1281 offset += relativePositionOffset();
1282
1283 if (!isInline() || isReplaced()) {
1284 if (style()->position() != AbsolutePosition && style()->position() != FixedPosition) {
1285 if (o->hasColumns()) {
1286 IntRect columnRect(frameRect());
1287 toRenderBlock(o)->flipForWritingModeIncludingColumns(columnRect);
1288 offset += IntSize(columnRect.location().x(), columnRect.location().y());
1289 columnRect.move(point.x(), point.y());
1290 o->adjustForColumns(offset, columnRect.location());
1291 } else
1292 offset += locationOffsetIncludingFlipping();
1293 } else
1294 offset += locationOffsetIncludingFlipping();
1295 }
1296
1297 if (o->hasOverflowClip())
1298 offset -= toRenderBox(o)->layer()->scrolledContentOffset();
1299
1300 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
1301 offset += toRenderInline(o)->relativePositionedInlineOffset(this);
1302
1303 return offset;
1304 }
1305
createInlineBox()1306 InlineBox* RenderBox::createInlineBox()
1307 {
1308 return new (renderArena()) InlineBox(this);
1309 }
1310
dirtyLineBoxes(bool fullLayout)1311 void RenderBox::dirtyLineBoxes(bool fullLayout)
1312 {
1313 if (m_inlineBoxWrapper) {
1314 if (fullLayout) {
1315 m_inlineBoxWrapper->destroy(renderArena());
1316 m_inlineBoxWrapper = 0;
1317 } else
1318 m_inlineBoxWrapper->dirtyLineBoxes();
1319 }
1320 }
1321
positionLineBox(InlineBox * box)1322 void RenderBox::positionLineBox(InlineBox* box)
1323 {
1324 if (isPositioned()) {
1325 // Cache the x position only if we were an INLINE type originally.
1326 bool wasInline = style()->isOriginalDisplayInlineType();
1327 if (wasInline) {
1328 // The value is cached in the xPos of the box. We only need this value if
1329 // our object was inline originally, since otherwise it would have ended up underneath
1330 // the inlines.
1331 layer()->setStaticInlinePosition(lroundf(box->logicalLeft()));
1332 if (style()->hasStaticInlinePosition(box->isHorizontal()))
1333 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
1334 } else {
1335 // Our object was a block originally, so we make our normal flow position be
1336 // just below the line box (as though all the inlines that came before us got
1337 // wrapped in an anonymous block, which is what would have happened had we been
1338 // in flow). This value was cached in the y() of the box.
1339 layer()->setStaticBlockPosition(box->logicalTop());
1340 if (style()->hasStaticBlockPosition(box->isHorizontal()))
1341 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
1342 }
1343
1344 // Nuke the box.
1345 box->remove();
1346 box->destroy(renderArena());
1347 } else if (isReplaced()) {
1348 setLocation(lroundf(box->x()), lroundf(box->y()));
1349 m_inlineBoxWrapper = box;
1350 }
1351 }
1352
deleteLineBoxWrapper()1353 void RenderBox::deleteLineBoxWrapper()
1354 {
1355 if (m_inlineBoxWrapper) {
1356 if (!documentBeingDestroyed())
1357 m_inlineBoxWrapper->remove();
1358 m_inlineBoxWrapper->destroy(renderArena());
1359 m_inlineBoxWrapper = 0;
1360 }
1361 }
1362
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)1363 IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
1364 {
1365 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
1366 return IntRect();
1367
1368 IntRect r = visualOverflowRect();
1369
1370 RenderView* v = view();
1371 if (v) {
1372 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
1373 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
1374 r.move(v->layoutDelta());
1375 }
1376
1377 if (style()) {
1378 if (style()->hasAppearance())
1379 // The theme may wish to inflate the rect used when repainting.
1380 theme()->adjustRepaintRect(this, r);
1381
1382 // We have to use maximalOutlineSize() because a child might have an outline
1383 // that projects outside of our overflowRect.
1384 if (v) {
1385 ASSERT(style()->outlineSize() <= v->maximalOutlineSize());
1386 r.inflate(v->maximalOutlineSize());
1387 }
1388 }
1389
1390 computeRectForRepaint(repaintContainer, r);
1391 return r;
1392 }
1393
computeRectForRepaint(RenderBoxModelObject * repaintContainer,IntRect & rect,bool fixed)1394 void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
1395 {
1396 // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space.
1397 // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate
1398 // offset corner for the enclosing container). This allows for a fully RL or BT document to repaint
1399 // properly even during layout, since the rect remains flipped all the way until the end.
1400 //
1401 // RenderView::computeRectForRepaint then converts the rect to physical coordinates. We also convert to
1402 // physical when we hit a repaintContainer boundary. Therefore the final rect returned is always in the
1403 // physical coordinate space of the repaintContainer.
1404 if (RenderView* v = view()) {
1405 // LayoutState is only valid for root-relative repainting
1406 if (v->layoutStateEnabled() && !repaintContainer) {
1407 LayoutState* layoutState = v->layoutState();
1408
1409 if (layer() && layer()->transform())
1410 rect = layer()->transform()->mapRect(rect);
1411
1412 if (style()->position() == RelativePosition && layer())
1413 rect.move(layer()->relativePositionOffset());
1414
1415 rect.move(x(), y());
1416 rect.move(layoutState->m_paintOffset);
1417 if (layoutState->m_clipped)
1418 rect.intersect(layoutState->m_clipRect);
1419 return;
1420 }
1421 }
1422
1423 if (hasReflection())
1424 rect.unite(reflectedRect(rect));
1425
1426 if (repaintContainer == this) {
1427 if (repaintContainer->style()->isFlippedBlocksWritingMode())
1428 flipForWritingMode(rect);
1429 return;
1430 }
1431
1432 bool containerSkipped;
1433 RenderObject* o = container(repaintContainer, &containerSkipped);
1434 if (!o)
1435 return;
1436
1437 if (isWritingModeRoot() && !isPositioned())
1438 flipForWritingMode(rect);
1439 IntPoint topLeft = rect.location();
1440 topLeft.move(x(), y());
1441
1442 EPosition position = style()->position();
1443
1444 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box
1445 // in the parent's coordinate space that encloses us.
1446 if (layer() && layer()->transform()) {
1447 fixed = position == FixedPosition;
1448 rect = layer()->transform()->mapRect(rect);
1449 topLeft = rect.location();
1450 topLeft.move(x(), y());
1451 } else if (position == FixedPosition)
1452 fixed = true;
1453
1454 if (position == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
1455 topLeft += toRenderInline(o)->relativePositionedInlineOffset(this);
1456 else if (position == RelativePosition && layer()) {
1457 // Apply the relative position offset when invalidating a rectangle. The layer
1458 // is translated, but the render box isn't, so we need to do this to get the
1459 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
1460 // flag on the RenderObject has been cleared, so use the one on the style().
1461 topLeft += layer()->relativePositionOffset();
1462 }
1463
1464 if (o->isBlockFlow() && position != AbsolutePosition && position != FixedPosition) {
1465 RenderBlock* cb = toRenderBlock(o);
1466 if (cb->hasColumns()) {
1467 IntRect repaintRect(topLeft, rect.size());
1468 cb->adjustRectForColumns(repaintRect);
1469 topLeft = repaintRect.location();
1470 rect = repaintRect;
1471 }
1472 }
1473
1474 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1475 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1476 if (o->hasOverflowClip()) {
1477 RenderBox* containerBox = toRenderBox(o);
1478
1479 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
1480 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
1481 // anyway if its size does change.
1482 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden.
1483
1484 IntRect repaintRect(topLeft, rect.size());
1485 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height());
1486 rect = intersection(repaintRect, boxRect);
1487 if (rect.isEmpty())
1488 return;
1489 } else
1490 rect.setLocation(topLeft);
1491
1492 if (containerSkipped) {
1493 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
1494 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1495 rect.move(-containerOffset);
1496 return;
1497 }
1498
1499 o->computeRectForRepaint(repaintContainer, rect, fixed);
1500 }
1501
repaintDuringLayoutIfMoved(const IntRect & rect)1502 void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect)
1503 {
1504 int newX = x();
1505 int newY = y();
1506 int newWidth = width();
1507 int newHeight = height();
1508 if (rect.x() != newX || rect.y() != newY) {
1509 // The child moved. Invalidate the object's old and new positions. We have to do this
1510 // since the object may not have gotten a layout.
1511 m_frameRect = rect;
1512 repaint();
1513 repaintOverhangingFloats(true);
1514 m_frameRect = IntRect(newX, newY, newWidth, newHeight);
1515 repaint();
1516 repaintOverhangingFloats(true);
1517 }
1518 }
1519
1520 #ifdef ANDROID_LAYOUT
setVisibleWidth(int newWidth)1521 void RenderBox::setVisibleWidth(int newWidth) {
1522 const Settings* settings = document()->settings();
1523 ASSERT(settings);
1524 if (settings->layoutAlgorithm() != Settings::kLayoutFitColumnToScreen
1525 || m_visibleWidth == newWidth)
1526 return;
1527 m_isVisibleWidthChangedBeforeLayout = true;
1528 m_visibleWidth = newWidth;
1529 }
1530
checkAndSetRelayoutChildren(bool * relayoutChildren)1531 bool RenderBox::checkAndSetRelayoutChildren(bool* relayoutChildren) {
1532 if (m_isVisibleWidthChangedBeforeLayout) {
1533 m_isVisibleWidthChangedBeforeLayout = false;
1534 *relayoutChildren = true;
1535 return true;
1536 }
1537 return false;
1538 }
1539 #endif
1540
computeLogicalWidth()1541 void RenderBox::computeLogicalWidth()
1542 {
1543 #ifdef ANDROID_LAYOUT
1544 if (view()->frameView())
1545 setVisibleWidth(view()->frameView()->textWrapWidth());
1546 #endif
1547
1548 if (isPositioned()) {
1549 // FIXME: This calculation is not patched for block-flow yet.
1550 // https://bugs.webkit.org/show_bug.cgi?id=46500
1551 computePositionedLogicalWidth();
1552 return;
1553 }
1554
1555 // If layout is limited to a subtree, the subtree root's logical width does not change.
1556 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this)
1557 return;
1558
1559 // The parent box is flexing us, so it has increased or decreased our
1560 // width. Use the width from the style context.
1561 // FIXME: Account for block-flow in flexible boxes.
1562 // https://bugs.webkit.org/show_bug.cgi?id=46418
1563 if (hasOverrideSize() && parent()->style()->boxOrient() == HORIZONTAL
1564 && parent()->isFlexibleBox() && parent()->isFlexingChildren()) {
1565 setLogicalWidth(overrideSize());
1566 return;
1567 }
1568
1569 // FIXME: Account for block-flow in flexible boxes.
1570 // https://bugs.webkit.org/show_bug.cgi?id=46418
1571 bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL);
1572 bool stretching = (parent()->style()->boxAlign() == BSTRETCH);
1573 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching);
1574
1575 Length logicalWidthLength = (treatAsReplaced) ? Length(computeReplacedLogicalWidth(), Fixed) : style()->logicalWidth();
1576
1577 RenderBlock* cb = containingBlock();
1578 int containerLogicalWidth = max(0, containingBlockLogicalWidthForContent());
1579 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode();
1580 int containerWidthInInlineDirection = containerLogicalWidth;
1581 if (hasPerpendicularContainingBlock)
1582 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight();
1583
1584 if (isInline() && !isInlineBlockOrInlineTable()) {
1585 // just calculate margins
1586 setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth));
1587 setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth));
1588 #ifdef ANDROID_LAYOUT
1589 if (treatAsReplaced) {
1590 #else
1591 if (treatAsReplaced)
1592 #endif
1593 setLogicalWidth(max(logicalWidthLength.value() + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth()));
1594
1595 #ifdef ANDROID_LAYOUT
1596 // in SSR mode with replaced box, if the box width is wider than the container width,
1597 // it will be shrinked to fit to the container.
1598 if (containerLogicalWidth && (width() + m_marginLeft + m_marginRight) > containerLogicalWidth &&
1599 document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
1600 m_marginLeft = m_marginRight = 0;
1601 setWidth(containerLogicalWidth);
1602 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth;
1603 }
1604 }
1605 #endif
1606 return;
1607 }
1608
1609 // Width calculations
1610 if (treatAsReplaced)
1611 setLogicalWidth(logicalWidthLength.value() + borderAndPaddingLogicalWidth());
1612 else {
1613 // Calculate LogicalWidth
1614 setLogicalWidth(computeLogicalWidthUsing(LogicalWidth, containerWidthInInlineDirection));
1615
1616 // Calculate MaxLogicalWidth
1617 if (!style()->logicalMaxWidth().isUndefined()) {
1618 int maxLogicalWidth = computeLogicalWidthUsing(MaxLogicalWidth, containerWidthInInlineDirection);
1619 if (logicalWidth() > maxLogicalWidth) {
1620 setLogicalWidth(maxLogicalWidth);
1621 logicalWidthLength = style()->logicalMaxWidth();
1622 }
1623 }
1624
1625 // Calculate MinLogicalWidth
1626 int minLogicalWidth = computeLogicalWidthUsing(MinLogicalWidth, containerWidthInInlineDirection);
1627 if (logicalWidth() < minLogicalWidth) {
1628 setLogicalWidth(minLogicalWidth);
1629 logicalWidthLength = style()->logicalMinWidth();
1630 }
1631 }
1632
1633 // Fieldsets are currently the only objects that stretch to their minimum width.
1634 if (stretchesToMinIntrinsicLogicalWidth()) {
1635 setLogicalWidth(max(logicalWidth(), minPreferredLogicalWidth()));
1636 logicalWidthLength = Length(logicalWidth(), Fixed);
1637 }
1638
1639 // Margin calculations.
1640 if (logicalWidthLength.isAuto() || hasPerpendicularContainingBlock || isFloating() || isInline()) {
1641 setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth));
1642 setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth));
1643 } else
1644 computeInlineDirectionMargins(cb, containerLogicalWidth, logicalWidth());
1645
1646 #ifdef ANDROID_LAYOUT
1647 // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin.
1648 // If the box width is wider than the container width, it will be shrinked to fit to the container.
1649 if (containerLogicalWidth && !treatAsReplaced &&
1650 document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
1651 setWidth(width() + m_marginLeft + m_marginRight);
1652 m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft;
1653 m_marginRight = m_marginRight > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginRight;
1654 if (width() > containerLogicalWidth) {
1655 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth-(m_marginLeft + m_marginRight);
1656 setWidth(m_minPreferredLogicalWidth);
1657 } else
1658 setWidth(width() -(m_marginLeft + m_marginRight));
1659 }
1660 #endif
1661
1662 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (logicalWidth() + marginStart() + marginEnd())
1663 && !isFloating() && !isInline() && !cb->isFlexibleBox())
1664 cb->setMarginEndForChild(this, containerLogicalWidth - logicalWidth() - cb->marginStartForChild(this));
1665 }
1666
computeLogicalWidthUsing(LogicalWidthType widthType,int availableLogicalWidth)1667 int RenderBox::computeLogicalWidthUsing(LogicalWidthType widthType, int availableLogicalWidth)
1668 {
1669 int logicalWidthResult = logicalWidth();
1670 Length logicalWidth;
1671 if (widthType == LogicalWidth)
1672 logicalWidth = style()->logicalWidth();
1673 else if (widthType == MinLogicalWidth)
1674 logicalWidth = style()->logicalMinWidth();
1675 else
1676 logicalWidth = style()->logicalMaxWidth();
1677
1678 if (logicalWidth.isIntrinsicOrAuto()) {
1679 int marginStart = style()->marginStart().calcMinValue(availableLogicalWidth);
1680 int marginEnd = style()->marginEnd().calcMinValue(availableLogicalWidth);
1681 if (availableLogicalWidth)
1682 logicalWidthResult = availableLogicalWidth - marginStart - marginEnd;
1683
1684 if (sizesToIntrinsicLogicalWidth(widthType)) {
1685 logicalWidthResult = max(logicalWidthResult, minPreferredLogicalWidth());
1686 logicalWidthResult = min(logicalWidthResult, maxPreferredLogicalWidth());
1687 }
1688 } else // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead.
1689 logicalWidthResult = computeBorderBoxLogicalWidth(logicalWidth.calcValue(availableLogicalWidth));
1690
1691 return logicalWidthResult;
1692 }
1693
sizesToIntrinsicLogicalWidth(LogicalWidthType widthType) const1694 bool RenderBox::sizesToIntrinsicLogicalWidth(LogicalWidthType widthType) const
1695 {
1696 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
1697 // but they allow text to sit on the same line as the marquee.
1698 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee()))
1699 return true;
1700
1701 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both
1702 // min-width and width. max-width is only clamped if it is also intrinsic.
1703 Length logicalWidth = (widthType == MaxLogicalWidth) ? style()->logicalMaxWidth() : style()->logicalWidth();
1704 if (logicalWidth.type() == Intrinsic)
1705 return true;
1706
1707 // Children of a horizontal marquee do not fill the container by default.
1708 // FIXME: Need to deal with MAUTO value properly. It could be vertical.
1709 // FIXME: Think about block-flow here. Need to find out how marquee direction relates to
1710 // block-flow (as well as how marquee overflow should relate to block flow).
1711 // https://bugs.webkit.org/show_bug.cgi?id=46472
1712 if (parent()->style()->overflowX() == OMARQUEE) {
1713 EMarqueeDirection dir = parent()->style()->marqueeDirection();
1714 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
1715 return true;
1716 }
1717
1718 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes
1719 // that don't stretch their kids lay out their children at their intrinsic widths.
1720 // FIXME: Think about block-flow here.
1721 // https://bugs.webkit.org/show_bug.cgi?id=46473
1722 if (parent()->isFlexibleBox()
1723 && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
1724 return true;
1725
1726 // Button, input, select, textarea, legend and datagrid treat
1727 // width value of 'auto' as 'intrinsic' unless it's in a
1728 // stretching vertical flexbox.
1729 // FIXME: Think about block-flow here.
1730 // https://bugs.webkit.org/show_bug.cgi?id=46473
1731 if (logicalWidth.type() == Auto && !(parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL && parent()->style()->boxAlign() == BSTRETCH) && node() && (node()->hasTagName(inputTag) || node()->hasTagName(selectTag) || node()->hasTagName(buttonTag) || node()->hasTagName(textareaTag) || node()->hasTagName(legendTag) || node()->hasTagName(datagridTag)))
1732 return true;
1733
1734 return false;
1735 }
1736
computeInlineDirectionMargins(RenderBlock * containingBlock,int containerWidth,int childWidth)1737 void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, int containerWidth, int childWidth)
1738 {
1739 const RenderStyle* containingBlockStyle = containingBlock->style();
1740 Length marginStartLength = style()->marginStartUsing(containingBlockStyle);
1741 Length marginEndLength = style()->marginEndUsing(containingBlockStyle);
1742
1743 // Case One: The object is being centered in the containing block's available logical width.
1744 if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth)
1745 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) {
1746 containingBlock->setMarginStartForChild(this, max(0, (containerWidth - childWidth) / 2));
1747 containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this));
1748 return;
1749 }
1750
1751 // Case Two: The object is being pushed to the start of the containing block's available logical width.
1752 if (marginEndLength.isAuto() && childWidth < containerWidth) {
1753 containingBlock->setMarginStartForChild(this, marginStartLength.calcValue(containerWidth));
1754 containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this));
1755 return;
1756 }
1757
1758 // Case Three: The object is being pushed to the end of the containing block's available logical width.
1759 bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT)
1760 || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT));
1761 if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) {
1762 containingBlock->setMarginEndForChild(this, marginEndLength.calcValue(containerWidth));
1763 containingBlock->setMarginStartForChild(this, containerWidth - childWidth - containingBlock->marginEndForChild(this));
1764 return;
1765 }
1766
1767 // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case
1768 // auto margins will just turn into 0.
1769 containingBlock->setMarginStartForChild(this, marginStartLength.calcMinValue(containerWidth));
1770 containingBlock->setMarginEndForChild(this, marginEndLength.calcMinValue(containerWidth));
1771 }
1772
computeLogicalHeight()1773 void RenderBox::computeLogicalHeight()
1774 {
1775 // Cell height is managed by the table and inline non-replaced elements do not support a height property.
1776 if (isTableCell() || (isInline() && !isReplaced()))
1777 return;
1778
1779 Length h;
1780 if (isPositioned()) {
1781 // FIXME: This calculation is not patched for block-flow yet.
1782 // https://bugs.webkit.org/show_bug.cgi?id=46500
1783 computePositionedLogicalHeight();
1784 } else {
1785 RenderBlock* cb = containingBlock();
1786 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode();
1787
1788 if (!hasPerpendicularContainingBlock)
1789 computeBlockDirectionMargins(cb);
1790
1791 // For tables, calculate margins only.
1792 if (isTable()) {
1793 if (hasPerpendicularContainingBlock)
1794 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), logicalHeight());
1795 return;
1796 }
1797
1798 // FIXME: Account for block-flow in flexible boxes.
1799 // https://bugs.webkit.org/show_bug.cgi?id=46418
1800 bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
1801 bool stretching = parent()->style()->boxAlign() == BSTRETCH;
1802 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching);
1803 bool checkMinMaxHeight = false;
1804
1805 // The parent box is flexing us, so it has increased or decreased our height. We have to
1806 // grab our cached flexible height.
1807 // FIXME: Account for block-flow in flexible boxes.
1808 // https://bugs.webkit.org/show_bug.cgi?id=46418
1809 if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL
1810 && parent()->isFlexingChildren())
1811 h = Length(overrideSize() - borderAndPaddingLogicalHeight(), Fixed);
1812 else if (treatAsReplaced)
1813 h = Length(computeReplacedLogicalHeight(), Fixed);
1814 else {
1815 h = style()->logicalHeight();
1816 checkMinMaxHeight = true;
1817 }
1818
1819 // Block children of horizontal flexible boxes fill the height of the box.
1820 // FIXME: Account for block-flow in flexible boxes.
1821 // https://bugs.webkit.org/show_bug.cgi?id=46418
1822 if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
1823 && parent()->isStretchingChildren()) {
1824 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed);
1825 checkMinMaxHeight = false;
1826 }
1827
1828 int heightResult;
1829 if (checkMinMaxHeight) {
1830 #ifdef ANDROID_LAYOUT
1831 // in SSR mode, ignore CSS height as layout is so different
1832 if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR)
1833 heightResult = -1;
1834 else
1835 #endif
1836 heightResult = computeLogicalHeightUsing(style()->logicalHeight());
1837 if (heightResult == -1)
1838 heightResult = logicalHeight();
1839 int minH = computeLogicalHeightUsing(style()->logicalMinHeight()); // Leave as -1 if unset.
1840 int maxH = style()->logicalMaxHeight().isUndefined() ? heightResult : computeLogicalHeightUsing(style()->logicalMaxHeight());
1841 if (maxH == -1)
1842 maxH = heightResult;
1843 heightResult = min(maxH, heightResult);
1844 heightResult = max(minH, heightResult);
1845 } else {
1846 // The only times we don't check min/max height are when a fixed length has
1847 // been given as an override. Just use that. The value has already been adjusted
1848 // for box-sizing.
1849 heightResult = h.value() + borderAndPaddingLogicalHeight();
1850 }
1851
1852 setLogicalHeight(heightResult);
1853
1854 if (hasPerpendicularContainingBlock)
1855 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult);
1856 }
1857
1858 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
1859 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
1860 // is specified. When we're printing, we also need this quirk if the body or root has a percentage
1861 // height since we don't set a height in RenderView when we're printing. So without this quirk, the
1862 // height has nothing to be a percentage of, and it ends up being 0. That is bad.
1863 bool paginatedContentNeedsBaseHeight = document()->printing() && h.isPercent()
1864 && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->logicalHeight().isPercent()));
1865 if (stretchesToViewport() || paginatedContentNeedsBaseHeight) {
1866 // FIXME: Finish accounting for block flow here.
1867 // https://bugs.webkit.org/show_bug.cgi?id=46603
1868 int margins = collapsedMarginBefore() + collapsedMarginAfter();
1869 int visHeight;
1870 if (document()->printing())
1871 visHeight = static_cast<int>(view()->pageLogicalHeight());
1872 else {
1873 if (isHorizontalWritingMode())
1874 visHeight = view()->viewHeight();
1875 else
1876 visHeight = view()->viewWidth();
1877 }
1878 if (isRoot())
1879 setLogicalHeight(max(logicalHeight(), visHeight - margins));
1880 else {
1881 int marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight();
1882 setLogicalHeight(max(logicalHeight(), visHeight - marginsBordersPadding));
1883 }
1884 }
1885 }
1886
computeLogicalHeightUsing(const Length & h)1887 int RenderBox::computeLogicalHeightUsing(const Length& h)
1888 {
1889 int logicalHeight = -1;
1890 if (!h.isAuto()) {
1891 if (h.isFixed())
1892 logicalHeight = h.value();
1893 else if (h.isPercent())
1894 logicalHeight = computePercentageLogicalHeight(h);
1895 if (logicalHeight != -1) {
1896 logicalHeight = computeBorderBoxLogicalHeight(logicalHeight);
1897 return logicalHeight;
1898 }
1899 }
1900 return logicalHeight;
1901 }
1902
computePercentageLogicalHeight(const Length & height)1903 int RenderBox::computePercentageLogicalHeight(const Length& height)
1904 {
1905 int result = -1;
1906
1907 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing
1908 // block that may have a specified height and then use it. In strict mode, this violates the
1909 // specification, which states that percentage heights just revert to auto if the containing
1910 // block has an auto height. We still skip anonymous containing blocks in both modes, though, and look
1911 // only at explicit containers.
1912 bool skippedAutoHeightContainingBlock = false;
1913 RenderBlock* cb = containingBlock();
1914 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->logicalHeight().isAuto()) {
1915 if (!document()->inQuirksMode() && !cb->isAnonymousBlock())
1916 break;
1917 skippedAutoHeightContainingBlock = true;
1918 cb = cb->containingBlock();
1919 cb->addPercentHeightDescendant(this);
1920 }
1921
1922 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
1923 // explicitly specified that can be used for any percentage computations.
1924 // FIXME: We can't just check top/bottom here.
1925 // https://bugs.webkit.org/show_bug.cgi?id=46500
1926 bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto()));
1927
1928 bool includeBorderPadding = isTable();
1929
1930 // Table cells violate what the CSS spec says to do with heights. Basically we
1931 // don't care if the cell specified a height or not. We just always make ourselves
1932 // be a percentage of the cell's current content height.
1933 if (cb->isTableCell()) {
1934 if (!skippedAutoHeightContainingBlock) {
1935 result = cb->overrideSize();
1936 if (result == -1) {
1937 // Normally we would let the cell size intrinsically, but scrolling overflow has to be
1938 // treated differently, since WinIE lets scrolled overflow regions shrink as needed.
1939 // While we can't get all cases right, we can at least detect when the cell has a specified
1940 // height or when the table has a specified height. In these cases we want to initially have
1941 // no size and allow the flexing of the table or the cell to its specified height to cause us
1942 // to grow to fill the space. This could end up being wrong in some cases, but it is
1943 // preferable to the alternative (sizing intrinsically and making the row end up too big).
1944 RenderTableCell* cell = toRenderTableCell(cb);
1945 if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto()))
1946 return 0;
1947 return -1;
1948 }
1949 includeBorderPadding = true;
1950 }
1951 }
1952 // Otherwise we only use our percentage height if our containing block had a specified
1953 // height.
1954 else if (cb->style()->logicalHeight().isFixed())
1955 result = cb->computeContentBoxLogicalHeight(cb->style()->logicalHeight().value());
1956 else if (cb->style()->logicalHeight().isPercent() && !isPositionedWithSpecifiedHeight) {
1957 // We need to recur and compute the percentage height for our containing block.
1958 result = cb->computePercentageLogicalHeight(cb->style()->logicalHeight());
1959 if (result != -1)
1960 result = cb->computeContentBoxLogicalHeight(result);
1961 } else if (cb->isRenderView() || (cb->isBody() && document()->inQuirksMode()) || isPositionedWithSpecifiedHeight) {
1962 // Don't allow this to affect the block' height() member variable, since this
1963 // can get called while the block is still laying out its kids.
1964 int oldHeight = cb->logicalHeight();
1965 cb->computeLogicalHeight();
1966 result = cb->contentLogicalHeight();
1967 cb->setLogicalHeight(oldHeight);
1968 } else if (cb->isRoot() && isPositioned())
1969 // Match the positioned objects behavior, which is that positioned objects will fill their viewport
1970 // always. Note we could only hit this case by recurring into computePercentageLogicalHeight on a positioned containing block.
1971 result = cb->computeContentBoxLogicalHeight(cb->availableLogicalHeight());
1972
1973 if (result != -1) {
1974 result = height.calcValue(result);
1975 if (includeBorderPadding) {
1976 // It is necessary to use the border-box to match WinIE's broken
1977 // box model. This is essential for sizing inside
1978 // table cells using percentage heights.
1979 result -= borderAndPaddingLogicalHeight();
1980 result = max(0, result);
1981 }
1982 }
1983 return result;
1984 }
1985
computeReplacedLogicalWidth(bool includeMaxWidth) const1986 int RenderBox::computeReplacedLogicalWidth(bool includeMaxWidth) const
1987 {
1988 int logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth());
1989 int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
1990 int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
1991
1992 return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
1993 }
1994
computeReplacedLogicalWidthUsing(Length logicalWidth) const1995 int RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const
1996 {
1997 switch (logicalWidth.type()) {
1998 case Fixed:
1999 return computeContentBoxLogicalWidth(logicalWidth.value());
2000 case Percent: {
2001 // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the
2002 // containing block's block-flow.
2003 // https://bugs.webkit.org/show_bug.cgi?id=46496
2004 const int cw = isPositioned() ? containingBlockLogicalWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent();
2005 if (cw > 0)
2006 return computeContentBoxLogicalWidth(logicalWidth.calcMinValue(cw));
2007 }
2008 // fall through
2009 default:
2010 return intrinsicLogicalWidth();
2011 }
2012 }
2013
computeReplacedLogicalHeight() const2014 int RenderBox::computeReplacedLogicalHeight() const
2015 {
2016 int logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight());
2017 int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
2018 int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
2019
2020 return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
2021 }
2022
computeReplacedLogicalHeightUsing(Length logicalHeight) const2023 int RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const
2024 {
2025 switch (logicalHeight.type()) {
2026 case Fixed:
2027 return computeContentBoxLogicalHeight(logicalHeight.value());
2028 case Percent:
2029 {
2030 RenderObject* cb = isPositioned() ? container() : containingBlock();
2031 while (cb->isAnonymous()) {
2032 cb = cb->containingBlock();
2033 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this));
2034 }
2035
2036 // FIXME: This calculation is not patched for block-flow yet.
2037 // https://bugs.webkit.org/show_bug.cgi?id=46500
2038 if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) {
2039 ASSERT(cb->isRenderBlock());
2040 RenderBlock* block = toRenderBlock(cb);
2041 int oldHeight = block->height();
2042 block->computeLogicalHeight();
2043 int newHeight = block->computeContentBoxLogicalHeight(block->contentHeight());
2044 block->setHeight(oldHeight);
2045 return computeContentBoxLogicalHeight(logicalHeight.calcValue(newHeight));
2046 }
2047
2048 // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the
2049 // containing block's block-flow.
2050 // https://bugs.webkit.org/show_bug.cgi?id=46496
2051 int availableHeight = isPositioned() ? containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableLogicalHeight();
2052
2053 // It is necessary to use the border-box to match WinIE's broken
2054 // box model. This is essential for sizing inside
2055 // table cells using percentage heights.
2056 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right.
2057 // https://bugs.webkit.org/show_bug.cgi?id=46997
2058 if (cb->isTableCell() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) {
2059 // Don't let table cells squeeze percent-height replaced elements
2060 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
2061 availableHeight = max(availableHeight, intrinsicLogicalHeight());
2062 return logicalHeight.calcValue(availableHeight - borderAndPaddingLogicalHeight());
2063 }
2064
2065 return computeContentBoxLogicalHeight(logicalHeight.calcValue(availableHeight));
2066 }
2067 default:
2068 return intrinsicLogicalHeight();
2069 }
2070 }
2071
availableLogicalHeight() const2072 int RenderBox::availableLogicalHeight() const
2073 {
2074 return availableLogicalHeightUsing(style()->logicalHeight());
2075 }
2076
availableLogicalHeightUsing(const Length & h) const2077 int RenderBox::availableLogicalHeightUsing(const Length& h) const
2078 {
2079 if (h.isFixed())
2080 return computeContentBoxLogicalHeight(h.value());
2081
2082 if (isRenderView())
2083 return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth();
2084
2085 // We need to stop here, since we don't want to increase the height of the table
2086 // artificially. We're going to rely on this cell getting expanded to some new
2087 // height, and then when we lay out again we'll use the calculation below.
2088 if (isTableCell() && (h.isAuto() || h.isPercent()))
2089 return overrideSize() - borderAndPaddingLogicalWidth();
2090
2091 if (h.isPercent())
2092 return computeContentBoxLogicalHeight(h.calcValue(containingBlock()->availableLogicalHeight()));
2093
2094 // FIXME: We can't just check top/bottom here.
2095 // https://bugs.webkit.org/show_bug.cgi?id=46500
2096 if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) {
2097 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this));
2098 int oldHeight = block->logicalHeight();
2099 block->computeLogicalHeight();
2100 int newHeight = block->computeContentBoxLogicalHeight(block->contentLogicalHeight());
2101 block->setLogicalHeight(oldHeight);
2102 return computeContentBoxLogicalHeight(newHeight);
2103 }
2104
2105 return containingBlock()->availableLogicalHeight();
2106 }
2107
computeBlockDirectionMargins(RenderBlock * containingBlock)2108 void RenderBox::computeBlockDirectionMargins(RenderBlock* containingBlock)
2109 {
2110 if (isTableCell()) {
2111 // FIXME: Not right if we allow cells to have different directionality than the table. If we do allow this, though,
2112 // we may just do it with an extra anonymous block inside the cell.
2113 setMarginBefore(0);
2114 setMarginAfter(0);
2115 return;
2116 }
2117
2118 // Margins are calculated with respect to the logical width of
2119 // the containing block (8.3)
2120 int cw = containingBlockLogicalWidthForContent();
2121
2122 RenderStyle* containingBlockStyle = containingBlock->style();
2123 containingBlock->setMarginBeforeForChild(this, style()->marginBeforeUsing(containingBlockStyle).calcMinValue(cw));
2124 containingBlock->setMarginAfterForChild(this, style()->marginAfterUsing(containingBlockStyle).calcMinValue(cw));
2125 }
2126
containingBlockLogicalWidthForPositioned(const RenderBoxModelObject * containingBlock,bool checkForPerpendicularWritingMode) const2127 int RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const
2128 {
2129 #if PLATFORM(ANDROID)
2130 // Fixed element's position should be decided by the visible screen size.
2131 // That is in the doc coordindate.
2132 if (style()->position() == FixedPosition && containingBlock->isRenderView()) {
2133 const RenderView* view = toRenderView(containingBlock);
2134 return PlatformBridge::screenWidthInDocCoord(view->frameView());
2135 }
2136 #endif
2137
2138 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode())
2139 return containingBlockLogicalHeightForPositioned(containingBlock, false);
2140
2141 if (containingBlock->isBox())
2142 return toRenderBox(containingBlock)->clientLogicalWidth();
2143
2144 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned());
2145
2146 const RenderInline* flow = toRenderInline(containingBlock);
2147 InlineFlowBox* first = flow->firstLineBox();
2148 InlineFlowBox* last = flow->lastLineBox();
2149
2150 // If the containing block is empty, return a width of 0.
2151 if (!first || !last)
2152 return 0;
2153
2154 int fromLeft;
2155 int fromRight;
2156 if (containingBlock->style()->isLeftToRightDirection()) {
2157 fromLeft = first->logicalLeft() + first->borderLogicalLeft();
2158 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight();
2159 } else {
2160 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight();
2161 fromLeft = last->logicalLeft() + last->borderLogicalLeft();
2162 }
2163
2164 return max(0, (fromRight - fromLeft));
2165 }
2166
containingBlockLogicalHeightForPositioned(const RenderBoxModelObject * containingBlock,bool checkForPerpendicularWritingMode) const2167 int RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const
2168 {
2169 #if PLATFORM(ANDROID)
2170 // Fixed element's position should be decided by the visible screen size.
2171 // That is in the doc coordindate.
2172 if (style()->position() == FixedPosition && containingBlock->isRenderView()) {
2173 const RenderView* view = toRenderView(containingBlock);
2174 return PlatformBridge::screenHeightInDocCoord(view->frameView());
2175 }
2176 #endif
2177
2178 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode())
2179 return containingBlockLogicalWidthForPositioned(containingBlock, false);
2180
2181 if (containingBlock->isBox())
2182 return toRenderBox(containingBlock)->clientLogicalHeight();
2183
2184 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned());
2185
2186 const RenderInline* flow = toRenderInline(containingBlock);
2187 InlineFlowBox* first = flow->firstLineBox();
2188 InlineFlowBox* last = flow->lastLineBox();
2189
2190 // If the containing block is empty, return a height of 0.
2191 if (!first || !last)
2192 return 0;
2193
2194 int heightResult;
2195 IntRect boundingBox = flow->linesBoundingBox();
2196 if (containingBlock->isHorizontalWritingMode())
2197 heightResult = boundingBox.height();
2198 else
2199 heightResult = boundingBox.width();
2200 heightResult -= (containingBlock->borderBefore() + containingBlock->borderAfter());
2201 return heightResult;
2202 }
2203
computeInlineStaticDistance(Length & logicalLeft,Length & logicalRight,const RenderBox * child,const RenderBoxModelObject * containerBlock,int containerLogicalWidth,TextDirection containerDirection)2204 static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const RenderBox* child, const RenderBoxModelObject* containerBlock, int containerLogicalWidth,
2205 TextDirection containerDirection)
2206 {
2207 if (!logicalLeft.isAuto() || !logicalRight.isAuto())
2208 return;
2209
2210 // FIXME: The static distance computation has not been patched for mixed writing modes yet.
2211 if (containerDirection == LTR) {
2212 int staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft();
2213 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) {
2214 if (curr->isBox())
2215 staticPosition += toRenderBox(curr)->logicalLeft();
2216 }
2217 logicalLeft.setValue(Fixed, staticPosition);
2218 } else {
2219 RenderBox* enclosingBox = child->parent()->enclosingBox();
2220 int staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalRight();
2221 staticPosition -= enclosingBox->logicalWidth();
2222 for (RenderObject* curr = enclosingBox; curr && curr != containerBlock; curr = curr->container()) {
2223 if (curr->isBox())
2224 staticPosition -= toRenderBox(curr)->logicalLeft();
2225 }
2226 logicalRight.setValue(Fixed, staticPosition);
2227 }
2228 }
2229
computePositionedLogicalWidth()2230 void RenderBox::computePositionedLogicalWidth()
2231 {
2232 if (isReplaced()) {
2233 computePositionedLogicalWidthReplaced();
2234 return;
2235 }
2236
2237 // QUESTIONS
2238 // FIXME 1: Which RenderObject's 'direction' property should used: the
2239 // containing block (cb) as the spec seems to imply, the parent (parent()) as
2240 // was previously done in calculating the static distances, or ourself, which
2241 // was also previously done for deciding what to override when you had
2242 // over-constrained margins? Also note that the container block is used
2243 // in similar situations in other parts of the RenderBox class (see computeLogicalWidth()
2244 // and computeMarginsInContainingBlockInlineDirection()). For now we are using the parent for quirks
2245 // mode and the containing block for strict mode.
2246
2247 // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having
2248 // the type 'static' in determining whether to calculate the static distance?
2249 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
2250
2251 // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater
2252 // than or less than the computed width(). Be careful of box-sizing and
2253 // percentage issues.
2254
2255 // The following is based off of the W3C Working Draft from April 11, 2006 of
2256 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
2257 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
2258 // (block-style-comments in this function and in computePositionedLogicalWidthUsing()
2259 // correspond to text from the spec)
2260
2261
2262 // We don't use containingBlock(), since we may be positioned by an enclosing
2263 // relative positioned inline.
2264 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2265
2266 const int containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock);
2267
2268 // To match WinIE, in quirks mode use the parent's 'direction' property
2269 // instead of the the container block's.
2270 TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction();
2271
2272 bool isHorizontal = isHorizontalWritingMode();
2273 const int bordersPlusPadding = borderAndPaddingLogicalWidth();
2274 const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop();
2275 const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom();
2276 int& marginLogicalLeftAlias = isHorizontal ? m_marginLeft : m_marginTop;
2277 int& marginLogicalRightAlias = isHorizontal ? m_marginRight : m_marginBottom;
2278
2279 Length logicalLeft = style()->logicalLeft();
2280 Length logicalRight = style()->logicalRight();
2281
2282 /*---------------------------------------------------------------------------*\
2283 * For the purposes of this section and the next, the term "static position"
2284 * (of an element) refers, roughly, to the position an element would have had
2285 * in the normal flow. More precisely:
2286 *
2287 * * The static position for 'left' is the distance from the left edge of the
2288 * containing block to the left margin edge of a hypothetical box that would
2289 * have been the first box of the element if its 'position' property had
2290 * been 'static' and 'float' had been 'none'. The value is negative if the
2291 * hypothetical box is to the left of the containing block.
2292 * * The static position for 'right' is the distance from the right edge of the
2293 * containing block to the right margin edge of the same hypothetical box as
2294 * above. The value is positive if the hypothetical box is to the left of the
2295 * containing block's edge.
2296 *
2297 * But rather than actually calculating the dimensions of that hypothetical box,
2298 * user agents are free to make a guess at its probable position.
2299 *
2300 * For the purposes of calculating the static position, the containing block of
2301 * fixed positioned elements is the initial containing block instead of the
2302 * viewport, and all scrollable boxes should be assumed to be scrolled to their
2303 * origin.
2304 \*---------------------------------------------------------------------------*/
2305
2306 // see FIXME 2
2307 // Calculate the static distance if needed.
2308 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, containerDirection);
2309
2310 // Calculate constraint equation values for 'width' case.
2311 int logicalWidthResult;
2312 int logicalLeftResult;
2313 computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection,
2314 containerLogicalWidth, bordersPlusPadding,
2315 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight,
2316 logicalWidthResult, marginLogicalLeftAlias, marginLogicalRightAlias, logicalLeftResult);
2317 setLogicalWidth(logicalWidthResult);
2318 setLogicalLeft(logicalLeftResult);
2319
2320 // Calculate constraint equation values for 'max-width' case.
2321 if (!style()->logicalMaxWidth().isUndefined()) {
2322 int maxLogicalWidth;
2323 int maxMarginLogicalLeft;
2324 int maxMarginLogicalRight;
2325 int maxLogicalLeftPos;
2326
2327 computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection,
2328 containerLogicalWidth, bordersPlusPadding,
2329 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight,
2330 maxLogicalWidth, maxMarginLogicalLeft, maxMarginLogicalRight, maxLogicalLeftPos);
2331
2332 if (logicalWidth() > maxLogicalWidth) {
2333 setLogicalWidth(maxLogicalWidth);
2334 marginLogicalLeftAlias = maxMarginLogicalLeft;
2335 marginLogicalRightAlias = maxMarginLogicalRight;
2336 setLogicalLeft(maxLogicalLeftPos);
2337 }
2338 }
2339
2340 // Calculate constraint equation values for 'min-width' case.
2341 if (!style()->logicalMinWidth().isZero()) {
2342 int minLogicalWidth;
2343 int minMarginLogicalLeft;
2344 int minMarginLogicalRight;
2345 int minLogicalLeftPos;
2346
2347 computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection,
2348 containerLogicalWidth, bordersPlusPadding,
2349 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight,
2350 minLogicalWidth, minMarginLogicalLeft, minMarginLogicalRight, minLogicalLeftPos);
2351
2352 if (logicalWidth() < minLogicalWidth) {
2353 setLogicalWidth(minLogicalWidth);
2354 marginLogicalLeftAlias = minMarginLogicalLeft;
2355 marginLogicalRightAlias = minMarginLogicalRight;
2356 setLogicalLeft(minLogicalLeftPos);
2357 }
2358 }
2359
2360 if (stretchesToMinIntrinsicLogicalWidth() && logicalWidth() < minPreferredLogicalWidth() - bordersPlusPadding) {
2361 computePositionedLogicalWidthUsing(Length(minPreferredLogicalWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection,
2362 containerLogicalWidth, bordersPlusPadding,
2363 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight,
2364 logicalWidthResult, marginLogicalLeftAlias, marginLogicalRightAlias, logicalLeftResult);
2365 setLogicalWidth(logicalWidthResult);
2366 setLogicalLeft(logicalLeftResult);
2367 }
2368
2369 // Put logicalWidth() into correct form.
2370 setLogicalWidth(logicalWidth() + bordersPlusPadding);
2371 }
2372
computeLogicalLeftPositionedOffset(int & logicalLeftPos,const RenderBox * child,int logicalWidthValue,const RenderBoxModelObject * containerBlock,int containerLogicalWidth)2373 static void computeLogicalLeftPositionedOffset(int& logicalLeftPos, const RenderBox* child, int logicalWidthValue, const RenderBoxModelObject* containerBlock, int containerLogicalWidth)
2374 {
2375 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
2376 // along this axis, then we need to flip the coordinate. This can only happen if the containing block is both a flipped mode and perpendicular to us.
2377 if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) {
2378 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos;
2379 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom());
2380 } else
2381 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop());
2382 }
2383
computePositionedLogicalWidthUsing(Length logicalWidth,const RenderBoxModelObject * containerBlock,TextDirection containerDirection,int containerLogicalWidth,int bordersPlusPadding,Length logicalLeft,Length logicalRight,Length marginLogicalLeft,Length marginLogicalRight,int & logicalWidthValue,int & marginLogicalLeftValue,int & marginLogicalRightValue,int & logicalLeftPos)2384 void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection,
2385 int containerLogicalWidth, int bordersPlusPadding,
2386 Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight,
2387 int& logicalWidthValue, int& marginLogicalLeftValue, int& marginLogicalRightValue, int& logicalLeftPos)
2388 {
2389 // 'left' and 'right' cannot both be 'auto' because one would of been
2390 // converted to the static position already
2391 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
2392
2393 int logicalLeftValue = 0;
2394
2395 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto();
2396 bool logicalLeftIsAuto = logicalLeft.isAuto();
2397 bool logicalRightIsAuto = logicalRight.isAuto();
2398
2399 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
2400 /*-----------------------------------------------------------------------*\
2401 * If none of the three is 'auto': If both 'margin-left' and 'margin-
2402 * right' are 'auto', solve the equation under the extra constraint that
2403 * the two margins get equal values, unless this would make them negative,
2404 * in which case when direction of the containing block is 'ltr' ('rtl'),
2405 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
2406 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
2407 * solve the equation for that value. If the values are over-constrained,
2408 * ignore the value for 'left' (in case the 'direction' property of the
2409 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
2410 * and solve for that value.
2411 \*-----------------------------------------------------------------------*/
2412 // NOTE: It is not necessary to solve for 'right' in the over constrained
2413 // case because the value is not used for any further calculations.
2414
2415 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2416 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth));
2417
2418 const int availableSpace = containerLogicalWidth - (logicalLeftValue + logicalWidthValue + logicalRight.calcValue(containerLogicalWidth) + bordersPlusPadding);
2419
2420 // Margins are now the only unknown
2421 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
2422 // Both margins auto, solve for equality
2423 if (availableSpace >= 0) {
2424 marginLogicalLeftValue = availableSpace / 2; // split the difference
2425 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences
2426 } else {
2427 // see FIXME 1
2428 if (containerDirection == LTR) {
2429 marginLogicalLeftValue = 0;
2430 marginLogicalRightValue = availableSpace; // will be negative
2431 } else {
2432 marginLogicalLeftValue = availableSpace; // will be negative
2433 marginLogicalRightValue = 0;
2434 }
2435 }
2436 } else if (marginLogicalLeft.isAuto()) {
2437 // Solve for left margin
2438 marginLogicalRightValue = marginLogicalRight.calcValue(containerLogicalWidth);
2439 marginLogicalLeftValue = availableSpace - marginLogicalRightValue;
2440 } else if (marginLogicalRight.isAuto()) {
2441 // Solve for right margin
2442 marginLogicalLeftValue = marginLogicalLeft.calcValue(containerLogicalWidth);
2443 marginLogicalRightValue = availableSpace - marginLogicalLeftValue;
2444 } else {
2445 // Over-constrained, solve for left if direction is RTL
2446 marginLogicalLeftValue = marginLogicalLeft.calcValue(containerLogicalWidth);
2447 marginLogicalRightValue = marginLogicalRight.calcValue(containerLogicalWidth);
2448
2449 // see FIXME 1 -- used to be "this->style()->direction()"
2450 if (containerDirection == RTL)
2451 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue;
2452 }
2453 } else {
2454 /*--------------------------------------------------------------------*\
2455 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
2456 * to 0, and pick the one of the following six rules that applies.
2457 *
2458 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
2459 * width is shrink-to-fit. Then solve for 'left'
2460 *
2461 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
2462 * ------------------------------------------------------------------
2463 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
2464 * the 'direction' property of the containing block is 'ltr' set
2465 * 'left' to the static position, otherwise set 'right' to the
2466 * static position. Then solve for 'left' (if 'direction is 'rtl')
2467 * or 'right' (if 'direction' is 'ltr').
2468 * ------------------------------------------------------------------
2469 *
2470 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
2471 * width is shrink-to-fit . Then solve for 'right'
2472 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
2473 * for 'left'
2474 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
2475 * for 'width'
2476 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
2477 * for 'right'
2478 *
2479 * Calculation of the shrink-to-fit width is similar to calculating the
2480 * width of a table cell using the automatic table layout algorithm.
2481 * Roughly: calculate the preferred width by formatting the content
2482 * without breaking lines other than where explicit line breaks occur,
2483 * and also calculate the preferred minimum width, e.g., by trying all
2484 * possible line breaks. CSS 2.1 does not define the exact algorithm.
2485 * Thirdly, calculate the available width: this is found by solving
2486 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
2487 * to 0.
2488 *
2489 * Then the shrink-to-fit width is:
2490 * min(max(preferred minimum width, available width), preferred width).
2491 \*--------------------------------------------------------------------*/
2492 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
2493 // because the value is not used for any further calculations.
2494
2495 // Calculate margins, 'auto' margins are ignored.
2496 marginLogicalLeftValue = marginLogicalLeft.calcMinValue(containerLogicalWidth);
2497 marginLogicalRightValue = marginLogicalRight.calcMinValue(containerLogicalWidth);
2498
2499 const int availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding);
2500
2501 // FIXME: Is there a faster way to find the correct case?
2502 // Use rule/case that applies.
2503 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
2504 // RULE 1: (use shrink-to-fit for width, and solve of left)
2505 int logicalRightValue = logicalRight.calcValue(containerLogicalWidth);
2506
2507 // FIXME: would it be better to have shrink-to-fit in one step?
2508 int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
2509 int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
2510 int availableWidth = availableSpace - logicalRightValue;
2511 logicalWidthValue = min(max(preferredMinWidth, availableWidth), preferredWidth);
2512 logicalLeftValue = availableSpace - (logicalWidthValue + logicalRightValue);
2513 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) {
2514 // RULE 3: (use shrink-to-fit for width, and no need solve of right)
2515 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2516
2517 // FIXME: would it be better to have shrink-to-fit in one step?
2518 int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
2519 int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
2520 int availableWidth = availableSpace - logicalLeftValue;
2521 logicalWidthValue = min(max(preferredMinWidth, availableWidth), preferredWidth);
2522 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
2523 // RULE 4: (solve for left)
2524 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth));
2525 logicalLeftValue = availableSpace - (logicalWidthValue + logicalRight.calcValue(containerLogicalWidth));
2526 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
2527 // RULE 5: (solve for width)
2528 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2529 logicalWidthValue = availableSpace - (logicalLeftValue + logicalRight.calcValue(containerLogicalWidth));
2530 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) {
2531 // RULE 6: (no need solve for right)
2532 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2533 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth));
2534 }
2535 }
2536
2537 // Use computed values to calculate the horizontal position.
2538
2539 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
2540 // positioned, inline because right now, it is using the logical left position
2541 // of the first line box when really it should use the last line box. When
2542 // this is fixed elsewhere, this block should be removed.
2543 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) {
2544 const RenderInline* flow = toRenderInline(containerBlock);
2545 InlineFlowBox* firstLine = flow->firstLineBox();
2546 InlineFlowBox* lastLine = flow->lastLineBox();
2547 if (firstLine && lastLine && firstLine != lastLine) {
2548 logicalLeftPos = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
2549 return;
2550 }
2551 }
2552
2553 logicalLeftPos = logicalLeftValue + marginLogicalLeftValue;
2554 computeLogicalLeftPositionedOffset(logicalLeftPos, this, logicalWidthValue, containerBlock, containerLogicalWidth);
2555 }
2556
computeBlockStaticDistance(Length & logicalTop,Length & logicalBottom,const RenderBox * child,const RenderBoxModelObject * containerBlock)2557 static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const RenderBox* child, const RenderBoxModelObject* containerBlock)
2558 {
2559 if (!logicalTop.isAuto() || !logicalBottom.isAuto())
2560 return;
2561
2562 // FIXME: The static distance computation has not been patched for mixed writing modes.
2563 int staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore();
2564 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) {
2565 if (curr->isBox() && !curr->isTableRow())
2566 staticLogicalTop += toRenderBox(curr)->logicalTop();
2567 }
2568 logicalTop.setValue(Fixed, staticLogicalTop);
2569 }
2570
computePositionedLogicalHeight()2571 void RenderBox::computePositionedLogicalHeight()
2572 {
2573 if (isReplaced()) {
2574 computePositionedLogicalHeightReplaced();
2575 return;
2576 }
2577
2578 // The following is based off of the W3C Working Draft from April 11, 2006 of
2579 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
2580 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
2581 // (block-style-comments in this function and in computePositionedLogicalHeightUsing()
2582 // correspond to text from the spec)
2583
2584
2585 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2586 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2587
2588 const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
2589
2590 bool isHorizontal = isHorizontalWritingMode();
2591 bool isFlipped = style()->isFlippedBlocksWritingMode();
2592 const int bordersPlusPadding = borderAndPaddingLogicalHeight();
2593 const Length marginBefore = style()->marginBefore();
2594 const Length marginAfter = style()->marginAfter();
2595 int& marginBeforeAlias = isHorizontal ? (isFlipped ? m_marginBottom : m_marginTop) : (isFlipped ? m_marginRight: m_marginLeft);
2596 int& marginAfterAlias = isHorizontal ? (isFlipped ? m_marginTop : m_marginBottom) : (isFlipped ? m_marginLeft: m_marginRight);
2597
2598 Length logicalTop = style()->logicalTop();
2599 Length logicalBottom = style()->logicalBottom();
2600
2601 /*---------------------------------------------------------------------------*\
2602 * For the purposes of this section and the next, the term "static position"
2603 * (of an element) refers, roughly, to the position an element would have had
2604 * in the normal flow. More precisely, the static position for 'top' is the
2605 * distance from the top edge of the containing block to the top margin edge
2606 * of a hypothetical box that would have been the first box of the element if
2607 * its 'position' property had been 'static' and 'float' had been 'none'. The
2608 * value is negative if the hypothetical box is above the containing block.
2609 *
2610 * But rather than actually calculating the dimensions of that hypothetical
2611 * box, user agents are free to make a guess at its probable position.
2612 *
2613 * For the purposes of calculating the static position, the containing block
2614 * of fixed positioned elements is the initial containing block instead of
2615 * the viewport.
2616 \*---------------------------------------------------------------------------*/
2617
2618 // see FIXME 2
2619 // Calculate the static distance if needed.
2620 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock);
2621
2622 int logicalHeightResult; // Needed to compute overflow.
2623 int logicalTopPos;
2624
2625 // Calculate constraint equation values for 'height' case.
2626 computePositionedLogicalHeightUsing(style()->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding,
2627 logicalTop, logicalBottom, marginBefore, marginAfter,
2628 logicalHeightResult, marginBeforeAlias, marginAfterAlias, logicalTopPos);
2629 setLogicalTop(logicalTopPos);
2630
2631 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
2632 // see FIXME 3
2633
2634 // Calculate constraint equation values for 'max-height' case.
2635 if (!style()->logicalMaxHeight().isUndefined()) {
2636 int maxLogicalHeight;
2637 int maxMarginBefore;
2638 int maxMarginAfter;
2639 int maxLogicalTopPos;
2640
2641 computePositionedLogicalHeightUsing(style()->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding,
2642 logicalTop, logicalBottom, marginBefore, marginAfter,
2643 maxLogicalHeight, maxMarginBefore, maxMarginAfter, maxLogicalTopPos);
2644
2645 if (logicalHeightResult > maxLogicalHeight) {
2646 logicalHeightResult = maxLogicalHeight;
2647 marginBeforeAlias = maxMarginBefore;
2648 marginAfterAlias = maxMarginAfter;
2649 setLogicalTop(maxLogicalTopPos);
2650 }
2651 }
2652
2653 // Calculate constraint equation values for 'min-height' case.
2654 if (!style()->logicalMinHeight().isZero()) {
2655 int minLogicalHeight;
2656 int minMarginBefore;
2657 int minMarginAfter;
2658 int minLogicalTopPos;
2659
2660 computePositionedLogicalHeightUsing(style()->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding,
2661 logicalTop, logicalBottom, marginBefore, marginAfter,
2662 minLogicalHeight, minMarginBefore, minMarginAfter, minLogicalTopPos);
2663
2664 if (logicalHeightResult < minLogicalHeight) {
2665 logicalHeightResult = minLogicalHeight;
2666 marginBeforeAlias = minMarginBefore;
2667 marginAfterAlias = minMarginAfter;
2668 setLogicalTop(minLogicalTopPos);
2669 }
2670 }
2671
2672 // Set final height value.
2673 setLogicalHeight(logicalHeightResult + bordersPlusPadding);
2674 }
2675
computeLogicalTopPositionedOffset(int & logicalTopPos,const RenderBox * child,int logicalHeightValue,const RenderBoxModelObject * containerBlock,int containerLogicalHeight)2676 static void computeLogicalTopPositionedOffset(int& logicalTopPos, const RenderBox* child, int logicalHeightValue, const RenderBoxModelObject* containerBlock, int containerLogicalHeight)
2677 {
2678 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
2679 // along this axis, then we need to flip the coordinate. This can only happen if the containing block is both a flipped mode and perpendicular to us.
2680 if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode())
2681 || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()))
2682 logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos;
2683
2684 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt.
2685 if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) {
2686 if (child->isHorizontalWritingMode())
2687 logicalTopPos += containerBlock->borderBottom();
2688 else
2689 logicalTopPos += containerBlock->borderRight();
2690 } else {
2691 if (child->isHorizontalWritingMode())
2692 logicalTopPos += containerBlock->borderTop();
2693 else
2694 logicalTopPos += containerBlock->borderLeft();
2695 }
2696 }
2697
computePositionedLogicalHeightUsing(Length logicalHeightLength,const RenderBoxModelObject * containerBlock,int containerLogicalHeight,int bordersPlusPadding,Length logicalTop,Length logicalBottom,Length marginBefore,Length marginAfter,int & logicalHeightValue,int & marginBeforeValue,int & marginAfterValue,int & logicalTopPos)2698 void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock,
2699 int containerLogicalHeight, int bordersPlusPadding,
2700 Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter,
2701 int& logicalHeightValue, int& marginBeforeValue, int& marginAfterValue, int& logicalTopPos)
2702 {
2703 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
2704 // converted to the static position in computePositionedLogicalHeight()
2705 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto()));
2706
2707 int contentLogicalHeight = logicalHeight() - bordersPlusPadding;
2708
2709 int logicalTopValue = 0;
2710
2711 bool logicalHeightIsAuto = logicalHeightLength.isAuto();
2712 bool logicalTopIsAuto = logicalTop.isAuto();
2713 bool logicalBottomIsAuto = logicalBottom.isAuto();
2714
2715 // Height is never unsolved for tables.
2716 if (isTable()) {
2717 logicalHeightLength.setValue(Fixed, contentLogicalHeight);
2718 logicalHeightIsAuto = false;
2719 }
2720
2721 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
2722 /*-----------------------------------------------------------------------*\
2723 * If none of the three are 'auto': If both 'margin-top' and 'margin-
2724 * bottom' are 'auto', solve the equation under the extra constraint that
2725 * the two margins get equal values. If one of 'margin-top' or 'margin-
2726 * bottom' is 'auto', solve the equation for that value. If the values
2727 * are over-constrained, ignore the value for 'bottom' and solve for that
2728 * value.
2729 \*-----------------------------------------------------------------------*/
2730 // NOTE: It is not necessary to solve for 'bottom' in the over constrained
2731 // case because the value is not used for any further calculations.
2732
2733 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight));
2734 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
2735
2736 const int availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight) + bordersPlusPadding);
2737
2738 // Margins are now the only unknown
2739 if (marginBefore.isAuto() && marginAfter.isAuto()) {
2740 // Both margins auto, solve for equality
2741 // NOTE: This may result in negative values.
2742 marginBeforeValue = availableSpace / 2; // split the difference
2743 marginAfterValue = availableSpace - marginBeforeValue; // account for odd valued differences
2744 } else if (marginBefore.isAuto()) {
2745 // Solve for top margin
2746 marginAfterValue = marginAfter.calcValue(containerLogicalHeight);
2747 marginBeforeValue = availableSpace - marginAfterValue;
2748 } else if (marginAfter.isAuto()) {
2749 // Solve for bottom margin
2750 marginBeforeValue = marginBefore.calcValue(containerLogicalHeight);
2751 marginAfterValue = availableSpace - marginBeforeValue;
2752 } else {
2753 // Over-constrained, (no need solve for bottom)
2754 marginBeforeValue = marginBefore.calcValue(containerLogicalHeight);
2755 marginAfterValue = marginAfter.calcValue(containerLogicalHeight);
2756 }
2757 } else {
2758 /*--------------------------------------------------------------------*\
2759 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
2760 * to 0, and pick the one of the following six rules that applies.
2761 *
2762 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
2763 * the height is based on the content, and solve for 'top'.
2764 *
2765 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
2766 * ------------------------------------------------------------------
2767 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
2768 * set 'top' to the static position, and solve for 'bottom'.
2769 * ------------------------------------------------------------------
2770 *
2771 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
2772 * the height is based on the content, and solve for 'bottom'.
2773 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
2774 * solve for 'top'.
2775 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
2776 * solve for 'height'.
2777 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
2778 * solve for 'bottom'.
2779 \*--------------------------------------------------------------------*/
2780 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
2781 // because the value is not used for any further calculations.
2782
2783 // Calculate margins, 'auto' margins are ignored.
2784 marginBeforeValue = marginBefore.calcMinValue(containerLogicalHeight);
2785 marginAfterValue = marginAfter.calcMinValue(containerLogicalHeight);
2786
2787 const int availableSpace = containerLogicalHeight - (marginBeforeValue + marginAfterValue + bordersPlusPadding);
2788
2789 // Use rule/case that applies.
2790 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
2791 // RULE 1: (height is content based, solve of top)
2792 logicalHeightValue = contentLogicalHeight;
2793 logicalTopValue = availableSpace - (logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight));
2794 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) {
2795 // RULE 3: (height is content based, no need solve of bottom)
2796 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
2797 logicalHeightValue = contentLogicalHeight;
2798 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
2799 // RULE 4: (solve of top)
2800 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight));
2801 logicalTopValue = availableSpace - (logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight));
2802 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
2803 // RULE 5: (solve of height)
2804 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
2805 logicalHeightValue = max(0, availableSpace - (logicalTopValue + logicalBottom.calcValue(containerLogicalHeight)));
2806 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) {
2807 // RULE 6: (no need solve of bottom)
2808 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight));
2809 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
2810 }
2811 }
2812
2813 // Use computed values to calculate the vertical position.
2814 logicalTopPos = logicalTopValue + marginBeforeValue;
2815 computeLogicalTopPositionedOffset(logicalTopPos, this, logicalHeightValue, containerBlock, containerLogicalHeight);
2816 }
2817
computePositionedLogicalWidthReplaced()2818 void RenderBox::computePositionedLogicalWidthReplaced()
2819 {
2820 // The following is based off of the W3C Working Draft from April 11, 2006 of
2821 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements"
2822 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
2823 // (block-style-comments in this function correspond to text from the spec and
2824 // the numbers correspond to numbers in spec)
2825
2826 // We don't use containingBlock(), since we may be positioned by an enclosing
2827 // relative positioned inline.
2828 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2829
2830 const int containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock);
2831
2832 // To match WinIE, in quirks mode use the parent's 'direction' property
2833 // instead of the the container block's.
2834 TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction();
2835
2836 // Variables to solve.
2837 bool isHorizontal = isHorizontalWritingMode();
2838 Length logicalLeft = style()->logicalLeft();
2839 Length logicalRight = style()->logicalRight();
2840 Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop();
2841 Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom();
2842 int& marginLogicalLeftAlias = isHorizontal ? m_marginLeft : m_marginTop;
2843 int& marginLogicalRightAlias = isHorizontal ? m_marginRight : m_marginBottom;
2844
2845 /*-----------------------------------------------------------------------*\
2846 * 1. The used value of 'width' is determined as for inline replaced
2847 * elements.
2848 \*-----------------------------------------------------------------------*/
2849 // NOTE: This value of width is FINAL in that the min/max width calculations
2850 // are dealt with in computeReplacedWidth(). This means that the steps to produce
2851 // correct max/min in the non-replaced version, are not necessary.
2852 setLogicalWidth(computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth());
2853 const int availableSpace = containerLogicalWidth - logicalWidth();
2854
2855 /*-----------------------------------------------------------------------*\
2856 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
2857 * of the containing block is 'ltr', set 'left' to the static position;
2858 * else if 'direction' is 'rtl', set 'right' to the static position.
2859 \*-----------------------------------------------------------------------*/
2860 // see FIXME 2
2861 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, containerDirection);
2862
2863 /*-----------------------------------------------------------------------*\
2864 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
2865 * or 'margin-right' with '0'.
2866 \*-----------------------------------------------------------------------*/
2867 if (logicalLeft.isAuto() || logicalRight.isAuto()) {
2868 if (marginLogicalLeft.isAuto())
2869 marginLogicalLeft.setValue(Fixed, 0);
2870 if (marginLogicalRight.isAuto())
2871 marginLogicalRight.setValue(Fixed, 0);
2872 }
2873
2874 /*-----------------------------------------------------------------------*\
2875 * 4. If at this point both 'margin-left' and 'margin-right' are still
2876 * 'auto', solve the equation under the extra constraint that the two
2877 * margins must get equal values, unless this would make them negative,
2878 * in which case when the direction of the containing block is 'ltr'
2879 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
2880 * 'margin-right' ('margin-left').
2881 \*-----------------------------------------------------------------------*/
2882 int logicalLeftValue = 0;
2883 int logicalRightValue = 0;
2884
2885 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
2886 // 'left' and 'right' cannot be 'auto' due to step 3
2887 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
2888
2889 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2890 logicalRightValue = logicalRight.calcValue(containerLogicalWidth);
2891
2892 int difference = availableSpace - (logicalLeftValue + logicalRightValue);
2893 if (difference > 0) {
2894 marginLogicalLeftAlias = difference / 2; // split the difference
2895 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences
2896 } else {
2897 // see FIXME 1
2898 if (containerDirection == LTR) {
2899 marginLogicalLeftAlias = 0;
2900 marginLogicalRightAlias = difference; // will be negative
2901 } else {
2902 marginLogicalLeftAlias = difference; // will be negative
2903 marginLogicalRightAlias = 0;
2904 }
2905 }
2906
2907 /*-----------------------------------------------------------------------*\
2908 * 5. If at this point there is an 'auto' left, solve the equation for
2909 * that value.
2910 \*-----------------------------------------------------------------------*/
2911 } else if (logicalLeft.isAuto()) {
2912 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth);
2913 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth);
2914 logicalRightValue = logicalRight.calcValue(containerLogicalWidth);
2915
2916 // Solve for 'left'
2917 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias);
2918 } else if (logicalRight.isAuto()) {
2919 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth);
2920 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth);
2921 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2922
2923 // Solve for 'right'
2924 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias);
2925 } else if (marginLogicalLeft.isAuto()) {
2926 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth);
2927 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2928 logicalRightValue = logicalRight.calcValue(containerLogicalWidth);
2929
2930 // Solve for 'margin-left'
2931 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias);
2932 } else if (marginLogicalRight.isAuto()) {
2933 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth);
2934 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2935 logicalRightValue = logicalRight.calcValue(containerLogicalWidth);
2936
2937 // Solve for 'margin-right'
2938 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias);
2939 } else {
2940 // Nothing is 'auto', just calculate the values.
2941 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth);
2942 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth);
2943 logicalRightValue = logicalRight.calcValue(containerLogicalWidth);
2944 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth);
2945 }
2946
2947 /*-----------------------------------------------------------------------*\
2948 * 6. If at this point the values are over-constrained, ignore the value
2949 * for either 'left' (in case the 'direction' property of the
2950 * containing block is 'rtl') or 'right' (in case 'direction' is
2951 * 'ltr') and solve for that value.
2952 \*-----------------------------------------------------------------------*/
2953 // NOTE: It is not necessary to solve for 'right' when the direction is
2954 // LTR because the value is not used.
2955 int totalLogicalWidth = logicalWidth() + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias;
2956 if (totalLogicalWidth > containerLogicalWidth && (containerDirection == RTL))
2957 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue);
2958
2959 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that
2960 // can make the result here rather complicated to compute.
2961
2962 // Use computed values to calculate the horizontal position.
2963
2964 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
2965 // positioned, inline containing block because right now, it is using the logical left position
2966 // of the first line box when really it should use the last line box. When
2967 // this is fixed elsewhere, this block should be removed.
2968 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) {
2969 const RenderInline* flow = toRenderInline(containerBlock);
2970 InlineFlowBox* firstLine = flow->firstLineBox();
2971 InlineFlowBox* lastLine = flow->lastLineBox();
2972 if (firstLine && lastLine && firstLine != lastLine) {
2973 setLogicalLeft(logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()));
2974 return;
2975 }
2976 }
2977
2978 int logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias;
2979 computeLogicalLeftPositionedOffset(logicalLeftPos, this, logicalWidth(), containerBlock, containerLogicalWidth);
2980 setLogicalLeft(logicalLeftPos);
2981 }
2982
computePositionedLogicalHeightReplaced()2983 void RenderBox::computePositionedLogicalHeightReplaced()
2984 {
2985 // The following is based off of the W3C Working Draft from April 11, 2006 of
2986 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements"
2987 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
2988 // (block-style-comments in this function correspond to text from the spec and
2989 // the numbers correspond to numbers in spec)
2990
2991 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2992 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2993
2994 const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
2995
2996 // Variables to solve.
2997 bool isHorizontal = isHorizontalWritingMode();
2998 bool isFlipped = style()->isFlippedBlocksWritingMode();
2999 Length marginBefore = style()->marginBefore();
3000 Length marginAfter = style()->marginAfter();
3001 int& marginBeforeAlias = isHorizontal ? (isFlipped ? m_marginBottom : m_marginTop) : (isFlipped ? m_marginRight: m_marginLeft);
3002 int& marginAfterAlias = isHorizontal ? (isFlipped ? m_marginTop : m_marginBottom) : (isFlipped ? m_marginLeft: m_marginRight);
3003
3004 Length logicalTop = style()->logicalTop();
3005 Length logicalBottom = style()->logicalBottom();
3006
3007 /*-----------------------------------------------------------------------*\
3008 * 1. The used value of 'height' is determined as for inline replaced
3009 * elements.
3010 \*-----------------------------------------------------------------------*/
3011 // NOTE: This value of height is FINAL in that the min/max height calculations
3012 // are dealt with in computeReplacedHeight(). This means that the steps to produce
3013 // correct max/min in the non-replaced version, are not necessary.
3014 setLogicalHeight(computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight());
3015 const int availableSpace = containerLogicalHeight - logicalHeight();
3016
3017 /*-----------------------------------------------------------------------*\
3018 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
3019 * with the element's static position.
3020 \*-----------------------------------------------------------------------*/
3021 // see FIXME 2
3022 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock);
3023
3024 /*-----------------------------------------------------------------------*\
3025 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
3026 * 'margin-bottom' with '0'.
3027 \*-----------------------------------------------------------------------*/
3028 // FIXME: The spec. says that this step should only be taken when bottom is
3029 // auto, but if only top is auto, this makes step 4 impossible.
3030 if (logicalTop.isAuto() || logicalBottom.isAuto()) {
3031 if (marginBefore.isAuto())
3032 marginBefore.setValue(Fixed, 0);
3033 if (marginAfter.isAuto())
3034 marginAfter.setValue(Fixed, 0);
3035 }
3036
3037 /*-----------------------------------------------------------------------*\
3038 * 4. If at this point both 'margin-top' and 'margin-bottom' are still
3039 * 'auto', solve the equation under the extra constraint that the two
3040 * margins must get equal values.
3041 \*-----------------------------------------------------------------------*/
3042 int logicalTopValue = 0;
3043 int logicalBottomValue = 0;
3044
3045 if (marginBefore.isAuto() && marginAfter.isAuto()) {
3046 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined.
3047 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto()));
3048
3049 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
3050 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight);
3051
3052 int difference = availableSpace - (logicalTopValue + logicalBottomValue);
3053 // NOTE: This may result in negative values.
3054 marginBeforeAlias = difference / 2; // split the difference
3055 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences
3056
3057 /*-----------------------------------------------------------------------*\
3058 * 5. If at this point there is only one 'auto' left, solve the equation
3059 * for that value.
3060 \*-----------------------------------------------------------------------*/
3061 } else if (logicalTop.isAuto()) {
3062 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight);
3063 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight);
3064 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight);
3065
3066 // Solve for 'top'
3067 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias);
3068 } else if (logicalBottom.isAuto()) {
3069 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight);
3070 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight);
3071 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
3072
3073 // Solve for 'bottom'
3074 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
3075 // use the value.
3076 } else if (marginBefore.isAuto()) {
3077 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight);
3078 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
3079 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight);
3080
3081 // Solve for 'margin-top'
3082 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias);
3083 } else if (marginAfter.isAuto()) {
3084 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight);
3085 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
3086 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight);
3087
3088 // Solve for 'margin-bottom'
3089 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias);
3090 } else {
3091 // Nothing is 'auto', just calculate the values.
3092 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight);
3093 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight);
3094 logicalTopValue = logicalTop.calcValue(containerLogicalHeight);
3095 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
3096 // use the value.
3097 }
3098
3099 /*-----------------------------------------------------------------------*\
3100 * 6. If at this point the values are over-constrained, ignore the value
3101 * for 'bottom' and solve for that value.
3102 \*-----------------------------------------------------------------------*/
3103 // NOTE: It is not necessary to do this step because we don't end up using
3104 // the value of 'bottom' regardless of whether the values are over-constrained
3105 // or not.
3106
3107 // Use computed values to calculate the vertical position.
3108 int logicalTopPos = logicalTopValue + marginBeforeAlias;
3109 computeLogicalTopPositionedOffset(logicalTopPos, this, logicalHeight(), containerBlock, containerLogicalHeight);
3110 setLogicalTop(logicalTopPos);
3111 }
3112
localCaretRect(InlineBox * box,int caretOffset,int * extraWidthToEndOfLine)3113 IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine)
3114 {
3115 // VisiblePositions at offsets inside containers either a) refer to the positions before/after
3116 // those containers (tables and select elements) or b) refer to the position inside an empty block.
3117 // They never refer to children.
3118 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements.
3119
3120 // FIXME: What about border and padding?
3121 IntRect rect(x(), y(), caretWidth, height());
3122 bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection();
3123
3124 if ((!caretOffset) ^ ltr)
3125 rect.move(IntSize(width() - caretWidth, 0));
3126
3127 if (box) {
3128 RootInlineBox* rootBox = box->root();
3129 int top = rootBox->lineTop();
3130 rect.setY(top);
3131 rect.setHeight(rootBox->lineBottom() - top);
3132 }
3133
3134 // If height of box is smaller than font height, use the latter one,
3135 // otherwise the caret might become invisible.
3136 //
3137 // Also, if the box is not a replaced element, always use the font height.
3138 // This prevents the "big caret" bug described in:
3139 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point
3140 //
3141 // FIXME: ignoring :first-line, missing good reason to take care of
3142 int fontHeight = style()->fontMetrics().height();
3143 if (fontHeight > rect.height() || (!isReplaced() && !isTable()))
3144 rect.setHeight(fontHeight);
3145
3146 if (extraWidthToEndOfLine)
3147 *extraWidthToEndOfLine = x() + width() - rect.maxX();
3148
3149 // Move to local coords
3150 rect.move(-x(), -y());
3151 return rect;
3152 }
3153
positionForPoint(const IntPoint & point)3154 VisiblePosition RenderBox::positionForPoint(const IntPoint& point)
3155 {
3156 // no children...return this render object's element, if there is one, and offset 0
3157 if (!firstChild())
3158 return createVisiblePosition(node() ? firstPositionInOrBeforeNode(node()) : Position(0, 0));
3159
3160 int xPos = point.x();
3161 int yPos = point.y();
3162
3163 if (isTable() && node()) {
3164 int right = contentWidth() + borderAndPaddingWidth();
3165 int bottom = contentHeight() + borderAndPaddingHeight();
3166
3167 if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) {
3168 if (xPos <= right / 2)
3169 return createVisiblePosition(firstPositionInOrBeforeNode(node()));
3170 return createVisiblePosition(lastPositionInOrAfterNode(node()));
3171 }
3172 }
3173
3174 // Pass off to the closest child.
3175 int minDist = INT_MAX;
3176 RenderBox* closestRenderer = 0;
3177 int newX = xPos;
3178 int newY = yPos;
3179 if (isTableRow()) {
3180 newX += x();
3181 newY += y();
3182 }
3183 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) {
3184 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() )
3185 || renderObject->style()->visibility() != VISIBLE)
3186 continue;
3187
3188 if (!renderObject->isBox())
3189 continue;
3190
3191 RenderBox* renderer = toRenderBox(renderObject);
3192
3193 int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y());
3194 int bottom = top + renderer->contentHeight();
3195 int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x());
3196 int right = left + renderer->contentWidth();
3197
3198 if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) {
3199 if (renderer->isTableRow())
3200 return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y());
3201 return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y());
3202 }
3203
3204 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces
3205 // and use a different compare depending on which piece (x, y) is in.
3206 IntPoint cmp;
3207 if (xPos > right) {
3208 if (yPos < top)
3209 cmp = IntPoint(right, top);
3210 else if (yPos > bottom)
3211 cmp = IntPoint(right, bottom);
3212 else
3213 cmp = IntPoint(right, yPos);
3214 } else if (xPos < left) {
3215 if (yPos < top)
3216 cmp = IntPoint(left, top);
3217 else if (yPos > bottom)
3218 cmp = IntPoint(left, bottom);
3219 else
3220 cmp = IntPoint(left, yPos);
3221 } else {
3222 if (yPos < top)
3223 cmp = IntPoint(xPos, top);
3224 else
3225 cmp = IntPoint(xPos, bottom);
3226 }
3227
3228 int x1minusx2 = cmp.x() - xPos;
3229 int y1minusy2 = cmp.y() - yPos;
3230
3231 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2;
3232 if (dist < minDist) {
3233 closestRenderer = renderer;
3234 minDist = dist;
3235 }
3236 }
3237
3238 if (closestRenderer)
3239 return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y());
3240
3241 return createVisiblePosition(firstPositionInOrBeforeNode(node()));
3242 }
3243
shrinkToAvoidFloats() const3244 bool RenderBox::shrinkToAvoidFloats() const
3245 {
3246 // Floating objects don't shrink. Objects that don't avoid floats don't shrink. Marquees don't shrink.
3247 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats() || isFloating())
3248 return false;
3249
3250 // All auto-width objects that avoid floats should always use lineWidth.
3251 return style()->width().isAuto();
3252 }
3253
avoidsFloats() const3254 bool RenderBox::avoidsFloats() const
3255 {
3256 return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot();
3257 }
3258
addShadowOverflow()3259 void RenderBox::addShadowOverflow()
3260 {
3261 int shadowLeft;
3262 int shadowRight;
3263 int shadowTop;
3264 int shadowBottom;
3265 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft);
3266 IntRect borderBox = borderBoxRect();
3267 int overflowLeft = borderBox.x() + shadowLeft;
3268 int overflowRight = borderBox.maxX() + shadowRight;
3269 int overflowTop = borderBox.y() + shadowTop;
3270 int overflowBottom = borderBox.maxY() + shadowBottom;
3271 addVisualOverflow(IntRect(overflowLeft, overflowTop, overflowRight - overflowLeft, overflowBottom - overflowTop));
3272 }
3273
addOverflowFromChild(RenderBox * child,const IntSize & delta)3274 void RenderBox::addOverflowFromChild(RenderBox* child, const IntSize& delta)
3275 {
3276 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then
3277 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this
3278 // and just propagates the border box rect instead.
3279 IntRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(style());
3280 childLayoutOverflowRect.move(delta);
3281 addLayoutOverflow(childLayoutOverflowRect);
3282
3283 // Add in visual overflow from the child. Even if the child clips its overflow, it may still
3284 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this
3285 // overflow if we are clipping our own overflow.
3286 if (child->hasSelfPaintingLayer() || hasOverflowClip())
3287 return;
3288 IntRect childVisualOverflowRect = child->visualOverflowRectForPropagation(style());
3289 childVisualOverflowRect.move(delta);
3290 addVisualOverflow(childVisualOverflowRect);
3291 }
3292
addLayoutOverflow(const IntRect & rect)3293 void RenderBox::addLayoutOverflow(const IntRect& rect)
3294 {
3295 IntRect clientBox = clientBoxRect();
3296 if (clientBox.contains(rect) || rect.isEmpty())
3297 return;
3298
3299 // For overflow clip objects, we don't want to propagate overflow into unreachable areas.
3300 IntRect overflowRect(rect);
3301 if (hasOverflowClip() || isRenderView()) {
3302 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl
3303 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same
3304 // and vertical-lr/rl as the same.
3305 bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode();
3306 bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode();
3307
3308 if (!hasTopOverflow)
3309 overflowRect.shiftYEdgeTo(max(overflowRect.y(), clientBox.y()));
3310 else
3311 overflowRect.shiftMaxYEdgeTo(min(overflowRect.maxY(), clientBox.maxY()));
3312 if (!hasLeftOverflow)
3313 overflowRect.shiftXEdgeTo(max(overflowRect.x(), clientBox.x()));
3314 else
3315 overflowRect.shiftMaxXEdgeTo(min(overflowRect.maxX(), clientBox.maxX()));
3316
3317 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully
3318 // contained.
3319 if (clientBox.contains(overflowRect) || overflowRect.isEmpty())
3320 return;
3321 }
3322
3323 if (!m_overflow)
3324 m_overflow.set(new RenderOverflow(clientBox, borderBoxRect()));
3325
3326 m_overflow->addLayoutOverflow(overflowRect);
3327 }
3328
addVisualOverflow(const IntRect & rect)3329 void RenderBox::addVisualOverflow(const IntRect& rect)
3330 {
3331 IntRect borderBox = borderBoxRect();
3332 if (borderBox.contains(rect) || rect.isEmpty())
3333 return;
3334
3335 if (!m_overflow)
3336 m_overflow.set(new RenderOverflow(clientBoxRect(), borderBox));
3337
3338 m_overflow->addVisualOverflow(rect);
3339 }
3340
clearLayoutOverflow()3341 void RenderBox::clearLayoutOverflow()
3342 {
3343 if (!m_overflow)
3344 return;
3345
3346 if (visualOverflowRect() == borderBoxRect()) {
3347 m_overflow.clear();
3348 return;
3349 }
3350
3351 m_overflow->resetLayoutOverflow(borderBoxRect());
3352 }
3353
lineHeight(bool,LineDirectionMode direction,LinePositionMode) const3354 int RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
3355 {
3356 if (isReplaced())
3357 return direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft;
3358 return 0;
3359 }
3360
baselinePosition(FontBaseline baselineType,bool,LineDirectionMode direction,LinePositionMode) const3361 int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
3362 {
3363 if (isReplaced()) {
3364 int result = direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft;
3365 if (baselineType == AlphabeticBaseline)
3366 return result;
3367 return result - result / 2;
3368 }
3369 return 0;
3370 }
3371
3372
enclosingFloatPaintingLayer() const3373 RenderLayer* RenderBox::enclosingFloatPaintingLayer() const
3374 {
3375 const RenderObject* curr = this;
3376 while (curr) {
3377 RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBoxModelObject(curr)->layer() : 0;
3378 if (layer && layer->isSelfPaintingLayer())
3379 return layer;
3380 curr = curr->parent();
3381 }
3382 return 0;
3383 }
3384
logicalVisualOverflowRectForPropagation(RenderStyle * parentStyle) const3385 IntRect RenderBox::logicalVisualOverflowRectForPropagation(RenderStyle* parentStyle) const
3386 {
3387 IntRect rect = visualOverflowRectForPropagation(parentStyle);
3388 if (!parentStyle->isHorizontalWritingMode())
3389 return rect.transposedRect();
3390 return rect;
3391 }
3392
visualOverflowRectForPropagation(RenderStyle * parentStyle) const3393 IntRect RenderBox::visualOverflowRectForPropagation(RenderStyle* parentStyle) const
3394 {
3395 // If the writing modes of the child and parent match, then we don't have to
3396 // do anything fancy. Just return the result.
3397 IntRect rect = visualOverflowRect();
3398 if (parentStyle->writingMode() == style()->writingMode())
3399 return rect;
3400
3401 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
3402 // in a particular axis, then we have to flip the rect along that axis.
3403 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode)
3404 rect.setX(width() - rect.maxX());
3405 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode)
3406 rect.setY(height() - rect.maxY());
3407
3408 return rect;
3409 }
3410
logicalLayoutOverflowRectForPropagation(RenderStyle * parentStyle) const3411 IntRect RenderBox::logicalLayoutOverflowRectForPropagation(RenderStyle* parentStyle) const
3412 {
3413 IntRect rect = layoutOverflowRectForPropagation(parentStyle);
3414 if (!parentStyle->isHorizontalWritingMode())
3415 return rect.transposedRect();
3416 return rect;
3417 }
3418
layoutOverflowRectForPropagation(RenderStyle * parentStyle) const3419 IntRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) const
3420 {
3421 // Only propagate interior layout overflow if we don't clip it.
3422 IntRect rect = borderBoxRect();
3423 if (!hasOverflowClip())
3424 rect.unite(layoutOverflowRect());
3425
3426 bool hasTransform = hasLayer() && layer()->transform();
3427 if (isRelPositioned() || hasTransform) {
3428 // If we are relatively positioned or if we have a transform, then we have to convert
3429 // this rectangle into physical coordinates, apply relative positioning and transforms
3430 // to it, and then convert it back.
3431 flipForWritingMode(rect);
3432
3433 if (hasTransform)
3434 rect = layer()->currentTransform().mapRect(rect);
3435
3436 if (isRelPositioned())
3437 rect.move(relativePositionOffsetX(), relativePositionOffsetY());
3438
3439 // Now we need to flip back.
3440 flipForWritingMode(rect);
3441 }
3442
3443 // If the writing modes of the child and parent match, then we don't have to
3444 // do anything fancy. Just return the result.
3445 if (parentStyle->writingMode() == style()->writingMode())
3446 return rect;
3447
3448 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
3449 // in a particular axis, then we have to flip the rect along that axis.
3450 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode)
3451 rect.setX(width() - rect.maxX());
3452 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode)
3453 rect.setY(height() - rect.maxY());
3454
3455 return rect;
3456 }
3457
flipForWritingMode(const RenderBox * child,const IntPoint & point,FlippingAdjustment adjustment) const3458 IntPoint RenderBox::flipForWritingMode(const RenderBox* child, const IntPoint& point, FlippingAdjustment adjustment) const
3459 {
3460 if (!style()->isFlippedBlocksWritingMode())
3461 return point;
3462
3463 // The child is going to add in its x() and y(), so we have to make sure it ends up in
3464 // the right place.
3465 if (isHorizontalWritingMode())
3466 return IntPoint(point.x(), point.y() + height() - child->height() - child->y() - (adjustment == ParentToChildFlippingAdjustment ? child->y() : 0));
3467 return IntPoint(point.x() + width() - child->width() - child->x() - (adjustment == ParentToChildFlippingAdjustment ? child->x() : 0), point.y());
3468 }
3469
flipForWritingMode(IntRect & rect) const3470 void RenderBox::flipForWritingMode(IntRect& rect) const
3471 {
3472 if (!style()->isFlippedBlocksWritingMode())
3473 return;
3474
3475 if (isHorizontalWritingMode())
3476 rect.setY(height() - rect.maxY());
3477 else
3478 rect.setX(width() - rect.maxX());
3479 }
3480
flipForWritingMode(int position) const3481 int RenderBox::flipForWritingMode(int position) const
3482 {
3483 if (!style()->isFlippedBlocksWritingMode())
3484 return position;
3485 return logicalHeight() - position;
3486 }
3487
flipForWritingMode(const IntPoint & position) const3488 IntPoint RenderBox::flipForWritingMode(const IntPoint& position) const
3489 {
3490 if (!style()->isFlippedBlocksWritingMode())
3491 return position;
3492 return isHorizontalWritingMode() ? IntPoint(position.x(), height() - position.y()) : IntPoint(width() - position.x(), position.y());
3493 }
3494
flipForWritingModeIncludingColumns(const IntPoint & point) const3495 IntPoint RenderBox::flipForWritingModeIncludingColumns(const IntPoint& point) const
3496 {
3497 if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
3498 return flipForWritingMode(point);
3499 return toRenderBlock(this)->flipForWritingModeIncludingColumns(point);
3500 }
3501
flipForWritingMode(const IntSize & offset) const3502 IntSize RenderBox::flipForWritingMode(const IntSize& offset) const
3503 {
3504 if (!style()->isFlippedBlocksWritingMode())
3505 return offset;
3506 return isHorizontalWritingMode() ? IntSize(offset.width(), height() - offset.height()) : IntSize(width() - offset.width(), offset.height());
3507 }
3508
flipForWritingMode(const FloatPoint & position) const3509 FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const
3510 {
3511 if (!style()->isFlippedBlocksWritingMode())
3512 return position;
3513 return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y());
3514 }
3515
flipForWritingMode(FloatRect & rect) const3516 void RenderBox::flipForWritingMode(FloatRect& rect) const
3517 {
3518 if (!style()->isFlippedBlocksWritingMode())
3519 return;
3520
3521 if (isHorizontalWritingMode())
3522 rect.setY(height() - rect.maxY());
3523 else
3524 rect.setX(width() - rect.maxX());
3525 }
3526
locationOffsetIncludingFlipping() const3527 IntSize RenderBox::locationOffsetIncludingFlipping() const
3528 {
3529 RenderBlock* containerBlock = containingBlock();
3530 if (!containerBlock || containerBlock == this)
3531 return locationOffset();
3532
3533 IntRect rect(frameRect());
3534 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline.
3535 return IntSize(rect.x(), rect.y());
3536 }
3537
3538 } // namespace WebCore
3539