blob: e53d7b1e7584186562aa25a86bc77125e4596474 [file] [log] [blame]
hyatt@apple.com5388e672013-09-06 20:54:47 +00001/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2007 David Smith (catfish.man@gmail.com)
5 * Copyright (C) 2003-2013 Apple Inc. All rights reserved.
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25#include "RenderBlockFlow.h"
26
hyatt@apple.comc9b1aef2013-09-09 20:23:21 +000027#include "LayoutRepainter.h"
28#include "RenderFlowThread.h"
29#include "RenderLayer.h"
30#include "RenderView.h"
31
32using namespace std;
33
hyatt@apple.com5388e672013-09-06 20:54:47 +000034namespace WebCore {
35
antti@apple.com8f335082013-09-09 19:54:33 +000036RenderBlockFlow::RenderBlockFlow(Element* element)
37 : RenderBlock(element)
hyatt@apple.com5388e672013-09-06 20:54:47 +000038{
39}
40
41RenderBlockFlow::~RenderBlockFlow()
42{
43}
44
hyatt@apple.comc9b1aef2013-09-09 20:23:21 +000045void RenderBlockFlow::clearFloats()
46{
47 if (m_floatingObjects)
48 m_floatingObjects->setHorizontalWritingMode(isHorizontalWritingMode());
49
50 HashSet<RenderBox*> oldIntrudingFloatSet;
51 if (!childrenInline() && m_floatingObjects) {
52 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
53 FloatingObjectSetIterator end = floatingObjectSet.end();
54 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
55 FloatingObject* floatingObject = *it;
56 if (!floatingObject->isDescendant())
57 oldIntrudingFloatSet.add(floatingObject->renderer());
58 }
59 }
60
61 // Inline blocks are covered by the isReplaced() check in the avoidFloats method.
62 if (avoidsFloats() || isRoot() || isRenderView() || isFloatingOrOutOfFlowPositioned() || isTableCell()) {
63 if (m_floatingObjects) {
64 deleteAllValues(m_floatingObjects->set());
65 m_floatingObjects->clear();
66 }
67 if (!oldIntrudingFloatSet.isEmpty())
68 markAllDescendantsWithFloatsForLayout();
69 return;
70 }
71
72 typedef HashMap<RenderObject*, FloatingObject*> RendererToFloatInfoMap;
73 RendererToFloatInfoMap floatMap;
74
75 if (m_floatingObjects) {
76 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
77 if (childrenInline()) {
78 FloatingObjectSetIterator end = floatingObjectSet.end();
79 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
80 FloatingObject* f = *it;
81 floatMap.add(f->renderer(), f);
82 }
83 } else
84 deleteAllValues(floatingObjectSet);
85 m_floatingObjects->clear();
86 }
87
88 // We should not process floats if the parent node is not a RenderBlock. Otherwise, we will add
89 // floats in an invalid context. This will cause a crash arising from a bad cast on the parent.
90 // See <rdar://problem/8049753>, where float property is applied on a text node in a SVG.
91 if (!parent() || !parent()->isRenderBlock())
92 return;
93
94 // Attempt to locate a previous sibling with overhanging floats. We skip any elements that are
95 // out of flow (like floating/positioned elements), and we also skip over any objects that may have shifted
96 // to avoid floats.
97 RenderBlock* parentBlock = toRenderBlock(parent());
98 bool parentHasFloats = false;
99 RenderObject* prev = previousSibling();
100 while (prev && (prev->isFloatingOrOutOfFlowPositioned() || !prev->isBox() || !prev->isRenderBlock() || toRenderBlock(prev)->avoidsFloats())) {
101 if (prev->isFloating())
102 parentHasFloats = true;
103 prev = prev->previousSibling();
104 }
105
106 // First add in floats from the parent.
107 LayoutUnit logicalTopOffset = logicalTop();
108 if (parentHasFloats)
109 addIntrudingFloats(parentBlock, parentBlock->logicalLeftOffsetForContent(), logicalTopOffset);
110
111 LayoutUnit logicalLeftOffset = 0;
112 if (prev)
113 logicalTopOffset -= toRenderBox(prev)->logicalTop();
114 else {
115 prev = parentBlock;
116 logicalLeftOffset += parentBlock->logicalLeftOffsetForContent();
117 }
118
119 // Add overhanging floats from the previous RenderBlock, but only if it has a float that intrudes into our space.
120 RenderBlock* block = toRenderBlock(prev);
121 if (block->m_floatingObjects && block->lowestFloatLogicalBottom() > logicalTopOffset)
122 addIntrudingFloats(block, logicalLeftOffset, logicalTopOffset);
123
124 if (childrenInline()) {
125 LayoutUnit changeLogicalTop = LayoutUnit::max();
126 LayoutUnit changeLogicalBottom = LayoutUnit::min();
127 if (m_floatingObjects) {
128 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
129 FloatingObjectSetIterator end = floatingObjectSet.end();
130 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
131 FloatingObject* f = *it;
132 FloatingObject* oldFloatingObject = floatMap.get(f->renderer());
133 LayoutUnit logicalBottom = f->logicalBottom(isHorizontalWritingMode());
134 if (oldFloatingObject) {
135 LayoutUnit oldLogicalBottom = oldFloatingObject->logicalBottom(isHorizontalWritingMode());
136 if (f->logicalWidth(isHorizontalWritingMode()) != oldFloatingObject->logicalWidth(isHorizontalWritingMode()) || f->logicalLeft(isHorizontalWritingMode()) != oldFloatingObject->logicalLeft(isHorizontalWritingMode())) {
137 changeLogicalTop = 0;
138 changeLogicalBottom = max(changeLogicalBottom, max(logicalBottom, oldLogicalBottom));
139 } else {
140 if (logicalBottom != oldLogicalBottom) {
141 changeLogicalTop = min(changeLogicalTop, min(logicalBottom, oldLogicalBottom));
142 changeLogicalBottom = max(changeLogicalBottom, max(logicalBottom, oldLogicalBottom));
143 }
144 LayoutUnit logicalTop = f->logicalTop(isHorizontalWritingMode());
145 LayoutUnit oldLogicalTop = oldFloatingObject->logicalTop(isHorizontalWritingMode());
146 if (logicalTop != oldLogicalTop) {
147 changeLogicalTop = min(changeLogicalTop, min(logicalTop, oldLogicalTop));
148 changeLogicalBottom = max(changeLogicalBottom, max(logicalTop, oldLogicalTop));
149 }
150 }
151
152 floatMap.remove(f->renderer());
153 if (oldFloatingObject->originatingLine() && !selfNeedsLayout()) {
154 ASSERT(&oldFloatingObject->originatingLine()->renderer() == this);
155 oldFloatingObject->originatingLine()->markDirty();
156 }
157 delete oldFloatingObject;
158 } else {
159 changeLogicalTop = 0;
160 changeLogicalBottom = max(changeLogicalBottom, logicalBottom);
161 }
162 }
163 }
164
165 RendererToFloatInfoMap::iterator end = floatMap.end();
166 for (RendererToFloatInfoMap::iterator it = floatMap.begin(); it != end; ++it) {
167 FloatingObject* floatingObject = (*it).value;
168 if (!floatingObject->isDescendant()) {
169 changeLogicalTop = 0;
170 changeLogicalBottom = max(changeLogicalBottom, floatingObject->logicalBottom(isHorizontalWritingMode()));
171 }
172 }
173 deleteAllValues(floatMap);
174
175 markLinesDirtyInBlockRange(changeLogicalTop, changeLogicalBottom);
176 } else if (!oldIntrudingFloatSet.isEmpty()) {
177 // If there are previously intruding floats that no longer intrude, then children with floats
178 // should also get layout because they might need their floating object lists cleared.
179 if (m_floatingObjects->set().size() < oldIntrudingFloatSet.size())
180 markAllDescendantsWithFloatsForLayout();
181 else {
182 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
183 FloatingObjectSetIterator end = floatingObjectSet.end();
184 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end && !oldIntrudingFloatSet.isEmpty(); ++it)
185 oldIntrudingFloatSet.remove((*it)->renderer());
186 if (!oldIntrudingFloatSet.isEmpty())
187 markAllDescendantsWithFloatsForLayout();
188 }
189 }
190}
191
192void RenderBlockFlow::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight)
193{
194 ASSERT(needsLayout());
195
196 if (!relayoutChildren && simplifiedLayout())
197 return;
198
199 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
200
201 if (updateLogicalWidthAndColumnWidth())
202 relayoutChildren = true;
203
204 clearFloats();
205
206 LayoutUnit previousHeight = logicalHeight();
207 // FIXME: should this start out as borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(),
208 // for consistency with other render classes?
209 setLogicalHeight(0);
210
211 bool pageLogicalHeightChanged = false;
212 bool hasSpecifiedPageLogicalHeight = false;
213 checkForPaginationLogicalHeightChange(pageLogicalHeight, pageLogicalHeightChanged, hasSpecifiedPageLogicalHeight);
214
215 RenderStyle* styleToUse = style();
216 LayoutStateMaintainer statePusher(&view(), this, locationOffset(), hasColumns() || hasTransform() || hasReflection() || styleToUse->isFlippedBlocksWritingMode(), pageLogicalHeight, pageLogicalHeightChanged, columnInfo());
217
218 // Regions changing widths can force us to relayout our children.
219 RenderFlowThread* flowThread = flowThreadContainingBlock();
220 if (logicalWidthChangedInRegions(flowThread))
221 relayoutChildren = true;
222 if (updateShapesBeforeBlockLayout())
223 relayoutChildren = true;
224
225 // We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track
226 // our current maximal positive and negative margins. These values are used when we
227 // are collapsed with adjacent blocks, so for example, if you have block A and B
228 // collapsing together, then you'd take the maximal positive margin from both A and B
229 // and subtract it from the maximal negative margin from both A and B to get the
230 // true collapsed margin. This algorithm is recursive, so when we finish layout()
231 // our block knows its current maximal positive/negative values.
232 //
233 // Start out by setting our margin values to our current margins. Table cells have
234 // no margins, so we don't fill in the values for table cells.
235 bool isCell = isTableCell();
236 if (!isCell) {
237 initMaxMarginValues();
238
239 setHasMarginBeforeQuirk(styleToUse->hasMarginBeforeQuirk());
240 setHasMarginAfterQuirk(styleToUse->hasMarginAfterQuirk());
241 setPaginationStrut(0);
242 }
243
244 LayoutUnit repaintLogicalTop = 0;
245 LayoutUnit repaintLogicalBottom = 0;
246 LayoutUnit maxFloatLogicalBottom = 0;
247 if (!firstChild() && !isAnonymousBlock())
248 setChildrenInline(true);
249 if (childrenInline())
250 layoutInlineChildren(relayoutChildren, repaintLogicalTop, repaintLogicalBottom);
251 else
252 layoutBlockChildren(relayoutChildren, maxFloatLogicalBottom);
253
254 // Expand our intrinsic height to encompass floats.
255 LayoutUnit toAdd = borderAndPaddingAfter() + scrollbarLogicalHeight();
256 if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && expandsToEncloseOverhangingFloats())
257 setLogicalHeight(lowestFloatLogicalBottom() + toAdd);
258
259 if (relayoutForPagination(hasSpecifiedPageLogicalHeight, pageLogicalHeight, statePusher) || relayoutToAvoidWidows(statePusher)) {
260 ASSERT(!shouldBreakAtLineToAvoidWidow());
261 return;
262 }
263
264 // Calculate our new height.
265 LayoutUnit oldHeight = logicalHeight();
266 LayoutUnit oldClientAfterEdge = clientLogicalBottom();
267
268 // Before updating the final size of the flow thread make sure a forced break is applied after the content.
269 // This ensures the size information is correctly computed for the last auto-height region receiving content.
270 if (isRenderFlowThread())
271 toRenderFlowThread(this)->applyBreakAfterContent(oldClientAfterEdge);
272
273 updateLogicalHeight();
274 LayoutUnit newHeight = logicalHeight();
275 if (oldHeight != newHeight) {
276 if (oldHeight > newHeight && maxFloatLogicalBottom > newHeight && !childrenInline()) {
277 // One of our children's floats may have become an overhanging float for us. We need to look for it.
278 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
279 if (child->isRenderBlockFlow() && !child->isFloatingOrOutOfFlowPositioned()) {
280 RenderBlock* block = toRenderBlock(child);
281 if (block->lowestFloatLogicalBottom() + block->logicalTop() > newHeight)
282 addOverhangingFloats(block, false);
283 }
284 }
285 }
286 }
287
288 bool heightChanged = (previousHeight != newHeight);
289 if (heightChanged)
290 relayoutChildren = true;
291
292 layoutPositionedObjects(relayoutChildren || isRoot());
293
294 updateShapesAfterBlockLayout(heightChanged);
295
296 // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway).
297 computeOverflow(oldClientAfterEdge);
298
299 statePusher.pop();
300
301 fitBorderToLinesIfNeeded();
302
303 if (view().layoutState()->m_pageLogicalHeight)
304 setPageLogicalOffset(view().layoutState()->pageLogicalOffset(this, logicalTop()));
305
306 updateLayerTransform();
307
308 // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
309 // we overflow or not.
310 updateScrollInfoAfterLayout();
311
312 // FIXME: This repaint logic should be moved into a separate helper function!
313 // Repaint with our new bounds if they are different from our old bounds.
314 bool didFullRepaint = repainter.repaintAfterLayout();
315 if (!didFullRepaint && repaintLogicalTop != repaintLogicalBottom && (styleToUse->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) {
316 // FIXME: We could tighten up the left and right invalidation points if we let layoutInlineChildren fill them in based off the particular lines
317 // it had to lay out. We wouldn't need the hasOverflowClip() hack in that case either.
318 LayoutUnit repaintLogicalLeft = logicalLeftVisualOverflow();
319 LayoutUnit repaintLogicalRight = logicalRightVisualOverflow();
320 if (hasOverflowClip()) {
321 // If we have clipped overflow, we should use layout overflow as well, since visual overflow from lines didn't propagate to our block's overflow.
322 // Note the old code did this as well but even for overflow:visible. The addition of hasOverflowClip() at least tightens up the hack a bit.
323 // layoutInlineChildren should be patched to compute the entire repaint rect.
324 repaintLogicalLeft = min(repaintLogicalLeft, logicalLeftLayoutOverflow());
325 repaintLogicalRight = max(repaintLogicalRight, logicalRightLayoutOverflow());
326 }
327
328 LayoutRect repaintRect;
329 if (isHorizontalWritingMode())
330 repaintRect = LayoutRect(repaintLogicalLeft, repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop);
331 else
332 repaintRect = LayoutRect(repaintLogicalTop, repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft);
333
334 // The repaint rect may be split across columns, in which case adjustRectForColumns() will return the union.
335 adjustRectForColumns(repaintRect);
336
337 repaintRect.inflate(maximalOutlineSize(PaintPhaseOutline));
338
339 if (hasOverflowClip()) {
340 // Adjust repaint rect for scroll offset
341 repaintRect.move(-scrolledContentOffset());
342
343 // Don't allow this rect to spill out of our overflow box.
344 repaintRect.intersect(LayoutRect(LayoutPoint(), size()));
345 }
346
347 // Make sure the rect is still non-empty after intersecting for overflow above
348 if (!repaintRect.isEmpty()) {
349 repaintRectangle(repaintRect); // We need to do a partial repaint of our content.
350 if (hasReflection())
351 repaintRectangle(reflectedRect(repaintRect));
352 }
353 }
354
355 setNeedsLayout(false);
356}
357
358void RenderBlockFlow::layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloatLogicalBottom)
359{
360 dirtyForLayoutFromPercentageHeightDescendants();
361
362 LayoutUnit beforeEdge = borderAndPaddingBefore();
363 LayoutUnit afterEdge = borderAndPaddingAfter() + scrollbarLogicalHeight();
364
365 setLogicalHeight(beforeEdge);
366
367 // Lay out our hypothetical grid line as though it occurs at the top of the block.
368 if (view().layoutState()->lineGrid() == this)
369 layoutLineGridBox();
370
371 // The margin struct caches all our current margin collapsing state.
372 MarginInfo marginInfo(this, beforeEdge, afterEdge);
373
374 // Fieldsets need to find their legend and position it inside the border of the object.
375 // The legend then gets skipped during normal layout. The same is true for ruby text.
376 // It doesn't get included in the normal layout process but is instead skipped.
377 RenderObject* childToExclude = layoutSpecialExcludedChild(relayoutChildren);
378
379 LayoutUnit previousFloatLogicalBottom = 0;
380 maxFloatLogicalBottom = 0;
381
382 RenderBox* next = firstChildBox();
383
384 while (next) {
385 RenderBox* child = next;
386 next = child->nextSiblingBox();
387
388 if (childToExclude == child)
389 continue; // Skip this child, since it will be positioned by the specialized subclass (fieldsets and ruby runs).
390
391 updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child);
392
393 if (child->isOutOfFlowPositioned()) {
394 child->containingBlock()->insertPositionedObject(child);
395 adjustPositionedBlock(child, marginInfo);
396 continue;
397 }
398 if (child->isFloating()) {
399 insertFloatingObject(child);
400 adjustFloatingBlock(marginInfo);
401 continue;
402 }
403
404 // Lay out the child.
405 layoutBlockChild(child, marginInfo, previousFloatLogicalBottom, maxFloatLogicalBottom);
406 }
407
408 // Now do the handling of the bottom of the block, adding in our bottom border/padding and
409 // determining the correct collapsed bottom margin information.
410 handleAfterSideOfBlock(beforeEdge, afterEdge, marginInfo);
411}
412
hyatt@apple.com5388e672013-09-06 20:54:47 +0000413} // namespace WebCore