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 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26 #include "config.h"
27 #include "core/rendering/RenderBox.h"
28
29 #include <math.h>
30 #include <algorithm>
31 #include "HTMLNames.h"
32 #include "core/dom/Document.h"
33 #include "core/editing/htmlediting.h"
34 #include "core/html/HTMLElement.h"
35 #include "core/html/HTMLFrameElementBase.h"
36 #include "core/html/HTMLFrameOwnerElement.h"
37 #include "core/html/HTMLHtmlElement.h"
38 #include "core/html/HTMLTextAreaElement.h"
39 #include "core/frame/Frame.h"
40 #include "core/frame/FrameView.h"
41 #include "core/page/AutoscrollController.h"
42 #include "core/page/EventHandler.h"
43 #include "core/page/Page.h"
44 #include "core/rendering/HitTestResult.h"
45 #include "core/rendering/LayoutRectRecorder.h"
46 #include "core/rendering/PaintInfo.h"
47 #include "core/rendering/RenderBoxRegionInfo.h"
48 #include "core/rendering/RenderFlexibleBox.h"
49 #include "core/rendering/RenderFlowThread.h"
50 #include "core/rendering/RenderGeometryMap.h"
51 #include "core/rendering/RenderGrid.h"
52 #include "core/rendering/RenderInline.h"
53 #include "core/rendering/RenderLayer.h"
54 #include "core/rendering/RenderLayerCompositor.h"
55 #include "core/rendering/RenderListMarker.h"
56 #include "core/rendering/RenderRegion.h"
57 #include "core/rendering/RenderTableCell.h"
58 #include "core/rendering/RenderTheme.h"
59 #include "core/rendering/RenderView.h"
60 #include "platform/geometry/FloatQuad.h"
61 #include "platform/geometry/TransformState.h"
62 #include "platform/graphics/GraphicsContextStateSaver.h"
63
64 using namespace std;
65
66 namespace WebCore {
67
68 using namespace HTMLNames;
69
70 // Used by flexible boxes when flexing this element and by table cells.
71 typedef WTF::HashMap<const RenderBox*, LayoutUnit> OverrideSizeMap;
72 static OverrideSizeMap* gOverrideHeightMap = 0;
73 static OverrideSizeMap* gOverrideWidthMap = 0;
74
75 // Used by grid elements to properly size their grid items.
76 static OverrideSizeMap* gOverrideContainingBlockLogicalHeightMap = 0;
77 static OverrideSizeMap* gOverrideContainingBlockLogicalWidthMap = 0;
78
79
80 // Size of border belt for autoscroll. When mouse pointer in border belt,
81 // autoscroll is started.
82 static const int autoscrollBeltSize = 20;
83 static const unsigned backgroundObscurationTestMaxDepth = 4;
84
skipBodyBackground(const RenderBox * bodyElementRenderer)85 static bool skipBodyBackground(const RenderBox* bodyElementRenderer)
86 {
87 ASSERT(bodyElementRenderer->isBody());
88 // The <body> only paints its background if the root element has defined a background independent of the body,
89 // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject).
90 RenderObject* documentElementRenderer = bodyElementRenderer->document().documentElement()->renderer();
91 return documentElementRenderer
92 && !documentElementRenderer->hasBackground()
93 && (documentElementRenderer == bodyElementRenderer->parent());
94 }
95
RenderBox(ContainerNode * node)96 RenderBox::RenderBox(ContainerNode* node)
97 : RenderBoxModelObject(node)
98 , m_minPreferredLogicalWidth(-1)
99 , m_maxPreferredLogicalWidth(-1)
100 , m_intrinsicContentLogicalHeight(-1)
101 , m_inlineBoxWrapper(0)
102 {
103 setIsBox();
104 }
105
~RenderBox()106 RenderBox::~RenderBox()
107 {
108 }
109
borderBoxRectInRegion(RenderRegion * region,RenderBoxRegionInfoFlags cacheFlag) const110 LayoutRect RenderBox::borderBoxRectInRegion(RenderRegion* region, RenderBoxRegionInfoFlags cacheFlag) const
111 {
112 if (!region)
113 return borderBoxRect();
114
115 // Compute the logical width and placement in this region.
116 RenderBoxRegionInfo* boxInfo = renderBoxRegionInfo(region, cacheFlag);
117 if (!boxInfo)
118 return borderBoxRect();
119
120 // We have cached insets.
121 LayoutUnit logicalWidth = boxInfo->logicalWidth();
122 LayoutUnit logicalLeft = boxInfo->logicalLeft();
123
124 // Now apply the parent inset since it is cumulative whenever anything in the containing block chain shifts.
125 // FIXME: Doesn't work right with perpendicular writing modes.
126 const RenderBlock* currentBox = containingBlock();
127 RenderBoxRegionInfo* currentBoxInfo = currentBox->renderBoxRegionInfo(region);
128 while (currentBoxInfo && currentBoxInfo->isShifted()) {
129 if (currentBox->style()->direction() == LTR)
130 logicalLeft += currentBoxInfo->logicalLeft();
131 else
132 logicalLeft -= (currentBox->logicalWidth() - currentBoxInfo->logicalWidth()) - currentBoxInfo->logicalLeft();
133 currentBox = currentBox->containingBlock();
134 region = currentBox->clampToStartAndEndRegions(region);
135 currentBoxInfo = currentBox->renderBoxRegionInfo(region);
136 }
137
138 if (cacheFlag == DoNotCacheRenderBoxRegionInfo)
139 delete boxInfo;
140
141 if (isHorizontalWritingMode())
142 return LayoutRect(logicalLeft, 0, logicalWidth, height());
143 return LayoutRect(0, logicalLeft, width(), logicalWidth);
144 }
145
clearRenderBoxRegionInfo()146 void RenderBox::clearRenderBoxRegionInfo()
147 {
148 if (isRenderFlowThread())
149 return;
150
151 RenderFlowThread* flowThread = flowThreadContainingBlock();
152 if (flowThread)
153 flowThread->removeRenderBoxRegionInfo(this);
154 }
155
willBeDestroyed()156 void RenderBox::willBeDestroyed()
157 {
158 clearOverrideSize();
159 clearContainingBlockOverrideSize();
160
161 RenderBlock::removePercentHeightDescendantIfNeeded(this);
162
163 ShapeOutsideInfo::removeInfo(this);
164
165 RenderBoxModelObject::willBeDestroyed();
166 }
167
removeFloatingOrPositionedChildFromBlockLists()168 void RenderBox::removeFloatingOrPositionedChildFromBlockLists()
169 {
170 ASSERT(isFloatingOrOutOfFlowPositioned());
171
172 if (documentBeingDestroyed())
173 return;
174
175 if (isFloating()) {
176 RenderBlockFlow* parentBlockFlow = 0;
177 for (RenderObject* curr = parent(); curr && !curr->isRenderView(); curr = curr->parent()) {
178 if (curr->isRenderBlockFlow()) {
179 RenderBlockFlow* currBlockFlow = toRenderBlockFlow(curr);
180 if (!parentBlockFlow || currBlockFlow->containsFloat(this))
181 parentBlockFlow = currBlockFlow;
182 }
183 }
184
185 if (parentBlockFlow) {
186 parentBlockFlow->markSiblingsWithFloatsForLayout(this);
187 parentBlockFlow->markAllDescendantsWithFloatsForLayout(this, false);
188 }
189 }
190
191 if (isOutOfFlowPositioned())
192 RenderBlock::removePositionedObject(this);
193 }
194
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)195 void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
196 {
197 RenderStyle* oldStyle = style();
198 if (oldStyle) {
199 // The background of the root element or the body element could propagate up to
200 // the canvas. Just dirty the entire canvas when our style changes substantially.
201 if (diff >= StyleDifferenceRepaint && node() &&
202 (isHTMLHtmlElement(node()) || node()->hasTagName(bodyTag))) {
203 view()->repaint();
204
205 if (oldStyle->hasEntirelyFixedBackground() != newStyle->hasEntirelyFixedBackground())
206 view()->compositor()->rootFixedBackgroundsChanged();
207 }
208
209 // When a layout hint happens and an object's position style changes, we have to do a layout
210 // to dirty the render tree using the old position value now.
211 if (diff == StyleDifferenceLayout && parent() && oldStyle->position() != newStyle->position()) {
212 markContainingBlocksForLayout();
213 if (oldStyle->position() == StaticPosition)
214 repaint();
215 else if (newStyle->hasOutOfFlowPosition())
216 parent()->setChildNeedsLayout();
217 if (isFloating() && !isOutOfFlowPositioned() && newStyle->hasOutOfFlowPosition())
218 removeFloatingOrPositionedChildFromBlockLists();
219 }
220 } else if (newStyle && isBody())
221 view()->repaint();
222
223 RenderBoxModelObject::styleWillChange(diff, newStyle);
224 }
225
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)226 void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
227 {
228 // Horizontal writing mode definition is updated in RenderBoxModelObject::updateFromStyle,
229 // (as part of the RenderBoxModelObject::styleDidChange call below). So, we can safely cache the horizontal
230 // writing mode value before style change here.
231 bool oldHorizontalWritingMode = isHorizontalWritingMode();
232
233 RenderBoxModelObject::styleDidChange(diff, oldStyle);
234
235 RenderStyle* newStyle = style();
236 if (needsLayout() && oldStyle) {
237 RenderBlock::removePercentHeightDescendantIfNeeded(this);
238
239 // Normally we can do optimized positioning layout for absolute/fixed positioned objects. There is one special case, however, which is
240 // 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
241 // to determine the new static position.
242 if (isOutOfFlowPositioned() && newStyle->hasStaticBlockPosition(isHorizontalWritingMode()) && oldStyle->marginBefore() != newStyle->marginBefore()
243 && parent() && !parent()->normalChildNeedsLayout())
244 parent()->setChildNeedsLayout();
245 }
246
247 if (RenderBlock::hasPercentHeightContainerMap() && firstChild()
248 && oldHorizontalWritingMode != isHorizontalWritingMode())
249 RenderBlock::clearPercentHeightDescendantsFrom(this);
250
251 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the
252 // new zoomed coordinate space.
253 if (hasOverflowClip() && oldStyle && newStyle && oldStyle->effectiveZoom() != newStyle->effectiveZoom() && layer()) {
254 if (int left = layer()->scrollableArea()->scrollXOffset()) {
255 left = (left / oldStyle->effectiveZoom()) * newStyle->effectiveZoom();
256 layer()->scrollableArea()->scrollToXOffset(left);
257 }
258 if (int top = layer()->scrollableArea()->scrollYOffset()) {
259 top = (top / oldStyle->effectiveZoom()) * newStyle->effectiveZoom();
260 layer()->scrollableArea()->scrollToYOffset(top);
261 }
262 }
263
264 // Our opaqueness might have changed without triggering layout.
265 if (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintIfTextOrColorChange || diff == StyleDifferenceRepaintLayer) {
266 RenderObject* parentToInvalidate = parent();
267 for (unsigned i = 0; i < backgroundObscurationTestMaxDepth && parentToInvalidate; ++i) {
268 parentToInvalidate->invalidateBackgroundObscurationStatus();
269 parentToInvalidate = parentToInvalidate->parent();
270 }
271 }
272
273 if (isRoot() || isBody())
274 document().view()->recalculateScrollbarOverlayStyle();
275
276 updateShapeOutsideInfoAfterStyleChange(*style(), oldStyle);
277 updateGridPositionAfterStyleChange(oldStyle);
278 }
279
updateShapeOutsideInfoAfterStyleChange(const RenderStyle & style,const RenderStyle * oldStyle)280 void RenderBox::updateShapeOutsideInfoAfterStyleChange(const RenderStyle& style, const RenderStyle* oldStyle)
281 {
282 const ShapeValue* shapeOutside = style.shapeOutside();
283 const ShapeValue* oldShapeOutside = oldStyle ? oldStyle->shapeOutside() : RenderStyle::initialShapeOutside();
284
285 Length shapeMargin = style.shapeMargin();
286 Length oldShapeMargin = oldStyle ? oldStyle->shapeMargin() : RenderStyle::initialShapeMargin();
287
288 float shapeImageThreshold = style.shapeImageThreshold();
289 float oldShapeImageThreshold = oldStyle ? oldStyle->shapeImageThreshold() : RenderStyle::initialShapeImageThreshold();
290
291 // FIXME: A future optimization would do a deep comparison for equality. (bug 100811)
292 if (shapeOutside == oldShapeOutside && shapeMargin == oldShapeMargin && shapeImageThreshold == oldShapeImageThreshold)
293 return;
294
295 if (!shapeOutside)
296 ShapeOutsideInfo::removeInfo(this);
297 else
298 ShapeOutsideInfo::ensureInfo(this)->dirtyShapeSize();
299
300 if (shapeOutside || shapeOutside != oldShapeOutside)
301 markShapeOutsideDependentsForLayout();
302 }
303
updateGridPositionAfterStyleChange(const RenderStyle * oldStyle)304 void RenderBox::updateGridPositionAfterStyleChange(const RenderStyle* oldStyle)
305 {
306 if (!oldStyle || !parent() || !parent()->isRenderGrid())
307 return;
308
309 if (oldStyle->gridColumnStart() == style()->gridColumnStart()
310 && oldStyle->gridColumnEnd() == style()->gridColumnEnd()
311 && oldStyle->gridRowStart() == style()->gridRowStart()
312 && oldStyle->gridRowEnd() == style()->gridRowEnd()
313 && oldStyle->order() == style()->order()
314 && oldStyle->hasOutOfFlowPosition() == style()->hasOutOfFlowPosition())
315 return;
316
317 // It should be possible to not dirty the grid in some cases (like moving an explicitly placed grid item).
318 // For now, it's more simple to just always recompute the grid.
319 toRenderGrid(parent())->dirtyGrid();
320 }
321
updateFromStyle()322 void RenderBox::updateFromStyle()
323 {
324 RenderBoxModelObject::updateFromStyle();
325
326 RenderStyle* styleToUse = style();
327 bool isRootObject = isRoot();
328 bool isViewObject = isRenderView();
329
330 // The root and the RenderView always paint their backgrounds/borders.
331 if (isRootObject || isViewObject)
332 setHasBoxDecorations(true);
333
334 setFloating(!isOutOfFlowPositioned() && styleToUse->isFloating());
335
336 bool boxHasOverflowClip = false;
337 // We also handle <body> and <html>, whose overflow applies to the viewport.
338 // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value.
339 if (styleToUse->overflowX() != OVISIBLE && !isRootObject && isRenderBlock()) {
340 // Overflow on the body can propagate to the viewport under the following conditions.
341 // (1) The root element is <html>.
342 // (2) We are the primary <body> (can be checked by looking at document.body).
343 // (3) The root element has visible overflow.
344 if (isBody() && isHTMLHtmlElement(document().documentElement())
345 && document().body() == node()
346 && document().documentElement()->renderer()->style()->overflowX() == OVISIBLE) {
347 boxHasOverflowClip = false;
348 } else {
349 boxHasOverflowClip = true;
350 if (!hasOverflowClip()) {
351 // If we are getting an overflow clip, preemptively erase any overflowing content.
352 // FIXME: This should probably consult RenderOverflow.
353 repaint();
354 }
355 }
356 }
357 setHasOverflowClip(boxHasOverflowClip);
358
359 setHasTransform(styleToUse->hasTransformRelatedProperty());
360 setHasReflection(styleToUse->boxReflect());
361 }
362
layout()363 void RenderBox::layout()
364 {
365 ASSERT(needsLayout());
366
367 LayoutRectRecorder recorder(*this);
368
369 RenderObject* child = firstChild();
370 if (!child) {
371 clearNeedsLayout();
372 return;
373 }
374
375 LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
376 while (child) {
377 child->layoutIfNeeded();
378 ASSERT(!child->needsLayout());
379 child = child->nextSibling();
380 }
381 statePusher.pop();
382 invalidateBackgroundObscurationStatus();
383 clearNeedsLayout();
384 }
385
386 // More IE extensions. clientWidth and clientHeight represent the interior of an object
387 // excluding border and scrollbar.
clientWidth() const388 LayoutUnit RenderBox::clientWidth() const
389 {
390 return width() - borderLeft() - borderRight() - verticalScrollbarWidth();
391 }
392
clientHeight() const393 LayoutUnit RenderBox::clientHeight() const
394 {
395 return height() - borderTop() - borderBottom() - horizontalScrollbarHeight();
396 }
397
pixelSnappedClientWidth() const398 int RenderBox::pixelSnappedClientWidth() const
399 {
400 return snapSizeToPixel(clientWidth(), x() + clientLeft());
401 }
402
pixelSnappedClientHeight() const403 int RenderBox::pixelSnappedClientHeight() const
404 {
405 return snapSizeToPixel(clientHeight(), y() + clientTop());
406 }
407
pixelSnappedOffsetWidth() const408 int RenderBox::pixelSnappedOffsetWidth() const
409 {
410 return snapSizeToPixel(offsetWidth(), x() + clientLeft());
411 }
412
pixelSnappedOffsetHeight() const413 int RenderBox::pixelSnappedOffsetHeight() const
414 {
415 return snapSizeToPixel(offsetHeight(), y() + clientTop());
416 }
417
canDetermineWidthWithoutLayout() const418 bool RenderBox::canDetermineWidthWithoutLayout() const
419 {
420 // FIXME: This optimization is incorrect as written.
421 // We need to be able to opt-in to this behavior only when
422 // it's guarentted correct.
423 // Until then disabling this optimization to be safe.
424 return false;
425
426 // FIXME: There are likely many subclasses of RenderBlockFlow which
427 // cannot determine their layout just from style!
428 // Perhaps we should create a "PlainRenderBlockFlow"
429 // and move this optimization there?
430 if (!isRenderBlockFlow()
431 // Flexbox items can be expanded beyond their width.
432 || isFlexItemIncludingDeprecated()
433 // Table Layout controls cell size and can expand beyond width.
434 || isTableCell())
435 return false;
436
437 RenderStyle* style = this->style();
438 return style->width().isFixed()
439 && style->minWidth().isFixed()
440 && (style->maxWidth().isUndefined() || style->maxWidth().isFixed())
441 && style->paddingLeft().isFixed()
442 && style->paddingRight().isFixed()
443 && style->boxSizing() == CONTENT_BOX;
444 }
445
fixedOffsetWidth() const446 LayoutUnit RenderBox::fixedOffsetWidth() const
447 {
448 ASSERT(canDetermineWidthWithoutLayout());
449
450 RenderStyle* style = this->style();
451
452 LayoutUnit width = std::max(LayoutUnit(style->minWidth().value()), LayoutUnit(style->width().value()));
453 if (style->maxWidth().isFixed())
454 width = std::min(LayoutUnit(style->maxWidth().value()), width);
455
456 LayoutUnit borderLeft = style->borderLeft().nonZero() ? style->borderLeft().width() : 0;
457 LayoutUnit borderRight = style->borderRight().nonZero() ? style->borderRight().width() : 0;
458
459 return width + borderLeft + borderRight + style->paddingLeft().value() + style->paddingRight().value();
460 }
461
scrollWidth() const462 int RenderBox::scrollWidth() const
463 {
464 if (hasOverflowClip())
465 return layer()->scrollableArea()->scrollWidth();
466 // For objects with visible overflow, this matches IE.
467 // FIXME: Need to work right with writing modes.
468 if (style()->isLeftToRightDirection())
469 return snapSizeToPixel(max(clientWidth(), layoutOverflowRect().maxX() - borderLeft()), x() + clientLeft());
470 return clientWidth() - min<LayoutUnit>(0, layoutOverflowRect().x() - borderLeft());
471 }
472
scrollHeight() const473 int RenderBox::scrollHeight() const
474 {
475 if (hasOverflowClip())
476 return layer()->scrollableArea()->scrollHeight();
477 // For objects with visible overflow, this matches IE.
478 // FIXME: Need to work right with writing modes.
479 return snapSizeToPixel(max(clientHeight(), layoutOverflowRect().maxY() - borderTop()), y() + clientTop());
480 }
481
scrollLeft() const482 int RenderBox::scrollLeft() const
483 {
484 return hasOverflowClip() ? layer()->scrollableArea()->scrollXOffset() : 0;
485 }
486
scrollTop() const487 int RenderBox::scrollTop() const
488 {
489 return hasOverflowClip() ? layer()->scrollableArea()->scrollYOffset() : 0;
490 }
491
setScrollLeft(int newLeft)492 void RenderBox::setScrollLeft(int newLeft)
493 {
494 if (hasOverflowClip())
495 layer()->scrollableArea()->scrollToXOffset(newLeft, ScrollOffsetClamped);
496 }
497
setScrollTop(int newTop)498 void RenderBox::setScrollTop(int newTop)
499 {
500 if (hasOverflowClip())
501 layer()->scrollableArea()->scrollToYOffset(newTop, ScrollOffsetClamped);
502 }
503
scrollToOffset(const IntSize & offset)504 void RenderBox::scrollToOffset(const IntSize& offset)
505 {
506 ASSERT(hasOverflowClip());
507 layer()->scrollableArea()->scrollToOffset(offset, ScrollOffsetClamped);
508 }
509
frameElementAndViewPermitScroll(HTMLFrameElementBase * frameElementBase,FrameView * frameView)510 static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView* frameView)
511 {
512 // If scrollbars aren't explicitly forbidden, permit scrolling.
513 if (frameElementBase && frameElementBase->scrollingMode() != ScrollbarAlwaysOff)
514 return true;
515
516 // If scrollbars are forbidden, user initiated scrolls should obviously be ignored.
517 if (frameView->wasScrolledByUser())
518 return false;
519
520 // Forbid autoscrolls when scrollbars are off, but permits other programmatic scrolls,
521 // like navigation to an anchor.
522 Page* page = frameView->frame().page();
523 if (!page)
524 return false;
525 return !page->autoscrollController().autoscrollInProgress();
526 }
527
scrollRectToVisible(const LayoutRect & rect,const ScrollAlignment & alignX,const ScrollAlignment & alignY)528 void RenderBox::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
529 {
530 RenderBox* parentBox = 0;
531 LayoutRect newRect = rect;
532
533 bool restrictedByLineClamp = false;
534 if (parent()) {
535 parentBox = parent()->enclosingBox();
536 restrictedByLineClamp = !parent()->style()->lineClamp().isNone();
537 }
538
539 if (hasOverflowClip() && !restrictedByLineClamp) {
540 // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property.
541 // This will prevent us from revealing text hidden by the slider in Safari RSS.
542 newRect = layer()->scrollableArea()->exposeRect(rect, alignX, alignY);
543 } else if (!parentBox && canBeProgramaticallyScrolled()) {
544 if (FrameView* frameView = this->frameView()) {
545 Element* ownerElement = document().ownerElement();
546
547 if (ownerElement && ownerElement->renderer()) {
548 HTMLFrameElementBase* frameElementBase = 0;
549
550 if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag))
551 frameElementBase = toHTMLFrameElementBase(ownerElement);
552
553 if (frameElementAndViewPermitScroll(frameElementBase, frameView)) {
554 LayoutRect viewRect = frameView->visibleContentRect();
555 LayoutRect exposeRect = ScrollAlignment::getRectToExpose(viewRect, rect, alignX, alignY);
556
557 int xOffset = roundToInt(exposeRect.x());
558 int yOffset = roundToInt(exposeRect.y());
559 // Adjust offsets if they're outside of the allowable range.
560 xOffset = max(0, min(frameView->contentsWidth(), xOffset));
561 yOffset = max(0, min(frameView->contentsHeight(), yOffset));
562
563 frameView->setScrollPosition(IntPoint(xOffset, yOffset));
564 if (frameView->safeToPropagateScrollToParent()) {
565 parentBox = ownerElement->renderer()->enclosingBox();
566 // FIXME: This doesn't correctly convert the rect to
567 // absolute coordinates in the parent.
568 newRect.setX(rect.x() - frameView->scrollX() + frameView->x());
569 newRect.setY(rect.y() - frameView->scrollY() + frameView->y());
570 } else {
571 parentBox = 0;
572 }
573 }
574 } else {
575 LayoutRect viewRect = frameView->visibleContentRect();
576 LayoutRect r = ScrollAlignment::getRectToExpose(viewRect, rect, alignX, alignY);
577 frameView->setScrollPosition(roundedIntPoint(r.location()));
578 }
579 }
580 }
581
582 if (frame()->page()->autoscrollController().autoscrollInProgress())
583 parentBox = enclosingScrollableBox();
584
585 if (parentBox)
586 parentBox->scrollRectToVisible(newRect, alignX, alignY);
587 }
588
absoluteRects(Vector<IntRect> & rects,const LayoutPoint & accumulatedOffset) const589 void RenderBox::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
590 {
591 rects.append(pixelSnappedIntRect(accumulatedOffset, size()));
592 }
593
absoluteQuads(Vector<FloatQuad> & quads,bool * wasFixed) const594 void RenderBox::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
595 {
596 quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height()), 0 /* mode */, wasFixed));
597 }
598
updateLayerTransform()599 void RenderBox::updateLayerTransform()
600 {
601 // Transform-origin depends on box size, so we need to update the layer transform after layout.
602 if (hasLayer())
603 layer()->updateTransform();
604 }
605
constrainLogicalWidthInRegionByMinMax(LayoutUnit logicalWidth,LayoutUnit availableWidth,RenderBlock * cb,RenderRegion * region) const606 LayoutUnit RenderBox::constrainLogicalWidthInRegionByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock* cb, RenderRegion* region) const
607 {
608 RenderStyle* styleToUse = style();
609 if (!styleToUse->logicalMaxWidth().isUndefined())
610 logicalWidth = min(logicalWidth, computeLogicalWidthInRegionUsing(MaxSize, styleToUse->logicalMaxWidth(), availableWidth, cb, region));
611 return max(logicalWidth, computeLogicalWidthInRegionUsing(MinSize, styleToUse->logicalMinWidth(), availableWidth, cb, region));
612 }
613
constrainLogicalHeightByMinMax(LayoutUnit logicalHeight,LayoutUnit intrinsicContentHeight) const614 LayoutUnit RenderBox::constrainLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const
615 {
616 RenderStyle* styleToUse = style();
617 if (!styleToUse->logicalMaxHeight().isUndefined()) {
618 LayoutUnit maxH = computeLogicalHeightUsing(styleToUse->logicalMaxHeight(), intrinsicContentHeight);
619 if (maxH != -1)
620 logicalHeight = min(logicalHeight, maxH);
621 }
622 return max(logicalHeight, computeLogicalHeightUsing(styleToUse->logicalMinHeight(), intrinsicContentHeight));
623 }
624
constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight,LayoutUnit intrinsicContentHeight) const625 LayoutUnit RenderBox::constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const
626 {
627 RenderStyle* styleToUse = style();
628 if (!styleToUse->logicalMaxHeight().isUndefined()) {
629 LayoutUnit maxH = computeContentLogicalHeight(styleToUse->logicalMaxHeight(), intrinsicContentHeight);
630 if (maxH != -1)
631 logicalHeight = min(logicalHeight, maxH);
632 }
633 return max(logicalHeight, computeContentLogicalHeight(styleToUse->logicalMinHeight(), intrinsicContentHeight));
634 }
635
absoluteContentBox() const636 IntRect RenderBox::absoluteContentBox() const
637 {
638 // This is wrong with transforms and flipped writing modes.
639 IntRect rect = pixelSnappedIntRect(contentBoxRect());
640 FloatPoint absPos = localToAbsolute();
641 rect.move(absPos.x(), absPos.y());
642 return rect;
643 }
644
absoluteContentQuad() const645 FloatQuad RenderBox::absoluteContentQuad() const
646 {
647 LayoutRect rect = contentBoxRect();
648 return localToAbsoluteQuad(FloatRect(rect));
649 }
650
outlineBoundsForRepaint(const RenderLayerModelObject * repaintContainer,const RenderGeometryMap * geometryMap) const651 LayoutRect RenderBox::outlineBoundsForRepaint(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap) const
652 {
653 LayoutRect box = borderBoundingBox();
654 adjustRectForOutlineAndShadow(box);
655
656 if (repaintContainer != this) {
657 FloatQuad containerRelativeQuad;
658 if (geometryMap)
659 containerRelativeQuad = geometryMap->mapToContainer(box, repaintContainer);
660 else
661 containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer);
662
663 box = containerRelativeQuad.enclosingBoundingBox();
664 }
665
666 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
667 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
668 box.move(view()->layoutDelta());
669
670 return box;
671 }
672
addFocusRingRects(Vector<IntRect> & rects,const LayoutPoint & additionalOffset,const RenderLayerModelObject *)673 void RenderBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*)
674 {
675 if (!size().isEmpty())
676 rects.append(pixelSnappedIntRect(additionalOffset, size()));
677 }
678
canResize() const679 bool RenderBox::canResize() const
680 {
681 // We need a special case for <iframe> because they never have
682 // hasOverflowClip(). However, they do "implicitly" clip their contents, so
683 // we want to allow resizing them also.
684 return (hasOverflowClip() || isRenderIFrame()) && style()->resize() != RESIZE_NONE;
685 }
686
addLayerHitTestRects(LayerHitTestRects & layerRects,const RenderLayer * currentLayer,const LayoutPoint & layerOffset,const LayoutRect & containerRect) const687 void RenderBox::addLayerHitTestRects(LayerHitTestRects& layerRects, const RenderLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const
688 {
689 LayoutPoint adjustedLayerOffset = layerOffset + locationOffset();
690 RenderBoxModelObject::addLayerHitTestRects(layerRects, currentLayer, adjustedLayerOffset, containerRect);
691 }
692
computeSelfHitTestRects(Vector<LayoutRect> & rects,const LayoutPoint & layerOffset) const693 void RenderBox::computeSelfHitTestRects(Vector<LayoutRect>& rects, const LayoutPoint& layerOffset) const
694 {
695 if (!size().isEmpty())
696 rects.append(LayoutRect(layerOffset, size()));
697 }
698
reflectionBox() const699 LayoutRect RenderBox::reflectionBox() const
700 {
701 LayoutRect result;
702 if (!style()->boxReflect())
703 return result;
704 LayoutRect box = borderBoxRect();
705 result = box;
706 switch (style()->boxReflect()->direction()) {
707 case ReflectionBelow:
708 result.move(0, box.height() + reflectionOffset());
709 break;
710 case ReflectionAbove:
711 result.move(0, -box.height() - reflectionOffset());
712 break;
713 case ReflectionLeft:
714 result.move(-box.width() - reflectionOffset(), 0);
715 break;
716 case ReflectionRight:
717 result.move(box.width() + reflectionOffset(), 0);
718 break;
719 }
720 return result;
721 }
722
reflectionOffset() const723 int RenderBox::reflectionOffset() const
724 {
725 if (!style()->boxReflect())
726 return 0;
727 RenderView* renderView = view();
728 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight)
729 return valueForLength(style()->boxReflect()->offset(), borderBoxRect().width(), renderView);
730 return valueForLength(style()->boxReflect()->offset(), borderBoxRect().height(), renderView);
731 }
732
reflectedRect(const LayoutRect & r) const733 LayoutRect RenderBox::reflectedRect(const LayoutRect& r) const
734 {
735 if (!style()->boxReflect())
736 return LayoutRect();
737
738 LayoutRect box = borderBoxRect();
739 LayoutRect result = r;
740 switch (style()->boxReflect()->direction()) {
741 case ReflectionBelow:
742 result.setY(box.maxY() + reflectionOffset() + (box.maxY() - r.maxY()));
743 break;
744 case ReflectionAbove:
745 result.setY(box.y() - reflectionOffset() - box.height() + (box.maxY() - r.maxY()));
746 break;
747 case ReflectionLeft:
748 result.setX(box.x() - reflectionOffset() - box.width() + (box.maxX() - r.maxX()));
749 break;
750 case ReflectionRight:
751 result.setX(box.maxX() + reflectionOffset() + (box.maxX() - r.maxX()));
752 break;
753 }
754 return result;
755 }
756
verticalScrollbarWidth() const757 int RenderBox::verticalScrollbarWidth() const
758 {
759 if (!hasOverflowClip() || style()->overflowY() == OOVERLAY)
760 return 0;
761
762 return layer()->scrollableArea()->verticalScrollbarWidth();
763 }
764
horizontalScrollbarHeight() const765 int RenderBox::horizontalScrollbarHeight() const
766 {
767 if (!hasOverflowClip() || style()->overflowX() == OOVERLAY)
768 return 0;
769
770 return layer()->scrollableArea()->horizontalScrollbarHeight();
771 }
772
instrinsicScrollbarLogicalWidth() const773 int RenderBox::instrinsicScrollbarLogicalWidth() const
774 {
775 if (!hasOverflowClip())
776 return 0;
777
778 if (isHorizontalWritingMode() && style()->overflowY() == OSCROLL) {
779 ASSERT(layer()->scrollableArea() && layer()->scrollableArea()->hasVerticalScrollbar());
780 return verticalScrollbarWidth();
781 }
782
783 if (!isHorizontalWritingMode() && style()->overflowX() == OSCROLL) {
784 ASSERT(layer()->scrollableArea() && layer()->scrollableArea()->hasHorizontalScrollbar());
785 return horizontalScrollbarHeight();
786 }
787
788 return 0;
789 }
790
scroll(ScrollDirection direction,ScrollGranularity granularity,float multiplier)791 bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
792 {
793 // Logical scroll is a higher level concept, all directions by here must be physical
794 ASSERT(!isLogical(direction));
795
796 if (!layer() || !layer()->scrollableArea())
797 return false;
798
799 return layer()->scrollableArea()->scroll(direction, granularity, multiplier);
800 }
801
canBeScrolledAndHasScrollableArea() const802 bool RenderBox::canBeScrolledAndHasScrollableArea() const
803 {
804 return canBeProgramaticallyScrolled() && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth());
805 }
806
canBeProgramaticallyScrolled() const807 bool RenderBox::canBeProgramaticallyScrolled() const
808 {
809 Node* node = this->node();
810 if (node && node->isDocumentNode())
811 return true;
812
813 if (!hasOverflowClip())
814 return false;
815
816 bool hasScrollableOverflow = hasScrollableOverflowX() || hasScrollableOverflowY();
817 if (scrollsOverflow() && hasScrollableOverflow)
818 return true;
819
820 return node && node->rendererIsEditable();
821 }
822
usesCompositedScrolling() const823 bool RenderBox::usesCompositedScrolling() const
824 {
825 return hasOverflowClip() && hasLayer() && layer()->scrollableArea()->usesCompositedScrolling();
826 }
827
autoscroll(const IntPoint & position)828 void RenderBox::autoscroll(const IntPoint& position)
829 {
830 Frame* frame = this->frame();
831 if (!frame)
832 return;
833
834 FrameView* frameView = frame->view();
835 if (!frameView)
836 return;
837
838 IntPoint currentDocumentPosition = frameView->windowToContents(position);
839 scrollRectToVisible(LayoutRect(currentDocumentPosition, LayoutSize(1, 1)), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded);
840 }
841
autoscrollInProgress() const842 bool RenderBox::autoscrollInProgress() const
843 {
844 return frame() && frame()->page() && frame()->page()->autoscrollController().autoscrollInProgress(this);
845 }
846
847 // There are two kinds of renderer that can autoscroll.
canAutoscroll() const848 bool RenderBox::canAutoscroll() const
849 {
850 if (node() && node()->isDocumentNode())
851 return view()->frameView()->isScrollable();
852
853 // Check for a box that can be scrolled in its own right.
854 return canBeScrolledAndHasScrollableArea();
855 }
856
857 // If specified point is in border belt, returned offset denotes direction of
858 // scrolling.
calculateAutoscrollDirection(const IntPoint & windowPoint) const859 IntSize RenderBox::calculateAutoscrollDirection(const IntPoint& windowPoint) const
860 {
861 if (!frame())
862 return IntSize();
863
864 FrameView* frameView = frame()->view();
865 if (!frameView)
866 return IntSize();
867
868 IntRect box(absoluteBoundingBoxRect());
869 box.move(view()->frameView()->scrollOffset());
870 IntRect windowBox = view()->frameView()->contentsToWindow(box);
871
872 IntPoint windowAutoscrollPoint = windowPoint;
873
874 if (windowAutoscrollPoint.x() < windowBox.x() + autoscrollBeltSize)
875 windowAutoscrollPoint.move(-autoscrollBeltSize, 0);
876 else if (windowAutoscrollPoint.x() > windowBox.maxX() - autoscrollBeltSize)
877 windowAutoscrollPoint.move(autoscrollBeltSize, 0);
878
879 if (windowAutoscrollPoint.y() < windowBox.y() + autoscrollBeltSize)
880 windowAutoscrollPoint.move(0, -autoscrollBeltSize);
881 else if (windowAutoscrollPoint.y() > windowBox.maxY() - autoscrollBeltSize)
882 windowAutoscrollPoint.move(0, autoscrollBeltSize);
883
884 return windowAutoscrollPoint - windowPoint;
885 }
886
findAutoscrollable(RenderObject * renderer)887 RenderBox* RenderBox::findAutoscrollable(RenderObject* renderer)
888 {
889 while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll())) {
890 if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document().ownerElement())
891 renderer = renderer->document().ownerElement()->renderer();
892 else
893 renderer = renderer->parent();
894 }
895
896 return renderer && renderer->isBox() ? toRenderBox(renderer) : 0;
897 }
898
adjustedScrollDelta(int beginningDelta)899 static inline int adjustedScrollDelta(int beginningDelta)
900 {
901 // This implemention matches Firefox's.
902 // http://mxr.mozilla.org/firefox/source/toolkit/content/widgets/browser.xml#856.
903 const int speedReducer = 12;
904
905 int adjustedDelta = beginningDelta / speedReducer;
906 if (adjustedDelta > 1)
907 adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(adjustedDelta))) - 1;
908 else if (adjustedDelta < -1)
909 adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(-adjustedDelta))) + 1;
910
911 return adjustedDelta;
912 }
913
adjustedScrollDelta(const IntSize & delta)914 static inline IntSize adjustedScrollDelta(const IntSize& delta)
915 {
916 return IntSize(adjustedScrollDelta(delta.width()), adjustedScrollDelta(delta.height()));
917 }
918
panScroll(const IntPoint & sourcePoint)919 void RenderBox::panScroll(const IntPoint& sourcePoint)
920 {
921 Frame* frame = this->frame();
922 if (!frame)
923 return;
924
925 IntPoint lastKnownMousePosition = frame->eventHandler().lastKnownMousePosition();
926
927 // We need to check if the last known mouse position is out of the window. When the mouse is out of the window, the position is incoherent
928 static IntPoint previousMousePosition;
929 if (lastKnownMousePosition.x() < 0 || lastKnownMousePosition.y() < 0)
930 lastKnownMousePosition = previousMousePosition;
931 else
932 previousMousePosition = lastKnownMousePosition;
933
934 IntSize delta = lastKnownMousePosition - sourcePoint;
935
936 if (abs(delta.width()) <= ScrollView::noPanScrollRadius) // at the center we let the space for the icon
937 delta.setWidth(0);
938 if (abs(delta.height()) <= ScrollView::noPanScrollRadius)
939 delta.setHeight(0);
940
941 scrollByRecursively(adjustedScrollDelta(delta), ScrollOffsetClamped);
942 }
943
scrollByRecursively(const IntSize & delta,ScrollOffsetClamping clamp)944 void RenderBox::scrollByRecursively(const IntSize& delta, ScrollOffsetClamping clamp)
945 {
946 if (delta.isZero())
947 return;
948
949 bool restrictedByLineClamp = false;
950 if (parent())
951 restrictedByLineClamp = !parent()->style()->lineClamp().isNone();
952
953 if (hasOverflowClip() && !restrictedByLineClamp) {
954 IntSize newScrollOffset = layer()->scrollableArea()->adjustedScrollOffset() + delta;
955 layer()->scrollableArea()->scrollToOffset(newScrollOffset, clamp);
956
957 // If this layer can't do the scroll we ask the next layer up that can scroll to try
958 IntSize remainingScrollOffset = newScrollOffset - layer()->scrollableArea()->adjustedScrollOffset();
959 if (!remainingScrollOffset.isZero() && parent()) {
960 if (RenderBox* scrollableBox = enclosingScrollableBox())
961 scrollableBox->scrollByRecursively(remainingScrollOffset, clamp);
962
963 Frame* frame = this->frame();
964 if (frame && frame->page())
965 frame->page()->autoscrollController().updateAutoscrollRenderer();
966 }
967 } else if (view()->frameView()) {
968 // If we are here, we were called on a renderer that can be programmatically scrolled, but doesn't
969 // have an overflow clip. Which means that it is a document node that can be scrolled.
970 view()->frameView()->scrollBy(delta);
971
972 // FIXME: If we didn't scroll the whole way, do we want to try looking at the frames ownerElement?
973 // https://bugs.webkit.org/show_bug.cgi?id=28237
974 }
975 }
976
needsPreferredWidthsRecalculation() const977 bool RenderBox::needsPreferredWidthsRecalculation() const
978 {
979 return style()->paddingStart().isPercent() || style()->paddingEnd().isPercent();
980 }
981
scrolledContentOffset() const982 IntSize RenderBox::scrolledContentOffset() const
983 {
984 ASSERT(hasOverflowClip());
985 ASSERT(hasLayer());
986 return layer()->scrollableArea()->scrollOffset();
987 }
988
cachedSizeForOverflowClip() const989 LayoutSize RenderBox::cachedSizeForOverflowClip() const
990 {
991 ASSERT(hasOverflowClip());
992 ASSERT(hasLayer());
993 return layer()->size();
994 }
995
applyCachedClipAndScrollOffsetForRepaint(LayoutRect & paintRect) const996 void RenderBox::applyCachedClipAndScrollOffsetForRepaint(LayoutRect& paintRect) const
997 {
998 paintRect.move(-scrolledContentOffset()); // For overflow:auto/scroll/hidden.
999
1000 // Do not clip scroll layer contents to reduce the number of repaints while scrolling.
1001 if (usesCompositedScrolling())
1002 return;
1003
1004 // height() is inaccurate if we're in the middle of a layout of this RenderBox, so use the
1005 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
1006 // anyway if its size does change.
1007 LayoutRect clipRect(LayoutPoint(), cachedSizeForOverflowClip());
1008 paintRect = intersection(paintRect, clipRect);
1009 }
1010
computeIntrinsicLogicalWidths(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth) const1011 void RenderBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
1012 {
1013 minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth();
1014 maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth();
1015 }
1016
minPreferredLogicalWidth() const1017 LayoutUnit RenderBox::minPreferredLogicalWidth() const
1018 {
1019 if (preferredLogicalWidthsDirty()) {
1020 #ifndef NDEBUG
1021 SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<RenderBox*>(this));
1022 #endif
1023 const_cast<RenderBox*>(this)->computePreferredLogicalWidths();
1024 }
1025
1026 return m_minPreferredLogicalWidth;
1027 }
1028
maxPreferredLogicalWidth() const1029 LayoutUnit RenderBox::maxPreferredLogicalWidth() const
1030 {
1031 if (preferredLogicalWidthsDirty()) {
1032 #ifndef NDEBUG
1033 SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<RenderBox*>(this));
1034 #endif
1035 const_cast<RenderBox*>(this)->computePreferredLogicalWidths();
1036 }
1037
1038 return m_maxPreferredLogicalWidth;
1039 }
1040
hasOverrideHeight() const1041 bool RenderBox::hasOverrideHeight() const
1042 {
1043 return gOverrideHeightMap && gOverrideHeightMap->contains(this);
1044 }
1045
hasOverrideWidth() const1046 bool RenderBox::hasOverrideWidth() const
1047 {
1048 return gOverrideWidthMap && gOverrideWidthMap->contains(this);
1049 }
1050
setOverrideLogicalContentHeight(LayoutUnit height)1051 void RenderBox::setOverrideLogicalContentHeight(LayoutUnit height)
1052 {
1053 if (!gOverrideHeightMap)
1054 gOverrideHeightMap = new OverrideSizeMap();
1055 gOverrideHeightMap->set(this, height);
1056 }
1057
setOverrideLogicalContentWidth(LayoutUnit width)1058 void RenderBox::setOverrideLogicalContentWidth(LayoutUnit width)
1059 {
1060 if (!gOverrideWidthMap)
1061 gOverrideWidthMap = new OverrideSizeMap();
1062 gOverrideWidthMap->set(this, width);
1063 }
1064
clearOverrideLogicalContentHeight()1065 void RenderBox::clearOverrideLogicalContentHeight()
1066 {
1067 if (gOverrideHeightMap)
1068 gOverrideHeightMap->remove(this);
1069 }
1070
clearOverrideLogicalContentWidth()1071 void RenderBox::clearOverrideLogicalContentWidth()
1072 {
1073 if (gOverrideWidthMap)
1074 gOverrideWidthMap->remove(this);
1075 }
1076
clearOverrideSize()1077 void RenderBox::clearOverrideSize()
1078 {
1079 clearOverrideLogicalContentHeight();
1080 clearOverrideLogicalContentWidth();
1081 }
1082
overrideLogicalContentWidth() const1083 LayoutUnit RenderBox::overrideLogicalContentWidth() const
1084 {
1085 ASSERT(hasOverrideWidth());
1086 return gOverrideWidthMap->get(this);
1087 }
1088
overrideLogicalContentHeight() const1089 LayoutUnit RenderBox::overrideLogicalContentHeight() const
1090 {
1091 ASSERT(hasOverrideHeight());
1092 return gOverrideHeightMap->get(this);
1093 }
1094
overrideContainingBlockContentLogicalWidth() const1095 LayoutUnit RenderBox::overrideContainingBlockContentLogicalWidth() const
1096 {
1097 ASSERT(hasOverrideContainingBlockLogicalWidth());
1098 return gOverrideContainingBlockLogicalWidthMap->get(this);
1099 }
1100
overrideContainingBlockContentLogicalHeight() const1101 LayoutUnit RenderBox::overrideContainingBlockContentLogicalHeight() const
1102 {
1103 ASSERT(hasOverrideContainingBlockLogicalHeight());
1104 return gOverrideContainingBlockLogicalHeightMap->get(this);
1105 }
1106
hasOverrideContainingBlockLogicalWidth() const1107 bool RenderBox::hasOverrideContainingBlockLogicalWidth() const
1108 {
1109 return gOverrideContainingBlockLogicalWidthMap && gOverrideContainingBlockLogicalWidthMap->contains(this);
1110 }
1111
hasOverrideContainingBlockLogicalHeight() const1112 bool RenderBox::hasOverrideContainingBlockLogicalHeight() const
1113 {
1114 return gOverrideContainingBlockLogicalHeightMap && gOverrideContainingBlockLogicalHeightMap->contains(this);
1115 }
1116
setOverrideContainingBlockContentLogicalWidth(LayoutUnit logicalWidth)1117 void RenderBox::setOverrideContainingBlockContentLogicalWidth(LayoutUnit logicalWidth)
1118 {
1119 if (!gOverrideContainingBlockLogicalWidthMap)
1120 gOverrideContainingBlockLogicalWidthMap = new OverrideSizeMap;
1121 gOverrideContainingBlockLogicalWidthMap->set(this, logicalWidth);
1122 }
1123
setOverrideContainingBlockContentLogicalHeight(LayoutUnit logicalHeight)1124 void RenderBox::setOverrideContainingBlockContentLogicalHeight(LayoutUnit logicalHeight)
1125 {
1126 if (!gOverrideContainingBlockLogicalHeightMap)
1127 gOverrideContainingBlockLogicalHeightMap = new OverrideSizeMap;
1128 gOverrideContainingBlockLogicalHeightMap->set(this, logicalHeight);
1129 }
1130
clearContainingBlockOverrideSize()1131 void RenderBox::clearContainingBlockOverrideSize()
1132 {
1133 if (gOverrideContainingBlockLogicalWidthMap)
1134 gOverrideContainingBlockLogicalWidthMap->remove(this);
1135 clearOverrideContainingBlockContentLogicalHeight();
1136 }
1137
clearOverrideContainingBlockContentLogicalHeight()1138 void RenderBox::clearOverrideContainingBlockContentLogicalHeight()
1139 {
1140 if (gOverrideContainingBlockLogicalHeightMap)
1141 gOverrideContainingBlockLogicalHeightMap->remove(this);
1142 }
1143
adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit width) const1144 LayoutUnit RenderBox::adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit width) const
1145 {
1146 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth();
1147 if (style()->boxSizing() == CONTENT_BOX)
1148 return width + bordersPlusPadding;
1149 return max(width, bordersPlusPadding);
1150 }
1151
adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const1152 LayoutUnit RenderBox::adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const
1153 {
1154 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
1155 if (style()->boxSizing() == CONTENT_BOX)
1156 return height + bordersPlusPadding;
1157 return max(height, bordersPlusPadding);
1158 }
1159
adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const1160 LayoutUnit RenderBox::adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const
1161 {
1162 if (style()->boxSizing() == BORDER_BOX)
1163 width -= borderAndPaddingLogicalWidth();
1164 return max<LayoutUnit>(0, width);
1165 }
1166
adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const1167 LayoutUnit RenderBox::adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const
1168 {
1169 if (style()->boxSizing() == BORDER_BOX)
1170 height -= borderAndPaddingLogicalHeight();
1171 return max<LayoutUnit>(0, height);
1172 }
1173
1174 // Hit Testing
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction action)1175 bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1176 {
1177 LayoutPoint adjustedLocation = accumulatedOffset + location();
1178
1179 // Check kids first.
1180 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1181 if (!child->hasLayer() && child->nodeAtPoint(request, result, locationInContainer, adjustedLocation, action)) {
1182 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
1183 return true;
1184 }
1185 }
1186
1187 // Check our bounds next. For this purpose always assume that we can only be hit in the
1188 // foreground phase (which is true for replaced elements like images).
1189 LayoutRect boundsRect = borderBoxRectInRegion(locationInContainer.region());
1190 boundsRect.moveBy(adjustedLocation);
1191 if (visibleToHitTestRequest(request) && action == HitTestForeground && locationInContainer.intersects(boundsRect)) {
1192 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
1193 if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect))
1194 return true;
1195 }
1196
1197 return false;
1198 }
1199
1200 // --------------------- painting stuff -------------------------------
1201
paint(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1202 void RenderBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1203 {
1204 LayoutPoint adjustedPaintOffset = paintOffset + location();
1205 // default implementation. Just pass paint through to the children
1206 PaintInfo childInfo(paintInfo);
1207 childInfo.updatePaintingRootForChildren(this);
1208 for (RenderObject* child = firstChild(); child; child = child->nextSibling())
1209 child->paint(childInfo, adjustedPaintOffset);
1210 }
1211
paintRootBoxFillLayers(const PaintInfo & paintInfo)1212 void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo)
1213 {
1214 if (paintInfo.skipRootBackground())
1215 return;
1216
1217 RenderObject* rootBackgroundRenderer = rendererForRootBackground();
1218
1219 const FillLayer* bgLayer = rootBackgroundRenderer->style()->backgroundLayers();
1220 Color bgColor = rootBackgroundRenderer->resolveColor(CSSPropertyBackgroundColor);
1221
1222 paintFillLayers(paintInfo, bgColor, bgLayer, view()->backgroundRect(this), BackgroundBleedNone, CompositeSourceOver, rootBackgroundRenderer);
1223 }
1224
determineBackgroundBleedAvoidance(GraphicsContext * context) const1225 BackgroundBleedAvoidance RenderBox::determineBackgroundBleedAvoidance(GraphicsContext* context) const
1226 {
1227 if (context->paintingDisabled())
1228 return BackgroundBleedNone;
1229
1230 const RenderStyle* style = this->style();
1231
1232 if (!style->hasBackground() || !style->hasBorder() || !style->hasBorderRadius() || borderImageIsLoadedAndCanBeRendered())
1233 return BackgroundBleedNone;
1234
1235 AffineTransform ctm = context->getCTM();
1236 FloatSize contextScaling(static_cast<float>(ctm.xScale()), static_cast<float>(ctm.yScale()));
1237
1238 // Because RoundedRect uses IntRect internally the inset applied by the
1239 // BackgroundBleedShrinkBackground strategy cannot be less than one integer
1240 // layout coordinate, even with subpixel layout enabled. To take that into
1241 // account, we clamp the contextScaling to 1.0 for the following test so
1242 // that borderObscuresBackgroundEdge can only return true if the border
1243 // widths are greater than 2 in both layout coordinates and screen
1244 // coordinates.
1245 // This precaution will become obsolete if RoundedRect is ever promoted to
1246 // a sub-pixel representation.
1247 if (contextScaling.width() > 1)
1248 contextScaling.setWidth(1);
1249 if (contextScaling.height() > 1)
1250 contextScaling.setHeight(1);
1251
1252 if (borderObscuresBackgroundEdge(contextScaling))
1253 return BackgroundBleedShrinkBackground;
1254 if (!style->hasAppearance() && borderObscuresBackground() && backgroundHasOpaqueTopLayer())
1255 return BackgroundBleedBackgroundOverBorder;
1256
1257 return BackgroundBleedUseTransparencyLayer;
1258 }
1259
paintBoxDecorations(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1260 void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1261 {
1262 if (!paintInfo.shouldPaintWithinRoot(this))
1263 return;
1264
1265 LayoutRect paintRect = borderBoxRectInRegion(paintInfo.renderRegion);
1266 paintRect.moveBy(paintOffset);
1267 paintBoxDecorationsWithRect(paintInfo, paintOffset, paintRect);
1268 }
1269
paintBoxDecorationsWithRect(PaintInfo & paintInfo,const LayoutPoint & paintOffset,const LayoutRect & paintRect)1270 void RenderBox::paintBoxDecorationsWithRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutRect& paintRect)
1271 {
1272 BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context);
1273
1274 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
1275 // custom shadows of their own.
1276 if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance))
1277 paintBoxShadow(paintInfo, paintRect, style(), Normal);
1278
1279 GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
1280 if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) {
1281 // To avoid the background color bleeding out behind the border, we'll render background and border
1282 // into a transparency layer, and then clip that in one go (which requires setting up the clip before
1283 // beginning the layer).
1284 RoundedRect border = style()->getRoundedBorderFor(paintRect, view());
1285 stateSaver.save();
1286 paintInfo.context->clipRoundedRect(border);
1287 paintInfo.context->beginTransparencyLayer(1);
1288 }
1289
1290 paintBackgroundWithBorderAndBoxShadow(paintInfo, paintRect, bleedAvoidance);
1291
1292 if (bleedAvoidance == BackgroundBleedUseTransparencyLayer)
1293 paintInfo.context->endLayer();
1294 }
1295
paintBackgroundWithBorderAndBoxShadow(PaintInfo & paintInfo,const LayoutRect & paintRect,BackgroundBleedAvoidance bleedAvoidance)1296 void RenderBox::paintBackgroundWithBorderAndBoxShadow(PaintInfo& paintInfo, const LayoutRect& paintRect, BackgroundBleedAvoidance bleedAvoidance)
1297 {
1298 // If we have a native theme appearance, paint that before painting our background.
1299 // The theme will tell us whether or not we should also paint the CSS background.
1300 IntRect snappedPaintRect(pixelSnappedIntRect(paintRect));
1301 bool themePainted = style()->hasAppearance() && !RenderTheme::theme().paint(this, paintInfo, snappedPaintRect);
1302 if (!themePainted) {
1303 if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
1304 paintBorder(paintInfo, paintRect, style(), bleedAvoidance);
1305
1306 paintBackground(paintInfo, paintRect, bleedAvoidance);
1307
1308 if (style()->hasAppearance())
1309 RenderTheme::theme().paintDecorations(this, paintInfo, snappedPaintRect);
1310 }
1311 paintBoxShadow(paintInfo, paintRect, style(), Inset);
1312
1313 // The theme will tell us whether or not we should also paint the CSS border.
1314 if (bleedAvoidance != BackgroundBleedBackgroundOverBorder && (!style()->hasAppearance() || (!themePainted && RenderTheme::theme().paintBorderOnly(this, paintInfo, snappedPaintRect))) && style()->hasBorder() && !(isTable() && toRenderTable(this)->collapseBorders()))
1315 paintBorder(paintInfo, paintRect, style(), bleedAvoidance);
1316 }
1317
paintBackground(const PaintInfo & paintInfo,const LayoutRect & paintRect,BackgroundBleedAvoidance bleedAvoidance)1318 void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, BackgroundBleedAvoidance bleedAvoidance)
1319 {
1320 if (isRoot()) {
1321 paintRootBoxFillLayers(paintInfo);
1322 return;
1323 }
1324 if (isBody() && skipBodyBackground(this))
1325 return;
1326 if (backgroundIsKnownToBeObscured())
1327 return;
1328 paintFillLayers(paintInfo, resolveColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect, bleedAvoidance);
1329 }
1330
backgroundPaintedExtent() const1331 LayoutRect RenderBox::backgroundPaintedExtent() const
1332 {
1333 ASSERT(hasBackground());
1334 LayoutRect backgroundRect = pixelSnappedIntRect(borderBoxRect());
1335
1336 Color backgroundColor = resolveColor(CSSPropertyBackgroundColor);
1337 if (backgroundColor.isValid() && backgroundColor.alpha())
1338 return backgroundRect;
1339 if (!style()->backgroundLayers()->image() || style()->backgroundLayers()->next())
1340 return backgroundRect;
1341 BackgroundImageGeometry geometry;
1342 const_cast<RenderBox*>(this)->calculateBackgroundImageGeometry(style()->backgroundLayers(), backgroundRect, geometry);
1343 return geometry.destRect();
1344 }
1345
backgroundIsKnownToBeOpaqueInRect(const LayoutRect & localRect) const1346 bool RenderBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const
1347 {
1348 if (isBody() && skipBodyBackground(this))
1349 return false;
1350
1351 Color backgroundColor = resolveColor(CSSPropertyBackgroundColor);
1352 if (!backgroundColor.isValid() || backgroundColor.hasAlpha())
1353 return false;
1354
1355 // If the element has appearance, it might be painted by theme.
1356 // We cannot be sure if theme paints the background opaque.
1357 // In this case it is safe to not assume opaqueness.
1358 // FIXME: May be ask theme if it paints opaque.
1359 if (style()->hasAppearance())
1360 return false;
1361 // FIXME: Check the opaqueness of background images.
1362
1363 // FIXME: Use rounded rect if border radius is present.
1364 if (style()->hasBorderRadius())
1365 return false;
1366 // FIXME: The background color clip is defined by the last layer.
1367 if (style()->backgroundLayers()->next())
1368 return false;
1369 LayoutRect backgroundRect;
1370 switch (style()->backgroundClip()) {
1371 case BorderFillBox:
1372 backgroundRect = borderBoxRect();
1373 break;
1374 case PaddingFillBox:
1375 backgroundRect = paddingBoxRect();
1376 break;
1377 case ContentFillBox:
1378 backgroundRect = contentBoxRect();
1379 break;
1380 default:
1381 break;
1382 }
1383 return backgroundRect.contains(localRect);
1384 }
1385
isCandidateForOpaquenessTest(RenderBox * childBox)1386 static bool isCandidateForOpaquenessTest(RenderBox* childBox)
1387 {
1388 RenderStyle* childStyle = childBox->style();
1389 if (childStyle->position() != StaticPosition && childBox->containingBlock() != childBox->parent())
1390 return false;
1391 if (childStyle->visibility() != VISIBLE || childStyle->shapeOutside())
1392 return false;
1393 if (!childBox->width() || !childBox->height())
1394 return false;
1395 if (RenderLayer* childLayer = childBox->layer()) {
1396 // FIXME: perhaps this could be less conservative?
1397 if (childLayer->compositingState() != NotComposited)
1398 return false;
1399 // FIXME: Deal with z-index.
1400 if (!childStyle->hasAutoZIndex())
1401 return false;
1402 if (childLayer->hasTransform() || childLayer->isTransparent() || childLayer->hasFilter())
1403 return false;
1404 if (childBox->hasOverflowClip() && childStyle->hasBorderRadius())
1405 return false;
1406 }
1407 return true;
1408 }
1409
foregroundIsKnownToBeOpaqueInRect(const LayoutRect & localRect,unsigned maxDepthToTest) const1410 bool RenderBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
1411 {
1412 if (!maxDepthToTest)
1413 return false;
1414 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
1415 if (!child->isBox())
1416 continue;
1417 RenderBox* childBox = toRenderBox(child);
1418 if (!isCandidateForOpaquenessTest(childBox))
1419 continue;
1420 LayoutPoint childLocation = childBox->location();
1421 if (childBox->isRelPositioned())
1422 childLocation.move(childBox->relativePositionOffset());
1423 LayoutRect childLocalRect = localRect;
1424 childLocalRect.moveBy(-childLocation);
1425 if (childLocalRect.y() < 0 || childLocalRect.x() < 0) {
1426 // If there is unobscured area above/left of a static positioned box then the rect is probably not covered.
1427 if (childBox->style()->position() == StaticPosition)
1428 return false;
1429 continue;
1430 }
1431 if (childLocalRect.maxY() > childBox->height() || childLocalRect.maxX() > childBox->width())
1432 continue;
1433 if (childBox->backgroundIsKnownToBeOpaqueInRect(childLocalRect))
1434 return true;
1435 if (childBox->foregroundIsKnownToBeOpaqueInRect(childLocalRect, maxDepthToTest - 1))
1436 return true;
1437 }
1438 return false;
1439 }
1440
computeBackgroundIsKnownToBeObscured()1441 bool RenderBox::computeBackgroundIsKnownToBeObscured()
1442 {
1443 // Test to see if the children trivially obscure the background.
1444 // FIXME: This test can be much more comprehensive.
1445 if (!hasBackground())
1446 return false;
1447 // Table and root background painting is special.
1448 if (isTable() || isRoot())
1449 return false;
1450 // FIXME: box-shadow is painted while background painting.
1451 if (style()->boxShadow())
1452 return false;
1453 LayoutRect backgroundRect = backgroundPaintedExtent();
1454 return foregroundIsKnownToBeOpaqueInRect(backgroundRect, backgroundObscurationTestMaxDepth);
1455 }
1456
backgroundHasOpaqueTopLayer() const1457 bool RenderBox::backgroundHasOpaqueTopLayer() const
1458 {
1459 const FillLayer* fillLayer = style()->backgroundLayers();
1460 if (!fillLayer || fillLayer->clip() != BorderFillBox)
1461 return false;
1462
1463 // Clipped with local scrolling
1464 if (hasOverflowClip() && fillLayer->attachment() == LocalBackgroundAttachment)
1465 return false;
1466
1467 if (fillLayer->hasOpaqueImage(this) && fillLayer->hasRepeatXY() && fillLayer->image()->canRender(this, style()->effectiveZoom()))
1468 return true;
1469
1470 // If there is only one layer and no image, check whether the background color is opaque
1471 if (!fillLayer->next() && !fillLayer->hasImage()) {
1472 Color bgColor = resolveColor(CSSPropertyBackgroundColor);
1473 if (bgColor.isValid() && bgColor.alpha() == 255)
1474 return true;
1475 }
1476
1477 return false;
1478 }
1479
paintMask(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1480 void RenderBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1481 {
1482 if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled())
1483 return;
1484
1485 LayoutRect paintRect = LayoutRect(paintOffset, size());
1486 paintMaskImages(paintInfo, paintRect);
1487 }
1488
paintClippingMask(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1489 void RenderBox::paintClippingMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1490 {
1491 if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseClippingMask || paintInfo.context->paintingDisabled())
1492 return;
1493
1494 if (!layer() || layer()->compositingState() != PaintsIntoOwnBacking)
1495 return;
1496
1497 // We should never have this state in this function. A layer with a mask
1498 // should have always created its own backing if it became composited.
1499 ASSERT(layer()->compositingState() != HasOwnBackingButPaintsIntoAncestor);
1500
1501 LayoutRect paintRect = LayoutRect(paintOffset, size());
1502 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black);
1503 }
1504
paintMaskImages(const PaintInfo & paintInfo,const LayoutRect & paintRect)1505 void RenderBox::paintMaskImages(const PaintInfo& paintInfo, const LayoutRect& paintRect)
1506 {
1507 // Figure out if we need to push a transparency layer to render our mask.
1508 bool pushTransparencyLayer = false;
1509 bool compositedMask = hasLayer() && layer()->hasCompositedMask();
1510 bool flattenCompositingLayers = view()->frameView() && view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers;
1511 CompositeOperator compositeOp = CompositeSourceOver;
1512
1513 bool allMaskImagesLoaded = true;
1514
1515 if (!compositedMask || flattenCompositingLayers) {
1516 pushTransparencyLayer = true;
1517 StyleImage* maskBoxImage = style()->maskBoxImage().image();
1518 const FillLayer* maskLayers = style()->maskLayers();
1519
1520 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content.
1521 if (maskBoxImage)
1522 allMaskImagesLoaded &= maskBoxImage->isLoaded();
1523
1524 if (maskLayers)
1525 allMaskImagesLoaded &= maskLayers->imagesAreLoaded();
1526
1527 paintInfo.context->setCompositeOperation(CompositeDestinationIn);
1528 paintInfo.context->beginTransparencyLayer(1);
1529 compositeOp = CompositeSourceOver;
1530 }
1531
1532 if (allMaskImagesLoaded) {
1533 paintFillLayers(paintInfo, Color(), style()->maskLayers(), paintRect, BackgroundBleedNone, compositeOp);
1534 paintNinePieceImage(paintInfo.context, paintRect, style(), style()->maskBoxImage(), compositeOp);
1535 }
1536
1537 if (pushTransparencyLayer)
1538 paintInfo.context->endLayer();
1539 }
1540
maskClipRect()1541 LayoutRect RenderBox::maskClipRect()
1542 {
1543 const NinePieceImage& maskBoxImage = style()->maskBoxImage();
1544 if (maskBoxImage.image()) {
1545 LayoutRect borderImageRect = borderBoxRect();
1546
1547 // Apply outsets to the border box.
1548 borderImageRect.expand(style()->maskBoxImageOutsets());
1549 return borderImageRect;
1550 }
1551
1552 LayoutRect result;
1553 LayoutRect borderBox = borderBoxRect();
1554 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
1555 if (maskLayer->image()) {
1556 BackgroundImageGeometry geometry;
1557 calculateBackgroundImageGeometry(maskLayer, borderBox, geometry);
1558 result.unite(geometry.destRect());
1559 }
1560 }
1561 return result;
1562 }
1563
paintFillLayers(const PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,const LayoutRect & rect,BackgroundBleedAvoidance bleedAvoidance,CompositeOperator op,RenderObject * backgroundObject)1564 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect,
1565 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject)
1566 {
1567 Vector<const FillLayer*, 8> layers;
1568 const FillLayer* curLayer = fillLayer;
1569 bool shouldDrawBackgroundInSeparateBuffer = false;
1570 while (curLayer) {
1571 layers.append(curLayer);
1572 // Stop traversal when an opaque layer is encountered.
1573 // FIXME : It would be possible for the following occlusion culling test to be more aggressive
1574 // on layers with no repeat by testing whether the image covers the layout rect.
1575 // Testing that here would imply duplicating a lot of calculations that are currently done in
1576 // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution might be to move
1577 // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here
1578 // and pass it down.
1579
1580 if (!shouldDrawBackgroundInSeparateBuffer && curLayer->blendMode() != blink::WebBlendModeNormal)
1581 shouldDrawBackgroundInSeparateBuffer = true;
1582
1583 // The clipOccludesNextLayers condition must be evaluated first to avoid short-circuiting.
1584 if (curLayer->clipOccludesNextLayers(curLayer == fillLayer) && curLayer->hasOpaqueImage(this) && curLayer->image()->canRender(this, style()->effectiveZoom()) && curLayer->hasRepeatXY() && curLayer->blendMode() == blink::WebBlendModeNormal && !boxShadowShouldBeAppliedToBackground(bleedAvoidance))
1585 break;
1586 curLayer = curLayer->next();
1587 }
1588
1589 GraphicsContext* context = paintInfo.context;
1590 if (!context)
1591 shouldDrawBackgroundInSeparateBuffer = false;
1592 if (shouldDrawBackgroundInSeparateBuffer)
1593 context->beginTransparencyLayer(1);
1594
1595 Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend();
1596 for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it)
1597 paintFillLayer(paintInfo, c, *it, rect, bleedAvoidance, op, backgroundObject);
1598
1599 if (shouldDrawBackgroundInSeparateBuffer)
1600 context->endLayer();
1601 }
1602
paintFillLayer(const PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,const LayoutRect & rect,BackgroundBleedAvoidance bleedAvoidance,CompositeOperator op,RenderObject * backgroundObject)1603 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect,
1604 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject)
1605 {
1606 paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, 0, LayoutSize(), op, backgroundObject);
1607 }
1608
layersUseImage(WrappedImagePtr image,const FillLayer * layers)1609 static bool layersUseImage(WrappedImagePtr image, const FillLayer* layers)
1610 {
1611 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
1612 if (curLayer->image() && image == curLayer->image()->data())
1613 return true;
1614 }
1615
1616 return false;
1617 }
1618
imageChanged(WrappedImagePtr image,const IntRect *)1619 void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*)
1620 {
1621 if (!parent())
1622 return;
1623
1624 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) ||
1625 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) {
1626 repaint();
1627 return;
1628 }
1629
1630 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true);
1631 if (!didFullRepaint)
1632 repaintLayerRectsForImage(image, style()->maskLayers(), false);
1633
1634
1635 if (hasLayer() && layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers()))
1636 layer()->contentChanged(MaskImageChanged);
1637 }
1638
repaintLayerRectsForImage(WrappedImagePtr image,const FillLayer * layers,bool drawingBackground)1639 bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground)
1640 {
1641 LayoutRect rendererRect;
1642 RenderBox* layerRenderer = 0;
1643
1644 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
1645 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(this, style()->effectiveZoom())) {
1646 // Now that we know this image is being used, compute the renderer and the rect
1647 // if we haven't already
1648 if (!layerRenderer) {
1649 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document().documentElement()->renderer()->hasBackground()));
1650 if (drawingRootBackground) {
1651 layerRenderer = view();
1652
1653 LayoutUnit rw;
1654 LayoutUnit rh;
1655
1656 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) {
1657 rw = frameView->contentsWidth();
1658 rh = frameView->contentsHeight();
1659 } else {
1660 rw = layerRenderer->width();
1661 rh = layerRenderer->height();
1662 }
1663 rendererRect = LayoutRect(-layerRenderer->marginLeft(),
1664 -layerRenderer->marginTop(),
1665 max(layerRenderer->width() + layerRenderer->marginWidth() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw),
1666 max(layerRenderer->height() + layerRenderer->marginHeight() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh));
1667 } else {
1668 layerRenderer = this;
1669 rendererRect = borderBoxRect();
1670 }
1671 }
1672
1673 BackgroundImageGeometry geometry;
1674 layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect, geometry);
1675 layerRenderer->repaintRectangle(geometry.destRect());
1676 if (geometry.destRect() == rendererRect)
1677 return true;
1678 }
1679 }
1680 return false;
1681 }
1682
pushContentsClip(PaintInfo & paintInfo,const LayoutPoint & accumulatedOffset,ContentsClipBehavior contentsClipBehavior)1683 bool RenderBox::pushContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset, ContentsClipBehavior contentsClipBehavior)
1684 {
1685 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask)
1686 return false;
1687
1688 bool isControlClip = hasControlClip();
1689 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer();
1690
1691 if (!isControlClip && !isOverflowClip)
1692 return false;
1693
1694 LayoutRect clipRect = isControlClip ? controlClipRect(accumulatedOffset) : overflowClipRect(accumulatedOffset, paintInfo.renderRegion);
1695 RoundedRect clipRoundedRect(0, 0, 0, 0);
1696 bool hasBorderRadius = style()->hasBorderRadius();
1697 if (hasBorderRadius)
1698 clipRoundedRect = style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size()));
1699
1700 if (contentsClipBehavior == SkipContentsClipIfPossible) {
1701 LayoutRect contentsVisualOverflow = contentsVisualOverflowRect();
1702 if (contentsVisualOverflow.isEmpty())
1703 return false;
1704
1705 // FIXME: Get rid of this slop from here and elsewhere.
1706 // Instead, properly include the outline in visual overflow.
1707 if (RenderView* view = this->view())
1708 contentsVisualOverflow.inflate(view->maximalOutlineSize());
1709
1710 LayoutRect conservativeClipRect = clipRect;
1711 if (hasBorderRadius)
1712 conservativeClipRect.intersect(clipRoundedRect.radiusCenterRect());
1713 conservativeClipRect.moveBy(-accumulatedOffset);
1714 if (hasLayer())
1715 conservativeClipRect.move(scrolledContentOffset());
1716 if (conservativeClipRect.contains(contentsVisualOverflow))
1717 return false;
1718 }
1719
1720 if (paintInfo.phase == PaintPhaseOutline)
1721 paintInfo.phase = PaintPhaseChildOutlines;
1722 else if (paintInfo.phase == PaintPhaseChildBlockBackground) {
1723 paintInfo.phase = PaintPhaseBlockBackground;
1724 paintObject(paintInfo, accumulatedOffset);
1725 paintInfo.phase = PaintPhaseChildBlockBackgrounds;
1726 }
1727 paintInfo.context->save();
1728 if (hasBorderRadius)
1729 paintInfo.context->clipRoundedRect(clipRoundedRect);
1730 paintInfo.context->clip(pixelSnappedIntRect(clipRect));
1731 return true;
1732 }
1733
popContentsClip(PaintInfo & paintInfo,PaintPhase originalPhase,const LayoutPoint & accumulatedOffset)1734 void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, const LayoutPoint& accumulatedOffset)
1735 {
1736 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer()));
1737
1738 paintInfo.context->restore();
1739 if (originalPhase == PaintPhaseOutline) {
1740 paintInfo.phase = PaintPhaseSelfOutline;
1741 paintObject(paintInfo, accumulatedOffset);
1742 paintInfo.phase = originalPhase;
1743 } else if (originalPhase == PaintPhaseChildBlockBackground)
1744 paintInfo.phase = originalPhase;
1745 }
1746
overflowClipRect(const LayoutPoint & location,RenderRegion * region,OverlayScrollbarSizeRelevancy relevancy)1747 LayoutRect RenderBox::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy)
1748 {
1749 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property
1750 // here.
1751 LayoutRect clipRect = borderBoxRectInRegion(region);
1752 clipRect.setLocation(location + clipRect.location() + LayoutSize(borderLeft(), borderTop()));
1753 clipRect.setSize(clipRect.size() - LayoutSize(borderLeft() + borderRight(), borderTop() + borderBottom()));
1754
1755 if (!hasOverflowClip())
1756 return clipRect;
1757
1758 // Subtract out scrollbars if we have them.
1759 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1760 clipRect.move(layer()->scrollableArea()->verticalScrollbarWidth(relevancy), 0);
1761 clipRect.contract(layer()->scrollableArea()->verticalScrollbarWidth(relevancy), layer()->scrollableArea()->horizontalScrollbarHeight(relevancy));
1762
1763 return clipRect;
1764 }
1765
clipRect(const LayoutPoint & location,RenderRegion * region)1766 LayoutRect RenderBox::clipRect(const LayoutPoint& location, RenderRegion* region)
1767 {
1768 LayoutRect borderBoxRect = borderBoxRectInRegion(region);
1769 LayoutRect clipRect = LayoutRect(borderBoxRect.location() + location, borderBoxRect.size());
1770 RenderView* renderView = view();
1771
1772 if (!style()->clipLeft().isAuto()) {
1773 LayoutUnit c = valueForLength(style()->clipLeft(), borderBoxRect.width(), renderView);
1774 clipRect.move(c, 0);
1775 clipRect.contract(c, 0);
1776 }
1777
1778 // We don't use the region-specific border box's width and height since clip offsets are (stupidly) specified
1779 // from the left and top edges. Therefore it's better to avoid constraining to smaller widths and heights.
1780
1781 if (!style()->clipRight().isAuto())
1782 clipRect.contract(width() - valueForLength(style()->clipRight(), width(), renderView), 0);
1783
1784 if (!style()->clipTop().isAuto()) {
1785 LayoutUnit c = valueForLength(style()->clipTop(), borderBoxRect.height(), renderView);
1786 clipRect.move(0, c);
1787 clipRect.contract(0, c);
1788 }
1789
1790 if (!style()->clipBottom().isAuto())
1791 clipRect.contract(0, height() - valueForLength(style()->clipBottom(), height(), renderView));
1792
1793 return clipRect;
1794 }
1795
shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart,LayoutUnit childMarginEnd,const RenderBlockFlow * cb,RenderRegion * region) const1796 LayoutUnit RenderBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const RenderBlockFlow* cb, RenderRegion* region) const
1797 {
1798 RenderRegion* containingBlockRegion = 0;
1799 LayoutUnit logicalTopPosition = logicalTop();
1800 if (region) {
1801 LayoutUnit offsetFromLogicalTopOfRegion = region ? region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage() : LayoutUnit();
1802 logicalTopPosition = max(logicalTopPosition, logicalTopPosition + offsetFromLogicalTopOfRegion);
1803 containingBlockRegion = cb->clampToStartAndEndRegions(region);
1804 }
1805
1806 LayoutUnit result = cb->availableLogicalWidthForLineInRegion(logicalTopPosition, false, containingBlockRegion) - childMarginStart - childMarginEnd;
1807
1808 // We need to see if margins on either the start side or the end side can contain the floats in question. If they can,
1809 // then just using the line width is inaccurate. In the case where a float completely fits, we don't need to use the line
1810 // offset at all, but can instead push all the way to the content edge of the containing block. In the case where the float
1811 // doesn't fit, we can use the line offset, but we need to grow it by the margin to reflect the fact that the margin was
1812 // "consumed" by the float. Negative margins aren't consumed by the float, and so we ignore them.
1813 if (childMarginStart > 0) {
1814 LayoutUnit startContentSide = cb->startOffsetForContent(containingBlockRegion);
1815 LayoutUnit startContentSideWithMargin = startContentSide + childMarginStart;
1816 LayoutUnit startOffset = cb->startOffsetForLineInRegion(logicalTopPosition, false, containingBlockRegion);
1817 if (startOffset > startContentSideWithMargin)
1818 result += childMarginStart;
1819 else
1820 result += startOffset - startContentSide;
1821 }
1822
1823 if (childMarginEnd > 0) {
1824 LayoutUnit endContentSide = cb->endOffsetForContent(containingBlockRegion);
1825 LayoutUnit endContentSideWithMargin = endContentSide + childMarginEnd;
1826 LayoutUnit endOffset = cb->endOffsetForLineInRegion(logicalTopPosition, false, containingBlockRegion);
1827 if (endOffset > endContentSideWithMargin)
1828 result += childMarginEnd;
1829 else
1830 result += endOffset - endContentSide;
1831 }
1832
1833 return result;
1834 }
1835
containingBlockLogicalWidthForContent() const1836 LayoutUnit RenderBox::containingBlockLogicalWidthForContent() const
1837 {
1838 if (hasOverrideContainingBlockLogicalWidth())
1839 return overrideContainingBlockContentLogicalWidth();
1840
1841 RenderBlock* cb = containingBlock();
1842 return cb->availableLogicalWidth();
1843 }
1844
containingBlockLogicalHeightForContent(AvailableLogicalHeightType heightType) const1845 LayoutUnit RenderBox::containingBlockLogicalHeightForContent(AvailableLogicalHeightType heightType) const
1846 {
1847 if (hasOverrideContainingBlockLogicalHeight())
1848 return overrideContainingBlockContentLogicalHeight();
1849
1850 RenderBlock* cb = containingBlock();
1851 return cb->availableLogicalHeight(heightType);
1852 }
1853
containingBlockLogicalWidthForContentInRegion(RenderRegion * region) const1854 LayoutUnit RenderBox::containingBlockLogicalWidthForContentInRegion(RenderRegion* region) const
1855 {
1856 if (!region)
1857 return containingBlockLogicalWidthForContent();
1858
1859 RenderBlock* cb = containingBlock();
1860 RenderRegion* containingBlockRegion = cb->clampToStartAndEndRegions(region);
1861 // FIXME: It's unclear if a region's content should use the containing block's override logical width.
1862 // If it should, the following line should call containingBlockLogicalWidthForContent.
1863 LayoutUnit result = cb->availableLogicalWidth();
1864 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(containingBlockRegion);
1865 if (!boxInfo)
1866 return result;
1867 return max<LayoutUnit>(0, result - (cb->logicalWidth() - boxInfo->logicalWidth()));
1868 }
1869
containingBlockAvailableLineWidthInRegion(RenderRegion * region) const1870 LayoutUnit RenderBox::containingBlockAvailableLineWidthInRegion(RenderRegion* region) const
1871 {
1872 RenderBlock* cb = containingBlock();
1873 RenderRegion* containingBlockRegion = 0;
1874 LayoutUnit logicalTopPosition = logicalTop();
1875 if (region) {
1876 LayoutUnit offsetFromLogicalTopOfRegion = region ? region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage() : LayoutUnit();
1877 logicalTopPosition = max(logicalTopPosition, logicalTopPosition + offsetFromLogicalTopOfRegion);
1878 containingBlockRegion = cb->clampToStartAndEndRegions(region);
1879 }
1880 return cb->availableLogicalWidthForLineInRegion(logicalTopPosition, false, containingBlockRegion, availableLogicalHeight(IncludeMarginBorderPadding));
1881 }
1882
perpendicularContainingBlockLogicalHeight() const1883 LayoutUnit RenderBox::perpendicularContainingBlockLogicalHeight() const
1884 {
1885 if (hasOverrideContainingBlockLogicalHeight())
1886 return overrideContainingBlockContentLogicalHeight();
1887
1888 RenderBlock* cb = containingBlock();
1889 if (cb->hasOverrideHeight())
1890 return cb->overrideLogicalContentHeight();
1891
1892 RenderStyle* containingBlockStyle = cb->style();
1893 Length logicalHeightLength = containingBlockStyle->logicalHeight();
1894
1895 // FIXME: For now just support fixed heights. Eventually should support percentage heights as well.
1896 if (!logicalHeightLength.isFixed()) {
1897 LayoutUnit fillFallbackExtent = containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth();
1898 LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding);
1899 return min(fillAvailableExtent, fillFallbackExtent);
1900 }
1901
1902 // Use the content box logical height as specified by the style.
1903 return cb->adjustContentBoxLogicalHeightForBoxSizing(logicalHeightLength.value());
1904 }
1905
mapLocalToContainer(const RenderLayerModelObject * repaintContainer,TransformState & transformState,MapCoordinatesFlags mode,bool * wasFixed) const1906 void RenderBox::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const
1907 {
1908 if (repaintContainer == this)
1909 return;
1910
1911 if (RenderView* v = view()) {
1912 if (v->layoutStateEnabled() && !repaintContainer) {
1913 LayoutState* layoutState = v->layoutState();
1914 LayoutSize offset = layoutState->m_paintOffset + locationOffset();
1915 if (style()->hasInFlowPosition() && layer())
1916 offset += layer()->offsetForInFlowPosition();
1917 transformState.move(offset);
1918 return;
1919 }
1920 }
1921
1922 bool containerSkipped;
1923 RenderObject* o = container(repaintContainer, &containerSkipped);
1924 if (!o)
1925 return;
1926
1927 bool isFixedPos = style()->position() == FixedPosition;
1928 bool hasTransform = hasLayer() && layer()->transform();
1929 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1930 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1931 if (hasTransform && !isFixedPos)
1932 mode &= ~IsFixed;
1933 else if (isFixedPos)
1934 mode |= IsFixed;
1935
1936 if (wasFixed)
1937 *wasFixed = mode & IsFixed;
1938
1939 LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint()));
1940
1941 bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D());
1942 if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
1943 TransformationMatrix t;
1944 getTransformFromContainer(o, containerOffset, t);
1945 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1946 } else
1947 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1948
1949 if (containerSkipped) {
1950 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1951 // to just subtract the delta between the repaintContainer and o.
1952 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1953 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1954 return;
1955 }
1956
1957 mode &= ~ApplyContainerFlip;
1958
1959 o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
1960 }
1961
mapAbsoluteToLocalPoint(MapCoordinatesFlags mode,TransformState & transformState) const1962 void RenderBox::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
1963 {
1964 // We don't expect to be called during layout.
1965 ASSERT(!view() || !view()->layoutStateEnabled());
1966
1967 bool isFixedPos = style()->position() == FixedPosition;
1968 bool hasTransform = hasLayer() && layer()->transform();
1969 if (hasTransform && !isFixedPos) {
1970 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1971 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1972 mode &= ~IsFixed;
1973 } else if (isFixedPos)
1974 mode |= IsFixed;
1975
1976 RenderBoxModelObject::mapAbsoluteToLocalPoint(mode, transformState);
1977 }
1978
offsetFromContainer(RenderObject * o,const LayoutPoint & point,bool * offsetDependsOnPoint) const1979 LayoutSize RenderBox::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const
1980 {
1981 // A region "has" boxes inside it without being their container.
1982 // FIXME: change container() / containingBlock() to count for boxes being positioned relative to the region, not the
1983 // FlowThread. This requires a separate patch as a simple test with such a change in container() causes 129 out of
1984 // 337 regions tests to fail.
1985 ASSERT(o == container() || o->isRenderRegion());
1986
1987 LayoutSize offset;
1988 if (isInFlowPositioned())
1989 offset += offsetForInFlowPosition();
1990
1991 if (!isInline() || isReplaced()) {
1992 if (!style()->hasOutOfFlowPosition() && o->hasColumns()) {
1993 RenderBlock* block = toRenderBlock(o);
1994 LayoutRect columnRect(frameRect());
1995 block->adjustStartEdgeForWritingModeIncludingColumns(columnRect);
1996 offset += toSize(columnRect.location());
1997 LayoutPoint columnPoint = block->flipForWritingModeIncludingColumns(point + offset);
1998 offset = toLayoutSize(block->flipForWritingModeIncludingColumns(toLayoutPoint(offset)));
1999 o->adjustForColumns(offset, columnPoint);
2000 offset = block->flipForWritingMode(offset);
2001
2002 if (offsetDependsOnPoint)
2003 *offsetDependsOnPoint = true;
2004 } else
2005 offset += topLeftLocationOffset();
2006 }
2007
2008 if (o->hasOverflowClip())
2009 offset -= toRenderBox(o)->scrolledContentOffset();
2010
2011 if (style()->position() == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline())
2012 offset += toRenderInline(o)->offsetForInFlowPositionedInline(this);
2013
2014 if (offsetDependsOnPoint)
2015 *offsetDependsOnPoint |= o->isRenderFlowThread();
2016
2017 return offset;
2018 }
2019
createInlineBox()2020 InlineBox* RenderBox::createInlineBox()
2021 {
2022 return new InlineBox(this);
2023 }
2024
dirtyLineBoxes(bool fullLayout)2025 void RenderBox::dirtyLineBoxes(bool fullLayout)
2026 {
2027 if (m_inlineBoxWrapper) {
2028 if (fullLayout) {
2029 m_inlineBoxWrapper->destroy();
2030 m_inlineBoxWrapper = 0;
2031 } else
2032 m_inlineBoxWrapper->dirtyLineBoxes();
2033 }
2034 }
2035
positionLineBox(InlineBox * box)2036 void RenderBox::positionLineBox(InlineBox* box)
2037 {
2038 if (isOutOfFlowPositioned()) {
2039 // Cache the x position only if we were an INLINE type originally.
2040 bool wasInline = style()->isOriginalDisplayInlineType();
2041 if (wasInline) {
2042 // The value is cached in the xPos of the box. We only need this value if
2043 // our object was inline originally, since otherwise it would have ended up underneath
2044 // the inlines.
2045 RootInlineBox* root = box->root();
2046 root->block()->setStaticInlinePositionForChild(this, root->lineTopWithLeading(), LayoutUnit::fromFloatRound(box->logicalLeft()));
2047 if (style()->hasStaticInlinePosition(box->isHorizontal()))
2048 setChildNeedsLayout(MarkOnlyThis); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
2049 } else {
2050 // Our object was a block originally, so we make our normal flow position be
2051 // just below the line box (as though all the inlines that came before us got
2052 // wrapped in an anonymous block, which is what would have happened had we been
2053 // in flow). This value was cached in the y() of the box.
2054 layer()->setStaticBlockPosition(box->logicalTop());
2055 if (style()->hasStaticBlockPosition(box->isHorizontal()))
2056 setChildNeedsLayout(MarkOnlyThis); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
2057 }
2058
2059 // Nuke the box.
2060 box->remove();
2061 box->destroy();
2062 } else if (isReplaced()) {
2063 setLocation(roundedLayoutPoint(box->topLeft()));
2064 setInlineBoxWrapper(box);
2065 }
2066 }
2067
deleteLineBoxWrapper()2068 void RenderBox::deleteLineBoxWrapper()
2069 {
2070 if (m_inlineBoxWrapper) {
2071 if (!documentBeingDestroyed())
2072 m_inlineBoxWrapper->remove();
2073 m_inlineBoxWrapper->destroy();
2074 m_inlineBoxWrapper = 0;
2075 }
2076 }
2077
clippedOverflowRectForRepaint(const RenderLayerModelObject * repaintContainer) const2078 LayoutRect RenderBox::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
2079 {
2080 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
2081 return LayoutRect();
2082
2083 LayoutRect r = visualOverflowRect();
2084
2085 RenderView* v = view();
2086 if (v) {
2087 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
2088 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
2089 r.move(v->layoutDelta());
2090 }
2091
2092 if (style()) {
2093 // We have to use maximalOutlineSize() because a child might have an outline
2094 // that projects outside of our overflowRect.
2095 if (v) {
2096 ASSERT(style()->outlineSize() <= v->maximalOutlineSize());
2097 r.inflate(v->maximalOutlineSize());
2098 }
2099 }
2100
2101 computeRectForRepaint(repaintContainer, r);
2102 return r;
2103 }
2104
computeRectForRepaint(const RenderLayerModelObject * repaintContainer,LayoutRect & rect,bool fixed) const2105 void RenderBox::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const
2106 {
2107 // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space.
2108 // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate
2109 // offset corner for the enclosing container). This allows for a fully RL or BT document to repaint
2110 // properly even during layout, since the rect remains flipped all the way until the end.
2111 //
2112 // RenderView::computeRectForRepaint then converts the rect to physical coordinates. We also convert to
2113 // physical when we hit a repaintContainer boundary. Therefore the final rect returned is always in the
2114 // physical coordinate space of the repaintContainer.
2115 RenderStyle* styleToUse = style();
2116 if (RenderView* v = view()) {
2117 // LayoutState is only valid for root-relative, non-fixed position repainting
2118 if (v->layoutStateEnabled() && !repaintContainer && styleToUse->position() != FixedPosition) {
2119 LayoutState* layoutState = v->layoutState();
2120
2121 if (layer() && layer()->transform())
2122 rect = layer()->transform()->mapRect(pixelSnappedIntRect(rect));
2123
2124 // We can't trust the bits on RenderObject, because this might be called while re-resolving style.
2125 if (styleToUse->hasInFlowPosition() && layer())
2126 rect.move(layer()->offsetForInFlowPosition());
2127
2128 rect.moveBy(location());
2129 rect.move(layoutState->m_paintOffset);
2130 if (layoutState->m_clipped)
2131 rect.intersect(layoutState->m_clipRect);
2132 return;
2133 }
2134 }
2135
2136 if (hasReflection())
2137 rect.unite(reflectedRect(rect));
2138
2139 if (repaintContainer == this) {
2140 if (repaintContainer->style()->isFlippedBlocksWritingMode())
2141 flipForWritingMode(rect);
2142 return;
2143 }
2144
2145 bool containerSkipped;
2146 RenderObject* o = container(repaintContainer, &containerSkipped);
2147 if (!o)
2148 return;
2149
2150 if (isWritingModeRoot() && !isOutOfFlowPositioned())
2151 flipForWritingMode(rect);
2152
2153 LayoutPoint topLeft = rect.location();
2154 topLeft.move(locationOffset());
2155
2156 EPosition position = styleToUse->position();
2157
2158 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box
2159 // in the parent's coordinate space that encloses us.
2160 if (hasLayer() && layer()->transform()) {
2161 fixed = position == FixedPosition;
2162 rect = layer()->transform()->mapRect(pixelSnappedIntRect(rect));
2163 topLeft = rect.location();
2164 topLeft.move(locationOffset());
2165 } else if (position == FixedPosition)
2166 fixed = true;
2167
2168 if (position == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline()) {
2169 topLeft += toRenderInline(o)->offsetForInFlowPositionedInline(this);
2170 } else if (styleToUse->hasInFlowPosition() && layer()) {
2171 // Apply the relative position offset when invalidating a rectangle. The layer
2172 // is translated, but the render box isn't, so we need to do this to get the
2173 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
2174 // flag on the RenderObject has been cleared, so use the one on the style().
2175 topLeft += layer()->offsetForInFlowPosition();
2176 }
2177
2178 if (position != AbsolutePosition && position != FixedPosition && o->hasColumns() && o->isRenderBlockFlow()) {
2179 LayoutRect repaintRect(topLeft, rect.size());
2180 toRenderBlock(o)->adjustRectForColumns(repaintRect);
2181 topLeft = repaintRect.location();
2182 rect = repaintRect;
2183 }
2184
2185 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
2186 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
2187 rect.setLocation(topLeft);
2188 if (o->hasOverflowClip()) {
2189 RenderBox* containerBox = toRenderBox(o);
2190 containerBox->applyCachedClipAndScrollOffsetForRepaint(rect);
2191 if (rect.isEmpty())
2192 return;
2193 }
2194
2195 if (containerSkipped) {
2196 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
2197 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
2198 rect.move(-containerOffset);
2199 return;
2200 }
2201
2202 o->computeRectForRepaint(repaintContainer, rect, fixed);
2203 }
2204
repaintDuringLayoutIfMoved(const LayoutRect & oldRect)2205 void RenderBox::repaintDuringLayoutIfMoved(const LayoutRect& oldRect)
2206 {
2207 if (oldRect.location() != m_frameRect.location()) {
2208 LayoutRect newRect = m_frameRect;
2209 // The child moved. Invalidate the object's old and new positions. We have to do this
2210 // since the object may not have gotten a layout.
2211 m_frameRect = oldRect;
2212 repaint();
2213 repaintOverhangingFloats(true);
2214 m_frameRect = newRect;
2215 repaint();
2216 repaintOverhangingFloats(true);
2217 }
2218 }
2219
repaintOverhangingFloats(bool)2220 void RenderBox::repaintOverhangingFloats(bool)
2221 {
2222 }
2223
updateLogicalWidth()2224 void RenderBox::updateLogicalWidth()
2225 {
2226 LogicalExtentComputedValues computedValues;
2227 computeLogicalWidthInRegion(computedValues);
2228
2229 setLogicalWidth(computedValues.m_extent);
2230 setLogicalLeft(computedValues.m_position);
2231 setMarginStart(computedValues.m_margins.m_start);
2232 setMarginEnd(computedValues.m_margins.m_end);
2233 }
2234
getMaxWidthListMarker(const RenderBox * renderer)2235 static float getMaxWidthListMarker(const RenderBox* renderer)
2236 {
2237 #ifndef NDEBUG
2238 ASSERT(renderer);
2239 Node* parentNode = renderer->generatingNode();
2240 ASSERT(parentNode);
2241 ASSERT(parentNode->hasTagName(olTag) || parentNode->hasTagName(ulTag));
2242 ASSERT(renderer->style()->textAutosizingMultiplier() != 1);
2243 #endif
2244 float maxWidth = 0;
2245 for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling()) {
2246 if (!child->isListItem())
2247 continue;
2248
2249 RenderBox* listItem = toRenderBox(child);
2250 for (RenderObject* itemChild = listItem->firstChild(); itemChild; itemChild = itemChild->nextSibling()) {
2251 if (!itemChild->isListMarker())
2252 continue;
2253 RenderBox* itemMarker = toRenderBox(itemChild);
2254 // FIXME: canDetermineWidthWithoutLayout expects us to use fixedOffsetWidth, which this code
2255 // does not do! This check is likely wrong.
2256 if (!itemMarker->canDetermineWidthWithoutLayout() && itemMarker->needsLayout()) {
2257 // Make sure to compute the autosized width.
2258 itemMarker->layout();
2259 }
2260 maxWidth = max<float>(maxWidth, toRenderListMarker(itemMarker)->logicalWidth().toFloat());
2261 break;
2262 }
2263 }
2264 return maxWidth;
2265 }
2266
computeLogicalWidthInRegion(LogicalExtentComputedValues & computedValues,RenderRegion * region) const2267 void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& computedValues, RenderRegion* region) const
2268 {
2269 computedValues.m_extent = logicalWidth();
2270 computedValues.m_position = logicalLeft();
2271 computedValues.m_margins.m_start = marginStart();
2272 computedValues.m_margins.m_end = marginEnd();
2273
2274 if (isOutOfFlowPositioned()) {
2275 // FIXME: This calculation is not patched for block-flow yet.
2276 // https://bugs.webkit.org/show_bug.cgi?id=46500
2277 computePositionedLogicalWidth(computedValues, region);
2278 return;
2279 }
2280
2281 // If layout is limited to a subtree, the subtree root's logical width does not change.
2282 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this)
2283 return;
2284
2285 // The parent box is flexing us, so it has increased or decreased our
2286 // width. Use the width from the style context.
2287 // FIXME: Account for block-flow in flexible boxes.
2288 // https://bugs.webkit.org/show_bug.cgi?id=46418
2289 if (hasOverrideWidth() && (style()->borderFit() == BorderFitLines || parent()->isFlexibleBoxIncludingDeprecated())) {
2290 computedValues.m_extent = overrideLogicalContentWidth() + borderAndPaddingLogicalWidth();
2291 return;
2292 }
2293
2294 // FIXME: Account for block-flow in flexible boxes.
2295 // https://bugs.webkit.org/show_bug.cgi?id=46418
2296 bool inVerticalBox = parent()->isDeprecatedFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL);
2297 bool stretching = (parent()->style()->boxAlign() == BSTRETCH);
2298 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching);
2299
2300 RenderStyle* styleToUse = style();
2301 Length logicalWidthLength = treatAsReplaced ? Length(computeReplacedLogicalWidth(), Fixed) : styleToUse->logicalWidth();
2302
2303 RenderBlock* cb = containingBlock();
2304 LayoutUnit containerLogicalWidth = max<LayoutUnit>(0, containingBlockLogicalWidthForContentInRegion(region));
2305 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode();
2306
2307 if (isInline() && !isInlineBlockOrInlineTable()) {
2308 // just calculate margins
2309 RenderView* renderView = view();
2310 computedValues.m_margins.m_start = minimumValueForLength(styleToUse->marginStart(), containerLogicalWidth, renderView);
2311 computedValues.m_margins.m_end = minimumValueForLength(styleToUse->marginEnd(), containerLogicalWidth, renderView);
2312 if (treatAsReplaced)
2313 computedValues.m_extent = max<LayoutUnit>(floatValueForLength(logicalWidthLength, 0, 0) + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth());
2314 return;
2315 }
2316
2317 // Width calculations
2318 if (treatAsReplaced)
2319 computedValues.m_extent = logicalWidthLength.value() + borderAndPaddingLogicalWidth();
2320 else {
2321 LayoutUnit containerWidthInInlineDirection = containerLogicalWidth;
2322 if (hasPerpendicularContainingBlock)
2323 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight();
2324 LayoutUnit preferredWidth = computeLogicalWidthInRegionUsing(MainOrPreferredSize, styleToUse->logicalWidth(), containerWidthInInlineDirection, cb, region);
2325 computedValues.m_extent = constrainLogicalWidthInRegionByMinMax(preferredWidth, containerWidthInInlineDirection, cb, region);
2326 }
2327
2328 // Margin calculations.
2329 if (hasPerpendicularContainingBlock || isFloating() || isInline()) {
2330 RenderView* renderView = view();
2331 computedValues.m_margins.m_start = minimumValueForLength(styleToUse->marginStart(), containerLogicalWidth, renderView);
2332 computedValues.m_margins.m_end = minimumValueForLength(styleToUse->marginEnd(), containerLogicalWidth, renderView);
2333 } else {
2334 LayoutUnit containerLogicalWidthForAutoMargins = containerLogicalWidth;
2335 if (avoidsFloats() && cb->containsFloats())
2336 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(region);
2337 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection();
2338 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, computedValues.m_extent,
2339 hasInvertedDirection ? computedValues.m_margins.m_end : computedValues.m_margins.m_start,
2340 hasInvertedDirection ? computedValues.m_margins.m_start : computedValues.m_margins.m_end);
2341 }
2342
2343 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (computedValues.m_extent + computedValues.m_margins.m_start + computedValues.m_margins.m_end)
2344 && !isFloating() && !isInline() && !cb->isFlexibleBoxIncludingDeprecated() && !cb->isRenderGrid()) {
2345 LayoutUnit newMargin = containerLogicalWidth - computedValues.m_extent - cb->marginStartForChild(this);
2346 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection();
2347 if (hasInvertedDirection)
2348 computedValues.m_margins.m_start = newMargin;
2349 else
2350 computedValues.m_margins.m_end = newMargin;
2351 }
2352
2353 if (styleToUse->textAutosizingMultiplier() != 1 && styleToUse->marginStart().type() == Fixed) {
2354 Node* parentNode = generatingNode();
2355 if (parentNode && (parentNode->hasTagName(olTag) || parentNode->hasTagName(ulTag))) {
2356 // Make sure the markers in a list are properly positioned (i.e. not chopped off) when autosized.
2357 const float adjustedMargin = (1 - 1.0 / styleToUse->textAutosizingMultiplier()) * getMaxWidthListMarker(this);
2358 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection();
2359 if (hasInvertedDirection)
2360 computedValues.m_margins.m_end += adjustedMargin;
2361 else
2362 computedValues.m_margins.m_start += adjustedMargin;
2363 }
2364 }
2365 }
2366
fillAvailableMeasure(LayoutUnit availableLogicalWidth) const2367 LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth) const
2368 {
2369 LayoutUnit marginStart = 0;
2370 LayoutUnit marginEnd = 0;
2371 return fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd);
2372 }
2373
fillAvailableMeasure(LayoutUnit availableLogicalWidth,LayoutUnit & marginStart,LayoutUnit & marginEnd) const2374 LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const
2375 {
2376 RenderView* renderView = view();
2377 marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView);
2378 marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView);
2379 return availableLogicalWidth - marginStart - marginEnd;
2380 }
2381
computeIntrinsicLogicalWidthUsing(Length logicalWidthLength,LayoutUnit availableLogicalWidth,LayoutUnit borderAndPadding) const2382 LayoutUnit RenderBox::computeIntrinsicLogicalWidthUsing(Length logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const
2383 {
2384 if (logicalWidthLength.type() == FillAvailable)
2385 return fillAvailableMeasure(availableLogicalWidth);
2386
2387 LayoutUnit minLogicalWidth = 0;
2388 LayoutUnit maxLogicalWidth = 0;
2389 computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth);
2390
2391 if (logicalWidthLength.type() == MinContent)
2392 return minLogicalWidth + borderAndPadding;
2393
2394 if (logicalWidthLength.type() == MaxContent)
2395 return maxLogicalWidth + borderAndPadding;
2396
2397 if (logicalWidthLength.type() == FitContent) {
2398 minLogicalWidth += borderAndPadding;
2399 maxLogicalWidth += borderAndPadding;
2400 return max(minLogicalWidth, min(maxLogicalWidth, fillAvailableMeasure(availableLogicalWidth)));
2401 }
2402
2403 ASSERT_NOT_REACHED();
2404 return 0;
2405 }
2406
computeLogicalWidthInRegionUsing(SizeType widthType,Length logicalWidth,LayoutUnit availableLogicalWidth,const RenderBlock * cb,RenderRegion * region) const2407 LayoutUnit RenderBox::computeLogicalWidthInRegionUsing(SizeType widthType, Length logicalWidth, LayoutUnit availableLogicalWidth,
2408 const RenderBlock* cb, RenderRegion* region) const
2409 {
2410 if (!logicalWidth.isIntrinsicOrAuto()) {
2411 // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead.
2412 return adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, availableLogicalWidth, view()));
2413 }
2414
2415 if (logicalWidth.isIntrinsic())
2416 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth());
2417
2418 LayoutUnit marginStart = 0;
2419 LayoutUnit marginEnd = 0;
2420 LayoutUnit logicalWidthResult = fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd);
2421
2422 if (shrinkToAvoidFloats() && cb->containsFloats())
2423 logicalWidthResult = min(logicalWidthResult, shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, toRenderBlockFlow(cb), region));
2424
2425 if (widthType == MainOrPreferredSize && sizesLogicalWidthToFitContent(widthType))
2426 return max(minPreferredLogicalWidth(), min(maxPreferredLogicalWidth(), logicalWidthResult));
2427 return logicalWidthResult;
2428 }
2429
columnFlexItemHasStretchAlignment(const RenderObject * flexitem)2430 static bool columnFlexItemHasStretchAlignment(const RenderObject* flexitem)
2431 {
2432 RenderObject* parent = flexitem->parent();
2433 // auto margins mean we don't stretch. Note that this function will only be used for
2434 // widths, so we don't have to check marginBefore/marginAfter.
2435 ASSERT(parent->style()->isColumnFlexDirection());
2436 if (flexitem->style()->marginStart().isAuto() || flexitem->style()->marginEnd().isAuto())
2437 return false;
2438 return flexitem->style()->alignSelf() == AlignStretch || (flexitem->style()->alignSelf() == AlignAuto && parent->style()->alignItems() == AlignStretch);
2439 }
2440
isStretchingColumnFlexItem(const RenderObject * flexitem)2441 static bool isStretchingColumnFlexItem(const RenderObject* flexitem)
2442 {
2443 RenderObject* parent = flexitem->parent();
2444 if (parent->isDeprecatedFlexibleBox() && parent->style()->boxOrient() == VERTICAL && parent->style()->boxAlign() == BSTRETCH)
2445 return true;
2446
2447 // We don't stretch multiline flexboxes because they need to apply line spacing (align-content) first.
2448 if (parent->isFlexibleBox() && parent->style()->flexWrap() == FlexNoWrap && parent->style()->isColumnFlexDirection() && columnFlexItemHasStretchAlignment(flexitem))
2449 return true;
2450 return false;
2451 }
2452
sizesLogicalWidthToFitContent(SizeType widthType) const2453 bool RenderBox::sizesLogicalWidthToFitContent(SizeType widthType) const
2454 {
2455 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
2456 // but they allow text to sit on the same line as the marquee.
2457 if (isFloating() || (isInlineBlockOrInlineTable() && !isMarquee()))
2458 return true;
2459
2460 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both
2461 // min-width and width. max-width is only clamped if it is also intrinsic.
2462 Length logicalWidth = (widthType == MaxSize) ? style()->logicalMaxWidth() : style()->logicalWidth();
2463 if (logicalWidth.type() == Intrinsic)
2464 return true;
2465
2466 // Children of a horizontal marquee do not fill the container by default.
2467 // FIXME: Need to deal with MAUTO value properly. It could be vertical.
2468 // FIXME: Think about block-flow here. Need to find out how marquee direction relates to
2469 // block-flow (as well as how marquee overflow should relate to block flow).
2470 // https://bugs.webkit.org/show_bug.cgi?id=46472
2471 if (parent()->isMarquee()) {
2472 EMarqueeDirection dir = parent()->style()->marqueeDirection();
2473 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
2474 return true;
2475 }
2476
2477 // Flexible box items should shrink wrap, so we lay them out at their intrinsic widths.
2478 // In the case of columns that have a stretch alignment, we go ahead and layout at the
2479 // stretched size to avoid an extra layout when applying alignment.
2480 if (parent()->isFlexibleBox()) {
2481 // For multiline columns, we need to apply align-content first, so we can't stretch now.
2482 if (!parent()->style()->isColumnFlexDirection() || parent()->style()->flexWrap() != FlexNoWrap)
2483 return true;
2484 if (!columnFlexItemHasStretchAlignment(this))
2485 return true;
2486 }
2487
2488 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes
2489 // that don't stretch their kids lay out their children at their intrinsic widths.
2490 // FIXME: Think about block-flow here.
2491 // https://bugs.webkit.org/show_bug.cgi?id=46473
2492 if (parent()->isDeprecatedFlexibleBox() && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
2493 return true;
2494
2495 // Button, input, select, textarea, and legend treat width value of 'auto' as 'intrinsic' unless it's in a
2496 // stretching column flexbox.
2497 // FIXME: Think about block-flow here.
2498 // https://bugs.webkit.org/show_bug.cgi?id=46473
2499 if (logicalWidth.type() == Auto && !isStretchingColumnFlexItem(this) && autoWidthShouldFitContent())
2500 return true;
2501
2502 if (isHorizontalWritingMode() != containingBlock()->isHorizontalWritingMode())
2503 return true;
2504
2505 return false;
2506 }
2507
autoWidthShouldFitContent() const2508 bool RenderBox::autoWidthShouldFitContent() const
2509 {
2510 if (node() && (node()->hasTagName(inputTag) || node()->hasTagName(selectTag) || node()->hasTagName(buttonTag)
2511 || isHTMLTextAreaElement(node()) || node()->hasTagName(legendTag)))
2512 return true;
2513
2514 return false;
2515 }
2516
computeInlineDirectionMargins(RenderBlock * containingBlock,LayoutUnit containerWidth,LayoutUnit childWidth,LayoutUnit & marginStart,LayoutUnit & marginEnd) const2517 void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, LayoutUnit containerWidth, LayoutUnit childWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const
2518 {
2519 const RenderStyle* containingBlockStyle = containingBlock->style();
2520 Length marginStartLength = style()->marginStartUsing(containingBlockStyle);
2521 Length marginEndLength = style()->marginEndUsing(containingBlockStyle);
2522 RenderView* renderView = view();
2523
2524 if (isFloating() || isInline()) {
2525 // Inline blocks/tables and floats don't have their margins increased.
2526 marginStart = minimumValueForLength(marginStartLength, containerWidth, renderView);
2527 marginEnd = minimumValueForLength(marginEndLength, containerWidth, renderView);
2528 return;
2529 }
2530
2531 if (containingBlock->isFlexibleBox()) {
2532 // We need to let flexbox handle the margin adjustment - otherwise, flexbox
2533 // will think we're wider than we actually are and calculate line sizes wrong.
2534 // See also http://dev.w3.org/csswg/css-flexbox/#auto-margins
2535 if (marginStartLength.isAuto())
2536 marginStartLength.setValue(0);
2537 if (marginEndLength.isAuto())
2538 marginEndLength.setValue(0);
2539 }
2540
2541 // Case One: The object is being centered in the containing block's available logical width.
2542 if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth)
2543 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) {
2544 // Other browsers center the margin box for align=center elements so we match them here.
2545 LayoutUnit marginStartWidth = minimumValueForLength(marginStartLength, containerWidth, renderView);
2546 LayoutUnit marginEndWidth = minimumValueForLength(marginEndLength, containerWidth, renderView);
2547 LayoutUnit centeredMarginBoxStart = max<LayoutUnit>(0, (containerWidth - childWidth - marginStartWidth - marginEndWidth) / 2);
2548 marginStart = centeredMarginBoxStart + marginStartWidth;
2549 marginEnd = containerWidth - childWidth - marginStart + marginEndWidth;
2550 return;
2551 }
2552
2553 // Case Two: The object is being pushed to the start of the containing block's available logical width.
2554 if (marginEndLength.isAuto() && childWidth < containerWidth) {
2555 marginStart = valueForLength(marginStartLength, containerWidth, renderView);
2556 marginEnd = containerWidth - childWidth - marginStart;
2557 return;
2558 }
2559
2560 // Case Three: The object is being pushed to the end of the containing block's available logical width.
2561 bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT)
2562 || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT));
2563 if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) {
2564 marginEnd = valueForLength(marginEndLength, containerWidth, renderView);
2565 marginStart = containerWidth - childWidth - marginEnd;
2566 return;
2567 }
2568
2569 // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case
2570 // auto margins will just turn into 0.
2571 marginStart = minimumValueForLength(marginStartLength, containerWidth, renderView);
2572 marginEnd = minimumValueForLength(marginEndLength, containerWidth, renderView);
2573 }
2574
renderBoxRegionInfo(RenderRegion * region,RenderBoxRegionInfoFlags cacheFlag) const2575 RenderBoxRegionInfo* RenderBox::renderBoxRegionInfo(RenderRegion* region, RenderBoxRegionInfoFlags cacheFlag) const
2576 {
2577 // Make sure nobody is trying to call this with a null region.
2578 if (!region)
2579 return 0;
2580
2581 // If we have computed our width in this region already, it will be cached, and we can
2582 // just return it.
2583 RenderBoxRegionInfo* boxInfo = region->renderBoxRegionInfo(this);
2584 if (boxInfo && cacheFlag == CacheRenderBoxRegionInfo)
2585 return boxInfo;
2586
2587 // No cached value was found, so we have to compute our insets in this region.
2588 // FIXME: For now we limit this computation to normal RenderBlocks. Future patches will expand
2589 // support to cover all boxes.
2590 RenderFlowThread* flowThread = flowThreadContainingBlock();
2591 if (isRenderFlowThread() || !flowThread || !canHaveBoxInfoInRegion() || flowThread->style()->writingMode() != style()->writingMode())
2592 return 0;
2593
2594 LogicalExtentComputedValues computedValues;
2595 computeLogicalWidthInRegion(computedValues, region);
2596
2597 // Now determine the insets based off where this object is supposed to be positioned.
2598 RenderBlock* cb = containingBlock();
2599 RenderRegion* clampedContainingBlockRegion = cb->clampToStartAndEndRegions(region);
2600 RenderBoxRegionInfo* containingBlockInfo = cb->renderBoxRegionInfo(clampedContainingBlockRegion);
2601 LayoutUnit containingBlockLogicalWidth = cb->logicalWidth();
2602 LayoutUnit containingBlockLogicalWidthInRegion = containingBlockInfo ? containingBlockInfo->logicalWidth() : containingBlockLogicalWidth;
2603
2604 LayoutUnit marginStartInRegion = computedValues.m_margins.m_start;
2605 LayoutUnit startMarginDelta = marginStartInRegion - marginStart();
2606 LayoutUnit logicalWidthInRegion = computedValues.m_extent;
2607 LayoutUnit logicalLeftInRegion = computedValues.m_position;
2608 LayoutUnit widthDelta = logicalWidthInRegion - logicalWidth();
2609 LayoutUnit logicalLeftDelta = isOutOfFlowPositioned() ? logicalLeftInRegion - logicalLeft() : startMarginDelta;
2610 LayoutUnit logicalRightInRegion = containingBlockLogicalWidthInRegion - (logicalLeftInRegion + logicalWidthInRegion);
2611 LayoutUnit oldLogicalRight = containingBlockLogicalWidth - (logicalLeft() + logicalWidth());
2612 LayoutUnit logicalRightDelta = isOutOfFlowPositioned() ? logicalRightInRegion - oldLogicalRight : startMarginDelta;
2613
2614 LayoutUnit logicalLeftOffset = 0;
2615
2616 if (!isOutOfFlowPositioned() && avoidsFloats() && cb->containsFloats()) {
2617 LayoutUnit startPositionDelta = cb->computeStartPositionDeltaForChildAvoidingFloats(this, marginStartInRegion, region);
2618 if (cb->style()->isLeftToRightDirection())
2619 logicalLeftDelta += startPositionDelta;
2620 else
2621 logicalRightDelta += startPositionDelta;
2622 }
2623
2624 if (cb->style()->isLeftToRightDirection())
2625 logicalLeftOffset += logicalLeftDelta;
2626 else
2627 logicalLeftOffset -= (widthDelta + logicalRightDelta);
2628
2629 LayoutUnit logicalRightOffset = logicalWidth() - (logicalLeftOffset + logicalWidthInRegion);
2630 bool isShifted = (containingBlockInfo && containingBlockInfo->isShifted())
2631 || (style()->isLeftToRightDirection() && logicalLeftOffset)
2632 || (!style()->isLeftToRightDirection() && logicalRightOffset);
2633
2634 // FIXME: Although it's unlikely, these boxes can go outside our bounds, and so we will need to incorporate them into overflow.
2635 if (cacheFlag == CacheRenderBoxRegionInfo)
2636 return region->setRenderBoxRegionInfo(this, logicalLeftOffset, logicalWidthInRegion, isShifted);
2637 return new RenderBoxRegionInfo(logicalLeftOffset, logicalWidthInRegion, isShifted);
2638 }
2639
shouldFlipBeforeAfterMargins(const RenderStyle * containingBlockStyle,const RenderStyle * childStyle)2640 static bool shouldFlipBeforeAfterMargins(const RenderStyle* containingBlockStyle, const RenderStyle* childStyle)
2641 {
2642 ASSERT(containingBlockStyle->isHorizontalWritingMode() != childStyle->isHorizontalWritingMode());
2643 WritingMode childWritingMode = childStyle->writingMode();
2644 bool shouldFlip = false;
2645 switch (containingBlockStyle->writingMode()) {
2646 case TopToBottomWritingMode:
2647 shouldFlip = (childWritingMode == RightToLeftWritingMode);
2648 break;
2649 case BottomToTopWritingMode:
2650 shouldFlip = (childWritingMode == RightToLeftWritingMode);
2651 break;
2652 case RightToLeftWritingMode:
2653 shouldFlip = (childWritingMode == BottomToTopWritingMode);
2654 break;
2655 case LeftToRightWritingMode:
2656 shouldFlip = (childWritingMode == BottomToTopWritingMode);
2657 break;
2658 }
2659
2660 if (!containingBlockStyle->isLeftToRightDirection())
2661 shouldFlip = !shouldFlip;
2662
2663 return shouldFlip;
2664 }
2665
updateLogicalHeight()2666 void RenderBox::updateLogicalHeight()
2667 {
2668 m_intrinsicContentLogicalHeight = contentLogicalHeight();
2669
2670 LogicalExtentComputedValues computedValues;
2671 computeLogicalHeight(logicalHeight(), logicalTop(), computedValues);
2672
2673 setLogicalHeight(computedValues.m_extent);
2674 setLogicalTop(computedValues.m_position);
2675 setMarginBefore(computedValues.m_margins.m_before);
2676 setMarginAfter(computedValues.m_margins.m_after);
2677 }
2678
computeLogicalHeight(LayoutUnit logicalHeight,LayoutUnit logicalTop,LogicalExtentComputedValues & computedValues) const2679 void RenderBox::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
2680 {
2681 computedValues.m_extent = logicalHeight;
2682 computedValues.m_position = logicalTop;
2683
2684 // Cell height is managed by the table and inline non-replaced elements do not support a height property.
2685 if (isTableCell() || (isInline() && !isReplaced()))
2686 return;
2687
2688 Length h;
2689 if (isOutOfFlowPositioned())
2690 computePositionedLogicalHeight(computedValues);
2691 else {
2692 RenderBlock* cb = containingBlock();
2693 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode();
2694
2695 if (!hasPerpendicularContainingBlock) {
2696 bool shouldFlipBeforeAfter = cb->style()->writingMode() != style()->writingMode();
2697 computeBlockDirectionMargins(cb,
2698 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
2699 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
2700 }
2701
2702 // For tables, calculate margins only.
2703 if (isTable()) {
2704 if (hasPerpendicularContainingBlock) {
2705 bool shouldFlipBeforeAfter = shouldFlipBeforeAfterMargins(cb->style(), style());
2706 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), computedValues.m_extent,
2707 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
2708 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
2709 }
2710 return;
2711 }
2712
2713 // FIXME: Account for block-flow in flexible boxes.
2714 // https://bugs.webkit.org/show_bug.cgi?id=46418
2715 bool inHorizontalBox = parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
2716 bool stretching = parent()->style()->boxAlign() == BSTRETCH;
2717 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching);
2718 bool checkMinMaxHeight = false;
2719
2720 // The parent box is flexing us, so it has increased or decreased our height. We have to
2721 // grab our cached flexible height.
2722 // FIXME: Account for block-flow in flexible boxes.
2723 // https://bugs.webkit.org/show_bug.cgi?id=46418
2724 if (hasOverrideHeight() && parent()->isFlexibleBoxIncludingDeprecated())
2725 h = Length(overrideLogicalContentHeight(), Fixed);
2726 else if (treatAsReplaced)
2727 h = Length(computeReplacedLogicalHeight(), Fixed);
2728 else {
2729 h = style()->logicalHeight();
2730 checkMinMaxHeight = true;
2731 }
2732
2733 // Block children of horizontal flexible boxes fill the height of the box.
2734 // FIXME: Account for block-flow in flexible boxes.
2735 // https://bugs.webkit.org/show_bug.cgi?id=46418
2736 if (h.isAuto() && parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
2737 && parent()->isStretchingChildren()) {
2738 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed);
2739 checkMinMaxHeight = false;
2740 }
2741
2742 LayoutUnit heightResult;
2743 if (checkMinMaxHeight) {
2744 heightResult = computeLogicalHeightUsing(style()->logicalHeight(), computedValues.m_extent - borderAndPaddingLogicalHeight());
2745 if (heightResult == -1)
2746 heightResult = computedValues.m_extent;
2747 heightResult = constrainLogicalHeightByMinMax(heightResult, computedValues.m_extent - borderAndPaddingLogicalHeight());
2748 } else {
2749 // The only times we don't check min/max height are when a fixed length has
2750 // been given as an override. Just use that. The value has already been adjusted
2751 // for box-sizing.
2752 ASSERT(h.isFixed());
2753 heightResult = h.value() + borderAndPaddingLogicalHeight();
2754 }
2755
2756 computedValues.m_extent = heightResult;
2757
2758 if (hasPerpendicularContainingBlock) {
2759 bool shouldFlipBeforeAfter = shouldFlipBeforeAfterMargins(cb->style(), style());
2760 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult,
2761 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
2762 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
2763 }
2764 }
2765
2766 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
2767 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
2768 // is specified. When we're printing, we also need this quirk if the body or root has a percentage
2769 // height since we don't set a height in RenderView when we're printing. So without this quirk, the
2770 // height has nothing to be a percentage of, and it ends up being 0. That is bad.
2771 bool paginatedContentNeedsBaseHeight = document().printing() && h.isPercent()
2772 && (isRoot() || (isBody() && document().documentElement()->renderer()->style()->logicalHeight().isPercent())) && !isInline();
2773 if (stretchesToViewport() || paginatedContentNeedsBaseHeight) {
2774 LayoutUnit margins = collapsedMarginBefore() + collapsedMarginAfter();
2775 LayoutUnit visibleHeight = viewLogicalHeightForPercentages();
2776 if (isRoot())
2777 computedValues.m_extent = max(computedValues.m_extent, visibleHeight - margins);
2778 else {
2779 LayoutUnit marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight();
2780 computedValues.m_extent = max(computedValues.m_extent, visibleHeight - marginsBordersPadding);
2781 }
2782 }
2783 }
2784
viewLogicalHeightForPercentages() const2785 LayoutUnit RenderBox::viewLogicalHeightForPercentages() const
2786 {
2787 if (document().printing())
2788 return static_cast<LayoutUnit>(view()->pageLogicalHeight());
2789 return view()->viewLogicalHeight();
2790 }
2791
computeLogicalHeightUsing(const Length & height,LayoutUnit intrinsicContentHeight) const2792 LayoutUnit RenderBox::computeLogicalHeightUsing(const Length& height, LayoutUnit intrinsicContentHeight) const
2793 {
2794 LayoutUnit logicalHeight = computeContentAndScrollbarLogicalHeightUsing(height, intrinsicContentHeight);
2795 if (logicalHeight != -1)
2796 logicalHeight = adjustBorderBoxLogicalHeightForBoxSizing(logicalHeight);
2797 return logicalHeight;
2798 }
2799
computeContentLogicalHeight(const Length & height,LayoutUnit intrinsicContentHeight) const2800 LayoutUnit RenderBox::computeContentLogicalHeight(const Length& height, LayoutUnit intrinsicContentHeight) const
2801 {
2802 LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(height, intrinsicContentHeight);
2803 if (heightIncludingScrollbar == -1)
2804 return -1;
2805 return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight());
2806 }
2807
computeIntrinsicLogicalContentHeightUsing(Length logicalHeightLength,LayoutUnit intrinsicContentHeight,LayoutUnit borderAndPadding) const2808 LayoutUnit RenderBox::computeIntrinsicLogicalContentHeightUsing(Length logicalHeightLength, LayoutUnit intrinsicContentHeight, LayoutUnit borderAndPadding) const
2809 {
2810 // FIXME(cbiesinger): The css-sizing spec is considering changing what min-content/max-content should resolve to.
2811 // If that happens, this code will have to change.
2812 if (logicalHeightLength.isMinContent() || logicalHeightLength.isMaxContent() || logicalHeightLength.isFitContent()) {
2813 if (isReplaced())
2814 return intrinsicSize().height();
2815 if (m_intrinsicContentLogicalHeight != -1)
2816 return m_intrinsicContentLogicalHeight;
2817 return intrinsicContentHeight;
2818 }
2819 if (logicalHeightLength.isFillAvailable())
2820 return containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding) - borderAndPadding;
2821 ASSERT_NOT_REACHED();
2822 return 0;
2823 }
2824
computeContentAndScrollbarLogicalHeightUsing(const Length & height,LayoutUnit intrinsicContentHeight) const2825 LayoutUnit RenderBox::computeContentAndScrollbarLogicalHeightUsing(const Length& height, LayoutUnit intrinsicContentHeight) const
2826 {
2827 // FIXME(cbiesinger): The css-sizing spec is considering changing what min-content/max-content should resolve to.
2828 // If that happens, this code will have to change.
2829 if (height.isIntrinsic()) {
2830 if (intrinsicContentHeight == -1)
2831 return -1; // Intrinsic height isn't available.
2832 return computeIntrinsicLogicalContentHeightUsing(height, intrinsicContentHeight, borderAndPaddingLogicalHeight());
2833 }
2834 if (height.isFixed())
2835 return height.value();
2836 if (height.isPercent())
2837 return computePercentageLogicalHeight(height);
2838 if (height.isViewportPercentage())
2839 return valueForLength(height, 0, view());
2840 return -1;
2841 }
2842
skipContainingBlockForPercentHeightCalculation(const RenderBox * containingBlock) const2843 bool RenderBox::skipContainingBlockForPercentHeightCalculation(const RenderBox* containingBlock) const
2844 {
2845 // For quirks mode and anonymous blocks, we skip auto-height containingBlocks when computing percentages.
2846 // For standards mode, we treat the percentage as auto if it has an auto-height containing block.
2847 if (!document().inQuirksMode() && !containingBlock->isAnonymousBlock())
2848 return false;
2849 return !containingBlock->isTableCell() && !containingBlock->isOutOfFlowPositioned() && containingBlock->style()->logicalHeight().isAuto() && isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode();
2850 }
2851
computePercentageLogicalHeight(const Length & height) const2852 LayoutUnit RenderBox::computePercentageLogicalHeight(const Length& height) const
2853 {
2854 LayoutUnit availableHeight = -1;
2855
2856 bool skippedAutoHeightContainingBlock = false;
2857 RenderBlock* cb = containingBlock();
2858 const RenderBox* containingBlockChild = this;
2859 LayoutUnit rootMarginBorderPaddingHeight = 0;
2860 while (!cb->isRenderView() && skipContainingBlockForPercentHeightCalculation(cb)) {
2861 if (cb->isBody() || cb->isRoot())
2862 rootMarginBorderPaddingHeight += cb->marginBefore() + cb->marginAfter() + cb->borderAndPaddingLogicalHeight();
2863 skippedAutoHeightContainingBlock = true;
2864 containingBlockChild = cb;
2865 cb = cb->containingBlock();
2866 cb->addPercentHeightDescendant(const_cast<RenderBox*>(this));
2867 }
2868
2869 RenderStyle* cbstyle = cb->style();
2870
2871 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
2872 // explicitly specified that can be used for any percentage computations.
2873 bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cbstyle->logicalHeight().isAuto() || (!cbstyle->logicalTop().isAuto() && !cbstyle->logicalBottom().isAuto()));
2874
2875 bool includeBorderPadding = isTable();
2876
2877 if (isHorizontalWritingMode() != cb->isHorizontalWritingMode())
2878 availableHeight = containingBlockChild->containingBlockLogicalWidthForContent();
2879 else if (hasOverrideContainingBlockLogicalHeight())
2880 availableHeight = overrideContainingBlockContentLogicalHeight();
2881 else if (cb->isTableCell()) {
2882 if (!skippedAutoHeightContainingBlock) {
2883 // Table cells violate what the CSS spec says to do with heights. Basically we
2884 // don't care if the cell specified a height or not. We just always make ourselves
2885 // be a percentage of the cell's current content height.
2886 if (!cb->hasOverrideHeight()) {
2887 // Normally we would let the cell size intrinsically, but scrolling overflow has to be
2888 // treated differently, since WinIE lets scrolled overflow regions shrink as needed.
2889 // While we can't get all cases right, we can at least detect when the cell has a specified
2890 // height or when the table has a specified height. In these cases we want to initially have
2891 // no size and allow the flexing of the table or the cell to its specified height to cause us
2892 // to grow to fill the space. This could end up being wrong in some cases, but it is
2893 // preferable to the alternative (sizing intrinsically and making the row end up too big).
2894 RenderTableCell* cell = toRenderTableCell(cb);
2895 if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto()))
2896 return 0;
2897 return -1;
2898 }
2899 availableHeight = cb->overrideLogicalContentHeight();
2900 includeBorderPadding = true;
2901 }
2902 } else if (cbstyle->logicalHeight().isFixed()) {
2903 LayoutUnit contentBoxHeight = cb->adjustContentBoxLogicalHeightForBoxSizing(cbstyle->logicalHeight().value());
2904 availableHeight = max<LayoutUnit>(0, cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeight - cb->scrollbarLogicalHeight(), -1));
2905 } else if (cbstyle->logicalHeight().isPercent() && !isOutOfFlowPositionedWithSpecifiedHeight) {
2906 // We need to recur and compute the percentage height for our containing block.
2907 LayoutUnit heightWithScrollbar = cb->computePercentageLogicalHeight(cbstyle->logicalHeight());
2908 if (heightWithScrollbar != -1) {
2909 LayoutUnit contentBoxHeightWithScrollbar = cb->adjustContentBoxLogicalHeightForBoxSizing(heightWithScrollbar);
2910 // We need to adjust for min/max height because this method does not
2911 // handle the min/max of the current block, its caller does. So the
2912 // return value from the recursive call will not have been adjusted
2913 // yet.
2914 LayoutUnit contentBoxHeight = cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeightWithScrollbar - cb->scrollbarLogicalHeight(), -1);
2915 availableHeight = max<LayoutUnit>(0, contentBoxHeight);
2916 }
2917 } else if (cbstyle->logicalHeight().isViewportPercentage()) {
2918 LayoutUnit heightWithScrollbar = valueForLength(cbstyle->logicalHeight(), 0, view());
2919 if (heightWithScrollbar != -1) {
2920 LayoutUnit contentBoxHeightWithScrollbar = cb->adjustContentBoxLogicalHeightForBoxSizing(heightWithScrollbar);
2921 // We need to adjust for min/max height because this method does not
2922 // handle the min/max of the current block, its caller does. So the
2923 // return value from the recursive call will not have been adjusted
2924 // yet.
2925 LayoutUnit contentBoxHeight = cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeightWithScrollbar - cb->scrollbarLogicalHeight(), -1);
2926 availableHeight = max<LayoutUnit>(0, contentBoxHeight);
2927 }
2928 } else if (isOutOfFlowPositionedWithSpecifiedHeight) {
2929 // Don't allow this to affect the block' height() member variable, since this
2930 // can get called while the block is still laying out its kids.
2931 LogicalExtentComputedValues computedValues;
2932 cb->computeLogicalHeight(cb->logicalHeight(), 0, computedValues);
2933 availableHeight = computedValues.m_extent - cb->borderAndPaddingLogicalHeight() - cb->scrollbarLogicalHeight();
2934 } else if (cb->isRenderView())
2935 availableHeight = viewLogicalHeightForPercentages();
2936
2937 if (availableHeight == -1)
2938 return availableHeight;
2939
2940 availableHeight -= rootMarginBorderPaddingHeight;
2941
2942 LayoutUnit result = valueForLength(height, availableHeight);
2943 if (includeBorderPadding) {
2944 // FIXME: Table cells should default to box-sizing: border-box so we can avoid this hack.
2945 // It is necessary to use the border-box to match WinIE's broken
2946 // box model. This is essential for sizing inside
2947 // table cells using percentage heights.
2948 result -= borderAndPaddingLogicalHeight();
2949 return max<LayoutUnit>(0, result);
2950 }
2951 return result;
2952 }
2953
computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const2954 LayoutUnit RenderBox::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
2955 {
2956 return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style()->logicalWidth()), shouldComputePreferred);
2957 }
2958
computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth,ShouldComputePreferred shouldComputePreferred) const2959 LayoutUnit RenderBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const
2960 {
2961 LayoutUnit minLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMinWidth().isPercent()) || style()->logicalMinWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
2962 LayoutUnit maxLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMaxWidth().isPercent()) || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
2963 return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
2964 }
2965
computeReplacedLogicalWidthUsing(Length logicalWidth) const2966 LayoutUnit RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const
2967 {
2968 switch (logicalWidth.type()) {
2969 case Fixed:
2970 return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth.value());
2971 case MinContent:
2972 case MaxContent: {
2973 // MinContent/MaxContent don't need the availableLogicalWidth argument.
2974 LayoutUnit availableLogicalWidth = 0;
2975 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
2976 }
2977 case ViewportPercentageWidth:
2978 case ViewportPercentageHeight:
2979 case ViewportPercentageMin:
2980 case ViewportPercentageMax:
2981 return adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, 0, view()));
2982 case FitContent:
2983 case FillAvailable:
2984 case Percent:
2985 case Calculated: {
2986 // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the
2987 // containing block's block-flow.
2988 // https://bugs.webkit.org/show_bug.cgi?id=46496
2989 const LayoutUnit cw = isOutOfFlowPositioned() ? containingBlockLogicalWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent();
2990 Length containerLogicalWidth = containingBlock()->style()->logicalWidth();
2991 // FIXME: Handle cases when containing block width is calculated or viewport percent.
2992 // https://bugs.webkit.org/show_bug.cgi?id=91071
2993 if (logicalWidth.isIntrinsic())
2994 return computeIntrinsicLogicalWidthUsing(logicalWidth, cw, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
2995 if (cw > 0 || (!cw && (containerLogicalWidth.isFixed() || containerLogicalWidth.isPercent())))
2996 return adjustContentBoxLogicalWidthForBoxSizing(minimumValueForLength(logicalWidth, cw));
2997 }
2998 // fall through
2999 case Intrinsic:
3000 case MinIntrinsic:
3001 case Auto:
3002 case ExtendToZoom:
3003 case Undefined:
3004 return intrinsicLogicalWidth();
3005 }
3006
3007 ASSERT_NOT_REACHED();
3008 return 0;
3009 }
3010
computeReplacedLogicalHeight() const3011 LayoutUnit RenderBox::computeReplacedLogicalHeight() const
3012 {
3013 return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style()->logicalHeight()));
3014 }
3015
computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const3016 LayoutUnit RenderBox::computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const
3017 {
3018 LayoutUnit minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
3019 LayoutUnit maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
3020 return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
3021 }
3022
computeReplacedLogicalHeightUsing(Length logicalHeight) const3023 LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const
3024 {
3025 switch (logicalHeight.type()) {
3026 case Fixed:
3027 return adjustContentBoxLogicalHeightForBoxSizing(logicalHeight.value());
3028 case Percent:
3029 case Calculated:
3030 {
3031 RenderObject* cb = isOutOfFlowPositioned() ? container() : containingBlock();
3032 while (cb->isAnonymous()) {
3033 cb = cb->containingBlock();
3034 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this));
3035 }
3036
3037 // FIXME: This calculation is not patched for block-flow yet.
3038 // https://bugs.webkit.org/show_bug.cgi?id=46500
3039 if (cb->isOutOfFlowPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) {
3040 ASSERT_WITH_SECURITY_IMPLICATION(cb->isRenderBlock());
3041 RenderBlock* block = toRenderBlock(cb);
3042 LogicalExtentComputedValues computedValues;
3043 block->computeLogicalHeight(block->logicalHeight(), 0, computedValues);
3044 LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight() - block->scrollbarLogicalHeight();
3045 LayoutUnit newHeight = block->adjustContentBoxLogicalHeightForBoxSizing(newContentHeight);
3046 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, newHeight));
3047 }
3048
3049 // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the
3050 // containing block's block-flow.
3051 // https://bugs.webkit.org/show_bug.cgi?id=46496
3052 LayoutUnit availableHeight;
3053 if (isOutOfFlowPositioned())
3054 availableHeight = containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb));
3055 else {
3056 availableHeight = containingBlockLogicalHeightForContent(IncludeMarginBorderPadding);
3057 // It is necessary to use the border-box to match WinIE's broken
3058 // box model. This is essential for sizing inside
3059 // table cells using percentage heights.
3060 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right.
3061 // https://bugs.webkit.org/show_bug.cgi?id=46997
3062 while (cb && !cb->isRenderView() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) {
3063 if (cb->isTableCell()) {
3064 // Don't let table cells squeeze percent-height replaced elements
3065 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
3066 availableHeight = max(availableHeight, intrinsicLogicalHeight());
3067 return valueForLength(logicalHeight, availableHeight - borderAndPaddingLogicalHeight());
3068 }
3069 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this));
3070 cb = cb->containingBlock();
3071 }
3072 }
3073 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, availableHeight));
3074 }
3075 case ViewportPercentageWidth:
3076 case ViewportPercentageHeight:
3077 case ViewportPercentageMin:
3078 case ViewportPercentageMax:
3079 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, 0, view()));
3080 case MinContent:
3081 case MaxContent:
3082 case FitContent:
3083 case FillAvailable:
3084 return adjustContentBoxLogicalHeightForBoxSizing(computeIntrinsicLogicalContentHeightUsing(logicalHeight, intrinsicLogicalHeight(), borderAndPaddingHeight()));
3085 default:
3086 return intrinsicLogicalHeight();
3087 }
3088 }
3089
availableLogicalHeight(AvailableLogicalHeightType heightType) const3090 LayoutUnit RenderBox::availableLogicalHeight(AvailableLogicalHeightType heightType) const
3091 {
3092 return constrainLogicalHeightByMinMax(availableLogicalHeightUsing(style()->logicalHeight(), heightType), -1);
3093 }
3094
availableLogicalHeightUsing(const Length & h,AvailableLogicalHeightType heightType) const3095 LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h, AvailableLogicalHeightType heightType) const
3096 {
3097 if (isRenderView())
3098 return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth();
3099
3100 // We need to stop here, since we don't want to increase the height of the table
3101 // artificially. We're going to rely on this cell getting expanded to some new
3102 // height, and then when we lay out again we'll use the calculation below.
3103 if (isTableCell() && (h.isAuto() || h.isPercent())) {
3104 if (hasOverrideHeight())
3105 return overrideLogicalContentHeight();
3106 return logicalHeight() - borderAndPaddingLogicalHeight();
3107 }
3108
3109 if (h.isPercent() && isOutOfFlowPositioned() && !isRenderFlowThread()) {
3110 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode.
3111 LayoutUnit availableHeight = containingBlockLogicalHeightForPositioned(containingBlock());
3112 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(h, availableHeight));
3113 }
3114
3115 LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(h, -1);
3116 if (heightIncludingScrollbar != -1)
3117 return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight());
3118
3119 // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical writing-mode.
3120 // https://bugs.webkit.org/show_bug.cgi?id=46500
3121 if (isRenderBlock() && isOutOfFlowPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) {
3122 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this));
3123 LogicalExtentComputedValues computedValues;
3124 block->computeLogicalHeight(block->logicalHeight(), 0, computedValues);
3125 LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight() - block->scrollbarLogicalHeight();
3126 return adjustContentBoxLogicalHeightForBoxSizing(newContentHeight);
3127 }
3128
3129 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode.
3130 LayoutUnit availableHeight = containingBlockLogicalHeightForContent(heightType);
3131 if (heightType == ExcludeMarginBorderPadding) {
3132 // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes collapsed margins.
3133 availableHeight -= marginBefore() + marginAfter() + borderAndPaddingLogicalHeight();
3134 }
3135 return availableHeight;
3136 }
3137
computeBlockDirectionMargins(const RenderBlock * containingBlock,LayoutUnit & marginBefore,LayoutUnit & marginAfter) const3138 void RenderBox::computeBlockDirectionMargins(const RenderBlock* containingBlock, LayoutUnit& marginBefore, LayoutUnit& marginAfter) const
3139 {
3140 if (isTableCell()) {
3141 // FIXME: Not right if we allow cells to have different directionality than the table. If we do allow this, though,
3142 // we may just do it with an extra anonymous block inside the cell.
3143 marginBefore = 0;
3144 marginAfter = 0;
3145 return;
3146 }
3147
3148 // Margins are calculated with respect to the logical width of
3149 // the containing block (8.3)
3150 LayoutUnit cw = containingBlockLogicalWidthForContent();
3151 RenderView* renderView = view();
3152 RenderStyle* containingBlockStyle = containingBlock->style();
3153 marginBefore = minimumValueForLength(style()->marginBeforeUsing(containingBlockStyle), cw, renderView);
3154 marginAfter = minimumValueForLength(style()->marginAfterUsing(containingBlockStyle), cw, renderView);
3155 }
3156
computeAndSetBlockDirectionMargins(const RenderBlock * containingBlock)3157 void RenderBox::computeAndSetBlockDirectionMargins(const RenderBlock* containingBlock)
3158 {
3159 LayoutUnit marginBefore;
3160 LayoutUnit marginAfter;
3161 computeBlockDirectionMargins(containingBlock, marginBefore, marginAfter);
3162 containingBlock->setMarginBeforeForChild(this, marginBefore);
3163 containingBlock->setMarginAfterForChild(this, marginAfter);
3164 }
3165
containingBlockLogicalWidthForPositioned(const RenderBoxModelObject * containingBlock,RenderRegion * region,bool checkForPerpendicularWritingMode) const3166 LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, RenderRegion* region, bool checkForPerpendicularWritingMode) const
3167 {
3168 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode())
3169 return containingBlockLogicalHeightForPositioned(containingBlock, false);
3170
3171 // Use viewport as container for top-level fixed-position elements.
3172 if (style()->position() == FixedPosition && containingBlock->isRenderView()) {
3173 const RenderView* view = toRenderView(containingBlock);
3174 if (FrameView* frameView = view->frameView()) {
3175 LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
3176 return containingBlock->isHorizontalWritingMode() ? viewportRect.width() : viewportRect.height();
3177 }
3178 }
3179
3180 if (containingBlock->isBox()) {
3181 RenderFlowThread* flowThread = flowThreadContainingBlock();
3182 if (!flowThread)
3183 return toRenderBox(containingBlock)->clientLogicalWidth();
3184
3185 const RenderBlock* cb = toRenderBlock(containingBlock);
3186 RenderBoxRegionInfo* boxInfo = 0;
3187 if (!region) {
3188 if (containingBlock->isRenderFlowThread() && !checkForPerpendicularWritingMode)
3189 return toRenderFlowThread(containingBlock)->contentLogicalWidthOfFirstRegion();
3190 if (isWritingModeRoot()) {
3191 LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage();
3192 RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset);
3193 if (cbRegion) {
3194 cbRegion = cb->clampToStartAndEndRegions(cbRegion);
3195 boxInfo = cb->renderBoxRegionInfo(cbRegion);
3196 }
3197 }
3198 } else if (region && flowThread->isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode()) {
3199 RenderRegion* containingBlockRegion = cb->clampToStartAndEndRegions(region);
3200 boxInfo = cb->renderBoxRegionInfo(containingBlockRegion);
3201 }
3202 return (boxInfo) ? max<LayoutUnit>(0, cb->clientLogicalWidth() - (cb->logicalWidth() - boxInfo->logicalWidth())) : cb->clientLogicalWidth();
3203 }
3204
3205 ASSERT(containingBlock->isRenderInline() && containingBlock->isInFlowPositioned());
3206
3207 const RenderInline* flow = toRenderInline(containingBlock);
3208 InlineFlowBox* first = flow->firstLineBox();
3209 InlineFlowBox* last = flow->lastLineBox();
3210
3211 // If the containing block is empty, return a width of 0.
3212 if (!first || !last)
3213 return 0;
3214
3215 LayoutUnit fromLeft;
3216 LayoutUnit fromRight;
3217 if (containingBlock->style()->isLeftToRightDirection()) {
3218 fromLeft = first->logicalLeft() + first->borderLogicalLeft();
3219 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight();
3220 } else {
3221 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight();
3222 fromLeft = last->logicalLeft() + last->borderLogicalLeft();
3223 }
3224
3225 return max<LayoutUnit>(0, fromRight - fromLeft);
3226 }
3227
containingBlockLogicalHeightForPositioned(const RenderBoxModelObject * containingBlock,bool checkForPerpendicularWritingMode) const3228 LayoutUnit RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const
3229 {
3230 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode())
3231 return containingBlockLogicalWidthForPositioned(containingBlock, 0, false);
3232
3233 // Use viewport as container for top-level fixed-position elements.
3234 if (style()->position() == FixedPosition && containingBlock->isRenderView()) {
3235 const RenderView* view = toRenderView(containingBlock);
3236 if (FrameView* frameView = view->frameView()) {
3237 LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
3238 return containingBlock->isHorizontalWritingMode() ? viewportRect.height() : viewportRect.width();
3239 }
3240 }
3241
3242 if (containingBlock->isBox()) {
3243 const RenderBlock* cb = toRenderBlock(containingBlock);
3244 LayoutUnit result = cb->clientLogicalHeight();
3245 RenderFlowThread* flowThread = flowThreadContainingBlock();
3246 if (flowThread && containingBlock->isRenderFlowThread() && flowThread->isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode())
3247 return toRenderFlowThread(containingBlock)->contentLogicalHeightOfFirstRegion();
3248 return result;
3249 }
3250
3251 ASSERT(containingBlock->isRenderInline() && containingBlock->isInFlowPositioned());
3252
3253 const RenderInline* flow = toRenderInline(containingBlock);
3254 InlineFlowBox* first = flow->firstLineBox();
3255 InlineFlowBox* last = flow->lastLineBox();
3256
3257 // If the containing block is empty, return a height of 0.
3258 if (!first || !last)
3259 return 0;
3260
3261 LayoutUnit heightResult;
3262 LayoutRect boundingBox = flow->linesBoundingBox();
3263 if (containingBlock->isHorizontalWritingMode())
3264 heightResult = boundingBox.height();
3265 else
3266 heightResult = boundingBox.width();
3267 heightResult -= (containingBlock->borderBefore() + containingBlock->borderAfter());
3268 return heightResult;
3269 }
3270
computeInlineStaticDistance(Length & logicalLeft,Length & logicalRight,const RenderBox * child,const RenderBoxModelObject * containerBlock,LayoutUnit containerLogicalWidth,RenderRegion * region)3271 static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const RenderBox* child, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth, RenderRegion* region)
3272 {
3273 if (!logicalLeft.isAuto() || !logicalRight.isAuto())
3274 return;
3275
3276 // FIXME: The static distance computation has not been patched for mixed writing modes yet.
3277 if (child->parent()->style()->direction() == LTR) {
3278 LayoutUnit staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft();
3279 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) {
3280 if (curr->isBox()) {
3281 staticPosition += toRenderBox(curr)->logicalLeft();
3282 if (toRenderBox(curr)->isRelPositioned())
3283 staticPosition += toRenderBox(curr)->relativePositionOffset().width();
3284 if (region && curr->isRenderBlock()) {
3285 const RenderBlock* cb = toRenderBlock(curr);
3286 region = cb->clampToStartAndEndRegions(region);
3287 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(region);
3288 if (boxInfo)
3289 staticPosition += boxInfo->logicalLeft();
3290 }
3291 } else if (curr->isInline()) {
3292 if (curr->isRelPositioned()) {
3293 if (!curr->style()->logicalLeft().isAuto())
3294 staticPosition += curr->style()->logicalLeft().value();
3295 else
3296 staticPosition -= curr->style()->logicalRight().value();
3297 }
3298 }
3299 }
3300 logicalLeft.setValue(Fixed, staticPosition);
3301 } else {
3302 RenderBox* enclosingBox = child->parent()->enclosingBox();
3303 LayoutUnit staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalLeft();
3304 for (RenderObject* curr = child->parent(); curr; curr = curr->container()) {
3305 if (curr->isBox()) {
3306 if (curr != containerBlock) {
3307 staticPosition -= toRenderBox(curr)->logicalLeft();
3308 if (toRenderBox(curr)->isRelPositioned())
3309 staticPosition -= toRenderBox(curr)->relativePositionOffset().width();
3310 }
3311 if (curr == enclosingBox)
3312 staticPosition -= enclosingBox->logicalWidth();
3313 if (region && curr->isRenderBlock()) {
3314 const RenderBlock* cb = toRenderBlock(curr);
3315 region = cb->clampToStartAndEndRegions(region);
3316 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(region);
3317 if (boxInfo) {
3318 if (curr != containerBlock)
3319 staticPosition -= cb->logicalWidth() - (boxInfo->logicalLeft() + boxInfo->logicalWidth());
3320 if (curr == enclosingBox)
3321 staticPosition += enclosingBox->logicalWidth() - boxInfo->logicalWidth();
3322 }
3323 }
3324 } else if (curr->isInline()) {
3325 if (curr->isRelPositioned()) {
3326 if (!curr->style()->logicalLeft().isAuto())
3327 staticPosition -= curr->style()->logicalLeft().value();
3328 else
3329 staticPosition += curr->style()->logicalRight().value();
3330 }
3331 }
3332 if (curr == containerBlock)
3333 break;
3334 }
3335 logicalRight.setValue(Fixed, staticPosition);
3336 }
3337 }
3338
computePositionedLogicalWidth(LogicalExtentComputedValues & computedValues,RenderRegion * region) const3339 void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& computedValues, RenderRegion* region) const
3340 {
3341 if (isReplaced()) {
3342 // FIXME: Positioned replaced elements inside a flow thread are not working properly
3343 // with variable width regions (see https://bugs.webkit.org/show_bug.cgi?id=69896 ).
3344 computePositionedLogicalWidthReplaced(computedValues);
3345 return;
3346 }
3347
3348 // QUESTIONS
3349 // FIXME 1: Should we still deal with these the cases of 'left' or 'right' having
3350 // the type 'static' in determining whether to calculate the static distance?
3351 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
3352
3353 // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater
3354 // than or less than the computed width(). Be careful of box-sizing and
3355 // percentage issues.
3356
3357 // The following is based off of the W3C Working Draft from April 11, 2006 of
3358 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
3359 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
3360 // (block-style-comments in this function and in computePositionedLogicalWidthUsing()
3361 // correspond to text from the spec)
3362
3363
3364 // We don't use containingBlock(), since we may be positioned by an enclosing
3365 // relative positioned inline.
3366 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
3367
3368 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, region);
3369
3370 // Use the container block's direction except when calculating the static distance
3371 // This conforms with the reference results for abspos-replaced-width-margin-000.htm
3372 // of the CSS 2.1 test suite
3373 TextDirection containerDirection = containerBlock->style()->direction();
3374
3375 bool isHorizontal = isHorizontalWritingMode();
3376 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth();
3377 const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop();
3378 const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom();
3379
3380 Length logicalLeftLength = style()->logicalLeft();
3381 Length logicalRightLength = style()->logicalRight();
3382
3383 /*---------------------------------------------------------------------------*\
3384 * For the purposes of this section and the next, the term "static position"
3385 * (of an element) refers, roughly, to the position an element would have had
3386 * in the normal flow. More precisely:
3387 *
3388 * * The static position for 'left' is the distance from the left edge of the
3389 * containing block to the left margin edge of a hypothetical box that would
3390 * have been the first box of the element if its 'position' property had
3391 * been 'static' and 'float' had been 'none'. The value is negative if the
3392 * hypothetical box is to the left of the containing block.
3393 * * The static position for 'right' is the distance from the right edge of the
3394 * containing block to the right margin edge of the same hypothetical box as
3395 * above. The value is positive if the hypothetical box is to the left of the
3396 * containing block's edge.
3397 *
3398 * But rather than actually calculating the dimensions of that hypothetical box,
3399 * user agents are free to make a guess at its probable position.
3400 *
3401 * For the purposes of calculating the static position, the containing block of
3402 * fixed positioned elements is the initial containing block instead of the
3403 * viewport, and all scrollable boxes should be assumed to be scrolled to their
3404 * origin.
3405 \*---------------------------------------------------------------------------*/
3406
3407 // see FIXME 1
3408 // Calculate the static distance if needed.
3409 computeInlineStaticDistance(logicalLeftLength, logicalRightLength, this, containerBlock, containerLogicalWidth, region);
3410
3411 // Calculate constraint equation values for 'width' case.
3412 computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection,
3413 containerLogicalWidth, bordersPlusPadding,
3414 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
3415 computedValues);
3416
3417 // Calculate constraint equation values for 'max-width' case.
3418 if (!style()->logicalMaxWidth().isUndefined()) {
3419 LogicalExtentComputedValues maxValues;
3420
3421 computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection,
3422 containerLogicalWidth, bordersPlusPadding,
3423 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
3424 maxValues);
3425
3426 if (computedValues.m_extent > maxValues.m_extent) {
3427 computedValues.m_extent = maxValues.m_extent;
3428 computedValues.m_position = maxValues.m_position;
3429 computedValues.m_margins.m_start = maxValues.m_margins.m_start;
3430 computedValues.m_margins.m_end = maxValues.m_margins.m_end;
3431 }
3432 }
3433
3434 // Calculate constraint equation values for 'min-width' case.
3435 if (!style()->logicalMinWidth().isZero() || style()->logicalMinWidth().isIntrinsic()) {
3436 LogicalExtentComputedValues minValues;
3437
3438 computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection,
3439 containerLogicalWidth, bordersPlusPadding,
3440 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
3441 minValues);
3442
3443 if (computedValues.m_extent < minValues.m_extent) {
3444 computedValues.m_extent = minValues.m_extent;
3445 computedValues.m_position = minValues.m_position;
3446 computedValues.m_margins.m_start = minValues.m_margins.m_start;
3447 computedValues.m_margins.m_end = minValues.m_margins.m_end;
3448 }
3449 }
3450
3451 computedValues.m_extent += bordersPlusPadding;
3452
3453 // Adjust logicalLeft if we need to for the flipped version of our writing mode in regions.
3454 // FIXME: Add support for other types of objects as containerBlock, not only RenderBlock.
3455 RenderFlowThread* flowThread = flowThreadContainingBlock();
3456 if (flowThread && !region && isWritingModeRoot() && isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode() && containerBlock->isRenderBlock()) {
3457 ASSERT(containerBlock->canHaveBoxInfoInRegion());
3458 LayoutUnit logicalLeftPos = computedValues.m_position;
3459 const RenderBlock* cb = toRenderBlock(containerBlock);
3460 LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage();
3461 RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset);
3462 if (cbRegion) {
3463 cbRegion = cb->clampToStartAndEndRegions(cbRegion);
3464 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(cbRegion);
3465 if (boxInfo) {
3466 logicalLeftPos += boxInfo->logicalLeft();
3467 computedValues.m_position = logicalLeftPos;
3468 }
3469 }
3470 }
3471 }
3472
computeLogicalLeftPositionedOffset(LayoutUnit & logicalLeftPos,const RenderBox * child,LayoutUnit logicalWidthValue,const RenderBoxModelObject * containerBlock,LayoutUnit containerLogicalWidth)3473 static void computeLogicalLeftPositionedOffset(LayoutUnit& logicalLeftPos, const RenderBox* child, LayoutUnit logicalWidthValue, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth)
3474 {
3475 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
3476 // 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.
3477 if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) {
3478 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos;
3479 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom());
3480 } else
3481 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop());
3482 }
3483
shrinkToFitWidth(const LayoutUnit availableSpace,const LayoutUnit logicalLeftValue,const LayoutUnit bordersPlusPadding,LogicalExtentComputedValues & computedValues) const3484 void RenderBox::shrinkToFitWidth(const LayoutUnit availableSpace, const LayoutUnit logicalLeftValue, const LayoutUnit bordersPlusPadding, LogicalExtentComputedValues& computedValues) const
3485 {
3486 // FIXME: would it be better to have shrink-to-fit in one step?
3487 LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
3488 LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
3489 LayoutUnit availableWidth = availableSpace - logicalLeftValue;
3490 computedValues.m_extent = min(max(preferredMinWidth, availableWidth), preferredWidth);
3491 }
3492
computePositionedLogicalWidthUsing(Length logicalWidth,const RenderBoxModelObject * containerBlock,TextDirection containerDirection,LayoutUnit containerLogicalWidth,LayoutUnit bordersPlusPadding,Length logicalLeft,Length logicalRight,Length marginLogicalLeft,Length marginLogicalRight,LogicalExtentComputedValues & computedValues) const3493 void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection,
3494 LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding,
3495 Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight,
3496 LogicalExtentComputedValues& computedValues) const
3497 {
3498 if (logicalWidth.isIntrinsic())
3499 logicalWidth = Length(computeIntrinsicLogicalWidthUsing(logicalWidth, containerLogicalWidth, bordersPlusPadding) - bordersPlusPadding, Fixed);
3500
3501 // 'left' and 'right' cannot both be 'auto' because one would of been
3502 // converted to the static position already
3503 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
3504
3505 LayoutUnit logicalLeftValue = 0;
3506
3507 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false);
3508
3509 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto();
3510 bool logicalLeftIsAuto = logicalLeft.isAuto();
3511 bool logicalRightIsAuto = logicalRight.isAuto();
3512 RenderView* renderView = view();
3513 LayoutUnit& marginLogicalLeftValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end;
3514 LayoutUnit& marginLogicalRightValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start;
3515 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
3516 /*-----------------------------------------------------------------------*\
3517 * If none of the three is 'auto': If both 'margin-left' and 'margin-
3518 * right' are 'auto', solve the equation under the extra constraint that
3519 * the two margins get equal values, unless this would make them negative,
3520 * in which case when direction of the containing block is 'ltr' ('rtl'),
3521 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
3522 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
3523 * solve the equation for that value. If the values are over-constrained,
3524 * ignore the value for 'left' (in case the 'direction' property of the
3525 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
3526 * and solve for that value.
3527 \*-----------------------------------------------------------------------*/
3528 // NOTE: It is not necessary to solve for 'right' in the over constrained
3529 // case because the value is not used for any further calculations.
3530
3531 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
3532 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth, renderView));
3533
3534 const LayoutUnit availableSpace = containerLogicalWidth - (logicalLeftValue + computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth, renderView) + bordersPlusPadding);
3535
3536 // Margins are now the only unknown
3537 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
3538 // Both margins auto, solve for equality
3539 if (availableSpace >= 0) {
3540 marginLogicalLeftValue = availableSpace / 2; // split the difference
3541 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences
3542 } else {
3543 // Use the containing block's direction rather than the parent block's
3544 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
3545 if (containerDirection == LTR) {
3546 marginLogicalLeftValue = 0;
3547 marginLogicalRightValue = availableSpace; // will be negative
3548 } else {
3549 marginLogicalLeftValue = availableSpace; // will be negative
3550 marginLogicalRightValue = 0;
3551 }
3552 }
3553 } else if (marginLogicalLeft.isAuto()) {
3554 // Solve for left margin
3555 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView);
3556 marginLogicalLeftValue = availableSpace - marginLogicalRightValue;
3557 } else if (marginLogicalRight.isAuto()) {
3558 // Solve for right margin
3559 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView);
3560 marginLogicalRightValue = availableSpace - marginLogicalLeftValue;
3561 } else {
3562 // Over-constrained, solve for left if direction is RTL
3563 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView);
3564 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView);
3565
3566 // Use the containing block's direction rather than the parent block's
3567 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
3568 if (containerDirection == RTL)
3569 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue;
3570 }
3571 } else {
3572 /*--------------------------------------------------------------------*\
3573 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
3574 * to 0, and pick the one of the following six rules that applies.
3575 *
3576 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
3577 * width is shrink-to-fit. Then solve for 'left'
3578 *
3579 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
3580 * ------------------------------------------------------------------
3581 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
3582 * the 'direction' property of the containing block is 'ltr' set
3583 * 'left' to the static position, otherwise set 'right' to the
3584 * static position. Then solve for 'left' (if 'direction is 'rtl')
3585 * or 'right' (if 'direction' is 'ltr').
3586 * ------------------------------------------------------------------
3587 *
3588 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
3589 * width is shrink-to-fit . Then solve for 'right'
3590 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
3591 * for 'left'
3592 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
3593 * for 'width'
3594 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
3595 * for 'right'
3596 *
3597 * Calculation of the shrink-to-fit width is similar to calculating the
3598 * width of a table cell using the automatic table layout algorithm.
3599 * Roughly: calculate the preferred width by formatting the content
3600 * without breaking lines other than where explicit line breaks occur,
3601 * and also calculate the preferred minimum width, e.g., by trying all
3602 * possible line breaks. CSS 2.1 does not define the exact algorithm.
3603 * Thirdly, calculate the available width: this is found by solving
3604 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
3605 * to 0.
3606 *
3607 * Then the shrink-to-fit width is:
3608 * min(max(preferred minimum width, available width), preferred width).
3609 \*--------------------------------------------------------------------*/
3610 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
3611 // because the value is not used for any further calculations.
3612
3613 // Calculate margins, 'auto' margins are ignored.
3614 marginLogicalLeftValue = minimumValueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView);
3615 marginLogicalRightValue = minimumValueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView);
3616
3617 const LayoutUnit availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding);
3618
3619 // FIXME: Is there a faster way to find the correct case?
3620 // Use rule/case that applies.
3621 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
3622 // RULE 1: (use shrink-to-fit for width, and solve of left)
3623 LayoutUnit logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView);
3624
3625 // FIXME: would it be better to have shrink-to-fit in one step?
3626 LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
3627 LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
3628 LayoutUnit availableWidth = availableSpace - logicalRightValue;
3629 computedValues.m_extent = min(max(preferredMinWidth, availableWidth), preferredWidth);
3630 logicalLeftValue = availableSpace - (computedValues.m_extent + logicalRightValue);
3631 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) {
3632 // RULE 3: (use shrink-to-fit for width, and no need solve of right)
3633 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
3634
3635 shrinkToFitWidth(availableSpace, logicalLeftValue, bordersPlusPadding, computedValues);
3636 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
3637 // RULE 4: (solve for left)
3638 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth, renderView));
3639 logicalLeftValue = availableSpace - (computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth, renderView));
3640 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
3641 // RULE 5: (solve for width)
3642 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
3643 if (autoWidthShouldFitContent())
3644 shrinkToFitWidth(availableSpace, logicalLeftValue, bordersPlusPadding, computedValues);
3645 else
3646 computedValues.m_extent = availableSpace - (logicalLeftValue + valueForLength(logicalRight, containerLogicalWidth, renderView));
3647 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) {
3648 // RULE 6: (no need solve for right)
3649 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
3650 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth, renderView));
3651 }
3652 }
3653
3654 // Use computed values to calculate the horizontal position.
3655
3656 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
3657 // positioned, inline because right now, it is using the logical left position
3658 // of the first line box when really it should use the last line box. When
3659 // this is fixed elsewhere, this block should be removed.
3660 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) {
3661 const RenderInline* flow = toRenderInline(containerBlock);
3662 InlineFlowBox* firstLine = flow->firstLineBox();
3663 InlineFlowBox* lastLine = flow->lastLineBox();
3664 if (firstLine && lastLine && firstLine != lastLine) {
3665 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
3666 return;
3667 }
3668 }
3669
3670 if (containerBlock->isBox() && toRenderBox(containerBlock)->scrollsOverflowY() && containerBlock->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
3671 logicalLeftValue = logicalLeftValue + toRenderBox(containerBlock)->verticalScrollbarWidth();
3672 }
3673
3674 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue;
3675 computeLogicalLeftPositionedOffset(computedValues.m_position, this, computedValues.m_extent, containerBlock, containerLogicalWidth);
3676 }
3677
computeBlockStaticDistance(Length & logicalTop,Length & logicalBottom,const RenderBox * child,const RenderBoxModelObject * containerBlock)3678 static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const RenderBox* child, const RenderBoxModelObject* containerBlock)
3679 {
3680 if (!logicalTop.isAuto() || !logicalBottom.isAuto())
3681 return;
3682
3683 // FIXME: The static distance computation has not been patched for mixed writing modes.
3684 LayoutUnit staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore();
3685 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) {
3686 if (curr->isBox() && !curr->isTableRow())
3687 staticLogicalTop += toRenderBox(curr)->logicalTop();
3688 }
3689 logicalTop.setValue(Fixed, staticLogicalTop);
3690 }
3691
computePositionedLogicalHeight(LogicalExtentComputedValues & computedValues) const3692 void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& computedValues) const
3693 {
3694 if (isReplaced()) {
3695 computePositionedLogicalHeightReplaced(computedValues);
3696 return;
3697 }
3698
3699 // The following is based off of the W3C Working Draft from April 11, 2006 of
3700 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
3701 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
3702 // (block-style-comments in this function and in computePositionedLogicalHeightUsing()
3703 // correspond to text from the spec)
3704
3705
3706 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
3707 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
3708
3709 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
3710
3711 RenderStyle* styleToUse = style();
3712 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
3713 const Length marginBefore = styleToUse->marginBefore();
3714 const Length marginAfter = styleToUse->marginAfter();
3715 Length logicalTopLength = styleToUse->logicalTop();
3716 Length logicalBottomLength = styleToUse->logicalBottom();
3717
3718 /*---------------------------------------------------------------------------*\
3719 * For the purposes of this section and the next, the term "static position"
3720 * (of an element) refers, roughly, to the position an element would have had
3721 * in the normal flow. More precisely, the static position for 'top' is the
3722 * distance from the top edge of the containing block to the top margin edge
3723 * of a hypothetical box that would have been the first box of the element if
3724 * its 'position' property had been 'static' and 'float' had been 'none'. The
3725 * value is negative if the hypothetical box is above the containing block.
3726 *
3727 * But rather than actually calculating the dimensions of that hypothetical
3728 * box, user agents are free to make a guess at its probable position.
3729 *
3730 * For the purposes of calculating the static position, the containing block
3731 * of fixed positioned elements is the initial containing block instead of
3732 * the viewport.
3733 \*---------------------------------------------------------------------------*/
3734
3735 // see FIXME 1
3736 // Calculate the static distance if needed.
3737 computeBlockStaticDistance(logicalTopLength, logicalBottomLength, this, containerBlock);
3738
3739 // Calculate constraint equation values for 'height' case.
3740 LayoutUnit logicalHeight = computedValues.m_extent;
3741 computePositionedLogicalHeightUsing(styleToUse->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3742 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3743 computedValues);
3744
3745 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
3746 // see FIXME 2
3747
3748 // Calculate constraint equation values for 'max-height' case.
3749 if (!styleToUse->logicalMaxHeight().isUndefined()) {
3750 LogicalExtentComputedValues maxValues;
3751
3752 computePositionedLogicalHeightUsing(styleToUse->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3753 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3754 maxValues);
3755
3756 if (computedValues.m_extent > maxValues.m_extent) {
3757 computedValues.m_extent = maxValues.m_extent;
3758 computedValues.m_position = maxValues.m_position;
3759 computedValues.m_margins.m_before = maxValues.m_margins.m_before;
3760 computedValues.m_margins.m_after = maxValues.m_margins.m_after;
3761 }
3762 }
3763
3764 // Calculate constraint equation values for 'min-height' case.
3765 if (!styleToUse->logicalMinHeight().isZero() || styleToUse->logicalMinHeight().isIntrinsic()) {
3766 LogicalExtentComputedValues minValues;
3767
3768 computePositionedLogicalHeightUsing(styleToUse->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3769 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3770 minValues);
3771
3772 if (computedValues.m_extent < minValues.m_extent) {
3773 computedValues.m_extent = minValues.m_extent;
3774 computedValues.m_position = minValues.m_position;
3775 computedValues.m_margins.m_before = minValues.m_margins.m_before;
3776 computedValues.m_margins.m_after = minValues.m_margins.m_after;
3777 }
3778 }
3779
3780 // Set final height value.
3781 computedValues.m_extent += bordersPlusPadding;
3782
3783 // Adjust logicalTop if we need to for perpendicular writing modes in regions.
3784 // FIXME: Add support for other types of objects as containerBlock, not only RenderBlock.
3785 RenderFlowThread* flowThread = flowThreadContainingBlock();
3786 if (flowThread && isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode() && containerBlock->isRenderBlock()) {
3787 ASSERT(containerBlock->canHaveBoxInfoInRegion());
3788 LayoutUnit logicalTopPos = computedValues.m_position;
3789 const RenderBlock* cb = toRenderBlock(containerBlock);
3790 LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage() - logicalLeft();
3791 RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset);
3792 if (cbRegion) {
3793 cbRegion = cb->clampToStartAndEndRegions(cbRegion);
3794 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(cbRegion);
3795 if (boxInfo) {
3796 logicalTopPos += boxInfo->logicalLeft();
3797 computedValues.m_position = logicalTopPos;
3798 }
3799 }
3800 }
3801 }
3802
computeLogicalTopPositionedOffset(LayoutUnit & logicalTopPos,const RenderBox * child,LayoutUnit logicalHeightValue,const RenderBoxModelObject * containerBlock,LayoutUnit containerLogicalHeight)3803 static void computeLogicalTopPositionedOffset(LayoutUnit& logicalTopPos, const RenderBox* child, LayoutUnit logicalHeightValue, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalHeight)
3804 {
3805 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
3806 // 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.
3807 if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode())
3808 || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()))
3809 logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos;
3810
3811 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt.
3812 if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) {
3813 if (child->isHorizontalWritingMode())
3814 logicalTopPos += containerBlock->borderBottom();
3815 else
3816 logicalTopPos += containerBlock->borderRight();
3817 } else {
3818 if (child->isHorizontalWritingMode())
3819 logicalTopPos += containerBlock->borderTop();
3820 else
3821 logicalTopPos += containerBlock->borderLeft();
3822 }
3823 }
3824
computePositionedLogicalHeightUsing(Length logicalHeightLength,const RenderBoxModelObject * containerBlock,LayoutUnit containerLogicalHeight,LayoutUnit bordersPlusPadding,LayoutUnit logicalHeight,Length logicalTop,Length logicalBottom,Length marginBefore,Length marginAfter,LogicalExtentComputedValues & computedValues) const3825 void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock,
3826 LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight,
3827 Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter,
3828 LogicalExtentComputedValues& computedValues) const
3829 {
3830 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
3831 // converted to the static position in computePositionedLogicalHeight()
3832 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto()));
3833
3834 LayoutUnit logicalHeightValue;
3835 LayoutUnit contentLogicalHeight = logicalHeight - bordersPlusPadding;
3836
3837 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false);
3838
3839 LayoutUnit logicalTopValue = 0;
3840
3841 bool logicalHeightIsAuto = logicalHeightLength.isAuto();
3842 bool logicalTopIsAuto = logicalTop.isAuto();
3843 bool logicalBottomIsAuto = logicalBottom.isAuto();
3844 RenderView* renderView = view();
3845
3846 LayoutUnit resolvedLogicalHeight;
3847 // Height is never unsolved for tables.
3848 if (isTable()) {
3849 resolvedLogicalHeight = contentLogicalHeight;
3850 logicalHeightIsAuto = false;
3851 } else {
3852 if (logicalHeightLength.isIntrinsic())
3853 resolvedLogicalHeight = computeIntrinsicLogicalContentHeightUsing(logicalHeightLength, contentLogicalHeight, bordersPlusPadding);
3854 else
3855 resolvedLogicalHeight = adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeightLength, containerLogicalHeight, renderView));
3856 }
3857
3858 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
3859 /*-----------------------------------------------------------------------*\
3860 * If none of the three are 'auto': If both 'margin-top' and 'margin-
3861 * bottom' are 'auto', solve the equation under the extra constraint that
3862 * the two margins get equal values. If one of 'margin-top' or 'margin-
3863 * bottom' is 'auto', solve the equation for that value. If the values
3864 * are over-constrained, ignore the value for 'bottom' and solve for that
3865 * value.
3866 \*-----------------------------------------------------------------------*/
3867 // NOTE: It is not necessary to solve for 'bottom' in the over constrained
3868 // case because the value is not used for any further calculations.
3869
3870 logicalHeightValue = resolvedLogicalHeight;
3871 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
3872
3873 const LayoutUnit availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight, renderView) + bordersPlusPadding);
3874
3875 // Margins are now the only unknown
3876 if (marginBefore.isAuto() && marginAfter.isAuto()) {
3877 // Both margins auto, solve for equality
3878 // NOTE: This may result in negative values.
3879 computedValues.m_margins.m_before = availableSpace / 2; // split the difference
3880 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; // account for odd valued differences
3881 } else if (marginBefore.isAuto()) {
3882 // Solve for top margin
3883 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView);
3884 computedValues.m_margins.m_before = availableSpace - computedValues.m_margins.m_after;
3885 } else if (marginAfter.isAuto()) {
3886 // Solve for bottom margin
3887 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView);
3888 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before;
3889 } else {
3890 // Over-constrained, (no need solve for bottom)
3891 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView);
3892 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView);
3893 }
3894 } else {
3895 /*--------------------------------------------------------------------*\
3896 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
3897 * to 0, and pick the one of the following six rules that applies.
3898 *
3899 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
3900 * the height is based on the content, and solve for 'top'.
3901 *
3902 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
3903 * ------------------------------------------------------------------
3904 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
3905 * set 'top' to the static position, and solve for 'bottom'.
3906 * ------------------------------------------------------------------
3907 *
3908 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
3909 * the height is based on the content, and solve for 'bottom'.
3910 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
3911 * solve for 'top'.
3912 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
3913 * solve for 'height'.
3914 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
3915 * solve for 'bottom'.
3916 \*--------------------------------------------------------------------*/
3917 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
3918 // because the value is not used for any further calculations.
3919
3920 // Calculate margins, 'auto' margins are ignored.
3921 computedValues.m_margins.m_before = minimumValueForLength(marginBefore, containerRelativeLogicalWidth, renderView);
3922 computedValues.m_margins.m_after = minimumValueForLength(marginAfter, containerRelativeLogicalWidth, renderView);
3923
3924 const LayoutUnit availableSpace = containerLogicalHeight - (computedValues.m_margins.m_before + computedValues.m_margins.m_after + bordersPlusPadding);
3925
3926 // Use rule/case that applies.
3927 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
3928 // RULE 1: (height is content based, solve of top)
3929 logicalHeightValue = contentLogicalHeight;
3930 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight, renderView));
3931 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) {
3932 // RULE 3: (height is content based, no need solve of bottom)
3933 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
3934 logicalHeightValue = contentLogicalHeight;
3935 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
3936 // RULE 4: (solve of top)
3937 logicalHeightValue = resolvedLogicalHeight;
3938 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight, renderView));
3939 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
3940 // RULE 5: (solve of height)
3941 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
3942 logicalHeightValue = max<LayoutUnit>(0, availableSpace - (logicalTopValue + valueForLength(logicalBottom, containerLogicalHeight, renderView)));
3943 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) {
3944 // RULE 6: (no need solve of bottom)
3945 logicalHeightValue = resolvedLogicalHeight;
3946 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
3947 }
3948 }
3949 computedValues.m_extent = logicalHeightValue;
3950
3951 // Use computed values to calculate the vertical position.
3952 computedValues.m_position = logicalTopValue + computedValues.m_margins.m_before;
3953 computeLogicalTopPositionedOffset(computedValues.m_position, this, logicalHeightValue, containerBlock, containerLogicalHeight);
3954 }
3955
computePositionedLogicalWidthReplaced(LogicalExtentComputedValues & computedValues) const3956 void RenderBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValues& computedValues) const
3957 {
3958 // The following is based off of the W3C Working Draft from April 11, 2006 of
3959 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements"
3960 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
3961 // (block-style-comments in this function correspond to text from the spec and
3962 // the numbers correspond to numbers in spec)
3963
3964 // We don't use containingBlock(), since we may be positioned by an enclosing
3965 // relative positioned inline.
3966 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
3967
3968 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock);
3969 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false);
3970
3971 // To match WinIE, in quirks mode use the parent's 'direction' property
3972 // instead of the the container block's.
3973 TextDirection containerDirection = containerBlock->style()->direction();
3974
3975 // Variables to solve.
3976 bool isHorizontal = isHorizontalWritingMode();
3977 Length logicalLeft = style()->logicalLeft();
3978 Length logicalRight = style()->logicalRight();
3979 Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop();
3980 Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom();
3981 LayoutUnit& marginLogicalLeftAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end;
3982 LayoutUnit& marginLogicalRightAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start;
3983
3984 /*-----------------------------------------------------------------------*\
3985 * 1. The used value of 'width' is determined as for inline replaced
3986 * elements.
3987 \*-----------------------------------------------------------------------*/
3988 // NOTE: This value of width is FINAL in that the min/max width calculations
3989 // are dealt with in computeReplacedWidth(). This means that the steps to produce
3990 // correct max/min in the non-replaced version, are not necessary.
3991 computedValues.m_extent = computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth();
3992
3993 const LayoutUnit availableSpace = containerLogicalWidth - computedValues.m_extent;
3994
3995 /*-----------------------------------------------------------------------*\
3996 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
3997 * of the containing block is 'ltr', set 'left' to the static position;
3998 * else if 'direction' is 'rtl', set 'right' to the static position.
3999 \*-----------------------------------------------------------------------*/
4000 // see FIXME 1
4001 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, 0); // FIXME: Pass the region.
4002
4003 /*-----------------------------------------------------------------------*\
4004 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
4005 * or 'margin-right' with '0'.
4006 \*-----------------------------------------------------------------------*/
4007 if (logicalLeft.isAuto() || logicalRight.isAuto()) {
4008 if (marginLogicalLeft.isAuto())
4009 marginLogicalLeft.setValue(Fixed, 0);
4010 if (marginLogicalRight.isAuto())
4011 marginLogicalRight.setValue(Fixed, 0);
4012 }
4013
4014 /*-----------------------------------------------------------------------*\
4015 * 4. If at this point both 'margin-left' and 'margin-right' are still
4016 * 'auto', solve the equation under the extra constraint that the two
4017 * margins must get equal values, unless this would make them negative,
4018 * in which case when the direction of the containing block is 'ltr'
4019 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
4020 * 'margin-right' ('margin-left').
4021 \*-----------------------------------------------------------------------*/
4022 LayoutUnit logicalLeftValue = 0;
4023 LayoutUnit logicalRightValue = 0;
4024 RenderView* renderView = view();
4025
4026 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
4027 // 'left' and 'right' cannot be 'auto' due to step 3
4028 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
4029
4030 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
4031 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView);
4032
4033 LayoutUnit difference = availableSpace - (logicalLeftValue + logicalRightValue);
4034 if (difference > 0) {
4035 marginLogicalLeftAlias = difference / 2; // split the difference
4036 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences
4037 } else {
4038 // Use the containing block's direction rather than the parent block's
4039 // per CSS 2.1 reference test abspos-replaced-width-margin-000.
4040 if (containerDirection == LTR) {
4041 marginLogicalLeftAlias = 0;
4042 marginLogicalRightAlias = difference; // will be negative
4043 } else {
4044 marginLogicalLeftAlias = difference; // will be negative
4045 marginLogicalRightAlias = 0;
4046 }
4047 }
4048
4049 /*-----------------------------------------------------------------------*\
4050 * 5. If at this point there is an 'auto' left, solve the equation for
4051 * that value.
4052 \*-----------------------------------------------------------------------*/
4053 } else if (logicalLeft.isAuto()) {
4054 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView);
4055 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView);
4056 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView);
4057
4058 // Solve for 'left'
4059 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias);
4060 } else if (logicalRight.isAuto()) {
4061 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView);
4062 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView);
4063 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
4064
4065 // Solve for 'right'
4066 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias);
4067 } else if (marginLogicalLeft.isAuto()) {
4068 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView);
4069 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
4070 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView);
4071
4072 // Solve for 'margin-left'
4073 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias);
4074 } else if (marginLogicalRight.isAuto()) {
4075 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView);
4076 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
4077 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView);
4078
4079 // Solve for 'margin-right'
4080 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias);
4081 } else {
4082 // Nothing is 'auto', just calculate the values.
4083 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView);
4084 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView);
4085 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView);
4086 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView);
4087 // If the containing block is right-to-left, then push the left position as far to the right as possible
4088 if (containerDirection == RTL) {
4089 int totalLogicalWidth = computedValues.m_extent + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias;
4090 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue);
4091 }
4092 }
4093
4094 /*-----------------------------------------------------------------------*\
4095 * 6. If at this point the values are over-constrained, ignore the value
4096 * for either 'left' (in case the 'direction' property of the
4097 * containing block is 'rtl') or 'right' (in case 'direction' is
4098 * 'ltr') and solve for that value.
4099 \*-----------------------------------------------------------------------*/
4100 // NOTE: Constraints imposed by the width of the containing block and its content have already been accounted for above.
4101
4102 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that
4103 // can make the result here rather complicated to compute.
4104
4105 // Use computed values to calculate the horizontal position.
4106
4107 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
4108 // positioned, inline containing block because right now, it is using the logical left position
4109 // of the first line box when really it should use the last line box. When
4110 // this is fixed elsewhere, this block should be removed.
4111 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) {
4112 const RenderInline* flow = toRenderInline(containerBlock);
4113 InlineFlowBox* firstLine = flow->firstLineBox();
4114 InlineFlowBox* lastLine = flow->lastLineBox();
4115 if (firstLine && lastLine && firstLine != lastLine) {
4116 computedValues.m_position = logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
4117 return;
4118 }
4119 }
4120
4121 LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias;
4122 computeLogicalLeftPositionedOffset(logicalLeftPos, this, computedValues.m_extent, containerBlock, containerLogicalWidth);
4123 computedValues.m_position = logicalLeftPos;
4124 }
4125
computePositionedLogicalHeightReplaced(LogicalExtentComputedValues & computedValues) const4126 void RenderBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValues& computedValues) const
4127 {
4128 // The following is based off of the W3C Working Draft from April 11, 2006 of
4129 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements"
4130 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
4131 // (block-style-comments in this function correspond to text from the spec and
4132 // the numbers correspond to numbers in spec)
4133
4134 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
4135 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
4136
4137 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
4138 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false);
4139
4140 // Variables to solve.
4141 Length marginBefore = style()->marginBefore();
4142 Length marginAfter = style()->marginAfter();
4143 LayoutUnit& marginBeforeAlias = computedValues.m_margins.m_before;
4144 LayoutUnit& marginAfterAlias = computedValues.m_margins.m_after;
4145
4146 Length logicalTop = style()->logicalTop();
4147 Length logicalBottom = style()->logicalBottom();
4148 RenderView* renderView = view();
4149
4150 /*-----------------------------------------------------------------------*\
4151 * 1. The used value of 'height' is determined as for inline replaced
4152 * elements.
4153 \*-----------------------------------------------------------------------*/
4154 // NOTE: This value of height is FINAL in that the min/max height calculations
4155 // are dealt with in computeReplacedHeight(). This means that the steps to produce
4156 // correct max/min in the non-replaced version, are not necessary.
4157 computedValues.m_extent = computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight();
4158 const LayoutUnit availableSpace = containerLogicalHeight - computedValues.m_extent;
4159
4160 /*-----------------------------------------------------------------------*\
4161 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
4162 * with the element's static position.
4163 \*-----------------------------------------------------------------------*/
4164 // see FIXME 1
4165 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock);
4166
4167 /*-----------------------------------------------------------------------*\
4168 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
4169 * 'margin-bottom' with '0'.
4170 \*-----------------------------------------------------------------------*/
4171 // FIXME: The spec. says that this step should only be taken when bottom is
4172 // auto, but if only top is auto, this makes step 4 impossible.
4173 if (logicalTop.isAuto() || logicalBottom.isAuto()) {
4174 if (marginBefore.isAuto())
4175 marginBefore.setValue(Fixed, 0);
4176 if (marginAfter.isAuto())
4177 marginAfter.setValue(Fixed, 0);
4178 }
4179
4180 /*-----------------------------------------------------------------------*\
4181 * 4. If at this point both 'margin-top' and 'margin-bottom' are still
4182 * 'auto', solve the equation under the extra constraint that the two
4183 * margins must get equal values.
4184 \*-----------------------------------------------------------------------*/
4185 LayoutUnit logicalTopValue = 0;
4186 LayoutUnit logicalBottomValue = 0;
4187
4188 if (marginBefore.isAuto() && marginAfter.isAuto()) {
4189 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined.
4190 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto()));
4191
4192 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
4193 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView);
4194
4195 LayoutUnit difference = availableSpace - (logicalTopValue + logicalBottomValue);
4196 // NOTE: This may result in negative values.
4197 marginBeforeAlias = difference / 2; // split the difference
4198 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences
4199
4200 /*-----------------------------------------------------------------------*\
4201 * 5. If at this point there is only one 'auto' left, solve the equation
4202 * for that value.
4203 \*-----------------------------------------------------------------------*/
4204 } else if (logicalTop.isAuto()) {
4205 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView);
4206 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView);
4207 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView);
4208
4209 // Solve for 'top'
4210 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias);
4211 } else if (logicalBottom.isAuto()) {
4212 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView);
4213 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView);
4214 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
4215
4216 // Solve for 'bottom'
4217 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
4218 // use the value.
4219 } else if (marginBefore.isAuto()) {
4220 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView);
4221 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
4222 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView);
4223
4224 // Solve for 'margin-top'
4225 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias);
4226 } else if (marginAfter.isAuto()) {
4227 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView);
4228 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
4229 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView);
4230
4231 // Solve for 'margin-bottom'
4232 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias);
4233 } else {
4234 // Nothing is 'auto', just calculate the values.
4235 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView);
4236 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView);
4237 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView);
4238 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
4239 // use the value.
4240 }
4241
4242 /*-----------------------------------------------------------------------*\
4243 * 6. If at this point the values are over-constrained, ignore the value
4244 * for 'bottom' and solve for that value.
4245 \*-----------------------------------------------------------------------*/
4246 // NOTE: It is not necessary to do this step because we don't end up using
4247 // the value of 'bottom' regardless of whether the values are over-constrained
4248 // or not.
4249
4250 // Use computed values to calculate the vertical position.
4251 LayoutUnit logicalTopPos = logicalTopValue + marginBeforeAlias;
4252 computeLogicalTopPositionedOffset(logicalTopPos, this, computedValues.m_extent, containerBlock, containerLogicalHeight);
4253 computedValues.m_position = logicalTopPos;
4254 }
4255
localCaretRect(InlineBox * box,int caretOffset,LayoutUnit * extraWidthToEndOfLine)4256 LayoutRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
4257 {
4258 // VisiblePositions at offsets inside containers either a) refer to the positions before/after
4259 // those containers (tables and select elements) or b) refer to the position inside an empty block.
4260 // They never refer to children.
4261 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements.
4262
4263 LayoutRect rect(location(), LayoutSize(caretWidth, height()));
4264 bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection();
4265
4266 if ((!caretOffset) ^ ltr)
4267 rect.move(LayoutSize(width() - caretWidth, 0));
4268
4269 if (box) {
4270 RootInlineBox* rootBox = box->root();
4271 LayoutUnit top = rootBox->lineTop();
4272 rect.setY(top);
4273 rect.setHeight(rootBox->lineBottom() - top);
4274 }
4275
4276 // If height of box is smaller than font height, use the latter one,
4277 // otherwise the caret might become invisible.
4278 //
4279 // Also, if the box is not a replaced element, always use the font height.
4280 // This prevents the "big caret" bug described in:
4281 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point
4282 //
4283 // FIXME: ignoring :first-line, missing good reason to take care of
4284 LayoutUnit fontHeight = style()->fontMetrics().height();
4285 if (fontHeight > rect.height() || (!isReplaced() && !isTable()))
4286 rect.setHeight(fontHeight);
4287
4288 if (extraWidthToEndOfLine)
4289 *extraWidthToEndOfLine = x() + width() - rect.maxX();
4290
4291 // Move to local coords
4292 rect.moveBy(-location());
4293
4294 // FIXME: Border/padding should be added for all elements but this workaround
4295 // is needed because we use offsets inside an "atomic" element to represent
4296 // positions before and after the element in deprecated editing offsets.
4297 if (node() && !(editingIgnoresContent(node()) || isRenderedTable(node()))) {
4298 rect.setX(rect.x() + borderLeft() + paddingLeft());
4299 rect.setY(rect.y() + paddingTop() + borderTop());
4300 }
4301
4302 if (!isHorizontalWritingMode())
4303 return rect.transposedRect();
4304
4305 return rect;
4306 }
4307
positionForPoint(const LayoutPoint & point)4308 PositionWithAffinity RenderBox::positionForPoint(const LayoutPoint& point)
4309 {
4310 // no children...return this render object's element, if there is one, and offset 0
4311 if (!firstChild())
4312 return createPositionWithAffinity(nonPseudoNode() ? firstPositionInOrBeforeNode(nonPseudoNode()) : Position());
4313
4314 if (isTable() && nonPseudoNode()) {
4315 LayoutUnit right = contentWidth() + borderAndPaddingWidth();
4316 LayoutUnit bottom = contentHeight() + borderAndPaddingHeight();
4317
4318 if (point.x() < 0 || point.x() > right || point.y() < 0 || point.y() > bottom) {
4319 if (point.x() <= right / 2)
4320 return createPositionWithAffinity(firstPositionInOrBeforeNode(nonPseudoNode()));
4321 return createPositionWithAffinity(lastPositionInOrAfterNode(nonPseudoNode()));
4322 }
4323 }
4324
4325 // Pass off to the closest child.
4326 LayoutUnit minDist = LayoutUnit::max();
4327 RenderBox* closestRenderer = 0;
4328 LayoutPoint adjustedPoint = point;
4329 if (isTableRow())
4330 adjustedPoint.moveBy(location());
4331
4332 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) {
4333 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isRenderBlockFlow() )
4334 || renderObject->style()->visibility() != VISIBLE)
4335 continue;
4336
4337 if (!renderObject->isBox())
4338 continue;
4339
4340 RenderBox* renderer = toRenderBox(renderObject);
4341
4342 LayoutUnit top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? LayoutUnit() : renderer->y());
4343 LayoutUnit bottom = top + renderer->contentHeight();
4344 LayoutUnit left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? LayoutUnit() : renderer->x());
4345 LayoutUnit right = left + renderer->contentWidth();
4346
4347 if (point.x() <= right && point.x() >= left && point.y() <= top && point.y() >= bottom) {
4348 if (renderer->isTableRow())
4349 return renderer->positionForPoint(point + adjustedPoint - renderer->locationOffset());
4350 return renderer->positionForPoint(point - renderer->locationOffset());
4351 }
4352
4353 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces
4354 // and use a different compare depending on which piece (x, y) is in.
4355 LayoutPoint cmp;
4356 if (point.x() > right) {
4357 if (point.y() < top)
4358 cmp = LayoutPoint(right, top);
4359 else if (point.y() > bottom)
4360 cmp = LayoutPoint(right, bottom);
4361 else
4362 cmp = LayoutPoint(right, point.y());
4363 } else if (point.x() < left) {
4364 if (point.y() < top)
4365 cmp = LayoutPoint(left, top);
4366 else if (point.y() > bottom)
4367 cmp = LayoutPoint(left, bottom);
4368 else
4369 cmp = LayoutPoint(left, point.y());
4370 } else {
4371 if (point.y() < top)
4372 cmp = LayoutPoint(point.x(), top);
4373 else
4374 cmp = LayoutPoint(point.x(), bottom);
4375 }
4376
4377 LayoutSize difference = cmp - point;
4378
4379 LayoutUnit dist = difference.width() * difference.width() + difference.height() * difference.height();
4380 if (dist < minDist) {
4381 closestRenderer = renderer;
4382 minDist = dist;
4383 }
4384 }
4385
4386 if (closestRenderer)
4387 return closestRenderer->positionForPoint(adjustedPoint - closestRenderer->locationOffset());
4388 return createPositionWithAffinity(firstPositionInOrBeforeNode(nonPseudoNode()));
4389 }
4390
shrinkToAvoidFloats() const4391 bool RenderBox::shrinkToAvoidFloats() const
4392 {
4393 // Floating objects don't shrink. Objects that don't avoid floats don't shrink. Marquees don't shrink.
4394 if ((isInline() && !isMarquee()) || !avoidsFloats() || isFloating())
4395 return false;
4396
4397 // Only auto width objects can possibly shrink to avoid floats.
4398 return style()->width().isAuto();
4399 }
4400
avoidsFloats() const4401 bool RenderBox::avoidsFloats() const
4402 {
4403 return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot() || isFlexItemIncludingDeprecated();
4404 }
4405
addVisualEffectOverflow()4406 void RenderBox::addVisualEffectOverflow()
4407 {
4408 if (!style()->boxShadow() && !style()->hasBorderImageOutsets())
4409 return;
4410
4411 bool isFlipped = style()->isFlippedBlocksWritingMode();
4412 bool isHorizontal = isHorizontalWritingMode();
4413
4414 LayoutRect borderBox = borderBoxRect();
4415 LayoutUnit overflowMinX = borderBox.x();
4416 LayoutUnit overflowMaxX = borderBox.maxX();
4417 LayoutUnit overflowMinY = borderBox.y();
4418 LayoutUnit overflowMaxY = borderBox.maxY();
4419
4420 // Compute box-shadow overflow first.
4421 if (style()->boxShadow()) {
4422 LayoutUnit shadowLeft;
4423 LayoutUnit shadowRight;
4424 LayoutUnit shadowTop;
4425 LayoutUnit shadowBottom;
4426 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft);
4427
4428 // In flipped blocks writing modes such as vertical-rl, the physical right shadow value is actually at the lower x-coordinate.
4429 overflowMinX = borderBox.x() + ((!isFlipped || isHorizontal) ? shadowLeft : -shadowRight);
4430 overflowMaxX = borderBox.maxX() + ((!isFlipped || isHorizontal) ? shadowRight : -shadowLeft);
4431 overflowMinY = borderBox.y() + ((!isFlipped || !isHorizontal) ? shadowTop : -shadowBottom);
4432 overflowMaxY = borderBox.maxY() + ((!isFlipped || !isHorizontal) ? shadowBottom : -shadowTop);
4433 }
4434
4435 // Now compute border-image-outset overflow.
4436 if (style()->hasBorderImageOutsets()) {
4437 LayoutBoxExtent borderOutsets = style()->borderImageOutsets();
4438
4439 // In flipped blocks writing modes, the physical sides are inverted. For example in vertical-rl, the right
4440 // border is at the lower x coordinate value.
4441 overflowMinX = min(overflowMinX, borderBox.x() - ((!isFlipped || isHorizontal) ? borderOutsets.left() : borderOutsets.right()));
4442 overflowMaxX = max(overflowMaxX, borderBox.maxX() + ((!isFlipped || isHorizontal) ? borderOutsets.right() : borderOutsets.left()));
4443 overflowMinY = min(overflowMinY, borderBox.y() - ((!isFlipped || !isHorizontal) ? borderOutsets.top() : borderOutsets.bottom()));
4444 overflowMaxY = max(overflowMaxY, borderBox.maxY() + ((!isFlipped || !isHorizontal) ? borderOutsets.bottom() : borderOutsets.top()));
4445 }
4446
4447 // Add in the final overflow with shadows and outsets combined.
4448 LayoutRect visualEffectOverflow(overflowMinX, overflowMinY, overflowMaxX - overflowMinX, overflowMaxY - overflowMinY);
4449 addVisualOverflow(visualEffectOverflow);
4450 }
4451
addOverflowFromChild(RenderBox * child,const LayoutSize & delta)4452 void RenderBox::addOverflowFromChild(RenderBox* child, const LayoutSize& delta)
4453 {
4454 // Never allow flow threads to propagate overflow up to a parent.
4455 if (child->isRenderFlowThread())
4456 return;
4457
4458 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then
4459 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this
4460 // and just propagates the border box rect instead.
4461 LayoutRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(style());
4462 childLayoutOverflowRect.move(delta);
4463 addLayoutOverflow(childLayoutOverflowRect);
4464
4465 // Add in visual overflow from the child. Even if the child clips its overflow, it may still
4466 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this
4467 // overflow if we are clipping our own overflow.
4468 if (child->hasSelfPaintingLayer())
4469 return;
4470 LayoutRect childVisualOverflowRect = child->visualOverflowRectForPropagation(style());
4471 childVisualOverflowRect.move(delta);
4472 addContentsVisualOverflow(childVisualOverflowRect);
4473 }
4474
addLayoutOverflow(const LayoutRect & rect)4475 void RenderBox::addLayoutOverflow(const LayoutRect& rect)
4476 {
4477 LayoutRect clientBox = noOverflowRect();
4478 if (clientBox.contains(rect) || rect.isEmpty())
4479 return;
4480
4481 // For overflow clip objects, we don't want to propagate overflow into unreachable areas.
4482 LayoutRect overflowRect(rect);
4483 if (hasOverflowClip() || isRenderView()) {
4484 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl
4485 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same
4486 // and vertical-lr/rl as the same.
4487 bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode();
4488 bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode();
4489 if (isFlexibleBox() && style()->isReverseFlexDirection()) {
4490 RenderFlexibleBox* flexibleBox = toRenderFlexibleBox(this);
4491 if (flexibleBox->isHorizontalFlow())
4492 hasLeftOverflow = true;
4493 else
4494 hasTopOverflow = true;
4495 }
4496
4497 if (hasColumns() && style()->columnProgression() == ReverseColumnProgression) {
4498 if (isHorizontalWritingMode() ^ !style()->hasInlineColumnAxis())
4499 hasLeftOverflow = !hasLeftOverflow;
4500 else
4501 hasTopOverflow = !hasTopOverflow;
4502 }
4503
4504 if (!hasTopOverflow)
4505 overflowRect.shiftYEdgeTo(max(overflowRect.y(), clientBox.y()));
4506 else
4507 overflowRect.shiftMaxYEdgeTo(min(overflowRect.maxY(), clientBox.maxY()));
4508 if (!hasLeftOverflow)
4509 overflowRect.shiftXEdgeTo(max(overflowRect.x(), clientBox.x()));
4510 else
4511 overflowRect.shiftMaxXEdgeTo(min(overflowRect.maxX(), clientBox.maxX()));
4512
4513 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully
4514 // contained.
4515 if (clientBox.contains(overflowRect) || overflowRect.isEmpty())
4516 return;
4517 }
4518
4519 if (!m_overflow)
4520 m_overflow = adoptPtr(new RenderOverflow(clientBox, borderBoxRect()));
4521
4522 m_overflow->addLayoutOverflow(overflowRect);
4523 }
4524
addVisualOverflow(const LayoutRect & rect)4525 void RenderBox::addVisualOverflow(const LayoutRect& rect)
4526 {
4527 LayoutRect borderBox = borderBoxRect();
4528 if (borderBox.contains(rect) || rect.isEmpty())
4529 return;
4530
4531 if (!m_overflow)
4532 m_overflow = adoptPtr(new RenderOverflow(noOverflowRect(), borderBox));
4533
4534 m_overflow->addVisualOverflow(rect);
4535 }
4536
addContentsVisualOverflow(const LayoutRect & rect)4537 void RenderBox::addContentsVisualOverflow(const LayoutRect& rect)
4538 {
4539 if (!hasOverflowClip()) {
4540 addVisualOverflow(rect);
4541 return;
4542 }
4543
4544 if (!m_overflow)
4545 m_overflow = adoptPtr(new RenderOverflow(clientBoxRect(), borderBoxRect()));
4546 m_overflow->addContentsVisualOverflow(rect);
4547 }
4548
clearLayoutOverflow()4549 void RenderBox::clearLayoutOverflow()
4550 {
4551 if (!m_overflow)
4552 return;
4553
4554 if (!hasVisualOverflow() && contentsVisualOverflowRect().isEmpty()) {
4555 m_overflow.clear();
4556 return;
4557 }
4558
4559 m_overflow->setLayoutOverflow(noOverflowRect());
4560 }
4561
percentageLogicalHeightIsResolvable(const RenderBox * box)4562 inline static bool percentageLogicalHeightIsResolvable(const RenderBox* box)
4563 {
4564 return RenderBox::percentageLogicalHeightIsResolvableFromBlock(box->containingBlock(), box->isOutOfFlowPositioned());
4565 }
4566
percentageLogicalHeightIsResolvableFromBlock(const RenderBlock * containingBlock,bool isOutOfFlowPositioned)4567 bool RenderBox::percentageLogicalHeightIsResolvableFromBlock(const RenderBlock* containingBlock, bool isOutOfFlowPositioned)
4568 {
4569 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing
4570 // block that may have a specified height and then use it. In strict mode, this violates the
4571 // specification, which states that percentage heights just revert to auto if the containing
4572 // block has an auto height. We still skip anonymous containing blocks in both modes, though, and look
4573 // only at explicit containers.
4574 const RenderBlock* cb = containingBlock;
4575 bool inQuirksMode = cb->document().inQuirksMode();
4576 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isOutOfFlowPositioned() && cb->style()->logicalHeight().isAuto()) {
4577 if (!inQuirksMode && !cb->isAnonymousBlock())
4578 break;
4579 cb = cb->containingBlock();
4580 }
4581
4582 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
4583 // explicitly specified that can be used for any percentage computations.
4584 // FIXME: We can't just check top/bottom here.
4585 // https://bugs.webkit.org/show_bug.cgi?id=46500
4586 bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto()));
4587
4588 // Table cells violate what the CSS spec says to do with heights. Basically we
4589 // don't care if the cell specified a height or not. We just always make ourselves
4590 // be a percentage of the cell's current content height.
4591 if (cb->isTableCell())
4592 return true;
4593
4594 // Otherwise we only use our percentage height if our containing block had a specified
4595 // height.
4596 if (cb->style()->logicalHeight().isFixed())
4597 return true;
4598 if (cb->style()->logicalHeight().isPercent() && !isOutOfFlowPositionedWithSpecifiedHeight)
4599 return percentageLogicalHeightIsResolvableFromBlock(cb->containingBlock(), cb->isOutOfFlowPositioned());
4600 if (cb->isRenderView() || inQuirksMode || isOutOfFlowPositionedWithSpecifiedHeight)
4601 return true;
4602 if (cb->isRoot() && isOutOfFlowPositioned) {
4603 // Match the positioned objects behavior, which is that positioned objects will fill their viewport
4604 // always. Note we could only hit this case by recurring into computePercentageLogicalHeight on a positioned containing block.
4605 return true;
4606 }
4607
4608 return false;
4609 }
4610
hasUnsplittableScrollingOverflow() const4611 bool RenderBox::hasUnsplittableScrollingOverflow() const
4612 {
4613 // We will paginate as long as we don't scroll overflow in the pagination direction.
4614 bool isHorizontal = isHorizontalWritingMode();
4615 if ((isHorizontal && !scrollsOverflowY()) || (!isHorizontal && !scrollsOverflowX()))
4616 return false;
4617
4618 // We do have overflow. We'll still be willing to paginate as long as the block
4619 // has auto logical height, auto or undefined max-logical-height and a zero or auto min-logical-height.
4620 // Note this is just a heuristic, and it's still possible to have overflow under these
4621 // conditions, but it should work out to be good enough for common cases. Paginating overflow
4622 // with scrollbars present is not the end of the world and is what we used to do in the old model anyway.
4623 return !style()->logicalHeight().isIntrinsicOrAuto()
4624 || (!style()->logicalMaxHeight().isIntrinsicOrAuto() && !style()->logicalMaxHeight().isUndefined() && (!style()->logicalMaxHeight().isPercent() || percentageLogicalHeightIsResolvable(this)))
4625 || (!style()->logicalMinHeight().isIntrinsicOrAuto() && style()->logicalMinHeight().isPositive() && (!style()->logicalMinHeight().isPercent() || percentageLogicalHeightIsResolvable(this)));
4626 }
4627
isUnsplittableForPagination() const4628 bool RenderBox::isUnsplittableForPagination() const
4629 {
4630 return isReplaced() || hasUnsplittableScrollingOverflow() || (parent() && isWritingModeRoot());
4631 }
4632
lineHeight(bool,LineDirectionMode direction,LinePositionMode) const4633 LayoutUnit RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
4634 {
4635 if (isReplaced())
4636 return direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left();
4637 return 0;
4638 }
4639
baselinePosition(FontBaseline baselineType,bool,LineDirectionMode direction,LinePositionMode linePositionMode) const4640 int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode linePositionMode) const
4641 {
4642 ASSERT(linePositionMode == PositionOnContainingLine);
4643 if (isReplaced()) {
4644 int result = direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left();
4645 if (baselineType == AlphabeticBaseline)
4646 return result;
4647 return result - result / 2;
4648 }
4649 return 0;
4650 }
4651
4652
enclosingFloatPaintingLayer() const4653 RenderLayer* RenderBox::enclosingFloatPaintingLayer() const
4654 {
4655 const RenderObject* curr = this;
4656 while (curr) {
4657 RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBox(curr)->layer() : 0;
4658 if (layer && layer->isSelfPaintingLayer())
4659 return layer;
4660 curr = curr->parent();
4661 }
4662 return 0;
4663 }
4664
logicalVisualOverflowRectForPropagation(RenderStyle * parentStyle) const4665 LayoutRect RenderBox::logicalVisualOverflowRectForPropagation(RenderStyle* parentStyle) const
4666 {
4667 LayoutRect rect = visualOverflowRectForPropagation(parentStyle);
4668 if (!parentStyle->isHorizontalWritingMode())
4669 return rect.transposedRect();
4670 return rect;
4671 }
4672
visualOverflowRectForPropagation(RenderStyle * parentStyle) const4673 LayoutRect RenderBox::visualOverflowRectForPropagation(RenderStyle* parentStyle) const
4674 {
4675 // If the writing modes of the child and parent match, then we don't have to
4676 // do anything fancy. Just return the result.
4677 LayoutRect rect = visualOverflowRect();
4678 if (parentStyle->writingMode() == style()->writingMode())
4679 return rect;
4680
4681 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
4682 // in a particular axis, then we have to flip the rect along that axis.
4683 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode)
4684 rect.setX(width() - rect.maxX());
4685 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode)
4686 rect.setY(height() - rect.maxY());
4687
4688 return rect;
4689 }
4690
logicalLayoutOverflowRectForPropagation(RenderStyle * parentStyle) const4691 LayoutRect RenderBox::logicalLayoutOverflowRectForPropagation(RenderStyle* parentStyle) const
4692 {
4693 LayoutRect rect = layoutOverflowRectForPropagation(parentStyle);
4694 if (!parentStyle->isHorizontalWritingMode())
4695 return rect.transposedRect();
4696 return rect;
4697 }
4698
layoutOverflowRectForPropagation(RenderStyle * parentStyle) const4699 LayoutRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) const
4700 {
4701 // Only propagate interior layout overflow if we don't clip it.
4702 LayoutRect rect = borderBoxRect();
4703 // We want to include the margin, but only when it adds height. Quirky margins don't contribute height
4704 // nor do the margins of self-collapsing blocks.
4705 if (!style()->hasMarginAfterQuirk() && !isSelfCollapsingBlock())
4706 rect.expand(isHorizontalWritingMode() ? LayoutSize(LayoutUnit(), marginAfter()) : LayoutSize(marginAfter(), LayoutUnit()));
4707
4708 if (!hasOverflowClip())
4709 rect.unite(layoutOverflowRect());
4710
4711 bool hasTransform = hasLayer() && layer()->transform();
4712 if (isInFlowPositioned() || hasTransform) {
4713 // If we are relatively positioned or if we have a transform, then we have to convert
4714 // this rectangle into physical coordinates, apply relative positioning and transforms
4715 // to it, and then convert it back.
4716 flipForWritingMode(rect);
4717
4718 if (hasTransform)
4719 rect = layer()->currentTransform().mapRect(rect);
4720
4721 if (isInFlowPositioned())
4722 rect.move(offsetForInFlowPosition());
4723
4724 // Now we need to flip back.
4725 flipForWritingMode(rect);
4726 }
4727
4728 // If the writing modes of the child and parent match, then we don't have to
4729 // do anything fancy. Just return the result.
4730 if (parentStyle->writingMode() == style()->writingMode())
4731 return rect;
4732
4733 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
4734 // in a particular axis, then we have to flip the rect along that axis.
4735 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode)
4736 rect.setX(width() - rect.maxX());
4737 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode)
4738 rect.setY(height() - rect.maxY());
4739
4740 return rect;
4741 }
4742
noOverflowRect() const4743 LayoutRect RenderBox::noOverflowRect() const
4744 {
4745 // Because of the special coodinate system used for overflow rectangles and many other
4746 // rectangles (not quite logical, not quite physical), we need to flip the block progression
4747 // coordinate in vertical-rl and horizontal-bt writing modes. In other words, the rectangle
4748 // returned is physical, except for the block direction progression coordinate (y in horizontal
4749 // writing modes, x in vertical writing modes), which is always "logical top". Apart from the
4750 // flipping, this method does the same as clientBoxRect().
4751
4752 LayoutUnit left = borderLeft() + (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft() ? verticalScrollbarWidth() : 0);
4753 LayoutUnit top = borderTop();
4754 LayoutUnit right = borderRight();
4755 LayoutUnit bottom = borderBottom();
4756 LayoutRect rect(left, top, width() - left - right, height() - top - bottom);
4757 flipForWritingMode(rect);
4758 // Subtract space occupied by scrollbars. Order is important here: first flip, then subtract
4759 // scrollbars. This may seem backwards and weird, since one would think that a horizontal
4760 // scrollbar at the physical bottom in horizontal-bt ought to be at the logical top (physical
4761 // bottom), between the logical top (physical bottom) border and the logical top (physical
4762 // bottom) padding. But this is how the rest of the code expects us to behave. This is highly
4763 // related to https://bugs.webkit.org/show_bug.cgi?id=76129
4764 // FIXME: when the above mentioned bug is fixed, it should hopefully be possible to call
4765 // clientBoxRect() or paddingBoxRect() in this method, rather than fiddling with the edges on
4766 // our own.
4767 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
4768 rect.contract(0, horizontalScrollbarHeight());
4769 else
4770 rect.contract(verticalScrollbarWidth(), horizontalScrollbarHeight());
4771 return rect;
4772 }
4773
overflowRectForPaintRejection() const4774 LayoutRect RenderBox::overflowRectForPaintRejection() const
4775 {
4776 LayoutRect overflowRect = visualOverflowRect();
4777 if (!m_overflow || !usesCompositedScrolling())
4778 return overflowRect;
4779
4780 overflowRect.unite(layoutOverflowRect());
4781 overflowRect.move(-scrolledContentOffset());
4782 return overflowRect;
4783 }
4784
offsetLeft() const4785 LayoutUnit RenderBox::offsetLeft() const
4786 {
4787 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).x();
4788 }
4789
offsetTop() const4790 LayoutUnit RenderBox::offsetTop() const
4791 {
4792 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).y();
4793 }
4794
flipForWritingModeForChild(const RenderBox * child,const LayoutPoint & point) const4795 LayoutPoint RenderBox::flipForWritingModeForChild(const RenderBox* child, const LayoutPoint& point) const
4796 {
4797 if (!style()->isFlippedBlocksWritingMode())
4798 return point;
4799
4800 // The child is going to add in its x() and y(), so we have to make sure it ends up in
4801 // the right place.
4802 if (isHorizontalWritingMode())
4803 return LayoutPoint(point.x(), point.y() + height() - child->height() - (2 * child->y()));
4804 return LayoutPoint(point.x() + width() - child->width() - (2 * child->x()), point.y());
4805 }
4806
flipForWritingMode(LayoutRect & rect) const4807 void RenderBox::flipForWritingMode(LayoutRect& rect) const
4808 {
4809 if (!style()->isFlippedBlocksWritingMode())
4810 return;
4811
4812 if (isHorizontalWritingMode())
4813 rect.setY(height() - rect.maxY());
4814 else
4815 rect.setX(width() - rect.maxX());
4816 }
4817
flipForWritingMode(LayoutUnit position) const4818 LayoutUnit RenderBox::flipForWritingMode(LayoutUnit position) const
4819 {
4820 if (!style()->isFlippedBlocksWritingMode())
4821 return position;
4822 return logicalHeight() - position;
4823 }
4824
flipForWritingMode(const LayoutPoint & position) const4825 LayoutPoint RenderBox::flipForWritingMode(const LayoutPoint& position) const
4826 {
4827 if (!style()->isFlippedBlocksWritingMode())
4828 return position;
4829 return isHorizontalWritingMode() ? LayoutPoint(position.x(), height() - position.y()) : LayoutPoint(width() - position.x(), position.y());
4830 }
4831
flipForWritingModeIncludingColumns(const LayoutPoint & point) const4832 LayoutPoint RenderBox::flipForWritingModeIncludingColumns(const LayoutPoint& point) const
4833 {
4834 if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
4835 return flipForWritingMode(point);
4836 return toRenderBlock(this)->flipForWritingModeIncludingColumns(point);
4837 }
4838
flipForWritingMode(const LayoutSize & offset) const4839 LayoutSize RenderBox::flipForWritingMode(const LayoutSize& offset) const
4840 {
4841 if (!style()->isFlippedBlocksWritingMode())
4842 return offset;
4843 return isHorizontalWritingMode() ? LayoutSize(offset.width(), height() - offset.height()) : LayoutSize(width() - offset.width(), offset.height());
4844 }
4845
flipForWritingMode(const FloatPoint & position) const4846 FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const
4847 {
4848 if (!style()->isFlippedBlocksWritingMode())
4849 return position;
4850 return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y());
4851 }
4852
flipForWritingMode(FloatRect & rect) const4853 void RenderBox::flipForWritingMode(FloatRect& rect) const
4854 {
4855 if (!style()->isFlippedBlocksWritingMode())
4856 return;
4857
4858 if (isHorizontalWritingMode())
4859 rect.setY(height() - rect.maxY());
4860 else
4861 rect.setX(width() - rect.maxX());
4862 }
4863
topLeftLocation() const4864 LayoutPoint RenderBox::topLeftLocation() const
4865 {
4866 RenderBlock* containerBlock = containingBlock();
4867 if (!containerBlock || containerBlock == this)
4868 return location();
4869 return containerBlock->flipForWritingModeForChild(this, location());
4870 }
4871
topLeftLocationOffset() const4872 LayoutSize RenderBox::topLeftLocationOffset() const
4873 {
4874 RenderBlock* containerBlock = containingBlock();
4875 if (!containerBlock || containerBlock == this)
4876 return locationOffset();
4877
4878 LayoutRect rect(frameRect());
4879 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline.
4880 return LayoutSize(rect.x(), rect.y());
4881 }
4882
hasRelativeDimensions() const4883 bool RenderBox::hasRelativeDimensions() const
4884 {
4885 // FIXME: This should probably include viewport percentage heights as well.
4886 return style()->height().isPercent() || style()->width().isPercent()
4887 || style()->maxHeight().isPercent() || style()->maxWidth().isPercent()
4888 || style()->minHeight().isPercent() || style()->minWidth().isPercent();
4889 }
4890
hasRelativeLogicalHeight() const4891 bool RenderBox::hasRelativeLogicalHeight() const
4892 {
4893 return style()->logicalHeight().isPercent()
4894 || style()->logicalMinHeight().isPercent()
4895 || style()->logicalMaxHeight().isPercent()
4896 || style()->logicalHeight().isViewportPercentage()
4897 || style()->logicalMinHeight().isViewportPercentage()
4898 || style()->logicalMaxHeight().isViewportPercentage();
4899 }
4900
markBoxForRelayoutAfterSplit(RenderBox * box)4901 static void markBoxForRelayoutAfterSplit(RenderBox* box)
4902 {
4903 // FIXME: The table code should handle that automatically. If not,
4904 // we should fix it and remove the table part checks.
4905 if (box->isTable()) {
4906 // Because we may have added some sections with already computed column structures, we need to
4907 // sync the table structure with them now. This avoids crashes when adding new cells to the table.
4908 toRenderTable(box)->forceSectionsRecalc();
4909 } else if (box->isTableSection())
4910 toRenderTableSection(box)->setNeedsCellRecalc();
4911
4912 box->setNeedsLayoutAndPrefWidthsRecalc();
4913 }
4914
splitAnonymousBoxesAroundChild(RenderObject * beforeChild)4915 RenderObject* RenderBox::splitAnonymousBoxesAroundChild(RenderObject* beforeChild)
4916 {
4917 bool didSplitParentAnonymousBoxes = false;
4918
4919 while (beforeChild->parent() != this) {
4920 RenderBox* boxToSplit = toRenderBox(beforeChild->parent());
4921 if (boxToSplit->firstChild() != beforeChild && boxToSplit->isAnonymous()) {
4922 didSplitParentAnonymousBoxes = true;
4923
4924 // We have to split the parent box into two boxes and move children
4925 // from |beforeChild| to end into the new post box.
4926 RenderBox* postBox = boxToSplit->createAnonymousBoxWithSameTypeAs(this);
4927 postBox->setChildrenInline(boxToSplit->childrenInline());
4928 RenderBox* parentBox = toRenderBox(boxToSplit->parent());
4929 // We need to invalidate the |parentBox| before inserting the new node
4930 // so that the table repainting logic knows the structure is dirty.
4931 // See for example RenderTableCell:clippedOverflowRectForRepaint.
4932 markBoxForRelayoutAfterSplit(parentBox);
4933 parentBox->virtualChildren()->insertChildNode(parentBox, postBox, boxToSplit->nextSibling());
4934 boxToSplit->moveChildrenTo(postBox, beforeChild, 0, true);
4935
4936 markBoxForRelayoutAfterSplit(boxToSplit);
4937 markBoxForRelayoutAfterSplit(postBox);
4938
4939 beforeChild = postBox;
4940 } else
4941 beforeChild = boxToSplit;
4942 }
4943
4944 if (didSplitParentAnonymousBoxes)
4945 markBoxForRelayoutAfterSplit(this);
4946
4947 ASSERT(beforeChild->parent() == this);
4948 return beforeChild;
4949 }
4950
offsetFromLogicalTopOfFirstPage() const4951 LayoutUnit RenderBox::offsetFromLogicalTopOfFirstPage() const
4952 {
4953 LayoutState* layoutState = view()->layoutState();
4954 if (layoutState && !layoutState->isPaginated())
4955 return 0;
4956
4957 if (!layoutState && !flowThreadContainingBlock())
4958 return 0;
4959
4960 RenderBlock* containerBlock = containingBlock();
4961 return containerBlock->offsetFromLogicalTopOfFirstPage() + logicalTop();
4962 }
4963
4964 } // namespace WebCore
4965