1 /*
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, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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 "RenderBlock.h"
26
27 #include "ColumnInfo.h"
28 #include "Document.h"
29 #include "Element.h"
30 #include "FloatQuad.h"
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "GraphicsContext.h"
34 #include "HTMLFormElement.h"
35 #include "HTMLNames.h"
36 #include "HitTestResult.h"
37 #include "InlineIterator.h"
38 #include "InlineTextBox.h"
39 #include "PaintInfo.h"
40 #include "RenderCombineText.h"
41 #include "RenderFlexibleBox.h"
42 #include "RenderImage.h"
43 #include "RenderInline.h"
44 #include "RenderLayer.h"
45 #include "RenderMarquee.h"
46 #include "RenderReplica.h"
47 #include "RenderTableCell.h"
48 #include "RenderTextFragment.h"
49 #include "RenderTheme.h"
50 #include "RenderView.h"
51 #include "SelectionController.h"
52 #include "Settings.h"
53 #include "TextRun.h"
54 #include "TransformState.h"
55 #include <wtf/StdLibExtras.h>
56
57 using namespace std;
58 using namespace WTF;
59 using namespace Unicode;
60
61 namespace WebCore {
62
63 using namespace HTMLNames;
64
65 typedef WTF::HashMap<const RenderBox*, ColumnInfo*> ColumnInfoMap;
66 static ColumnInfoMap* gColumnInfoMap = 0;
67
68 typedef WTF::HashMap<const RenderBlock*, HashSet<RenderBox*>*> PercentHeightDescendantsMap;
69 static PercentHeightDescendantsMap* gPercentHeightDescendantsMap = 0;
70
71 typedef WTF::HashMap<const RenderBox*, HashSet<RenderBlock*>*> PercentHeightContainerMap;
72 static PercentHeightContainerMap* gPercentHeightContainerMap = 0;
73
74 typedef WTF::HashMap<RenderBlock*, ListHashSet<RenderInline*>*> ContinuationOutlineTableMap;
75
76 typedef WTF::HashSet<RenderBlock*> DelayedUpdateScrollInfoSet;
77 static int gDelayUpdateScrollInfo = 0;
78 static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0;
79
80 bool RenderBlock::s_canPropagateFloatIntoSibling = false;
81
82 // Our MarginInfo state used when laying out block children.
MarginInfo(RenderBlock * block,int beforeBorderPadding,int afterBorderPadding)83 RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int beforeBorderPadding, int afterBorderPadding)
84 : m_atBeforeSideOfBlock(true)
85 , m_atAfterSideOfBlock(false)
86 , m_marginBeforeQuirk(false)
87 , m_marginAfterQuirk(false)
88 , m_determinedMarginBeforeQuirk(false)
89 {
90 // Whether or not we can collapse our own margins with our children. We don't do this
91 // if we had any border/padding (obviously), if we're the root or HTML elements, or if
92 // we're positioned, floating, a table cell.
93 m_canCollapseWithChildren = !block->isRenderView() && !block->isRoot() && !block->isPositioned()
94 && !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable()
95 && !block->isWritingModeRoot();
96
97 m_canCollapseMarginBeforeWithChildren = m_canCollapseWithChildren && (beforeBorderPadding == 0) && block->style()->marginBeforeCollapse() != MSEPARATE;
98
99 // If any height other than auto is specified in CSS, then we don't collapse our bottom
100 // margins with our children's margins. To do otherwise would be to risk odd visual
101 // effects when the children overflow out of the parent block and yet still collapse
102 // with it. We also don't collapse if we have any bottom border/padding.
103 m_canCollapseMarginAfterWithChildren = m_canCollapseWithChildren && (afterBorderPadding == 0) &&
104 (block->style()->logicalHeight().isAuto() && block->style()->logicalHeight().value() == 0) && block->style()->marginAfterCollapse() != MSEPARATE;
105
106 m_quirkContainer = block->isTableCell() || block->isBody() || block->style()->marginBeforeCollapse() == MDISCARD ||
107 block->style()->marginAfterCollapse() == MDISCARD;
108
109 m_positiveMargin = m_canCollapseMarginBeforeWithChildren ? block->maxPositiveMarginBefore() : 0;
110 m_negativeMargin = m_canCollapseMarginBeforeWithChildren ? block->maxNegativeMarginBefore() : 0;
111 }
112
113 // -------------------------------------------------------------------------------------------------------
114
RenderBlock(Node * node)115 RenderBlock::RenderBlock(Node* node)
116 : RenderBox(node)
117 , m_lineHeight(-1)
118 , m_beingDestroyed(false)
119 {
120 setChildrenInline(true);
121 }
122
~RenderBlock()123 RenderBlock::~RenderBlock()
124 {
125 if (m_floatingObjects)
126 deleteAllValues(m_floatingObjects->set());
127
128 if (hasColumns())
129 delete gColumnInfoMap->take(this);
130
131 if (gPercentHeightDescendantsMap) {
132 if (HashSet<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->take(this)) {
133 HashSet<RenderBox*>::iterator end = descendantSet->end();
134 for (HashSet<RenderBox*>::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) {
135 HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->get(*descendant);
136 ASSERT(containerSet);
137 if (!containerSet)
138 continue;
139 ASSERT(containerSet->contains(this));
140 containerSet->remove(this);
141 if (containerSet->isEmpty()) {
142 gPercentHeightContainerMap->remove(*descendant);
143 delete containerSet;
144 }
145 }
146 delete descendantSet;
147 }
148 }
149 }
150
destroy()151 void RenderBlock::destroy()
152 {
153 // Mark as being destroyed to avoid trouble with merges in removeChild().
154 m_beingDestroyed = true;
155
156 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
157 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
158 children()->destroyLeftoverChildren();
159
160 // Destroy our continuation before anything other than anonymous children.
161 // The reason we don't destroy it before anonymous children is that they may
162 // have continuations of their own that are anonymous children of our continuation.
163 RenderBoxModelObject* continuation = this->continuation();
164 if (continuation) {
165 continuation->destroy();
166 setContinuation(0);
167 }
168
169 if (!documentBeingDestroyed()) {
170 if (firstLineBox()) {
171 // We can't wait for RenderBox::destroy to clear the selection,
172 // because by then we will have nuked the line boxes.
173 // FIXME: The SelectionController should be responsible for this when it
174 // is notified of DOM mutations.
175 if (isSelectionBorder())
176 view()->clearSelection();
177
178 // If we are an anonymous block, then our line boxes might have children
179 // that will outlast this block. In the non-anonymous block case those
180 // children will be destroyed by the time we return from this function.
181 if (isAnonymousBlock()) {
182 for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) {
183 while (InlineBox* childBox = box->firstChild())
184 childBox->remove();
185 }
186 }
187 } else if (parent())
188 parent()->dirtyLinesFromChangedChild(this);
189 }
190
191 m_lineBoxes.deleteLineBoxes(renderArena());
192
193 RenderBox::destroy();
194 }
195
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)196 void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
197 {
198 s_canPropagateFloatIntoSibling = style() ? !isFloatingOrPositioned() && !avoidsFloats() : false;
199
200 setReplaced(newStyle->isDisplayInlineType());
201
202 if (style() && parent() && diff == StyleDifferenceLayout && style()->position() != newStyle->position()) {
203 if (newStyle->position() == StaticPosition)
204 // Clear our positioned objects list. Our absolutely positioned descendants will be
205 // inserted into our containing block's positioned objects list during layout.
206 removePositionedObjects(0);
207 else if (style()->position() == StaticPosition) {
208 // Remove our absolutely positioned descendants from their current containing block.
209 // They will be inserted into our positioned objects list during layout.
210 RenderObject* cb = parent();
211 while (cb && (cb->style()->position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) {
212 if (cb->style()->position() == RelativePosition && cb->isInline() && !cb->isReplaced()) {
213 cb = cb->containingBlock();
214 break;
215 }
216 cb = cb->parent();
217 }
218
219 if (cb->isRenderBlock())
220 toRenderBlock(cb)->removePositionedObjects(this);
221 }
222
223 if (containsFloats() && !isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition))
224 markAllDescendantsWithFloatsForLayout();
225 }
226
227 RenderBox::styleWillChange(diff, newStyle);
228 }
229
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)230 void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
231 {
232 RenderBox::styleDidChange(diff, oldStyle);
233
234 if (!isAnonymousBlock()) {
235 // Ensure that all of our continuation blocks pick up the new style.
236 for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) {
237 RenderBoxModelObject* nextCont = currCont->continuation();
238 currCont->setContinuation(0);
239 currCont->setStyle(style());
240 currCont->setContinuation(nextCont);
241 }
242 }
243
244 propagateStyleToAnonymousChildren(true);
245 m_lineHeight = -1;
246
247 // Update pseudos for :before and :after now.
248 if (!isAnonymous() && document()->usesBeforeAfterRules() && canHaveChildren()) {
249 updateBeforeAfterContent(BEFORE);
250 updateBeforeAfterContent(AFTER);
251 }
252
253 // After our style changed, if we lose our ability to propagate floats into next sibling
254 // blocks, then we need to find the top most parent containing that overhanging float and
255 // then mark its descendants with floats for layout and clear all floats from its next
256 // sibling blocks that exist in our floating objects list. See bug 56299 and 62875.
257 bool canPropagateFloatIntoSibling = !isFloatingOrPositioned() && !avoidsFloats();
258 if (diff == StyleDifferenceLayout && s_canPropagateFloatIntoSibling && !canPropagateFloatIntoSibling && hasOverhangingFloats()) {
259 RenderBlock* parentBlock = this;
260 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
261 FloatingObjectSetIterator end = floatingObjectSet.end();
262
263 for (RenderObject* curr = parent(); curr && !curr->isRenderView(); curr = curr->parent()) {
264 if (curr->isRenderBlock()) {
265 RenderBlock* currBlock = toRenderBlock(curr);
266
267 if (currBlock->hasOverhangingFloats()) {
268 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
269 RenderBox* renderer = (*it)->renderer();
270 if (currBlock->hasOverhangingFloat(renderer)) {
271 parentBlock = currBlock;
272 break;
273 }
274 }
275 }
276 }
277 }
278
279 parentBlock->markAllDescendantsWithFloatsForLayout();
280 parentBlock->markSiblingsWithFloatsForLayout();
281 }
282 }
283
updateBeforeAfterContent(PseudoId pseudoId)284 void RenderBlock::updateBeforeAfterContent(PseudoId pseudoId)
285 {
286 // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it.
287 if (parent() && parent()->createsAnonymousWrapper())
288 return;
289 return children()->updateBeforeAfterContent(this, pseudoId);
290 }
291
continuationBefore(RenderObject * beforeChild)292 RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild)
293 {
294 if (beforeChild && beforeChild->parent() == this)
295 return this;
296
297 RenderBlock* curr = toRenderBlock(continuation());
298 RenderBlock* nextToLast = this;
299 RenderBlock* last = this;
300 while (curr) {
301 if (beforeChild && beforeChild->parent() == curr) {
302 if (curr->firstChild() == beforeChild)
303 return last;
304 return curr;
305 }
306
307 nextToLast = last;
308 last = curr;
309 curr = toRenderBlock(curr->continuation());
310 }
311
312 if (!beforeChild && !last->firstChild())
313 return nextToLast;
314 return last;
315 }
316
addChildToContinuation(RenderObject * newChild,RenderObject * beforeChild)317 void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
318 {
319 RenderBlock* flow = continuationBefore(beforeChild);
320 ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock());
321 RenderBoxModelObject* beforeChildParent = 0;
322 if (beforeChild)
323 beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
324 else {
325 RenderBoxModelObject* cont = flow->continuation();
326 if (cont)
327 beforeChildParent = cont;
328 else
329 beforeChildParent = flow;
330 }
331
332 if (newChild->isFloatingOrPositioned())
333 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
334
335 // A continuation always consists of two potential candidates: a block or an anonymous
336 // column span box holding column span children.
337 bool childIsNormal = newChild->isInline() || !newChild->style()->columnSpan();
338 bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style()->columnSpan();
339 bool flowIsNormal = flow->isInline() || !flow->style()->columnSpan();
340
341 if (flow == beforeChildParent)
342 return flow->addChildIgnoringContinuation(newChild, beforeChild);
343
344 // The goal here is to match up if we can, so that we can coalesce and create the
345 // minimal # of continuations needed for the inline.
346 if (childIsNormal == bcpIsNormal)
347 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
348 if (flowIsNormal == childIsNormal)
349 return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append.
350 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
351 }
352
353
addChildToAnonymousColumnBlocks(RenderObject * newChild,RenderObject * beforeChild)354 void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild)
355 {
356 ASSERT(!continuation()); // We don't yet support column spans that aren't immediate children of the multi-column block.
357
358 // The goal is to locate a suitable box in which to place our child.
359 RenderBlock* beforeChildParent = toRenderBlock(beforeChild && beforeChild->parent()->isRenderBlock() ? beforeChild->parent() : lastChild());
360
361 // If the new child is floating or positioned it can just go in that block.
362 if (newChild->isFloatingOrPositioned())
363 return beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
364
365 // See if the child can be placed in the box.
366 bool newChildHasColumnSpan = newChild->style()->columnSpan() && !newChild->isInline();
367 bool beforeChildParentHoldsColumnSpans = beforeChildParent->isAnonymousColumnSpanBlock();
368
369 if (newChildHasColumnSpan == beforeChildParentHoldsColumnSpans)
370 return beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
371
372 if (!beforeChild) {
373 // Create a new block of the correct type.
374 RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock();
375 children()->appendChildNode(this, newBox);
376 newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0);
377 return;
378 }
379
380 RenderObject* immediateChild = beforeChild;
381 bool isPreviousBlockViable = true;
382 while (immediateChild->parent() != this) {
383 if (isPreviousBlockViable)
384 isPreviousBlockViable = !immediateChild->previousSibling();
385 immediateChild = immediateChild->parent();
386 }
387 if (isPreviousBlockViable && immediateChild->previousSibling())
388 return toRenderBlock(immediateChild->previousSibling())->addChildIgnoringAnonymousColumnBlocks(newChild, 0); // Treat like an append.
389
390 // Split our anonymous blocks.
391 RenderObject* newBeforeChild = splitAnonymousBlocksAroundChild(beforeChild);
392
393 // Create a new anonymous box of the appropriate type.
394 RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock();
395 children()->insertChildNode(this, newBox, newBeforeChild);
396 newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0);
397 return;
398 }
399
containingColumnsBlock(bool allowAnonymousColumnBlock)400 RenderBlock* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBlock)
401 {
402 for (RenderObject* curr = this; curr; curr = curr->parent()) {
403 if (!curr->isRenderBlock() || curr->isFloatingOrPositioned() || curr->isTableCell() || curr->isRoot() || curr->isRenderView() || curr->hasOverflowClip()
404 || curr->isInlineBlockOrInlineTable())
405 return 0;
406
407 RenderBlock* currBlock = toRenderBlock(curr);
408 if (currBlock->style()->specifiesColumns() && (allowAnonymousColumnBlock || !currBlock->isAnonymousColumnsBlock()))
409 return currBlock;
410
411 if (currBlock->isAnonymousColumnSpanBlock())
412 return 0;
413 }
414 return 0;
415 }
416
clone() const417 RenderBlock* RenderBlock::clone() const
418 {
419 RenderBlock* cloneBlock;
420 if (isAnonymousBlock())
421 cloneBlock = createAnonymousBlock();
422 else {
423 cloneBlock = new (renderArena()) RenderBlock(node());
424 cloneBlock->setStyle(style());
425 }
426 cloneBlock->setChildrenInline(childrenInline());
427 return cloneBlock;
428 }
429
splitBlocks(RenderBlock * fromBlock,RenderBlock * toBlock,RenderBlock * middleBlock,RenderObject * beforeChild,RenderBoxModelObject * oldCont)430 void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock,
431 RenderBlock* middleBlock,
432 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
433 {
434 // Create a clone of this inline.
435 RenderBlock* cloneBlock = clone();
436 if (!isAnonymousBlock())
437 cloneBlock->setContinuation(oldCont);
438
439 // Now take all of the children from beforeChild to the end and remove
440 // them from |this| and place them in the clone.
441 if (!beforeChild && isAfterContent(lastChild()))
442 beforeChild = lastChild();
443 moveChildrenTo(cloneBlock, beforeChild, 0);
444
445 // Hook |clone| up as the continuation of the middle block.
446 if (!cloneBlock->isAnonymousBlock())
447 middleBlock->setContinuation(cloneBlock);
448
449 // We have been reparented and are now under the fromBlock. We need
450 // to walk up our block parent chain until we hit the containing anonymous columns block.
451 // Once we hit the anonymous columns block we're done.
452 RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
453 RenderBoxModelObject* currChild = this;
454
455 while (curr && curr != fromBlock) {
456 ASSERT(curr->isRenderBlock());
457
458 RenderBlock* blockCurr = toRenderBlock(curr);
459
460 // Create a new clone.
461 RenderBlock* cloneChild = cloneBlock;
462 cloneBlock = blockCurr->clone();
463
464 // Insert our child clone as the first child.
465 cloneBlock->children()->appendChildNode(cloneBlock, cloneChild);
466
467 // Hook the clone up as a continuation of |curr|. Note we do encounter
468 // anonymous blocks possibly as we walk up the block chain. When we split an
469 // anonymous block, there's no need to do any continuation hookup, since we haven't
470 // actually split a real element.
471 if (!blockCurr->isAnonymousBlock()) {
472 oldCont = blockCurr->continuation();
473 blockCurr->setContinuation(cloneBlock);
474 cloneBlock->setContinuation(oldCont);
475 }
476
477 // Someone may have indirectly caused a <q> to split. When this happens, the :after content
478 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after
479 // content gets properly destroyed.
480 if (document()->usesBeforeAfterRules())
481 blockCurr->children()->updateBeforeAfterContent(blockCurr, AFTER);
482
483 // Now we need to take all of the children starting from the first child
484 // *after* currChild and append them all to the clone.
485 RenderObject* afterContent = isAfterContent(cloneBlock->lastChild()) ? cloneBlock->lastChild() : 0;
486 blockCurr->moveChildrenTo(cloneBlock, currChild->nextSibling(), 0, afterContent);
487
488 // Keep walking up the chain.
489 currChild = curr;
490 curr = toRenderBoxModelObject(curr->parent());
491 }
492
493 // Now we are at the columns block level. We need to put the clone into the toBlock.
494 toBlock->children()->appendChildNode(toBlock, cloneBlock);
495
496 // Now take all the children after currChild and remove them from the fromBlock
497 // and put them in the toBlock.
498 fromBlock->moveChildrenTo(toBlock, currChild->nextSibling(), 0);
499 }
500
splitFlow(RenderObject * beforeChild,RenderBlock * newBlockBox,RenderObject * newChild,RenderBoxModelObject * oldCont)501 void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
502 RenderObject* newChild, RenderBoxModelObject* oldCont)
503 {
504 RenderBlock* pre = 0;
505 RenderBlock* block = containingColumnsBlock();
506
507 // Delete our line boxes before we do the inline split into continuations.
508 block->deleteLineBoxTree();
509
510 bool madeNewBeforeBlock = false;
511 if (block->isAnonymousColumnsBlock()) {
512 // We can reuse this block and make it the preBlock of the next continuation.
513 pre = block;
514 pre->removePositionedObjects(0);
515 block = toRenderBlock(block->parent());
516 } else {
517 // No anonymous block available for use. Make one.
518 pre = block->createAnonymousColumnsBlock();
519 pre->setChildrenInline(false);
520 madeNewBeforeBlock = true;
521 }
522
523 RenderBlock* post = block->createAnonymousColumnsBlock();
524 post->setChildrenInline(false);
525
526 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
527 if (madeNewBeforeBlock)
528 block->children()->insertChildNode(block, pre, boxFirst);
529 block->children()->insertChildNode(block, newBlockBox, boxFirst);
530 block->children()->insertChildNode(block, post, boxFirst);
531 block->setChildrenInline(false);
532
533 if (madeNewBeforeBlock)
534 block->moveChildrenTo(pre, boxFirst, 0);
535
536 splitBlocks(pre, post, newBlockBox, beforeChild, oldCont);
537
538 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
539 // time in makeChildrenNonInline by just setting this explicitly up front.
540 newBlockBox->setChildrenInline(false);
541
542 // We delayed adding the newChild until now so that the |newBlockBox| would be fully
543 // connected, thus allowing newChild access to a renderArena should it need
544 // to wrap itself in additional boxes (e.g., table construction).
545 newBlockBox->addChild(newChild);
546
547 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
548 // get deleted properly. Because objects moves from the pre block into the post block, we want to
549 // make new line boxes instead of leaving the old line boxes around.
550 pre->setNeedsLayoutAndPrefWidthsRecalc();
551 block->setNeedsLayoutAndPrefWidthsRecalc();
552 post->setNeedsLayoutAndPrefWidthsRecalc();
553 }
554
splitAnonymousBlocksAroundChild(RenderObject * beforeChild)555 RenderObject* RenderBlock::splitAnonymousBlocksAroundChild(RenderObject* beforeChild)
556 {
557 while (beforeChild->parent() != this) {
558 RenderBlock* blockToSplit = toRenderBlock(beforeChild->parent());
559 if (blockToSplit->firstChild() != beforeChild) {
560 // We have to split the parentBlock into two blocks.
561 RenderBlock* post = createAnonymousBlockWithSameTypeAs(blockToSplit);
562 post->setChildrenInline(blockToSplit->childrenInline());
563 RenderBlock* parentBlock = toRenderBlock(blockToSplit->parent());
564 parentBlock->children()->insertChildNode(parentBlock, post, blockToSplit->nextSibling());
565 blockToSplit->moveChildrenTo(post, beforeChild, 0, blockToSplit->hasLayer());
566 post->setNeedsLayoutAndPrefWidthsRecalc();
567 blockToSplit->setNeedsLayoutAndPrefWidthsRecalc();
568 beforeChild = post;
569 } else
570 beforeChild = blockToSplit;
571 }
572 return beforeChild;
573 }
574
makeChildrenAnonymousColumnBlocks(RenderObject * beforeChild,RenderBlock * newBlockBox,RenderObject * newChild)575 void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlock* newBlockBox, RenderObject* newChild)
576 {
577 RenderBlock* pre = 0;
578 RenderBlock* post = 0;
579 RenderBlock* block = this; // Eventually block will not just be |this|, but will also be a block nested inside |this|. Assign to a variable
580 // so that we don't have to patch all of the rest of the code later on.
581
582 // Delete the block's line boxes before we do the split.
583 block->deleteLineBoxTree();
584
585 if (beforeChild && beforeChild->parent() != this)
586 beforeChild = splitAnonymousBlocksAroundChild(beforeChild);
587
588 if (beforeChild != firstChild()) {
589 pre = block->createAnonymousColumnsBlock();
590 pre->setChildrenInline(block->childrenInline());
591 }
592
593 if (beforeChild) {
594 post = block->createAnonymousColumnsBlock();
595 post->setChildrenInline(block->childrenInline());
596 }
597
598 RenderObject* boxFirst = block->firstChild();
599 if (pre)
600 block->children()->insertChildNode(block, pre, boxFirst);
601 block->children()->insertChildNode(block, newBlockBox, boxFirst);
602 if (post)
603 block->children()->insertChildNode(block, post, boxFirst);
604 block->setChildrenInline(false);
605
606 // The pre/post blocks always have layers, so we know to always do a full insert/remove (so we pass true as the last argument).
607 block->moveChildrenTo(pre, boxFirst, beforeChild, true);
608 block->moveChildrenTo(post, beforeChild, 0, true);
609
610 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
611 // time in makeChildrenNonInline by just setting this explicitly up front.
612 newBlockBox->setChildrenInline(false);
613
614 // We delayed adding the newChild until now so that the |newBlockBox| would be fully
615 // connected, thus allowing newChild access to a renderArena should it need
616 // to wrap itself in additional boxes (e.g., table construction).
617 newBlockBox->addChild(newChild);
618
619 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
620 // get deleted properly. Because objects moved from the pre block into the post block, we want to
621 // make new line boxes instead of leaving the old line boxes around.
622 if (pre)
623 pre->setNeedsLayoutAndPrefWidthsRecalc();
624 block->setNeedsLayoutAndPrefWidthsRecalc();
625 if (post)
626 post->setNeedsLayoutAndPrefWidthsRecalc();
627 }
628
columnsBlockForSpanningElement(RenderObject * newChild)629 RenderBlock* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild)
630 {
631 // FIXME: This function is the gateway for the addition of column-span support. It will
632 // be added to in three stages:
633 // (1) Immediate children of a multi-column block can span.
634 // (2) Nested block-level children with only block-level ancestors between them and the multi-column block can span.
635 // (3) Nested children with block or inline ancestors between them and the multi-column block can span (this is when we
636 // cross the streams and have to cope with both types of continuations mixed together).
637 // This function currently supports (1) and (2).
638 RenderBlock* columnsBlockAncestor = 0;
639 if (!newChild->isText() && newChild->style()->columnSpan() && !newChild->isFloatingOrPositioned()
640 && !newChild->isInline() && !isAnonymousColumnSpanBlock()) {
641 if (style()->specifiesColumns())
642 columnsBlockAncestor = this;
643 else if (!isInline() && parent() && parent()->isRenderBlock()) {
644 columnsBlockAncestor = toRenderBlock(parent())->containingColumnsBlock(false);
645
646 if (columnsBlockAncestor) {
647 // Make sure that none of the parent ancestors have a continuation.
648 // If yes, we do not want split the block into continuations.
649 RenderObject* curr = this;
650 while (curr && curr != columnsBlockAncestor) {
651 if (curr->isRenderBlock() && toRenderBlock(curr)->continuation()) {
652 columnsBlockAncestor = 0;
653 break;
654 }
655 curr = curr->parent();
656 }
657 }
658 }
659 }
660 return columnsBlockAncestor;
661 }
662
addChildIgnoringAnonymousColumnBlocks(RenderObject * newChild,RenderObject * beforeChild)663 void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild)
664 {
665 // Make sure we don't append things after :after-generated content if we have it.
666 if (!beforeChild) {
667 RenderObject* lastRenderer = lastChild();
668 while (lastRenderer && lastRenderer->isAnonymous() && !isAfterContent(lastRenderer))
669 lastRenderer = lastRenderer->lastChild();
670 if (lastRenderer && isAfterContent(lastRenderer))
671 beforeChild = lastRenderer;
672 }
673
674 // If the requested beforeChild is not one of our children, then this is because
675 // there is an anonymous container within this object that contains the beforeChild.
676 if (beforeChild && beforeChild->parent() != this) {
677 RenderObject* anonymousChild = beforeChild->parent();
678 ASSERT(anonymousChild);
679
680 while (anonymousChild->parent() != this)
681 anonymousChild = anonymousChild->parent();
682
683 ASSERT(anonymousChild->isAnonymous());
684
685 if (anonymousChild->isAnonymousBlock()) {
686 // Insert the child into the anonymous block box instead of here.
687 if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild)
688 beforeChild->parent()->addChild(newChild, beforeChild);
689 else
690 addChild(newChild, beforeChild->parent());
691 return;
692 }
693
694 ASSERT(anonymousChild->isTable());
695 if ((newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP)
696 || (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION)
697 || newChild->isTableSection()
698 || newChild->isTableRow()
699 || newChild->isTableCell()) {
700 // Insert into the anonymous table.
701 anonymousChild->addChild(newChild, beforeChild);
702 return;
703 }
704
705 // Go on to insert before the anonymous table.
706 beforeChild = anonymousChild;
707 }
708
709 // Check for a spanning element in columns.
710 RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild);
711 if (columnsBlockAncestor) {
712 // We are placing a column-span element inside a block.
713 RenderBlock* newBox = createAnonymousColumnSpanBlock();
714
715 if (columnsBlockAncestor != this) {
716 // We are nested inside a multi-column element and are being split by the span. We have to break up
717 // our block into continuations.
718 RenderBoxModelObject* oldContinuation = continuation();
719 setContinuation(newBox);
720
721 // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content
722 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after
723 // content gets properly destroyed.
724 bool isLastChild = (beforeChild == lastChild());
725 if (document()->usesBeforeAfterRules())
726 children()->updateBeforeAfterContent(this, AFTER);
727 if (isLastChild && beforeChild != lastChild())
728 beforeChild = 0; // We destroyed the last child, so now we need to update our insertion
729 // point to be 0. It's just a straight append now.
730
731 splitFlow(beforeChild, newBox, newChild, oldContinuation);
732 return;
733 }
734
735 // We have to perform a split of this block's children. This involves creating an anonymous block box to hold
736 // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into
737 // one anonymous columns block, and all of the children after |newChild| go into another anonymous block.
738 makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild);
739 return;
740 }
741
742 bool madeBoxesNonInline = false;
743
744 // A block has to either have all of its children inline, or all of its children as blocks.
745 // So, if our children are currently inline and a block child has to be inserted, we move all our
746 // inline children into anonymous block boxes.
747 if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrPositioned()) {
748 // This is a block with inline content. Wrap the inline content in anonymous blocks.
749 makeChildrenNonInline(beforeChild);
750 madeBoxesNonInline = true;
751
752 if (beforeChild && beforeChild->parent() != this) {
753 beforeChild = beforeChild->parent();
754 ASSERT(beforeChild->isAnonymousBlock());
755 ASSERT(beforeChild->parent() == this);
756 }
757 } else if (!childrenInline() && (newChild->isFloatingOrPositioned() || newChild->isInline())) {
758 // If we're inserting an inline child but all of our children are blocks, then we have to make sure
759 // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise
760 // a new one is created and inserted into our list of children in the appropriate position.
761 RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild();
762
763 if (afterChild && afterChild->isAnonymousBlock()) {
764 afterChild->addChild(newChild);
765 return;
766 }
767
768 if (newChild->isInline()) {
769 // No suitable existing anonymous box - create a new one.
770 RenderBlock* newBox = createAnonymousBlock();
771 RenderBox::addChild(newBox, beforeChild);
772 newBox->addChild(newChild);
773 return;
774 }
775 }
776
777 RenderBox::addChild(newChild, beforeChild);
778
779 if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock())
780 toRenderBlock(parent())->removeLeftoverAnonymousBlock(this);
781 // this object may be dead here
782 }
783
addChild(RenderObject * newChild,RenderObject * beforeChild)784 void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild)
785 {
786 if (continuation() && !isAnonymousBlock())
787 return addChildToContinuation(newChild, beforeChild);
788 return addChildIgnoringContinuation(newChild, beforeChild);
789 }
790
addChildIgnoringContinuation(RenderObject * newChild,RenderObject * beforeChild)791 void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
792 {
793 if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock()))
794 return addChildToAnonymousColumnBlocks(newChild, beforeChild);
795 return addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
796 }
797
getInlineRun(RenderObject * start,RenderObject * boundary,RenderObject * & inlineRunStart,RenderObject * & inlineRunEnd)798 static void getInlineRun(RenderObject* start, RenderObject* boundary,
799 RenderObject*& inlineRunStart,
800 RenderObject*& inlineRunEnd)
801 {
802 // Beginning at |start| we find the largest contiguous run of inlines that
803 // we can. We denote the run with start and end points, |inlineRunStart|
804 // and |inlineRunEnd|. Note that these two values may be the same if
805 // we encounter only one inline.
806 //
807 // We skip any non-inlines we encounter as long as we haven't found any
808 // inlines yet.
809 //
810 // |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary|
811 // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered
812 // a non-inline.
813
814 // Start by skipping as many non-inlines as we can.
815 RenderObject * curr = start;
816 bool sawInline;
817 do {
818 while (curr && !(curr->isInline() || curr->isFloatingOrPositioned()))
819 curr = curr->nextSibling();
820
821 inlineRunStart = inlineRunEnd = curr;
822
823 if (!curr)
824 return; // No more inline children to be found.
825
826 sawInline = curr->isInline();
827
828 curr = curr->nextSibling();
829 while (curr && (curr->isInline() || curr->isFloatingOrPositioned()) && (curr != boundary)) {
830 inlineRunEnd = curr;
831 if (curr->isInline())
832 sawInline = true;
833 curr = curr->nextSibling();
834 }
835 } while (!sawInline);
836 }
837
deleteLineBoxTree()838 void RenderBlock::deleteLineBoxTree()
839 {
840 m_lineBoxes.deleteLineBoxTree(renderArena());
841 }
842
createRootInlineBox()843 RootInlineBox* RenderBlock::createRootInlineBox()
844 {
845 return new (renderArena()) RootInlineBox(this);
846 }
847
createAndAppendRootInlineBox()848 RootInlineBox* RenderBlock::createAndAppendRootInlineBox()
849 {
850 RootInlineBox* rootBox = createRootInlineBox();
851 m_lineBoxes.appendLineBox(rootBox);
852 return rootBox;
853 }
854
moveChildTo(RenderBlock * to,RenderObject * child,RenderObject * beforeChild,bool fullRemoveInsert)855 void RenderBlock::moveChildTo(RenderBlock* to, RenderObject* child, RenderObject* beforeChild, bool fullRemoveInsert)
856 {
857 ASSERT(this == child->parent());
858 ASSERT(!beforeChild || to == beforeChild->parent());
859 to->children()->insertChildNode(to, children()->removeChildNode(this, child, fullRemoveInsert), beforeChild, fullRemoveInsert);
860 }
861
moveChildrenTo(RenderBlock * to,RenderObject * startChild,RenderObject * endChild,RenderObject * beforeChild,bool fullRemoveInsert)862 void RenderBlock::moveChildrenTo(RenderBlock* to, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, bool fullRemoveInsert)
863 {
864 ASSERT(!beforeChild || to == beforeChild->parent());
865 RenderObject* nextChild = startChild;
866 while (nextChild && nextChild != endChild) {
867 RenderObject* child = nextChild;
868 nextChild = child->nextSibling();
869 to->children()->insertChildNode(to, children()->removeChildNode(this, child, fullRemoveInsert), beforeChild, fullRemoveInsert);
870 if (child == endChild)
871 return;
872 }
873 }
874
makeChildrenNonInline(RenderObject * insertionPoint)875 void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint)
876 {
877 // makeChildrenNonInline takes a block whose children are *all* inline and it
878 // makes sure that inline children are coalesced under anonymous
879 // blocks. If |insertionPoint| is defined, then it represents the insertion point for
880 // the new block child that is causing us to have to wrap all the inlines. This
881 // means that we cannot coalesce inlines before |insertionPoint| with inlines following
882 // |insertionPoint|, because the new child is going to be inserted in between the inlines,
883 // splitting them.
884 ASSERT(isInlineBlockOrInlineTable() || !isInline());
885 ASSERT(!insertionPoint || insertionPoint->parent() == this);
886
887 setChildrenInline(false);
888
889 RenderObject *child = firstChild();
890 if (!child)
891 return;
892
893 deleteLineBoxTree();
894
895 while (child) {
896 RenderObject *inlineRunStart, *inlineRunEnd;
897 getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd);
898
899 if (!inlineRunStart)
900 break;
901
902 child = inlineRunEnd->nextSibling();
903
904 RenderBlock* block = createAnonymousBlock();
905 children()->insertChildNode(this, block, inlineRunStart);
906 moveChildrenTo(block, inlineRunStart, child);
907 }
908
909 #ifndef NDEBUG
910 for (RenderObject *c = firstChild(); c; c = c->nextSibling())
911 ASSERT(!c->isInline());
912 #endif
913
914 repaint();
915 }
916
removeLeftoverAnonymousBlock(RenderBlock * child)917 void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child)
918 {
919 ASSERT(child->isAnonymousBlock());
920 ASSERT(!child->childrenInline());
921
922 if (child->continuation() || (child->firstChild() && (child->isAnonymousColumnSpanBlock() || child->isAnonymousColumnsBlock())))
923 return;
924
925 RenderObject* firstAnChild = child->m_children.firstChild();
926 RenderObject* lastAnChild = child->m_children.lastChild();
927 if (firstAnChild) {
928 RenderObject* o = firstAnChild;
929 while (o) {
930 o->setParent(this);
931 o = o->nextSibling();
932 }
933 firstAnChild->setPreviousSibling(child->previousSibling());
934 lastAnChild->setNextSibling(child->nextSibling());
935 if (child->previousSibling())
936 child->previousSibling()->setNextSibling(firstAnChild);
937 if (child->nextSibling())
938 child->nextSibling()->setPreviousSibling(lastAnChild);
939
940 if (child == m_children.firstChild())
941 m_children.setFirstChild(firstAnChild);
942 if (child == m_children.lastChild())
943 m_children.setLastChild(lastAnChild);
944 } else {
945 if (child == m_children.firstChild())
946 m_children.setFirstChild(child->nextSibling());
947 if (child == m_children.lastChild())
948 m_children.setLastChild(child->previousSibling());
949
950 if (child->previousSibling())
951 child->previousSibling()->setNextSibling(child->nextSibling());
952 if (child->nextSibling())
953 child->nextSibling()->setPreviousSibling(child->previousSibling());
954 }
955 child->setParent(0);
956 child->setPreviousSibling(0);
957 child->setNextSibling(0);
958
959 child->children()->setFirstChild(0);
960 child->m_next = 0;
961
962 child->destroy();
963 }
964
canMergeContiguousAnonymousBlocks(RenderObject * oldChild,RenderObject * prev,RenderObject * next)965 static bool canMergeContiguousAnonymousBlocks(RenderObject* oldChild, RenderObject* prev, RenderObject* next)
966 {
967 if (oldChild->documentBeingDestroyed() || oldChild->isInline() || oldChild->virtualContinuation())
968 return false;
969
970 #if ENABLE(DETAILS)
971 if (oldChild->parent() && oldChild->parent()->isDetails())
972 return false;
973 #endif
974
975 if ((prev && (!prev->isAnonymousBlock() || toRenderBlock(prev)->continuation() || toRenderBlock(prev)->beingDestroyed()))
976 || (next && (!next->isAnonymousBlock() || toRenderBlock(next)->continuation() || toRenderBlock(next)->beingDestroyed())))
977 return false;
978
979 // FIXME: This check isn't required when inline run-ins can't be split into continuations.
980 if (prev && prev->firstChild() && prev->firstChild()->isInline() && prev->firstChild()->isRunIn())
981 return false;
982
983 if ((prev && (prev->isRubyRun() || prev->isRubyBase()))
984 || (next && (next->isRubyRun() || next->isRubyBase())))
985 return false;
986
987 if (!prev || !next)
988 return true;
989
990 // Make sure the types of the anonymous blocks match up.
991 return prev->isAnonymousColumnsBlock() == next->isAnonymousColumnsBlock()
992 && prev->isAnonymousColumnSpanBlock() == next->isAnonymousColumnSpanBlock();
993 }
994
removeChild(RenderObject * oldChild)995 void RenderBlock::removeChild(RenderObject* oldChild)
996 {
997 // If this child is a block, and if our previous and next siblings are
998 // both anonymous blocks with inline content, then we can go ahead and
999 // fold the inline content back together.
1000 RenderObject* prev = oldChild->previousSibling();
1001 RenderObject* next = oldChild->nextSibling();
1002 bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next);
1003 if (canMergeAnonymousBlocks && prev && next) {
1004 prev->setNeedsLayoutAndPrefWidthsRecalc();
1005 RenderBlock* nextBlock = toRenderBlock(next);
1006 RenderBlock* prevBlock = toRenderBlock(prev);
1007
1008 if (prev->childrenInline() != next->childrenInline()) {
1009 RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock;
1010 RenderBlock* blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock;
1011
1012 // Place the inline children block inside of the block children block instead of deleting it.
1013 // In order to reuse it, we have to reset it to just be a generic anonymous block. Make sure
1014 // to clear out inherited column properties by just making a new style, and to also clear the
1015 // column span flag if it is set.
1016 ASSERT(!inlineChildrenBlock->continuation());
1017 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style());
1018 children()->removeChildNode(this, inlineChildrenBlock, inlineChildrenBlock->hasLayer());
1019 inlineChildrenBlock->setStyle(newStyle);
1020
1021 // Now just put the inlineChildrenBlock inside the blockChildrenBlock.
1022 blockChildrenBlock->children()->insertChildNode(blockChildrenBlock, inlineChildrenBlock, prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : 0,
1023 inlineChildrenBlock->hasLayer() || blockChildrenBlock->hasLayer());
1024 next->setNeedsLayoutAndPrefWidthsRecalc();
1025
1026 // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child
1027 // of "this". we null out prev or next so that is not used later in the function.
1028 if (inlineChildrenBlock == prevBlock)
1029 prev = 0;
1030 else
1031 next = 0;
1032 } else {
1033 // Take all the children out of the |next| block and put them in
1034 // the |prev| block.
1035 nextBlock->moveAllChildrenTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer());
1036
1037 // Delete the now-empty block's lines and nuke it.
1038 nextBlock->deleteLineBoxTree();
1039 nextBlock->destroy();
1040 next = 0;
1041 }
1042 }
1043
1044 RenderBox::removeChild(oldChild);
1045
1046 RenderObject* child = prev ? prev : next;
1047 if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && !isFlexibleBox()) {
1048 // The removal has knocked us down to containing only a single anonymous
1049 // box. We can go ahead and pull the content right back up into our
1050 // box.
1051 setNeedsLayoutAndPrefWidthsRecalc();
1052 setChildrenInline(child->childrenInline());
1053 RenderBlock* anonBlock = toRenderBlock(children()->removeChildNode(this, child, child->hasLayer()));
1054 anonBlock->moveAllChildrenTo(this, child->hasLayer());
1055 // Delete the now-empty block's lines and nuke it.
1056 anonBlock->deleteLineBoxTree();
1057 anonBlock->destroy();
1058 }
1059
1060 if (!firstChild() && !documentBeingDestroyed()) {
1061 // If this was our last child be sure to clear out our line boxes.
1062 if (childrenInline())
1063 lineBoxes()->deleteLineBoxes(renderArena());
1064 }
1065 }
1066
isSelfCollapsingBlock() const1067 bool RenderBlock::isSelfCollapsingBlock() const
1068 {
1069 // We are not self-collapsing if we
1070 // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
1071 // (b) are a table,
1072 // (c) have border/padding,
1073 // (d) have a min-height
1074 // (e) have specified that one of our margins can't collapse using a CSS extension
1075 if (logicalHeight() > 0
1076 || isTable() || borderAndPaddingLogicalHeight()
1077 || style()->logicalMinHeight().isPositive()
1078 || style()->marginBeforeCollapse() == MSEPARATE || style()->marginAfterCollapse() == MSEPARATE)
1079 return false;
1080
1081 Length logicalHeightLength = style()->logicalHeight();
1082 bool hasAutoHeight = logicalHeightLength.isAuto();
1083 if (logicalHeightLength.isPercent() && !document()->inQuirksMode()) {
1084 hasAutoHeight = true;
1085 for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) {
1086 if (cb->style()->logicalHeight().isFixed() || cb->isTableCell())
1087 hasAutoHeight = false;
1088 }
1089 }
1090
1091 // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
1092 // on whether we have content that is all self-collapsing or not.
1093 if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) {
1094 // If the block has inline children, see if we generated any line boxes. If we have any
1095 // line boxes, then we can't be self-collapsing, since we have content.
1096 if (childrenInline())
1097 return !firstLineBox();
1098
1099 // Whether or not we collapse is dependent on whether all our normal flow children
1100 // are also self-collapsing.
1101 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
1102 if (child->isFloatingOrPositioned())
1103 continue;
1104 if (!child->isSelfCollapsingBlock())
1105 return false;
1106 }
1107 return true;
1108 }
1109 return false;
1110 }
1111
startDelayUpdateScrollInfo()1112 void RenderBlock::startDelayUpdateScrollInfo()
1113 {
1114 if (gDelayUpdateScrollInfo == 0) {
1115 ASSERT(!gDelayedUpdateScrollInfoSet);
1116 gDelayedUpdateScrollInfoSet = new DelayedUpdateScrollInfoSet;
1117 }
1118 ASSERT(gDelayedUpdateScrollInfoSet);
1119 ++gDelayUpdateScrollInfo;
1120 }
1121
finishDelayUpdateScrollInfo()1122 void RenderBlock::finishDelayUpdateScrollInfo()
1123 {
1124 --gDelayUpdateScrollInfo;
1125 ASSERT(gDelayUpdateScrollInfo >= 0);
1126 if (gDelayUpdateScrollInfo == 0) {
1127 ASSERT(gDelayedUpdateScrollInfoSet);
1128
1129 OwnPtr<DelayedUpdateScrollInfoSet> infoSet(adoptPtr(gDelayedUpdateScrollInfoSet));
1130 gDelayedUpdateScrollInfoSet = 0;
1131
1132 for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) {
1133 RenderBlock* block = *it;
1134 if (block->hasOverflowClip()) {
1135 block->layer()->updateScrollInfoAfterLayout();
1136 }
1137 }
1138 }
1139 }
1140
updateScrollInfoAfterLayout()1141 void RenderBlock::updateScrollInfoAfterLayout()
1142 {
1143 if (hasOverflowClip()) {
1144 if (gDelayUpdateScrollInfo)
1145 gDelayedUpdateScrollInfoSet->add(this);
1146 else
1147 layer()->updateScrollInfoAfterLayout();
1148 }
1149 }
1150
layout()1151 void RenderBlock::layout()
1152 {
1153 // Update our first letter info now.
1154 updateFirstLetter();
1155
1156 // Table cells call layoutBlock directly, so don't add any logic here. Put code into
1157 // layoutBlock().
1158 layoutBlock(false);
1159
1160 // It's safe to check for control clip here, since controls can never be table cells.
1161 // If we have a lightweight clip, there can never be any overflow from children.
1162 if (hasControlClip() && m_overflow)
1163 clearLayoutOverflow();
1164 }
1165
layoutBlock(bool relayoutChildren,int pageLogicalHeight)1166 void RenderBlock::layoutBlock(bool relayoutChildren, int pageLogicalHeight)
1167 {
1168 ASSERT(needsLayout());
1169
1170 if (isInline() && !isInlineBlockOrInlineTable()) // Inline <form>s inside various table elements can
1171 return; // cause us to come in here. Just bail.
1172
1173 if (!relayoutChildren && simplifiedLayout())
1174 return;
1175
1176 LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
1177
1178 int oldWidth = logicalWidth();
1179 int oldColumnWidth = desiredColumnWidth();
1180
1181 computeLogicalWidth();
1182 calcColumnWidth();
1183
1184 m_overflow.clear();
1185
1186 if (oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth())
1187 relayoutChildren = true;
1188
1189 clearFloats();
1190
1191 int previousHeight = logicalHeight();
1192 setLogicalHeight(0);
1193 bool hasSpecifiedPageLogicalHeight = false;
1194 bool pageLogicalHeightChanged = false;
1195 ColumnInfo* colInfo = columnInfo();
1196 if (hasColumns()) {
1197 if (!pageLogicalHeight) {
1198 // We need to go ahead and set our explicit page height if one exists, so that we can
1199 // avoid doing two layout passes.
1200 computeLogicalHeight();
1201 int columnHeight = contentLogicalHeight();
1202 if (columnHeight > 0) {
1203 pageLogicalHeight = columnHeight;
1204 hasSpecifiedPageLogicalHeight = true;
1205 }
1206 setLogicalHeight(0);
1207 }
1208 if (colInfo->columnHeight() != pageLogicalHeight && m_everHadLayout) {
1209 colInfo->setColumnHeight(pageLogicalHeight);
1210 pageLogicalHeightChanged = true;
1211 }
1212
1213 if (!hasSpecifiedPageLogicalHeight && !pageLogicalHeight)
1214 colInfo->clearForcedBreaks();
1215 }
1216
1217 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode(), pageLogicalHeight, pageLogicalHeightChanged, colInfo);
1218
1219 // We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track
1220 // our current maximal positive and negative margins. These values are used when we
1221 // are collapsed with adjacent blocks, so for example, if you have block A and B
1222 // collapsing together, then you'd take the maximal positive margin from both A and B
1223 // and subtract it from the maximal negative margin from both A and B to get the
1224 // true collapsed margin. This algorithm is recursive, so when we finish layout()
1225 // our block knows its current maximal positive/negative values.
1226 //
1227 // Start out by setting our margin values to our current margins. Table cells have
1228 // no margins, so we don't fill in the values for table cells.
1229 bool isCell = isTableCell();
1230 if (!isCell) {
1231 initMaxMarginValues();
1232
1233 setMarginBeforeQuirk(style()->marginBefore().quirk());
1234 setMarginAfterQuirk(style()->marginAfter().quirk());
1235
1236 Node* n = node();
1237 if (n && n->hasTagName(formTag) && static_cast<HTMLFormElement*>(n)->isMalformed()) {
1238 // See if this form is malformed (i.e., unclosed). If so, don't give the form
1239 // a bottom margin.
1240 setMaxMarginAfterValues(0, 0);
1241 }
1242
1243 setPaginationStrut(0);
1244 }
1245
1246 // For overflow:scroll blocks, ensure we have both scrollbars in place always.
1247 if (scrollsOverflow()) {
1248 if (style()->overflowX() == OSCROLL)
1249 layer()->setHasHorizontalScrollbar(true);
1250 if (style()->overflowY() == OSCROLL)
1251 layer()->setHasVerticalScrollbar(true);
1252 }
1253
1254 int repaintLogicalTop = 0;
1255 int repaintLogicalBottom = 0;
1256 int maxFloatLogicalBottom = 0;
1257 if (!firstChild() && !isAnonymousBlock())
1258 setChildrenInline(true);
1259 if (childrenInline())
1260 layoutInlineChildren(relayoutChildren, repaintLogicalTop, repaintLogicalBottom);
1261 else
1262 layoutBlockChildren(relayoutChildren, maxFloatLogicalBottom);
1263
1264 // Expand our intrinsic height to encompass floats.
1265 int toAdd = borderAfter() + paddingAfter() + scrollbarLogicalHeight();
1266 if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && expandsToEncloseOverhangingFloats())
1267 setLogicalHeight(lowestFloatLogicalBottom() + toAdd);
1268
1269 if (layoutColumns(hasSpecifiedPageLogicalHeight, pageLogicalHeight, statePusher))
1270 return;
1271
1272 // Calculate our new height.
1273 int oldHeight = logicalHeight();
1274 int oldClientAfterEdge = clientLogicalBottom();
1275 computeLogicalHeight();
1276 int newHeight = logicalHeight();
1277 if (oldHeight != newHeight) {
1278 if (oldHeight > newHeight && maxFloatLogicalBottom > newHeight && !childrenInline()) {
1279 // One of our children's floats may have become an overhanging float for us. We need to look for it.
1280 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
1281 if (child->isBlockFlow() && !child->isFloatingOrPositioned()) {
1282 RenderBlock* block = toRenderBlock(child);
1283 if (block->lowestFloatLogicalBottom() + block->logicalTop() > newHeight)
1284 addOverhangingFloats(block, -block->logicalLeft(), -block->logicalTop(), false);
1285 }
1286 }
1287 }
1288 }
1289
1290 if (previousHeight != newHeight)
1291 relayoutChildren = true;
1292
1293 layoutPositionedObjects(relayoutChildren || isRoot());
1294
1295 // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway).
1296 computeOverflow(oldClientAfterEdge);
1297
1298 statePusher.pop();
1299
1300 if (view()->layoutState()->m_pageLogicalHeight)
1301 setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(logicalTop()));
1302
1303 updateLayerTransform();
1304
1305 // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
1306 // we overflow or not.
1307 updateScrollInfoAfterLayout();
1308
1309 // Repaint with our new bounds if they are different from our old bounds.
1310 bool didFullRepaint = repainter.repaintAfterLayout();
1311 if (!didFullRepaint && repaintLogicalTop != repaintLogicalBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) {
1312 // FIXME: We could tighten up the left and right invalidation points if we let layoutInlineChildren fill them in based off the particular lines
1313 // it had to lay out. We wouldn't need the hasOverflowClip() hack in that case either.
1314 int repaintLogicalLeft = logicalLeftVisualOverflow();
1315 int repaintLogicalRight = logicalRightVisualOverflow();
1316 if (hasOverflowClip()) {
1317 // 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.
1318 // 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.
1319 // layoutInlineChildren should be patched to compute the entire repaint rect.
1320 repaintLogicalLeft = min(repaintLogicalLeft, logicalLeftLayoutOverflow());
1321 repaintLogicalRight = max(repaintLogicalRight, logicalRightLayoutOverflow());
1322 }
1323
1324 IntRect repaintRect;
1325 if (isHorizontalWritingMode())
1326 repaintRect = IntRect(repaintLogicalLeft, repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop);
1327 else
1328 repaintRect = IntRect(repaintLogicalTop, repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft);
1329
1330 // The repaint rect may be split across columns, in which case adjustRectForColumns() will return the union.
1331 adjustRectForColumns(repaintRect);
1332
1333 repaintRect.inflate(maximalOutlineSize(PaintPhaseOutline));
1334
1335 if (hasOverflowClip()) {
1336 // Adjust repaint rect for scroll offset
1337 repaintRect.move(-layer()->scrolledContentOffset());
1338
1339 // Don't allow this rect to spill out of our overflow box.
1340 repaintRect.intersect(IntRect(0, 0, width(), height()));
1341 }
1342
1343 // Make sure the rect is still non-empty after intersecting for overflow above
1344 if (!repaintRect.isEmpty()) {
1345 repaintRectangle(repaintRect); // We need to do a partial repaint of our content.
1346 if (hasReflection())
1347 repaintRectangle(reflectedRect(repaintRect));
1348 }
1349 }
1350 setNeedsLayout(false);
1351 }
1352
addOverflowFromChildren()1353 void RenderBlock::addOverflowFromChildren()
1354 {
1355 if (!hasColumns()) {
1356 if (childrenInline())
1357 addOverflowFromInlineChildren();
1358 else
1359 addOverflowFromBlockChildren();
1360 } else {
1361 ColumnInfo* colInfo = columnInfo();
1362 if (columnCount(colInfo)) {
1363 IntRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1);
1364 if (isHorizontalWritingMode()) {
1365 int overflowLeft = !style()->isLeftToRightDirection() ? min(0, lastRect.x()) : 0;
1366 int overflowRight = style()->isLeftToRightDirection() ? max(width(), lastRect.maxX()) : 0;
1367 int overflowHeight = borderBefore() + paddingBefore() + colInfo->columnHeight();
1368 addLayoutOverflow(IntRect(overflowLeft, 0, overflowRight - overflowLeft, overflowHeight));
1369 if (!hasOverflowClip())
1370 addVisualOverflow(IntRect(overflowLeft, 0, overflowRight - overflowLeft, overflowHeight));
1371 } else {
1372 IntRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1);
1373 int overflowTop = !style()->isLeftToRightDirection() ? min(0, lastRect.y()) : 0;
1374 int overflowBottom = style()->isLeftToRightDirection() ? max(height(), lastRect.maxY()) : 0;
1375 int overflowWidth = borderBefore() + paddingBefore() + colInfo->columnHeight();
1376 addLayoutOverflow(IntRect(0, overflowTop, overflowWidth, overflowBottom - overflowTop));
1377 if (!hasOverflowClip())
1378 addVisualOverflow(IntRect(0, overflowTop, overflowWidth, overflowBottom - overflowTop));
1379 }
1380 }
1381 }
1382 }
1383
computeOverflow(int oldClientAfterEdge,bool recomputeFloats)1384 void RenderBlock::computeOverflow(int oldClientAfterEdge, bool recomputeFloats)
1385 {
1386 // Add overflow from children.
1387 addOverflowFromChildren();
1388
1389 if (!hasColumns() && (recomputeFloats || isRoot() || expandsToEncloseOverhangingFloats() || hasSelfPaintingLayer()))
1390 addOverflowFromFloats();
1391
1392 // Add in the overflow from positioned objects.
1393 addOverflowFromPositionedObjects();
1394
1395 if (hasOverflowClip()) {
1396 // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins
1397 // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always
1398 // be considered reachable.
1399 IntRect clientRect(clientBoxRect());
1400 IntRect rectToApply;
1401 if (isHorizontalWritingMode())
1402 rectToApply = IntRect(clientRect.x(), clientRect.y(), 1, max(0, oldClientAfterEdge - clientRect.y()));
1403 else
1404 rectToApply = IntRect(clientRect.x(), clientRect.y(), max(0, oldClientAfterEdge - clientRect.x()), 1);
1405 addLayoutOverflow(rectToApply);
1406 }
1407
1408 // Add visual overflow from box-shadow and reflections.
1409 addShadowOverflow();
1410 }
1411
addOverflowFromBlockChildren()1412 void RenderBlock::addOverflowFromBlockChildren()
1413 {
1414 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
1415 if (!child->isFloatingOrPositioned())
1416 addOverflowFromChild(child);
1417 }
1418 }
1419
addOverflowFromFloats()1420 void RenderBlock::addOverflowFromFloats()
1421 {
1422 if (!m_floatingObjects)
1423 return;
1424
1425 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1426 FloatingObjectSetIterator end = floatingObjectSet.end();
1427 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
1428 FloatingObject* r = *it;
1429 if (r->m_isDescendant)
1430 addOverflowFromChild(r->m_renderer, IntSize(xPositionForFloatIncludingMargin(r), yPositionForFloatIncludingMargin(r)));
1431 }
1432 return;
1433 }
1434
addOverflowFromPositionedObjects()1435 void RenderBlock::addOverflowFromPositionedObjects()
1436 {
1437 if (!m_positionedObjects)
1438 return;
1439
1440 RenderBox* positionedObject;
1441 Iterator end = m_positionedObjects->end();
1442 for (Iterator it = m_positionedObjects->begin(); it != end; ++it) {
1443 positionedObject = *it;
1444
1445 // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content.
1446 if (positionedObject->style()->position() != FixedPosition)
1447 addOverflowFromChild(positionedObject);
1448 }
1449 }
1450
expandsToEncloseOverhangingFloats() const1451 bool RenderBlock::expandsToEncloseOverhangingFloats() const
1452 {
1453 return isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBox())
1454 || hasColumns() || isTableCell() || isFieldset() || isWritingModeRoot() || isRoot();
1455 }
1456
adjustPositionedBlock(RenderBox * child,const MarginInfo & marginInfo)1457 void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marginInfo)
1458 {
1459 bool isHorizontal = isHorizontalWritingMode();
1460 bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontal);
1461 RenderLayer* childLayer = child->layer();
1462
1463 childLayer->setStaticInlinePosition(borderAndPaddingStart());
1464
1465 int logicalTop = logicalHeight();
1466 if (!marginInfo.canCollapseWithMarginBefore()) {
1467 child->computeBlockDirectionMargins(this);
1468 int marginBefore = marginBeforeForChild(child);
1469 int collapsedBeforePos = marginInfo.positiveMargin();
1470 int collapsedBeforeNeg = marginInfo.negativeMargin();
1471 if (marginBefore > 0) {
1472 if (marginBefore > collapsedBeforePos)
1473 collapsedBeforePos = marginBefore;
1474 } else {
1475 if (-marginBefore > collapsedBeforeNeg)
1476 collapsedBeforeNeg = -marginBefore;
1477 }
1478 logicalTop += (collapsedBeforePos - collapsedBeforeNeg) - marginBefore;
1479 }
1480 if (childLayer->staticBlockPosition() != logicalTop) {
1481 childLayer->setStaticBlockPosition(logicalTop);
1482 if (hasStaticBlockPosition)
1483 child->setChildNeedsLayout(true, false);
1484 }
1485 }
1486
adjustFloatingBlock(const MarginInfo & marginInfo)1487 void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo)
1488 {
1489 // The float should be positioned taking into account the bottom margin
1490 // of the previous flow. We add that margin into the height, get the
1491 // float positioned properly, and then subtract the margin out of the
1492 // height again. In the case of self-collapsing blocks, we always just
1493 // use the top margins, since the self-collapsing block collapsed its
1494 // own bottom margin into its top margin.
1495 //
1496 // Note also that the previous flow may collapse its margin into the top of
1497 // our block. If this is the case, then we do not add the margin in to our
1498 // height when computing the position of the float. This condition can be tested
1499 // for by simply calling canCollapseWithMarginBefore. See
1500 // http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for
1501 // an example of this scenario.
1502 int marginOffset = marginInfo.canCollapseWithMarginBefore() ? 0 : marginInfo.margin();
1503 setLogicalHeight(logicalHeight() + marginOffset);
1504 positionNewFloats();
1505 setLogicalHeight(logicalHeight() - marginOffset);
1506 }
1507
handleSpecialChild(RenderBox * child,const MarginInfo & marginInfo)1508 bool RenderBlock::handleSpecialChild(RenderBox* child, const MarginInfo& marginInfo)
1509 {
1510 // Handle in the given order
1511 return handlePositionedChild(child, marginInfo)
1512 || handleFloatingChild(child, marginInfo)
1513 || handleRunInChild(child);
1514 }
1515
1516
handlePositionedChild(RenderBox * child,const MarginInfo & marginInfo)1517 bool RenderBlock::handlePositionedChild(RenderBox* child, const MarginInfo& marginInfo)
1518 {
1519 if (child->isPositioned()) {
1520 child->containingBlock()->insertPositionedObject(child);
1521 adjustPositionedBlock(child, marginInfo);
1522 return true;
1523 }
1524 return false;
1525 }
1526
handleFloatingChild(RenderBox * child,const MarginInfo & marginInfo)1527 bool RenderBlock::handleFloatingChild(RenderBox* child, const MarginInfo& marginInfo)
1528 {
1529 if (child->isFloating()) {
1530 insertFloatingObject(child);
1531 adjustFloatingBlock(marginInfo);
1532 return true;
1533 }
1534 return false;
1535 }
1536
handleRunInChild(RenderBox * child)1537 bool RenderBlock::handleRunInChild(RenderBox* child)
1538 {
1539 // See if we have a run-in element with inline children. If the
1540 // children aren't inline, then just treat the run-in as a normal
1541 // block.
1542 if (!child->isRunIn() || !child->childrenInline())
1543 return false;
1544 // FIXME: We don't handle non-block elements with run-in for now.
1545 if (!child->isRenderBlock())
1546 return false;
1547
1548 // Get the next non-positioned/non-floating RenderBlock.
1549 RenderBlock* blockRunIn = toRenderBlock(child);
1550 RenderObject* curr = blockRunIn->nextSibling();
1551 while (curr && curr->isFloatingOrPositioned())
1552 curr = curr->nextSibling();
1553
1554 if (!curr || !curr->isRenderBlock() || !curr->childrenInline() || curr->isRunIn() || curr->isAnonymous())
1555 return false;
1556
1557 RenderBlock* currBlock = toRenderBlock(curr);
1558
1559 // Remove the old child.
1560 children()->removeChildNode(this, blockRunIn);
1561
1562 // Create an inline.
1563 Node* runInNode = blockRunIn->node();
1564 RenderInline* inlineRunIn = new (renderArena()) RenderInline(runInNode ? runInNode : document());
1565 inlineRunIn->setStyle(blockRunIn->style());
1566
1567 bool runInIsGenerated = child->style()->styleType() == BEFORE || child->style()->styleType() == AFTER;
1568
1569 // Move the nodes from the old child to the new child, but skip any :before/:after content. It has already
1570 // been regenerated by the new inline.
1571 for (RenderObject* runInChild = blockRunIn->firstChild(); runInChild;) {
1572 RenderObject* nextSibling = runInChild->nextSibling();
1573 if (runInIsGenerated || (runInChild->style()->styleType() != BEFORE && runInChild->style()->styleType() != AFTER)) {
1574 blockRunIn->children()->removeChildNode(blockRunIn, runInChild, false);
1575 inlineRunIn->addChild(runInChild); // Use addChild instead of appendChildNode since it handles correct placement of the children relative to :after-generated content.
1576 }
1577 runInChild = nextSibling;
1578 }
1579
1580 // Now insert the new child under |currBlock|. Use addChild instead of insertChildNode since it handles correct placement of the children, esp where we cannot insert
1581 // anything before the first child. e.g. details tag. See https://bugs.webkit.org/show_bug.cgi?id=58228.
1582 currBlock->addChild(inlineRunIn, currBlock->firstChild());
1583
1584 // If the run-in had an element, we need to set the new renderer.
1585 if (runInNode)
1586 runInNode->setRenderer(inlineRunIn);
1587
1588 // Destroy the block run-in, which includes deleting its line box tree.
1589 blockRunIn->deleteLineBoxTree();
1590 blockRunIn->destroy();
1591
1592 // The block acts like an inline, so just null out its
1593 // position.
1594
1595 return true;
1596 }
1597
collapseMargins(RenderBox * child,MarginInfo & marginInfo)1598 int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo)
1599 {
1600 // Get the four margin values for the child and cache them.
1601 const MarginValues childMargins = marginValuesForChild(child);
1602
1603 // Get our max pos and neg top margins.
1604 int posTop = childMargins.positiveMarginBefore();
1605 int negTop = childMargins.negativeMarginBefore();
1606
1607 // For self-collapsing blocks, collapse our bottom margins into our
1608 // top to get new posTop and negTop values.
1609 if (child->isSelfCollapsingBlock()) {
1610 posTop = max(posTop, childMargins.positiveMarginAfter());
1611 negTop = max(negTop, childMargins.negativeMarginAfter());
1612 }
1613
1614 // See if the top margin is quirky. We only care if this child has
1615 // margins that will collapse with us.
1616 bool topQuirk = child->isMarginBeforeQuirk() || style()->marginBeforeCollapse() == MDISCARD;
1617
1618 if (marginInfo.canCollapseWithMarginBefore()) {
1619 // This child is collapsing with the top of the
1620 // block. If it has larger margin values, then we need to update
1621 // our own maximal values.
1622 if (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !topQuirk)
1623 setMaxMarginBeforeValues(max(posTop, maxPositiveMarginBefore()), max(negTop, maxNegativeMarginBefore()));
1624
1625 // The minute any of the margins involved isn't a quirk, don't
1626 // collapse it away, even if the margin is smaller (www.webreference.com
1627 // has an example of this, a <dt> with 0.8em author-specified inside
1628 // a <dl> inside a <td>.
1629 if (!marginInfo.determinedMarginBeforeQuirk() && !topQuirk && (posTop - negTop)) {
1630 setMarginBeforeQuirk(false);
1631 marginInfo.setDeterminedMarginBeforeQuirk(true);
1632 }
1633
1634 if (!marginInfo.determinedMarginBeforeQuirk() && topQuirk && !marginBefore())
1635 // We have no top margin and our top child has a quirky margin.
1636 // We will pick up this quirky margin and pass it through.
1637 // This deals with the <td><div><p> case.
1638 // Don't do this for a block that split two inlines though. You do
1639 // still apply margins in this case.
1640 setMarginBeforeQuirk(true);
1641 }
1642
1643 if (marginInfo.quirkContainer() && marginInfo.atBeforeSideOfBlock() && (posTop - negTop))
1644 marginInfo.setMarginBeforeQuirk(topQuirk);
1645
1646 int beforeCollapseLogicalTop = logicalHeight();
1647 int logicalTop = beforeCollapseLogicalTop;
1648 if (child->isSelfCollapsingBlock()) {
1649 // This child has no height. We need to compute our
1650 // position before we collapse the child's margins together,
1651 // so that we can get an accurate position for the zero-height block.
1652 int collapsedBeforePos = max(marginInfo.positiveMargin(), childMargins.positiveMarginBefore());
1653 int collapsedBeforeNeg = max(marginInfo.negativeMargin(), childMargins.negativeMarginBefore());
1654 marginInfo.setMargin(collapsedBeforePos, collapsedBeforeNeg);
1655
1656 // Now collapse the child's margins together, which means examining our
1657 // bottom margin values as well.
1658 marginInfo.setPositiveMarginIfLarger(childMargins.positiveMarginAfter());
1659 marginInfo.setNegativeMarginIfLarger(childMargins.negativeMarginAfter());
1660
1661 if (!marginInfo.canCollapseWithMarginBefore())
1662 // We need to make sure that the position of the self-collapsing block
1663 // is correct, since it could have overflowing content
1664 // that needs to be positioned correctly (e.g., a block that
1665 // had a specified height of 0 but that actually had subcontent).
1666 logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg;
1667 }
1668 else {
1669 if (child->style()->marginBeforeCollapse() == MSEPARATE) {
1670 setLogicalHeight(logicalHeight() + marginInfo.margin() + marginBeforeForChild(child));
1671 logicalTop = logicalHeight();
1672 }
1673 else if (!marginInfo.atBeforeSideOfBlock() ||
1674 (!marginInfo.canCollapseMarginBeforeWithChildren()
1675 && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginBeforeQuirk()))) {
1676 // We're collapsing with a previous sibling's margins and not
1677 // with the top of the block.
1678 setLogicalHeight(logicalHeight() + max(marginInfo.positiveMargin(), posTop) - max(marginInfo.negativeMargin(), negTop));
1679 logicalTop = logicalHeight();
1680 }
1681
1682 marginInfo.setPositiveMargin(childMargins.positiveMarginAfter());
1683 marginInfo.setNegativeMargin(childMargins.negativeMarginAfter());
1684
1685 if (marginInfo.margin())
1686 marginInfo.setMarginAfterQuirk(child->isMarginAfterQuirk() || style()->marginAfterCollapse() == MDISCARD);
1687 }
1688
1689 // If margins would pull us past the top of the next page, then we need to pull back and pretend like the margins
1690 // collapsed into the page edge.
1691 bool paginated = view()->layoutState()->isPaginated();
1692 if (paginated && logicalTop > beforeCollapseLogicalTop) {
1693 int oldLogicalTop = logicalTop;
1694 logicalTop = min(logicalTop, nextPageLogicalTop(beforeCollapseLogicalTop));
1695 setLogicalHeight(logicalHeight() + (logicalTop - oldLogicalTop));
1696 }
1697 return logicalTop;
1698 }
1699
clearFloatsIfNeeded(RenderBox * child,MarginInfo & marginInfo,int oldTopPosMargin,int oldTopNegMargin,int yPos)1700 int RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin, int yPos)
1701 {
1702 int heightIncrease = getClearDelta(child, yPos);
1703 if (!heightIncrease)
1704 return yPos;
1705
1706 if (child->isSelfCollapsingBlock()) {
1707 // For self-collapsing blocks that clear, they can still collapse their
1708 // margins with following siblings. Reset the current margins to represent
1709 // the self-collapsing block's margins only.
1710 // CSS2.1 states:
1711 // "An element that has had clearance applied to it never collapses its top margin with its parent block's bottom margin.
1712 // Therefore if we are at the bottom of the block, let's go ahead and reset margins to only include the
1713 // self-collapsing block's bottom margin.
1714 bool atBottomOfBlock = true;
1715 for (RenderBox* curr = child->nextSiblingBox(); curr && atBottomOfBlock; curr = curr->nextSiblingBox()) {
1716 if (!curr->isFloatingOrPositioned())
1717 atBottomOfBlock = false;
1718 }
1719
1720 MarginValues childMargins = marginValuesForChild(child);
1721 if (atBottomOfBlock) {
1722 marginInfo.setPositiveMargin(childMargins.positiveMarginAfter());
1723 marginInfo.setNegativeMargin(childMargins.negativeMarginAfter());
1724 } else {
1725 marginInfo.setPositiveMargin(max(childMargins.positiveMarginBefore(), childMargins.positiveMarginAfter()));
1726 marginInfo.setNegativeMargin(max(childMargins.negativeMarginBefore(), childMargins.negativeMarginAfter()));
1727 }
1728
1729 // Adjust our height such that we are ready to be collapsed with subsequent siblings (or the bottom
1730 // of the parent block).
1731 setLogicalHeight(child->y() - max(0, marginInfo.margin()));
1732 } else
1733 // Increase our height by the amount we had to clear.
1734 setLogicalHeight(height() + heightIncrease);
1735
1736 if (marginInfo.canCollapseWithMarginBefore()) {
1737 // We can no longer collapse with the top of the block since a clear
1738 // occurred. The empty blocks collapse into the cleared block.
1739 // FIXME: This isn't quite correct. Need clarification for what to do
1740 // if the height the cleared block is offset by is smaller than the
1741 // margins involved.
1742 setMaxMarginBeforeValues(oldTopPosMargin, oldTopNegMargin);
1743 marginInfo.setAtBeforeSideOfBlock(false);
1744 }
1745
1746 return yPos + heightIncrease;
1747 }
1748
estimateLogicalTopPosition(RenderBox * child,const MarginInfo & marginInfo)1749 int RenderBlock::estimateLogicalTopPosition(RenderBox* child, const MarginInfo& marginInfo)
1750 {
1751 // FIXME: We need to eliminate the estimation of vertical position, because when it's wrong we sometimes trigger a pathological
1752 // relayout if there are intruding floats.
1753 int logicalTopEstimate = logicalHeight();
1754 if (!marginInfo.canCollapseWithMarginBefore()) {
1755 int childMarginBefore = child->selfNeedsLayout() ? marginBeforeForChild(child) : collapsedMarginBeforeForChild(child);
1756 logicalTopEstimate += max(marginInfo.margin(), childMarginBefore);
1757 }
1758
1759 bool paginated = view()->layoutState()->isPaginated();
1760
1761 // Adjust logicalTopEstimate down to the next page if the margins are so large that we don't fit on the current
1762 // page.
1763 if (paginated && logicalTopEstimate > logicalHeight())
1764 logicalTopEstimate = min(logicalTopEstimate, nextPageLogicalTop(logicalHeight()));
1765
1766 logicalTopEstimate += getClearDelta(child, logicalTopEstimate);
1767
1768 if (paginated) {
1769 // If the object has a page or column break value of "before", then we should shift to the top of the next page.
1770 logicalTopEstimate = applyBeforeBreak(child, logicalTopEstimate);
1771
1772 // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one.
1773 logicalTopEstimate = adjustForUnsplittableChild(child, logicalTopEstimate);
1774
1775 if (!child->selfNeedsLayout() && child->isRenderBlock())
1776 logicalTopEstimate += toRenderBlock(child)->paginationStrut();
1777 }
1778
1779 return logicalTopEstimate;
1780 }
1781
determineLogicalLeftPositionForChild(RenderBox * child)1782 void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child)
1783 {
1784 int startPosition = borderStart() + paddingStart();
1785 int totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + availableLogicalWidth();
1786
1787 // Add in our start margin.
1788 int childMarginStart = marginStartForChild(child);
1789 int newPosition = startPosition + childMarginStart;
1790
1791 // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need
1792 // to shift over as necessary to dodge any floats that might get in the way.
1793 if (child->avoidsFloats()) {
1794 int startOff = style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(logicalHeight(), false) : totalAvailableLogicalWidth - logicalRightOffsetForLine(logicalHeight(), false);
1795 if (style()->textAlign() != WEBKIT_CENTER && !child->style()->marginStartUsing(style()).isAuto()) {
1796 if (childMarginStart < 0)
1797 startOff += childMarginStart;
1798 newPosition = max(newPosition, startOff); // Let the float sit in the child's margin if it can fit.
1799 } else if (startOff != startPosition) {
1800 // The object is shifting to the "end" side of the block. The object might be centered, so we need to
1801 // recalculate our inline direction margins. Note that the containing block content
1802 // width computation will take into account the delta between |startOff| and |startPosition|
1803 // so that we can just pass the content width in directly to the |computeMarginsInContainingBlockInlineDirection|
1804 // function.
1805 child->computeInlineDirectionMargins(this, availableLogicalWidthForLine(logicalTopForChild(child), false), logicalWidthForChild(child));
1806 newPosition = startOff + marginStartForChild(child);
1807 }
1808 }
1809
1810 setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), ApplyLayoutDelta);
1811 }
1812
setCollapsedBottomMargin(const MarginInfo & marginInfo)1813 void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo)
1814 {
1815 if (marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore()) {
1816 // Update our max pos/neg bottom margins, since we collapsed our bottom margins
1817 // with our children.
1818 setMaxMarginAfterValues(max(maxPositiveMarginAfter(), marginInfo.positiveMargin()), max(maxNegativeMarginAfter(), marginInfo.negativeMargin()));
1819
1820 if (!marginInfo.marginAfterQuirk())
1821 setMarginAfterQuirk(false);
1822
1823 if (marginInfo.marginAfterQuirk() && marginAfter() == 0)
1824 // We have no bottom margin and our last child has a quirky margin.
1825 // We will pick up this quirky margin and pass it through.
1826 // This deals with the <td><div><p> case.
1827 setMarginAfterQuirk(true);
1828 }
1829 }
1830
handleAfterSideOfBlock(int beforeSide,int afterSide,MarginInfo & marginInfo)1831 void RenderBlock::handleAfterSideOfBlock(int beforeSide, int afterSide, MarginInfo& marginInfo)
1832 {
1833 marginInfo.setAtAfterSideOfBlock(true);
1834
1835 // If we can't collapse with children then go ahead and add in the bottom margin.
1836 if (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore()
1837 && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginAfterQuirk()))
1838 setLogicalHeight(logicalHeight() + marginInfo.margin());
1839
1840 // Now add in our bottom border/padding.
1841 setLogicalHeight(logicalHeight() + afterSide);
1842
1843 // Negative margins can cause our height to shrink below our minimal height (border/padding).
1844 // If this happens, ensure that the computed height is increased to the minimal height.
1845 setLogicalHeight(max(logicalHeight(), beforeSide + afterSide));
1846
1847 // Update our bottom collapsed margin info.
1848 setCollapsedBottomMargin(marginInfo);
1849 }
1850
setLogicalLeftForChild(RenderBox * child,int logicalLeft,ApplyLayoutDeltaMode applyDelta)1851 void RenderBlock::setLogicalLeftForChild(RenderBox* child, int logicalLeft, ApplyLayoutDeltaMode applyDelta)
1852 {
1853 if (isHorizontalWritingMode()) {
1854 if (applyDelta == ApplyLayoutDelta)
1855 view()->addLayoutDelta(IntSize(child->x() - logicalLeft, 0));
1856 child->setX(logicalLeft);
1857 } else {
1858 if (applyDelta == ApplyLayoutDelta)
1859 view()->addLayoutDelta(IntSize(0, child->y() - logicalLeft));
1860 child->setY(logicalLeft);
1861 }
1862 }
1863
setLogicalTopForChild(RenderBox * child,int logicalTop,ApplyLayoutDeltaMode applyDelta)1864 void RenderBlock::setLogicalTopForChild(RenderBox* child, int logicalTop, ApplyLayoutDeltaMode applyDelta)
1865 {
1866 if (isHorizontalWritingMode()) {
1867 if (applyDelta == ApplyLayoutDelta)
1868 view()->addLayoutDelta(IntSize(0, child->y() - logicalTop));
1869 child->setY(logicalTop);
1870 } else {
1871 if (applyDelta == ApplyLayoutDelta)
1872 view()->addLayoutDelta(IntSize(child->x() - logicalTop, 0));
1873 child->setX(logicalTop);
1874 }
1875 }
1876
layoutBlockChildren(bool relayoutChildren,int & maxFloatLogicalBottom)1877 void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatLogicalBottom)
1878 {
1879 if (gPercentHeightDescendantsMap) {
1880 if (HashSet<RenderBox*>* descendants = gPercentHeightDescendantsMap->get(this)) {
1881 HashSet<RenderBox*>::iterator end = descendants->end();
1882 for (HashSet<RenderBox*>::iterator it = descendants->begin(); it != end; ++it) {
1883 RenderBox* box = *it;
1884 while (box != this) {
1885 if (box->normalChildNeedsLayout())
1886 break;
1887 box->setChildNeedsLayout(true, false);
1888 box = box->containingBlock();
1889 ASSERT(box);
1890 if (!box)
1891 break;
1892 }
1893 }
1894 }
1895 }
1896
1897 int beforeEdge = borderBefore() + paddingBefore();
1898 int afterEdge = borderAfter() + paddingAfter() + scrollbarLogicalHeight();
1899
1900 setLogicalHeight(beforeEdge);
1901
1902 // The margin struct caches all our current margin collapsing state. The compact struct caches state when we encounter compacts,
1903 MarginInfo marginInfo(this, beforeEdge, afterEdge);
1904
1905 // Fieldsets need to find their legend and position it inside the border of the object.
1906 // The legend then gets skipped during normal layout. The same is true for ruby text.
1907 // It doesn't get included in the normal layout process but is instead skipped.
1908 RenderObject* childToExclude = layoutSpecialExcludedChild(relayoutChildren);
1909
1910 int previousFloatLogicalBottom = 0;
1911 maxFloatLogicalBottom = 0;
1912
1913 RenderBox* next = firstChildBox();
1914
1915 while (next) {
1916 RenderBox* child = next;
1917 next = child->nextSiblingBox();
1918
1919 if (childToExclude == child)
1920 continue; // Skip this child, since it will be positioned by the specialized subclass (fieldsets and ruby runs).
1921
1922 // Make sure we layout children if they need it.
1923 // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into
1924 // an auto value. Add a method to determine this, so that we can avoid the relayout.
1925 if (relayoutChildren || ((child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() || child->style()->logicalMaxHeight().isPercent()) && !isRenderView()))
1926 child->setChildNeedsLayout(true, false);
1927
1928 // If relayoutChildren is set and the child has percentage padding, we also need to invalidate the child's pref widths.
1929 if (relayoutChildren && (child->style()->paddingStart().isPercent() || child->style()->paddingEnd().isPercent()))
1930 child->setPreferredLogicalWidthsDirty(true, false);
1931
1932 // Handle the four types of special elements first. These include positioned content, floating content, compacts and
1933 // run-ins. When we encounter these four types of objects, we don't actually lay them out as normal flow blocks.
1934 if (handleSpecialChild(child, marginInfo))
1935 continue;
1936
1937 // Lay out the child.
1938 layoutBlockChild(child, marginInfo, previousFloatLogicalBottom, maxFloatLogicalBottom);
1939 }
1940
1941 // Now do the handling of the bottom of the block, adding in our bottom border/padding and
1942 // determining the correct collapsed bottom margin information.
1943 handleAfterSideOfBlock(beforeEdge, afterEdge, marginInfo);
1944 }
1945
layoutBlockChild(RenderBox * child,MarginInfo & marginInfo,int & previousFloatLogicalBottom,int & maxFloatLogicalBottom)1946 void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int& previousFloatLogicalBottom, int& maxFloatLogicalBottom)
1947 {
1948 int oldPosMarginBefore = maxPositiveMarginBefore();
1949 int oldNegMarginBefore = maxNegativeMarginBefore();
1950
1951 // The child is a normal flow object. Compute the margins we will use for collapsing now.
1952 child->computeBlockDirectionMargins(this);
1953
1954 // Do not allow a collapse if the margin-before-collapse style is set to SEPARATE.
1955 if (child->style()->marginBeforeCollapse() == MSEPARATE) {
1956 marginInfo.setAtBeforeSideOfBlock(false);
1957 marginInfo.clearMargin();
1958 }
1959
1960 // Try to guess our correct logical top position. In most cases this guess will
1961 // be correct. Only if we're wrong (when we compute the real logical top position)
1962 // will we have to potentially relayout.
1963 int logicalTopEstimate = estimateLogicalTopPosition(child, marginInfo);
1964
1965 // Cache our old rect so that we can dirty the proper repaint rects if the child moves.
1966 IntRect oldRect(child->x(), child->y() , child->width(), child->height());
1967 int oldLogicalTop = logicalTopForChild(child);
1968
1969 #ifndef NDEBUG
1970 IntSize oldLayoutDelta = view()->layoutDelta();
1971 #endif
1972 // Go ahead and position the child as though it didn't collapse with the top.
1973 setLogicalTopForChild(child, logicalTopEstimate, ApplyLayoutDelta);
1974
1975 RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0;
1976 bool markDescendantsWithFloats = false;
1977 if (logicalTopEstimate != oldLogicalTop && !child->avoidsFloats() && childRenderBlock && childRenderBlock->containsFloats())
1978 markDescendantsWithFloats = true;
1979 else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) {
1980 // If an element might be affected by the presence of floats, then always mark it for
1981 // layout.
1982 int fb = max(previousFloatLogicalBottom, lowestFloatLogicalBottom());
1983 if (fb > logicalTopEstimate)
1984 markDescendantsWithFloats = true;
1985 }
1986
1987 if (childRenderBlock) {
1988 if (markDescendantsWithFloats)
1989 childRenderBlock->markAllDescendantsWithFloatsForLayout();
1990 if (!child->isWritingModeRoot())
1991 previousFloatLogicalBottom = max(previousFloatLogicalBottom, oldLogicalTop + childRenderBlock->lowestFloatLogicalBottom());
1992 }
1993
1994 if (!child->needsLayout())
1995 child->markForPaginationRelayoutIfNeeded();
1996
1997 bool childHadLayout = child->m_everHadLayout;
1998 bool childNeededLayout = child->needsLayout();
1999 if (childNeededLayout)
2000 child->layout();
2001
2002 // Cache if we are at the top of the block right now.
2003 bool atBeforeSideOfBlock = marginInfo.atBeforeSideOfBlock();
2004
2005 // Now determine the correct ypos based off examination of collapsing margin
2006 // values.
2007 int logicalTopBeforeClear = collapseMargins(child, marginInfo);
2008
2009 // Now check for clear.
2010 int logicalTopAfterClear = clearFloatsIfNeeded(child, marginInfo, oldPosMarginBefore, oldNegMarginBefore, logicalTopBeforeClear);
2011
2012 bool paginated = view()->layoutState()->isPaginated();
2013 if (paginated) {
2014 int oldTop = logicalTopAfterClear;
2015
2016 // If the object has a page or column break value of "before", then we should shift to the top of the next page.
2017 logicalTopAfterClear = applyBeforeBreak(child, logicalTopAfterClear);
2018
2019 // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one.
2020 int logicalTopBeforeUnsplittableAdjustment = logicalTopAfterClear;
2021 int logicalTopAfterUnsplittableAdjustment = adjustForUnsplittableChild(child, logicalTopAfterClear);
2022
2023 int paginationStrut = 0;
2024 int unsplittableAdjustmentDelta = logicalTopAfterUnsplittableAdjustment - logicalTopBeforeUnsplittableAdjustment;
2025 if (unsplittableAdjustmentDelta)
2026 paginationStrut = unsplittableAdjustmentDelta;
2027 else if (childRenderBlock && childRenderBlock->paginationStrut())
2028 paginationStrut = childRenderBlock->paginationStrut();
2029
2030 if (paginationStrut) {
2031 // We are willing to propagate out to our parent block as long as we were at the top of the block prior
2032 // to collapsing our margins, and as long as we didn't clear or move as a result of other pagination.
2033 if (atBeforeSideOfBlock && oldTop == logicalTopBeforeClear && !isPositioned() && !isTableCell()) {
2034 // FIXME: Should really check if we're exceeding the page height before propagating the strut, but we don't
2035 // have all the information to do so (the strut only has the remaining amount to push). Gecko gets this wrong too
2036 // and pushes to the next page anyway, so not too concerned about it.
2037 setPaginationStrut(logicalTopAfterClear + paginationStrut);
2038 if (childRenderBlock)
2039 childRenderBlock->setPaginationStrut(0);
2040 } else
2041 logicalTopAfterClear += paginationStrut;
2042 }
2043
2044 // Similar to how we apply clearance. Go ahead and boost height() to be the place where we're going to position the child.
2045 setLogicalHeight(logicalHeight() + (logicalTopAfterClear - oldTop));
2046 }
2047
2048 setLogicalTopForChild(child, logicalTopAfterClear, ApplyLayoutDelta);
2049
2050 // Now we have a final top position. See if it really does end up being different from our estimate.
2051 if (logicalTopAfterClear != logicalTopEstimate || (paginated && childRenderBlock && childRenderBlock->shouldBreakAtLineToAvoidWidow())) {
2052 if (child->shrinkToAvoidFloats()) {
2053 // The child's width depends on the line width.
2054 // When the child shifts to clear an item, its width can
2055 // change (because it has more available line width).
2056 // So go ahead and mark the item as dirty.
2057 child->setChildNeedsLayout(true, false);
2058 }
2059 if (childRenderBlock) {
2060 if (!child->avoidsFloats() && childRenderBlock->containsFloats())
2061 childRenderBlock->markAllDescendantsWithFloatsForLayout();
2062 if (!child->needsLayout())
2063 child->markForPaginationRelayoutIfNeeded();
2064 }
2065
2066 // Our guess was wrong. Make the child lay itself out again.
2067 child->layoutIfNeeded();
2068 }
2069
2070 // We are no longer at the top of the block if we encounter a non-empty child.
2071 // This has to be done after checking for clear, so that margins can be reset if a clear occurred.
2072 if (marginInfo.atBeforeSideOfBlock() && !child->isSelfCollapsingBlock())
2073 marginInfo.setAtBeforeSideOfBlock(false);
2074
2075 // Now place the child in the correct left position
2076 determineLogicalLeftPositionForChild(child);
2077
2078 // Update our height now that the child has been placed in the correct position.
2079 setLogicalHeight(logicalHeight() + logicalHeightForChild(child));
2080 if (child->style()->marginAfterCollapse() == MSEPARATE) {
2081 setLogicalHeight(logicalHeight() + marginAfterForChild(child));
2082 marginInfo.clearMargin();
2083 }
2084 // If the child has overhanging floats that intrude into following siblings (or possibly out
2085 // of this block), then the parent gets notified of the floats now.
2086 if (childRenderBlock && childRenderBlock->containsFloats())
2087 maxFloatLogicalBottom = max(maxFloatLogicalBottom, addOverhangingFloats(toRenderBlock(child), -child->logicalLeft(), -child->logicalTop(), !childNeededLayout));
2088
2089 IntSize childOffset(child->x() - oldRect.x(), child->y() - oldRect.y());
2090 if (childOffset.width() || childOffset.height()) {
2091 view()->addLayoutDelta(childOffset);
2092
2093 // If the child moved, we have to repaint it as well as any floating/positioned
2094 // descendants. An exception is if we need a layout. In this case, we know we're going to
2095 // repaint ourselves (and the child) anyway.
2096 if (childHadLayout && !selfNeedsLayout() && child->checkForRepaintDuringLayout())
2097 child->repaintDuringLayoutIfMoved(oldRect);
2098 }
2099
2100 if (!childHadLayout && child->checkForRepaintDuringLayout()) {
2101 child->repaint();
2102 child->repaintOverhangingFloats(true);
2103 }
2104
2105 if (paginated) {
2106 // Check for an after page/column break.
2107 int newHeight = applyAfterBreak(child, logicalHeight(), marginInfo);
2108 if (newHeight != height())
2109 setLogicalHeight(newHeight);
2110 }
2111
2112 ASSERT(oldLayoutDelta == view()->layoutDelta());
2113 }
2114
simplifiedNormalFlowLayout()2115 void RenderBlock::simplifiedNormalFlowLayout()
2116 {
2117 if (childrenInline()) {
2118 ListHashSet<RootInlineBox*> lineBoxes;
2119 bool endOfInline = false;
2120 RenderObject* o = bidiFirstNotSkippingInlines(this);
2121 while (o) {
2122 if (!o->isPositioned() && (o->isReplaced() || o->isFloating())) {
2123 o->layoutIfNeeded();
2124 if (toRenderBox(o)->inlineBoxWrapper()) {
2125 RootInlineBox* box = toRenderBox(o)->inlineBoxWrapper()->root();
2126 lineBoxes.add(box);
2127 }
2128 } else if (o->isText() || (o->isRenderInline() && !endOfInline))
2129 o->setNeedsLayout(false);
2130 o = bidiNext(this, o, 0, false, &endOfInline);
2131 }
2132
2133 // FIXME: Glyph overflow will get lost in this case, but not really a big deal.
2134 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
2135 for (ListHashSet<RootInlineBox*>::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) {
2136 RootInlineBox* box = *it;
2137 box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap);
2138 }
2139 } else {
2140 for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) {
2141 if (!box->isPositioned())
2142 box->layoutIfNeeded();
2143 }
2144 }
2145 }
2146
simplifiedLayout()2147 bool RenderBlock::simplifiedLayout()
2148 {
2149 if ((!posChildNeedsLayout() && !needsSimplifiedNormalFlowLayout()) || normalChildNeedsLayout() || selfNeedsLayout())
2150 return false;
2151
2152 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode());
2153
2154 if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly())
2155 return false;
2156
2157 // Lay out positioned descendants or objects that just need to recompute overflow.
2158 if (needsSimplifiedNormalFlowLayout())
2159 simplifiedNormalFlowLayout();
2160
2161 // Lay out our positioned objects if our positioned child bit is set.
2162 if (posChildNeedsLayout())
2163 layoutPositionedObjects(false);
2164
2165 // Recompute our overflow information.
2166 // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only
2167 // updating our overflow if we either used to have overflow or if the new temporary object has overflow.
2168 // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and
2169 // lowestPosition on every relayout so it's not a regression.
2170 m_overflow.clear();
2171 computeOverflow(clientLogicalBottom(), true);
2172
2173 statePusher.pop();
2174
2175 updateLayerTransform();
2176
2177 updateScrollInfoAfterLayout();
2178
2179 setNeedsLayout(false);
2180 return true;
2181 }
2182
layoutPositionedObjects(bool relayoutChildren)2183 void RenderBlock::layoutPositionedObjects(bool relayoutChildren)
2184 {
2185 if (!m_positionedObjects)
2186 return;
2187
2188 if (hasColumns())
2189 view()->layoutState()->clearPaginationInformation(); // Positioned objects are not part of the column flow, so they don't paginate with the columns.
2190
2191 RenderBox* r;
2192 Iterator end = m_positionedObjects->end();
2193 for (Iterator it = m_positionedObjects->begin(); it != end; ++it) {
2194 r = *it;
2195 // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the
2196 // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned
2197 // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is
2198 // positioned explicitly) this should not incur a performance penalty.
2199 if (relayoutChildren || (r->style()->hasStaticBlockPosition(isHorizontalWritingMode()) && r->parent() != this && r->parent()->isBlockFlow()))
2200 r->setChildNeedsLayout(true, false);
2201
2202 // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths.
2203 if (relayoutChildren && (r->style()->paddingStart().isPercent() || r->style()->paddingEnd().isPercent()))
2204 r->setPreferredLogicalWidthsDirty(true, false);
2205
2206 if (!r->needsLayout())
2207 r->markForPaginationRelayoutIfNeeded();
2208
2209 // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width
2210 // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout.
2211 if (r->needsPositionedMovementLayoutOnly() && r->tryLayoutDoingPositionedMovementOnly())
2212 r->setNeedsLayout(false);
2213 r->layoutIfNeeded();
2214 }
2215
2216 if (hasColumns())
2217 view()->layoutState()->m_columnInfo = columnInfo(); // FIXME: Kind of gross. We just put this back into the layout state so that pop() will work.
2218 }
2219
markPositionedObjectsForLayout()2220 void RenderBlock::markPositionedObjectsForLayout()
2221 {
2222 if (m_positionedObjects) {
2223 RenderBox* r;
2224 Iterator end = m_positionedObjects->end();
2225 for (Iterator it = m_positionedObjects->begin(); it != end; ++it) {
2226 r = *it;
2227 r->setChildNeedsLayout(true);
2228 }
2229 }
2230 }
2231
markForPaginationRelayoutIfNeeded()2232 void RenderBlock::markForPaginationRelayoutIfNeeded()
2233 {
2234 ASSERT(!needsLayout());
2235 if (needsLayout())
2236 return;
2237
2238 if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(logicalTop()) != pageLogicalOffset()) || shouldBreakAtLineToAvoidWidow())
2239 setChildNeedsLayout(true, false);
2240 }
2241
repaintOverhangingFloats(bool paintAllDescendants)2242 void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants)
2243 {
2244 // Repaint any overhanging floats (if we know we're the one to paint them).
2245 // Otherwise, bail out.
2246 if (!hasOverhangingFloats())
2247 return;
2248
2249 // FIXME: Avoid disabling LayoutState. At the very least, don't disable it for floats originating
2250 // in this block. Better yet would be to push extra state for the containers of other floats.
2251 view()->disableLayoutState();
2252 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
2253 FloatingObjectSetIterator end = floatingObjectSet.end();
2254 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
2255 FloatingObject* r = *it;
2256 // Only repaint the object if it is overhanging, is not in its own layer, and
2257 // is our responsibility to paint (m_shouldPaint is set). When paintAllDescendants is true, the latter
2258 // condition is replaced with being a descendant of us.
2259 if (logicalBottomForFloat(r) > logicalHeight() && ((paintAllDescendants && r->m_renderer->isDescendantOf(this)) || r->m_shouldPaint) && !r->m_renderer->hasSelfPaintingLayer()) {
2260 r->m_renderer->repaint();
2261 r->m_renderer->repaintOverhangingFloats();
2262 }
2263 }
2264 view()->enableLayoutState();
2265 }
2266
paint(PaintInfo & paintInfo,int tx,int ty)2267 void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty)
2268 {
2269 tx += x();
2270 ty += y();
2271
2272 PaintPhase phase = paintInfo.phase;
2273
2274 // Check if we need to do anything at all.
2275 // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView
2276 // paints the root's background.
2277 if (!isRoot()) {
2278 IntRect overflowBox = visualOverflowRect();
2279 flipForWritingMode(overflowBox);
2280 overflowBox.inflate(maximalOutlineSize(paintInfo.phase));
2281 overflowBox.move(tx, ty);
2282 if (!overflowBox.intersects(paintInfo.rect))
2283 return;
2284 }
2285
2286 bool pushedClip = pushContentsClip(paintInfo, tx, ty);
2287 paintObject(paintInfo, tx, ty);
2288 if (pushedClip)
2289 popContentsClip(paintInfo, phase, tx, ty);
2290
2291 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
2292 // z-index. We paint after we painted the background/border, so that the scrollbars will
2293 // sit above the background/border.
2294 if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this))
2295 layer()->paintOverflowControls(paintInfo.context, tx, ty, paintInfo.rect);
2296 }
2297
paintColumnRules(PaintInfo & paintInfo,int tx,int ty)2298 void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty)
2299 {
2300 if (paintInfo.context->paintingDisabled())
2301 return;
2302
2303 const Color& ruleColor = style()->visitedDependentColor(CSSPropertyWebkitColumnRuleColor);
2304 bool ruleTransparent = style()->columnRuleIsTransparent();
2305 EBorderStyle ruleStyle = style()->columnRuleStyle();
2306 int ruleWidth = style()->columnRuleWidth();
2307 int colGap = columnGap();
2308 bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent && ruleWidth <= colGap;
2309 if (!renderRule)
2310 return;
2311
2312 // We need to do multiple passes, breaking up our child painting into strips.
2313 ColumnInfo* colInfo = columnInfo();
2314 unsigned colCount = columnCount(colInfo);
2315 int currLogicalLeftOffset = style()->isLeftToRightDirection() ? 0 : contentLogicalWidth();
2316 int ruleAdd = logicalLeftOffsetForContent();
2317 int ruleLogicalLeft = style()->isLeftToRightDirection() ? 0 : contentLogicalWidth();
2318
2319 const AffineTransform& currentCTM = paintInfo.context->getCTM();
2320 bool antialias = !currentCTM.isIdentityOrTranslationOrFlipped();
2321
2322 for (unsigned i = 0; i < colCount; i++) {
2323 IntRect colRect = columnRectAt(colInfo, i);
2324
2325 int inlineDirectionSize = isHorizontalWritingMode() ? colRect.width() : colRect.height();
2326
2327 // Move to the next position.
2328 if (style()->isLeftToRightDirection()) {
2329 ruleLogicalLeft += inlineDirectionSize + colGap / 2;
2330 currLogicalLeftOffset += inlineDirectionSize + colGap;
2331 } else {
2332 ruleLogicalLeft -= (inlineDirectionSize + colGap / 2);
2333 currLogicalLeftOffset -= (inlineDirectionSize + colGap);
2334 }
2335
2336 // Now paint the column rule.
2337 if (i < colCount - 1) {
2338 int ruleLeft = isHorizontalWritingMode() ? tx + ruleLogicalLeft - ruleWidth / 2 + ruleAdd : tx + borderBefore() + paddingBefore();
2339 int ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleWidth : ruleLeft + contentWidth();
2340 int ruleTop = isHorizontalWritingMode() ? ty + borderTop() + paddingTop() : ty + ruleLogicalLeft - ruleWidth / 2 + ruleAdd;
2341 int ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleWidth;
2342 drawLineForBoxSide(paintInfo.context, ruleLeft, ruleTop, ruleRight, ruleBottom,
2343 style()->isLeftToRightDirection() ? BSLeft : BSRight, ruleColor, ruleStyle, 0, 0, antialias);
2344 }
2345
2346 ruleLogicalLeft = currLogicalLeftOffset;
2347 }
2348 }
2349
paintColumnContents(PaintInfo & paintInfo,int tx,int ty,bool paintingFloats)2350 void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool paintingFloats)
2351 {
2352 // We need to do multiple passes, breaking up our child painting into strips.
2353 GraphicsContext* context = paintInfo.context;
2354 ColumnInfo* colInfo = columnInfo();
2355 unsigned colCount = columnCount(colInfo);
2356 if (!colCount)
2357 return;
2358 int currLogicalTopOffset = 0;
2359 for (unsigned i = 0; i < colCount; i++) {
2360 // For each rect, we clip to the rect, and then we adjust our coords.
2361 IntRect colRect = columnRectAt(colInfo, i);
2362 flipForWritingMode(colRect);
2363 int logicalLeftOffset = (isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent();
2364 IntSize offset = isHorizontalWritingMode() ? IntSize(logicalLeftOffset, currLogicalTopOffset) : IntSize(currLogicalTopOffset, logicalLeftOffset);
2365 colRect.move(tx, ty);
2366 PaintInfo info(paintInfo);
2367 info.rect.intersect(colRect);
2368
2369 if (!info.rect.isEmpty()) {
2370 GraphicsContextStateSaver stateSaver(*context);
2371
2372 // Each strip pushes a clip, since column boxes are specified as being
2373 // like overflow:hidden.
2374 context->clip(colRect);
2375
2376 // Adjust our x and y when painting.
2377 int finalX = tx + offset.width();
2378 int finalY = ty + offset.height();
2379 if (paintingFloats)
2380 paintFloats(info, finalX, finalY, paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip);
2381 else
2382 paintContents(info, finalX, finalY);
2383 }
2384
2385 int blockDelta = (isHorizontalWritingMode() ? colRect.height() : colRect.width());
2386 if (style()->isFlippedBlocksWritingMode())
2387 currLogicalTopOffset += blockDelta;
2388 else
2389 currLogicalTopOffset -= blockDelta;
2390 }
2391 }
2392
paintContents(PaintInfo & paintInfo,int tx,int ty)2393 void RenderBlock::paintContents(PaintInfo& paintInfo, int tx, int ty)
2394 {
2395 // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC.
2396 // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document
2397 // will do a full repaint().
2398 if (document()->didLayoutWithPendingStylesheets() && !isRenderView())
2399 return;
2400
2401 if (childrenInline())
2402 m_lineBoxes.paint(this, paintInfo, tx, ty);
2403 else
2404 paintChildren(paintInfo, tx, ty);
2405 }
2406
paintChildren(PaintInfo & paintInfo,int tx,int ty)2407 void RenderBlock::paintChildren(PaintInfo& paintInfo, int tx, int ty)
2408 {
2409 PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase;
2410 newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase;
2411
2412 // We don't paint our own background, but we do let the kids paint their backgrounds.
2413 PaintInfo info(paintInfo);
2414 info.phase = newPhase;
2415 info.updatePaintingRootForChildren(this);
2416
2417 // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit
2418 // NSViews. Do not add any more code for this.
2419 RenderView* renderView = view();
2420 bool usePrintRect = !renderView->printRect().isEmpty();
2421
2422 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
2423 // Check for page-break-before: always, and if it's set, break and bail.
2424 bool checkBeforeAlways = !childrenInline() && (usePrintRect && child->style()->pageBreakBefore() == PBALWAYS);
2425 if (checkBeforeAlways
2426 && (ty + child->y()) > paintInfo.rect.y()
2427 && (ty + child->y()) < paintInfo.rect.maxY()) {
2428 view()->setBestTruncatedAt(ty + child->y(), this, true);
2429 return;
2430 }
2431
2432 if (!child->isFloating() && child->isReplaced() && usePrintRect && child->height() <= renderView->printRect().height()) {
2433 // Paginate block-level replaced elements.
2434 if (ty + child->y() + child->height() > renderView->printRect().maxY()) {
2435 if (ty + child->y() < renderView->truncatedAt())
2436 renderView->setBestTruncatedAt(ty + child->y(), child);
2437 // If we were able to truncate, don't paint.
2438 if (ty + child->y() >= renderView->truncatedAt())
2439 break;
2440 }
2441 }
2442
2443 IntPoint childPoint = flipForWritingMode(child, IntPoint(tx, ty), ParentToChildFlippingAdjustment);
2444 if (!child->hasSelfPaintingLayer() && !child->isFloating())
2445 child->paint(info, childPoint.x(), childPoint.y());
2446
2447 // Check for page-break-after: always, and if it's set, break and bail.
2448 bool checkAfterAlways = !childrenInline() && (usePrintRect && child->style()->pageBreakAfter() == PBALWAYS);
2449 if (checkAfterAlways
2450 && (ty + child->y() + child->height()) > paintInfo.rect.y()
2451 && (ty + child->y() + child->height()) < paintInfo.rect.maxY()) {
2452 view()->setBestTruncatedAt(ty + child->y() + child->height() + max(0, child->collapsedMarginAfter()), this, true);
2453 return;
2454 }
2455 }
2456 }
2457
paintCaret(PaintInfo & paintInfo,int tx,int ty,CaretType type)2458 void RenderBlock::paintCaret(PaintInfo& paintInfo, int tx, int ty, CaretType type)
2459 {
2460 SelectionController* selection = type == CursorCaret ? frame()->selection() : frame()->page()->dragCaretController();
2461
2462 // Paint the caret if the SelectionController says so or if caret browsing is enabled
2463 bool caretBrowsing = frame()->settings() && frame()->settings()->caretBrowsingEnabled();
2464 RenderObject* caretPainter = selection->caretRenderer();
2465 if (caretPainter == this && (selection->isContentEditable() || caretBrowsing)) {
2466 // Convert the painting offset into the local coordinate system of this renderer,
2467 // to match the localCaretRect computed by the SelectionController
2468 offsetForContents(tx, ty);
2469
2470 if (type == CursorCaret)
2471 frame()->selection()->paintCaret(paintInfo.context, tx, ty, paintInfo.rect);
2472 else
2473 frame()->selection()->paintDragCaret(paintInfo.context, tx, ty, paintInfo.rect);
2474 }
2475 }
2476
paintObject(PaintInfo & paintInfo,int tx,int ty)2477 void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty)
2478 {
2479 PaintPhase paintPhase = paintInfo.phase;
2480
2481 // 1. paint background, borders etc
2482 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) {
2483 if (hasBoxDecorations())
2484 paintBoxDecorations(paintInfo, tx, ty);
2485 if (hasColumns())
2486 paintColumnRules(paintInfo, tx, ty);
2487 }
2488
2489 if (paintPhase == PaintPhaseMask && style()->visibility() == VISIBLE) {
2490 paintMask(paintInfo, tx, ty);
2491 return;
2492 }
2493
2494 // We're done. We don't bother painting any children.
2495 if (paintPhase == PaintPhaseBlockBackground)
2496 return;
2497
2498 // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div).
2499 int scrolledX = tx;
2500 int scrolledY = ty;
2501 if (hasOverflowClip()) {
2502 IntSize offset = layer()->scrolledContentOffset();
2503 scrolledX -= offset.width();
2504 scrolledY -= offset.height();
2505 }
2506
2507 // 2. paint contents
2508 if (paintPhase != PaintPhaseSelfOutline) {
2509 if (hasColumns())
2510 paintColumnContents(paintInfo, scrolledX, scrolledY);
2511 else
2512 paintContents(paintInfo, scrolledX, scrolledY);
2513 }
2514
2515 // 3. paint selection
2516 // FIXME: Make this work with multi column layouts. For now don't fill gaps.
2517 bool isPrinting = document()->printing();
2518 if (!isPrinting && !hasColumns())
2519 paintSelection(paintInfo, scrolledX, scrolledY); // Fill in gaps in selection on lines and between blocks.
2520
2521 // 4. paint floats.
2522 if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) {
2523 if (hasColumns())
2524 paintColumnContents(paintInfo, scrolledX, scrolledY, true);
2525 else
2526 paintFloats(paintInfo, scrolledX, scrolledY, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip);
2527 }
2528
2529 // 5. paint outline.
2530 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE)
2531 paintOutline(paintInfo.context, tx, ty, width(), height());
2532
2533 // 6. paint continuation outlines.
2534 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) {
2535 RenderInline* inlineCont = inlineElementContinuation();
2536 if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE) {
2537 RenderInline* inlineRenderer = toRenderInline(inlineCont->node()->renderer());
2538 RenderBlock* cb = containingBlock();
2539
2540 bool inlineEnclosedInSelfPaintingLayer = false;
2541 for (RenderBoxModelObject* box = inlineRenderer; box != cb; box = box->parent()->enclosingBoxModelObject()) {
2542 if (box->hasSelfPaintingLayer()) {
2543 inlineEnclosedInSelfPaintingLayer = true;
2544 break;
2545 }
2546 }
2547
2548 if (!inlineEnclosedInSelfPaintingLayer)
2549 cb->addContinuationWithOutline(inlineRenderer);
2550 else if (!inlineRenderer->firstLineBox())
2551 inlineRenderer->paintOutline(paintInfo.context, tx - x() + inlineRenderer->containingBlock()->x(),
2552 ty - y() + inlineRenderer->containingBlock()->y());
2553 }
2554 paintContinuationOutlines(paintInfo, tx, ty);
2555 }
2556
2557 // 7. paint caret.
2558 // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground,
2559 // then paint the caret.
2560 if (paintPhase == PaintPhaseForeground) {
2561 paintCaret(paintInfo, scrolledX, scrolledY, CursorCaret);
2562 paintCaret(paintInfo, scrolledX, scrolledY, DragCaret);
2563 }
2564 }
2565
flipFloatForWritingMode(const FloatingObject * child,const IntPoint & point) const2566 IntPoint RenderBlock::flipFloatForWritingMode(const FloatingObject* child, const IntPoint& point) const
2567 {
2568 if (!style()->isFlippedBlocksWritingMode())
2569 return point;
2570
2571 // This is similar to the ParentToChildFlippingAdjustment in RenderBox::flipForWritingMode. We have to subtract out our left/top offsets twice, since
2572 // it's going to get added back in. We hide this complication here so that the calling code looks normal for the unflipped
2573 // case.
2574 if (isHorizontalWritingMode())
2575 return IntPoint(point.x(), point.y() + height() - child->renderer()->height() - 2 * yPositionForFloatIncludingMargin(child));
2576 return IntPoint(point.x() + width() - child->width() - 2 * xPositionForFloatIncludingMargin(child), point.y());
2577 }
2578
paintFloats(PaintInfo & paintInfo,int tx,int ty,bool preservePhase)2579 void RenderBlock::paintFloats(PaintInfo& paintInfo, int tx, int ty, bool preservePhase)
2580 {
2581 if (!m_floatingObjects)
2582 return;
2583
2584 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
2585 FloatingObjectSetIterator end = floatingObjectSet.end();
2586 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
2587 FloatingObject* r = *it;
2588 // Only paint the object if our m_shouldPaint flag is set.
2589 if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) {
2590 PaintInfo currentPaintInfo(paintInfo);
2591 currentPaintInfo.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground;
2592 IntPoint childPoint = flipFloatForWritingMode(r, IntPoint(tx + xPositionForFloatIncludingMargin(r) - r->m_renderer->x(), ty + yPositionForFloatIncludingMargin(r) - r->m_renderer->y()));
2593 r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y());
2594 if (!preservePhase) {
2595 currentPaintInfo.phase = PaintPhaseChildBlockBackgrounds;
2596 r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y());
2597 currentPaintInfo.phase = PaintPhaseFloat;
2598 r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y());
2599 currentPaintInfo.phase = PaintPhaseForeground;
2600 r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y());
2601 currentPaintInfo.phase = PaintPhaseOutline;
2602 r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y());
2603 }
2604 }
2605 }
2606 }
2607
paintEllipsisBoxes(PaintInfo & paintInfo,int tx,int ty)2608 void RenderBlock::paintEllipsisBoxes(PaintInfo& paintInfo, int tx, int ty)
2609 {
2610 if (!paintInfo.shouldPaintWithinRoot(this) || !firstLineBox())
2611 return;
2612
2613 if (style()->visibility() == VISIBLE && paintInfo.phase == PaintPhaseForeground) {
2614 // We can check the first box and last box and avoid painting if we don't
2615 // intersect.
2616 int yPos = ty + firstLineBox()->y();
2617 int h = lastLineBox()->y() + lastLineBox()->logicalHeight() - firstLineBox()->y();
2618 if (yPos >= paintInfo.rect.maxY() || yPos + h <= paintInfo.rect.y())
2619 return;
2620
2621 // See if our boxes intersect with the dirty rect. If so, then we paint
2622 // them. Note that boxes can easily overlap, so we can't make any assumptions
2623 // based off positions of our first line box or our last line box.
2624 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2625 yPos = ty + curr->y();
2626 h = curr->logicalHeight();
2627 if (curr->ellipsisBox() && yPos < paintInfo.rect.maxY() && yPos + h > paintInfo.rect.y())
2628 curr->paintEllipsisBox(paintInfo, tx, ty, curr->lineTop(), curr->lineBottom());
2629 }
2630 }
2631 }
2632
inlineElementContinuation() const2633 RenderInline* RenderBlock::inlineElementContinuation() const
2634 {
2635 RenderBoxModelObject* continuation = this->continuation();
2636 return continuation && continuation->isInline() ? toRenderInline(continuation) : 0;
2637 }
2638
blockElementContinuation() const2639 RenderBlock* RenderBlock::blockElementContinuation() const
2640 {
2641 RenderBoxModelObject* currentContinuation = continuation();
2642 if (!currentContinuation || currentContinuation->isInline())
2643 return 0;
2644 RenderBlock* nextContinuation = toRenderBlock(currentContinuation);
2645 if (nextContinuation->isAnonymousBlock())
2646 return nextContinuation->blockElementContinuation();
2647 return nextContinuation;
2648 }
2649
continuationOutlineTable()2650 static ContinuationOutlineTableMap* continuationOutlineTable()
2651 {
2652 DEFINE_STATIC_LOCAL(ContinuationOutlineTableMap, table, ());
2653 return &table;
2654 }
2655
addContinuationWithOutline(RenderInline * flow)2656 void RenderBlock::addContinuationWithOutline(RenderInline* flow)
2657 {
2658 // We can't make this work if the inline is in a layer. We'll just rely on the broken
2659 // way of painting.
2660 ASSERT(!flow->layer() && !flow->isInlineElementContinuation());
2661
2662 ContinuationOutlineTableMap* table = continuationOutlineTable();
2663 ListHashSet<RenderInline*>* continuations = table->get(this);
2664 if (!continuations) {
2665 continuations = new ListHashSet<RenderInline*>;
2666 table->set(this, continuations);
2667 }
2668
2669 continuations->add(flow);
2670 }
2671
paintsContinuationOutline(RenderInline * flow)2672 bool RenderBlock::paintsContinuationOutline(RenderInline* flow)
2673 {
2674 ContinuationOutlineTableMap* table = continuationOutlineTable();
2675 if (table->isEmpty())
2676 return false;
2677
2678 ListHashSet<RenderInline*>* continuations = table->get(this);
2679 if (!continuations)
2680 return false;
2681
2682 return continuations->contains(flow);
2683 }
2684
paintContinuationOutlines(PaintInfo & info,int tx,int ty)2685 void RenderBlock::paintContinuationOutlines(PaintInfo& info, int tx, int ty)
2686 {
2687 ContinuationOutlineTableMap* table = continuationOutlineTable();
2688 if (table->isEmpty())
2689 return;
2690
2691 ListHashSet<RenderInline*>* continuations = table->get(this);
2692 if (!continuations)
2693 return;
2694
2695 // Paint each continuation outline.
2696 ListHashSet<RenderInline*>::iterator end = continuations->end();
2697 for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) {
2698 // Need to add in the coordinates of the intervening blocks.
2699 RenderInline* flow = *it;
2700 RenderBlock* block = flow->containingBlock();
2701 for ( ; block && block != this; block = block->containingBlock()) {
2702 tx += block->x();
2703 ty += block->y();
2704 }
2705 ASSERT(block);
2706 flow->paintOutline(info.context, tx, ty);
2707 }
2708
2709 // Delete
2710 delete continuations;
2711 table->remove(this);
2712 }
2713
shouldPaintSelectionGaps() const2714 bool RenderBlock::shouldPaintSelectionGaps() const
2715 {
2716 return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot();
2717 }
2718
isSelectionRoot() const2719 bool RenderBlock::isSelectionRoot() const
2720 {
2721 if (!node())
2722 return false;
2723
2724 // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases.
2725 if (isTable())
2726 return false;
2727
2728 if (isBody() || isRoot() || hasOverflowClip() || isRelPositioned() ||
2729 isFloatingOrPositioned() || isTableCell() || isInlineBlockOrInlineTable() || hasTransform() ||
2730 hasReflection() || hasMask() || isWritingModeRoot())
2731 return true;
2732
2733 if (view() && view()->selectionStart()) {
2734 Node* startElement = view()->selectionStart()->node();
2735 if (startElement && startElement->rootEditableElement() == node())
2736 return true;
2737 }
2738
2739 return false;
2740 }
2741
selectionGapRectsForRepaint(RenderBoxModelObject * repaintContainer)2742 GapRects RenderBlock::selectionGapRectsForRepaint(RenderBoxModelObject* repaintContainer)
2743 {
2744 ASSERT(!needsLayout());
2745
2746 if (!shouldPaintSelectionGaps())
2747 return GapRects();
2748
2749 // FIXME: this is broken with transforms
2750 TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint());
2751 mapLocalToContainer(repaintContainer, false, false, transformState);
2752 IntPoint offsetFromRepaintContainer = roundedIntPoint(transformState.mappedPoint());
2753
2754 if (hasOverflowClip())
2755 offsetFromRepaintContainer -= layer()->scrolledContentOffset();
2756
2757 int lastTop = 0;
2758 int lastLeft = logicalLeftSelectionOffset(this, lastTop);
2759 int lastRight = logicalRightSelectionOffset(this, lastTop);
2760
2761 return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight);
2762 }
2763
paintSelection(PaintInfo & paintInfo,int tx,int ty)2764 void RenderBlock::paintSelection(PaintInfo& paintInfo, int tx, int ty)
2765 {
2766 if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) {
2767 int lastTop = 0;
2768 int lastLeft = logicalLeftSelectionOffset(this, lastTop);
2769 int lastRight = logicalRightSelectionOffset(this, lastTop);
2770 GraphicsContextStateSaver stateSaver(*paintInfo.context);
2771
2772 IntRect gapRectsBounds = selectionGaps(this, IntPoint(tx, ty), IntSize(), lastTop, lastLeft, lastRight, &paintInfo);
2773 if (!gapRectsBounds.isEmpty()) {
2774 if (RenderLayer* layer = enclosingLayer()) {
2775 gapRectsBounds.move(IntSize(-tx, -ty));
2776 if (!hasLayer()) {
2777 IntRect localBounds(gapRectsBounds);
2778 flipForWritingMode(localBounds);
2779 gapRectsBounds = localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox();
2780 gapRectsBounds.move(layer->scrolledContentOffset());
2781 }
2782 layer->addBlockSelectionGapsBounds(gapRectsBounds);
2783 }
2784 }
2785 }
2786 }
2787
clipOutPositionedObjects(const PaintInfo * paintInfo,const IntPoint & offset,RenderBlock::PositionedObjectsListHashSet * positionedObjects)2788 static void clipOutPositionedObjects(const PaintInfo* paintInfo, const IntPoint& offset, RenderBlock::PositionedObjectsListHashSet* positionedObjects)
2789 {
2790 if (!positionedObjects)
2791 return;
2792
2793 RenderBlock::PositionedObjectsListHashSet::const_iterator end = positionedObjects->end();
2794 for (RenderBlock::PositionedObjectsListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) {
2795 RenderBox* r = *it;
2796 paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height()));
2797 }
2798 }
2799
blockDirectionOffset(RenderBlock * rootBlock,const IntSize & offsetFromRootBlock)2800 static int blockDirectionOffset(RenderBlock* rootBlock, const IntSize& offsetFromRootBlock)
2801 {
2802 return rootBlock->isHorizontalWritingMode() ? offsetFromRootBlock.height() : offsetFromRootBlock.width();
2803 }
2804
inlineDirectionOffset(RenderBlock * rootBlock,const IntSize & offsetFromRootBlock)2805 static int inlineDirectionOffset(RenderBlock* rootBlock, const IntSize& offsetFromRootBlock)
2806 {
2807 return rootBlock->isHorizontalWritingMode() ? offsetFromRootBlock.width() : offsetFromRootBlock.height();
2808 }
2809
logicalRectToPhysicalRect(const IntPoint & rootBlockPhysicalPosition,const IntRect & logicalRect)2810 IntRect RenderBlock::logicalRectToPhysicalRect(const IntPoint& rootBlockPhysicalPosition, const IntRect& logicalRect)
2811 {
2812 IntRect result;
2813 if (isHorizontalWritingMode())
2814 result = logicalRect;
2815 else
2816 result = IntRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width());
2817 flipForWritingMode(result);
2818 result.move(rootBlockPhysicalPosition.x(), rootBlockPhysicalPosition.y());
2819 return result;
2820 }
2821
selectionGaps(RenderBlock * rootBlock,const IntPoint & rootBlockPhysicalPosition,const IntSize & offsetFromRootBlock,int & lastLogicalTop,int & lastLogicalLeft,int & lastLogicalRight,const PaintInfo * paintInfo)2822 GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock,
2823 int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo* paintInfo)
2824 {
2825 // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore.
2826 // Clip out floating and positioned objects when painting selection gaps.
2827 if (paintInfo) {
2828 // Note that we don't clip out overflow for positioned objects. We just stick to the border box.
2829 IntRect flippedBlockRect = IntRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height());
2830 rootBlock->flipForWritingMode(flippedBlockRect);
2831 flippedBlockRect.move(rootBlockPhysicalPosition.x(), rootBlockPhysicalPosition.y());
2832 clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), m_positionedObjects.get());
2833 if (isBody() || isRoot()) // The <body> must make sure to examine its containingBlock's positioned objects.
2834 for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock())
2835 clipOutPositionedObjects(paintInfo, IntPoint(cb->x(), cb->y()), cb->m_positionedObjects.get()); // FIXME: Not right for flipped writing modes.
2836 if (m_floatingObjects) {
2837 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
2838 FloatingObjectSetIterator end = floatingObjectSet.end();
2839 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
2840 FloatingObject* r = *it;
2841 IntRect floatBox = IntRect(offsetFromRootBlock.width() + xPositionForFloatIncludingMargin(r),
2842 offsetFromRootBlock.height() + yPositionForFloatIncludingMargin(r),
2843 r->m_renderer->width(), r->m_renderer->height());
2844 rootBlock->flipForWritingMode(floatBox);
2845 floatBox.move(rootBlockPhysicalPosition.x(), rootBlockPhysicalPosition.y());
2846 paintInfo->context->clipOut(floatBox);
2847 }
2848 }
2849 }
2850
2851 // FIXME: overflow: auto/scroll regions need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is
2852 // fixed).
2853 GapRects result;
2854 if (!isBlockFlow()) // FIXME: Make multi-column selection gap filling work someday.
2855 return result;
2856
2857 if (hasColumns() || hasTransform() || style()->columnSpan()) {
2858 // FIXME: We should learn how to gap fill multiple columns and transforms eventually.
2859 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight();
2860 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight());
2861 lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight());
2862 return result;
2863 }
2864
2865 if (childrenInline())
2866 result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
2867 else
2868 result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
2869
2870 // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block.
2871 if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd))
2872 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
2873 logicalHeight(), paintInfo));
2874 return result;
2875 }
2876
inlineSelectionGaps(RenderBlock * rootBlock,const IntPoint & rootBlockPhysicalPosition,const IntSize & offsetFromRootBlock,int & lastLogicalTop,int & lastLogicalLeft,int & lastLogicalRight,const PaintInfo * paintInfo)2877 GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock,
2878 int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo* paintInfo)
2879 {
2880 GapRects result;
2881
2882 bool containsStart = selectionState() == SelectionStart || selectionState() == SelectionBoth;
2883
2884 if (!firstLineBox()) {
2885 if (containsStart) {
2886 // Go ahead and update our lastLogicalTop to be the bottom of the block. <hr>s or empty blocks with height can trip this
2887 // case.
2888 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight();
2889 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight());
2890 lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight());
2891 }
2892 return result;
2893 }
2894
2895 RootInlineBox* lastSelectedLine = 0;
2896 RootInlineBox* curr;
2897 for (curr = firstRootBox(); curr && !curr->hasSelectedChildren(); curr = curr->nextRootBox()) { }
2898
2899 // Now paint the gaps for the lines.
2900 for (; curr && curr->hasSelectedChildren(); curr = curr->nextRootBox()) {
2901 int selTop = curr->selectionTop();
2902 int selHeight = curr->selectionHeight();
2903
2904 if (!containsStart && !lastSelectedLine &&
2905 selectionState() != SelectionStart && selectionState() != SelectionBoth)
2906 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
2907 selTop, paintInfo));
2908
2909 IntRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), selTop + selHeight);
2910 logicalRect.move(isHorizontalWritingMode() ? offsetFromRootBlock : IntSize(offsetFromRootBlock.height(), offsetFromRootBlock.width()));
2911 IntRect physicalRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect);
2912 if (!paintInfo || (isHorizontalWritingMode() && physicalRect.y() < paintInfo->rect.maxY() && physicalRect.maxY() > paintInfo->rect.y())
2913 || (!isHorizontalWritingMode() && physicalRect.x() < paintInfo->rect.maxX() && physicalRect.maxX() > paintInfo->rect.x()))
2914 result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, paintInfo));
2915
2916 lastSelectedLine = curr;
2917 }
2918
2919 if (containsStart && !lastSelectedLine)
2920 // VisibleSelection must start just after our last line.
2921 lastSelectedLine = lastRootBox();
2922
2923 if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) {
2924 // Go ahead and update our lastY to be the bottom of the last selected line.
2925 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + lastSelectedLine->selectionBottom();
2926 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom());
2927 lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom());
2928 }
2929 return result;
2930 }
2931
blockSelectionGaps(RenderBlock * rootBlock,const IntPoint & rootBlockPhysicalPosition,const IntSize & offsetFromRootBlock,int & lastLogicalTop,int & lastLogicalLeft,int & lastLogicalRight,const PaintInfo * paintInfo)2932 GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock,
2933 int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo* paintInfo)
2934 {
2935 GapRects result;
2936
2937 // Go ahead and jump right to the first block child that contains some selected objects.
2938 RenderBox* curr;
2939 for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { }
2940
2941 for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) {
2942 SelectionState childState = curr->selectionState();
2943 if (childState == SelectionBoth || childState == SelectionEnd)
2944 sawSelectionEnd = true;
2945
2946 if (curr->isFloatingOrPositioned())
2947 continue; // We must be a normal flow object in order to even be considered.
2948
2949 if (curr->isRelPositioned() && curr->hasLayer()) {
2950 // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element.
2951 // Just disregard it completely.
2952 IntSize relOffset = curr->layer()->relativePositionOffset();
2953 if (relOffset.width() || relOffset.height())
2954 continue;
2955 }
2956
2957 bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this.
2958 bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone);
2959 if (fillBlockGaps) {
2960 // We need to fill the vertical gap above this object.
2961 if (childState == SelectionEnd || childState == SelectionInside)
2962 // Fill the gap above the object.
2963 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
2964 curr->logicalTop(), paintInfo));
2965
2966 // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past*
2967 // our object. We know this if the selection did not end inside our object.
2968 if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd))
2969 childState = SelectionNone;
2970
2971 // Fill side gaps on this object based off its state.
2972 bool leftGap, rightGap;
2973 getSelectionGapInfo(childState, leftGap, rightGap);
2974
2975 if (leftGap)
2976 result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
2977 if (rightGap)
2978 result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
2979
2980 // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as
2981 // they can without bumping into floating or positioned objects. Ideally they will go right up
2982 // to the border of the root selection block.
2983 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + curr->logicalBottom();
2984 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom());
2985 lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom());
2986 } else if (childState != SelectionNone)
2987 // We must be a block that has some selected object inside it. Go ahead and recur.
2988 result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, IntSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()),
2989 lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo));
2990 }
2991 return result;
2992 }
2993
blockSelectionGap(RenderBlock * rootBlock,const IntPoint & rootBlockPhysicalPosition,const IntSize & offsetFromRootBlock,int lastLogicalTop,int lastLogicalLeft,int lastLogicalRight,int logicalBottom,const PaintInfo * paintInfo)2994 IntRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock,
2995 int lastLogicalTop, int lastLogicalLeft, int lastLogicalRight, int logicalBottom, const PaintInfo* paintInfo)
2996 {
2997 int logicalTop = lastLogicalTop;
2998 int logicalHeight = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalBottom - logicalTop;
2999 if (logicalHeight <= 0)
3000 return IntRect();
3001
3002 // Get the selection offsets for the bottom of the gap
3003 int logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom));
3004 int logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom));
3005 int logicalWidth = logicalRight - logicalLeft;
3006 if (logicalWidth <= 0)
3007 return IntRect();
3008
3009 IntRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, IntRect(logicalLeft, logicalTop, logicalWidth, logicalHeight));
3010 if (paintInfo)
3011 paintInfo->context->fillRect(gapRect, selectionBackgroundColor(), style()->colorSpace());
3012 return gapRect;
3013 }
3014
logicalLeftSelectionGap(RenderBlock * rootBlock,const IntPoint & rootBlockPhysicalPosition,const IntSize & offsetFromRootBlock,RenderObject * selObj,int logicalLeft,int logicalTop,int logicalHeight,const PaintInfo * paintInfo)3015 IntRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock,
3016 RenderObject* selObj, int logicalLeft, int logicalTop, int logicalHeight, const PaintInfo* paintInfo)
3017 {
3018 int rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop;
3019 int rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight));
3020 int rootBlockLogicalRight = min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalLeft, min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)));
3021 int rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
3022 if (rootBlockLogicalWidth <= 0)
3023 return IntRect();
3024
3025 IntRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, IntRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
3026 if (paintInfo)
3027 paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor(), selObj->style()->colorSpace());
3028 return gapRect;
3029 }
3030
logicalRightSelectionGap(RenderBlock * rootBlock,const IntPoint & rootBlockPhysicalPosition,const IntSize & offsetFromRootBlock,RenderObject * selObj,int logicalRight,int logicalTop,int logicalHeight,const PaintInfo * paintInfo)3031 IntRect RenderBlock::logicalRightSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock,
3032 RenderObject* selObj, int logicalRight, int logicalTop, int logicalHeight, const PaintInfo* paintInfo)
3033 {
3034 int rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop;
3035 int rootBlockLogicalLeft = max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalRight, max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)));
3036 int rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight));
3037 int rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
3038 if (rootBlockLogicalWidth <= 0)
3039 return IntRect();
3040
3041 IntRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, IntRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
3042 if (paintInfo)
3043 paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor(), selObj->style()->colorSpace());
3044 return gapRect;
3045 }
3046
getSelectionGapInfo(SelectionState state,bool & leftGap,bool & rightGap)3047 void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap)
3048 {
3049 bool ltr = style()->isLeftToRightDirection();
3050 leftGap = (state == RenderObject::SelectionInside) ||
3051 (state == RenderObject::SelectionEnd && ltr) ||
3052 (state == RenderObject::SelectionStart && !ltr);
3053 rightGap = (state == RenderObject::SelectionInside) ||
3054 (state == RenderObject::SelectionStart && ltr) ||
3055 (state == RenderObject::SelectionEnd && !ltr);
3056 }
3057
logicalLeftSelectionOffset(RenderBlock * rootBlock,int position)3058 int RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, int position)
3059 {
3060 int logicalLeft = logicalLeftOffsetForLine(position, false);
3061 if (logicalLeft == logicalLeftOffsetForContent()) {
3062 if (rootBlock != this)
3063 // The border can potentially be further extended by our containingBlock().
3064 return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop());
3065 return logicalLeft;
3066 } else {
3067 RenderBlock* cb = this;
3068 while (cb != rootBlock) {
3069 logicalLeft += cb->logicalLeft();
3070 cb = cb->containingBlock();
3071 }
3072 }
3073 return logicalLeft;
3074 }
3075
logicalRightSelectionOffset(RenderBlock * rootBlock,int position)3076 int RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, int position)
3077 {
3078 int logicalRight = logicalRightOffsetForLine(position, false);
3079 if (logicalRight == logicalRightOffsetForContent()) {
3080 if (rootBlock != this)
3081 // The border can potentially be further extended by our containingBlock().
3082 return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop());
3083 return logicalRight;
3084 } else {
3085 RenderBlock* cb = this;
3086 while (cb != rootBlock) {
3087 logicalRight += cb->logicalLeft();
3088 cb = cb->containingBlock();
3089 }
3090 }
3091 return logicalRight;
3092 }
3093
insertPositionedObject(RenderBox * o)3094 void RenderBlock::insertPositionedObject(RenderBox* o)
3095 {
3096 // Create the list of special objects if we don't aleady have one
3097 if (!m_positionedObjects)
3098 m_positionedObjects = adoptPtr(new PositionedObjectsListHashSet);
3099
3100 m_positionedObjects->add(o);
3101 }
3102
removePositionedObject(RenderBox * o)3103 void RenderBlock::removePositionedObject(RenderBox* o)
3104 {
3105 if (m_positionedObjects)
3106 m_positionedObjects->remove(o);
3107 }
3108
removePositionedObjects(RenderBlock * o)3109 void RenderBlock::removePositionedObjects(RenderBlock* o)
3110 {
3111 if (!m_positionedObjects)
3112 return;
3113
3114 RenderBox* r;
3115
3116 Iterator end = m_positionedObjects->end();
3117
3118 Vector<RenderBox*, 16> deadObjects;
3119
3120 for (Iterator it = m_positionedObjects->begin(); it != end; ++it) {
3121 r = *it;
3122 if (!o || r->isDescendantOf(o)) {
3123 if (o)
3124 r->setChildNeedsLayout(true, false);
3125
3126 // It is parent blocks job to add positioned child to positioned objects list of its containing block
3127 // Parent layout needs to be invalidated to ensure this happens.
3128 RenderObject* p = r->parent();
3129 while (p && !p->isRenderBlock())
3130 p = p->parent();
3131 if (p)
3132 p->setChildNeedsLayout(true);
3133
3134 deadObjects.append(r);
3135 }
3136 }
3137
3138 for (unsigned i = 0; i < deadObjects.size(); i++)
3139 m_positionedObjects->remove(deadObjects.at(i));
3140 }
3141
insertFloatingObject(RenderBox * o)3142 RenderBlock::FloatingObject* RenderBlock::insertFloatingObject(RenderBox* o)
3143 {
3144 ASSERT(o->isFloating());
3145
3146 // Create the list of special objects if we don't aleady have one
3147 if (!m_floatingObjects)
3148 m_floatingObjects = adoptPtr(new FloatingObjects);
3149 else {
3150 // Don't insert the object again if it's already in the list
3151 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3152 FloatingObjectSetIterator it = floatingObjectSet.find<RenderBox*, FloatingObjectHashTranslator>(o);
3153 if (it != floatingObjectSet.end())
3154 return *it;
3155 }
3156
3157 // Create the special object entry & append it to the list
3158
3159 FloatingObject* newObj = new FloatingObject(o->style()->floating() == FLEFT ? FloatingObject::FloatLeft : FloatingObject::FloatRight);
3160
3161 // Our location is irrelevant if we're unsplittable or no pagination is in effect.
3162 // Just go ahead and lay out the float.
3163 bool isChildRenderBlock = o->isRenderBlock();
3164 if (isChildRenderBlock && !o->needsLayout() && view()->layoutState()->pageLogicalHeightChanged())
3165 o->setChildNeedsLayout(true, false);
3166
3167 bool affectedByPagination = isChildRenderBlock && view()->layoutState()->m_pageLogicalHeight;
3168 if (!affectedByPagination || isWritingModeRoot()) // We are unsplittable if we're a block flow root.
3169 o->layoutIfNeeded();
3170 else {
3171 o->computeLogicalWidth();
3172 o->computeBlockDirectionMargins(this);
3173 }
3174 setLogicalWidthForFloat(newObj, logicalWidthForChild(o) + marginStartForChild(o) + marginEndForChild(o));
3175
3176 newObj->m_shouldPaint = !o->hasSelfPaintingLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will.
3177 newObj->m_isDescendant = true;
3178 newObj->m_renderer = o;
3179
3180 m_floatingObjects->increaseObjectsCount(newObj->type());
3181 m_floatingObjects->set().add(newObj);
3182
3183 return newObj;
3184 }
3185
removeFloatingObject(RenderBox * o)3186 void RenderBlock::removeFloatingObject(RenderBox* o)
3187 {
3188 if (m_floatingObjects) {
3189 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3190 FloatingObjectSet::iterator it = floatingObjectSet.find<RenderBox*, FloatingObjectHashTranslator>(o);
3191 if (it != floatingObjectSet.end()) {
3192 FloatingObject* r = *it;
3193 if (childrenInline()) {
3194 int logicalTop = logicalTopForFloat(r);
3195 int logicalBottom = logicalBottomForFloat(r);
3196
3197 // Fix for https://bugs.webkit.org/show_bug.cgi?id=54995.
3198 if (logicalBottom < 0 || logicalBottom < logicalTop || logicalTop == numeric_limits<int>::max())
3199 logicalBottom = numeric_limits<int>::max();
3200 else {
3201 // Special-case zero- and less-than-zero-height floats: those don't touch
3202 // the line that they're on, but it still needs to be dirtied. This is
3203 // accomplished by pretending they have a height of 1.
3204 logicalBottom = max(logicalBottom, logicalTop + 1);
3205 }
3206 if (r->m_originatingLine) {
3207 if (!selfNeedsLayout()) {
3208 ASSERT(r->m_originatingLine->renderer() == this);
3209 r->m_originatingLine->markDirty();
3210 }
3211 #if !ASSERT_DISABLED
3212 r->m_originatingLine = 0;
3213 #endif
3214 }
3215 markLinesDirtyInBlockRange(0, logicalBottom);
3216 }
3217 m_floatingObjects->decreaseObjectsCount(r->type());
3218 floatingObjectSet.remove(it);
3219 ASSERT(!r->m_originatingLine);
3220 delete r;
3221 }
3222 }
3223 }
3224
removeFloatingObjectsBelow(FloatingObject * lastFloat,int logicalOffset)3225 void RenderBlock::removeFloatingObjectsBelow(FloatingObject* lastFloat, int logicalOffset)
3226 {
3227 if (!m_floatingObjects)
3228 return;
3229
3230 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3231 FloatingObject* curr = floatingObjectSet.last();
3232 while (curr != lastFloat && (!curr->isPlaced() || logicalTopForFloat(curr) >= logicalOffset)) {
3233 m_floatingObjects->decreaseObjectsCount(curr->type());
3234 floatingObjectSet.removeLast();
3235 ASSERT(!curr->m_originatingLine);
3236 delete curr;
3237 if (floatingObjectSet.isEmpty())
3238 break;
3239 curr = floatingObjectSet.last();
3240 }
3241 }
3242
positionNewFloats()3243 bool RenderBlock::positionNewFloats()
3244 {
3245 if (!m_floatingObjects)
3246 return false;
3247
3248 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3249 if (floatingObjectSet.isEmpty())
3250 return false;
3251
3252 // If all floats have already been positioned, then we have no work to do.
3253 if (floatingObjectSet.last()->isPlaced())
3254 return false;
3255
3256 // Move backwards through our floating object list until we find a float that has
3257 // already been positioned. Then we'll be able to move forward, positioning all of
3258 // the new floats that need it.
3259 FloatingObjectSetIterator it = floatingObjectSet.end();
3260 --it; // Go to last item.
3261 FloatingObjectSetIterator begin = floatingObjectSet.begin();
3262 FloatingObject* lastPlacedFloatingObject = 0;
3263 while (it != begin) {
3264 --it;
3265 if ((*it)->isPlaced()) {
3266 lastPlacedFloatingObject = *it;
3267 ++it;
3268 break;
3269 }
3270 }
3271
3272 int logicalTop = logicalHeight();
3273
3274 // The float cannot start above the top position of the last positioned float.
3275 if (lastPlacedFloatingObject)
3276 logicalTop = max(logicalTopForFloat(lastPlacedFloatingObject), logicalTop);
3277
3278 FloatingObjectSetIterator end = floatingObjectSet.end();
3279 // Now walk through the set of unpositioned floats and place them.
3280 for (; it != end; ++it) {
3281 FloatingObject* floatingObject = *it;
3282 // The containing block is responsible for positioning floats, so if we have floats in our
3283 // list that come from somewhere else, do not attempt to position them.
3284 if (floatingObject->renderer()->containingBlock() != this)
3285 continue;
3286
3287 RenderBox* childBox = floatingObject->renderer();
3288 int childLogicalLeftMargin = style()->isLeftToRightDirection() ? marginStartForChild(childBox) : marginEndForChild(childBox);
3289
3290 int rightOffset = logicalRightOffsetForContent(); // Constant part of right offset.
3291 int leftOffset = logicalLeftOffsetForContent(); // Constant part of left offset.
3292 int floatLogicalWidth = logicalWidthForFloat(floatingObject); // The width we look for.
3293 if (rightOffset - leftOffset < floatLogicalWidth)
3294 floatLogicalWidth = rightOffset - leftOffset; // Never look for more than what will be available.
3295
3296 IntRect oldRect(childBox->x(), childBox->y() , childBox->width(), childBox->height());
3297
3298 if (childBox->style()->clear() & CLEFT)
3299 logicalTop = max(lowestFloatLogicalBottom(FloatingObject::FloatLeft), logicalTop);
3300 if (childBox->style()->clear() & CRIGHT)
3301 logicalTop = max(lowestFloatLogicalBottom(FloatingObject::FloatRight), logicalTop);
3302
3303 int floatLogicalLeft;
3304 if (childBox->style()->floating() == FLEFT) {
3305 int heightRemainingLeft = 1;
3306 int heightRemainingRight = 1;
3307 floatLogicalLeft = logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft);
3308 while (logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight) - floatLogicalLeft < floatLogicalWidth) {
3309 logicalTop += min(heightRemainingLeft, heightRemainingRight);
3310 floatLogicalLeft = logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft);
3311 }
3312 floatLogicalLeft = max(0, floatLogicalLeft);
3313 } else {
3314 int heightRemainingLeft = 1;
3315 int heightRemainingRight = 1;
3316 floatLogicalLeft = logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight);
3317 while (floatLogicalLeft - logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft) < floatLogicalWidth) {
3318 logicalTop += min(heightRemainingLeft, heightRemainingRight);
3319 floatLogicalLeft = logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight);
3320 }
3321 floatLogicalLeft -= logicalWidthForFloat(floatingObject); // Use the original width of the float here, since the local variable
3322 // |floatLogicalWidth| was capped to the available line width.
3323 // See fast/block/float/clamped-right-float.html.
3324 }
3325
3326 setLogicalLeftForFloat(floatingObject, floatLogicalLeft);
3327 setLogicalLeftForChild(childBox, floatLogicalLeft + childLogicalLeftMargin);
3328 setLogicalTopForChild(childBox, logicalTop + marginBeforeForChild(childBox));
3329
3330 if (view()->layoutState()->isPaginated()) {
3331 RenderBlock* childBlock = childBox->isRenderBlock() ? toRenderBlock(childBox) : 0;
3332
3333 if (!childBox->needsLayout())
3334 childBox->markForPaginationRelayoutIfNeeded();;
3335 childBox->layoutIfNeeded();
3336
3337 // If we are unsplittable and don't fit, then we need to move down.
3338 // We include our margins as part of the unsplittable area.
3339 int newLogicalTop = adjustForUnsplittableChild(childBox, logicalTop, true);
3340
3341 // See if we have a pagination strut that is making us move down further.
3342 // Note that an unsplittable child can't also have a pagination strut, so this is
3343 // exclusive with the case above.
3344 if (childBlock && childBlock->paginationStrut()) {
3345 newLogicalTop += childBlock->paginationStrut();
3346 childBlock->setPaginationStrut(0);
3347 }
3348
3349 if (newLogicalTop != logicalTop) {
3350 floatingObject->m_paginationStrut = newLogicalTop - logicalTop;
3351 logicalTop = newLogicalTop;
3352 setLogicalTopForChild(childBox, logicalTop + marginBeforeForChild(childBox));
3353 if (childBlock)
3354 childBlock->setChildNeedsLayout(true, false);
3355 childBox->layoutIfNeeded();
3356 }
3357 }
3358
3359 setLogicalTopForFloat(floatingObject, logicalTop);
3360 setLogicalHeightForFloat(floatingObject, logicalHeightForChild(childBox) + marginBeforeForChild(childBox) + marginAfterForChild(childBox));
3361
3362 floatingObject->setIsPlaced();
3363
3364 // If the child moved, we have to repaint it.
3365 if (childBox->checkForRepaintDuringLayout())
3366 childBox->repaintDuringLayoutIfMoved(oldRect);
3367 }
3368 return true;
3369 }
3370
newLine(EClear clear)3371 void RenderBlock::newLine(EClear clear)
3372 {
3373 positionNewFloats();
3374 // set y position
3375 int newY = 0;
3376 switch (clear)
3377 {
3378 case CLEFT:
3379 newY = lowestFloatLogicalBottom(FloatingObject::FloatLeft);
3380 break;
3381 case CRIGHT:
3382 newY = lowestFloatLogicalBottom(FloatingObject::FloatRight);
3383 break;
3384 case CBOTH:
3385 newY = lowestFloatLogicalBottom();
3386 default:
3387 break;
3388 }
3389 if (height() < newY)
3390 setLogicalHeight(newY);
3391 }
3392
addPercentHeightDescendant(RenderBox * descendant)3393 void RenderBlock::addPercentHeightDescendant(RenderBox* descendant)
3394 {
3395 if (!gPercentHeightDescendantsMap) {
3396 gPercentHeightDescendantsMap = new PercentHeightDescendantsMap;
3397 gPercentHeightContainerMap = new PercentHeightContainerMap;
3398 }
3399
3400 HashSet<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->get(this);
3401 if (!descendantSet) {
3402 descendantSet = new HashSet<RenderBox*>;
3403 gPercentHeightDescendantsMap->set(this, descendantSet);
3404 }
3405 bool added = descendantSet->add(descendant).second;
3406 if (!added) {
3407 ASSERT(gPercentHeightContainerMap->get(descendant));
3408 ASSERT(gPercentHeightContainerMap->get(descendant)->contains(this));
3409 return;
3410 }
3411
3412 HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->get(descendant);
3413 if (!containerSet) {
3414 containerSet = new HashSet<RenderBlock*>;
3415 gPercentHeightContainerMap->set(descendant, containerSet);
3416 }
3417 ASSERT(!containerSet->contains(this));
3418 containerSet->add(this);
3419 }
3420
removePercentHeightDescendant(RenderBox * descendant)3421 void RenderBlock::removePercentHeightDescendant(RenderBox* descendant)
3422 {
3423 if (!gPercentHeightContainerMap)
3424 return;
3425
3426 HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->take(descendant);
3427 if (!containerSet)
3428 return;
3429
3430 HashSet<RenderBlock*>::iterator end = containerSet->end();
3431 for (HashSet<RenderBlock*>::iterator it = containerSet->begin(); it != end; ++it) {
3432 RenderBlock* container = *it;
3433 HashSet<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->get(container);
3434 ASSERT(descendantSet);
3435 if (!descendantSet)
3436 continue;
3437 ASSERT(descendantSet->contains(descendant));
3438 descendantSet->remove(descendant);
3439 if (descendantSet->isEmpty()) {
3440 gPercentHeightDescendantsMap->remove(container);
3441 delete descendantSet;
3442 }
3443 }
3444
3445 delete containerSet;
3446 }
3447
percentHeightDescendants() const3448 HashSet<RenderBox*>* RenderBlock::percentHeightDescendants() const
3449 {
3450 return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0;
3451 }
3452
3453 // FIXME: The logicalLeftOffsetForLine/logicalRightOffsetForLine functions are very slow if there are many floats
3454 // present. We need to add a structure to floating objects to represent "lines" of floats. Then instead of checking
3455 // each float individually, we'd just walk backwards through the "lines" and stop when we hit a line that is fully above
3456 // the vertical offset that we'd like to check. Computing the "lines" would be rather complicated, but could replace the left
3457 // objects and right objects count hack that is currently used here.
logicalLeftOffsetForLine(int logicalTop,int fixedOffset,bool applyTextIndent,int * heightRemaining) const3458 int RenderBlock::logicalLeftOffsetForLine(int logicalTop, int fixedOffset, bool applyTextIndent, int* heightRemaining) const
3459 {
3460 int left = fixedOffset;
3461 if (m_floatingObjects && m_floatingObjects->hasLeftObjects()) {
3462 if (heightRemaining)
3463 *heightRemaining = 1;
3464
3465 // We know the list is non-empty, since we have "left" objects to search for.
3466 // Therefore we can assume that begin != end, and that we can do at least one
3467 // decrement.
3468 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3469 FloatingObjectSetIterator begin = floatingObjectSet.begin();
3470 FloatingObjectSetIterator it = floatingObjectSet.end();
3471 do {
3472 --it;
3473 FloatingObject* r = *it;
3474 if (r->isPlaced() && logicalTopForFloat(r) <= logicalTop && logicalBottomForFloat(r) > logicalTop
3475 && r->type() == FloatingObject::FloatLeft
3476 && logicalRightForFloat(r) > left) {
3477 left = max(left, logicalRightForFloat(r));
3478 if (heightRemaining)
3479 *heightRemaining = logicalBottomForFloat(r) - logicalTop;
3480 }
3481 } while (it != begin);
3482 }
3483
3484 if (applyTextIndent && style()->isLeftToRightDirection()) {
3485 int cw = 0;
3486 if (style()->textIndent().isPercent())
3487 cw = containingBlock()->availableLogicalWidth();
3488 left += style()->textIndent().calcMinValue(cw);
3489 }
3490
3491 return left;
3492 }
3493
logicalRightOffsetForLine(int logicalTop,int fixedOffset,bool applyTextIndent,int * heightRemaining) const3494 int RenderBlock::logicalRightOffsetForLine(int logicalTop, int fixedOffset, bool applyTextIndent, int* heightRemaining) const
3495 {
3496 int right = fixedOffset;
3497
3498 if (m_floatingObjects && m_floatingObjects->hasRightObjects()) {
3499 if (heightRemaining)
3500 *heightRemaining = 1;
3501
3502 // We know the list is non-empty, since we have "right" objects to search for.
3503 // Therefore we can assume that begin != end, and that we can do at least one
3504 // decrement.
3505 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3506 FloatingObjectSetIterator begin = floatingObjectSet.begin();
3507 FloatingObjectSetIterator it = floatingObjectSet.end();
3508 do {
3509 --it;
3510 FloatingObject* r = *it;
3511 if (r->isPlaced() && logicalTopForFloat(r) <= logicalTop && logicalBottomForFloat(r) > logicalTop
3512 && r->type() == FloatingObject::FloatRight
3513 && logicalLeftForFloat(r) < right) {
3514 right = min(right, logicalLeftForFloat(r));
3515 if (heightRemaining)
3516 *heightRemaining = logicalBottomForFloat(r) - logicalTop;
3517 }
3518 } while (it != begin);
3519 }
3520
3521 if (applyTextIndent && !style()->isLeftToRightDirection()) {
3522 int cw = 0;
3523 if (style()->textIndent().isPercent())
3524 cw = containingBlock()->availableLogicalWidth();
3525 right -= style()->textIndent().calcMinValue(cw);
3526 }
3527
3528 return right;
3529 }
3530
availableLogicalWidthForLine(int position,bool firstLine) const3531 int RenderBlock::availableLogicalWidthForLine(int position, bool firstLine) const
3532 {
3533 int result = logicalRightOffsetForLine(position, firstLine) - logicalLeftOffsetForLine(position, firstLine);
3534 return (result < 0) ? 0 : result;
3535 }
3536
nextFloatLogicalBottomBelow(int logicalHeight) const3537 int RenderBlock::nextFloatLogicalBottomBelow(int logicalHeight) const
3538 {
3539 if (!m_floatingObjects)
3540 return 0;
3541
3542 int bottom = INT_MAX;
3543 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3544 FloatingObjectSetIterator end = floatingObjectSet.end();
3545 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
3546 FloatingObject* r = *it;
3547 int floatBottom = logicalBottomForFloat(r);
3548 if (floatBottom > logicalHeight)
3549 bottom = min(floatBottom, bottom);
3550 }
3551
3552 return bottom == INT_MAX ? 0 : bottom;
3553 }
3554
lowestFloatLogicalBottom(FloatingObject::Type floatType) const3555 int RenderBlock::lowestFloatLogicalBottom(FloatingObject::Type floatType) const
3556 {
3557 if (!m_floatingObjects)
3558 return 0;
3559 int lowestFloatBottom = 0;
3560 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3561 FloatingObjectSetIterator end = floatingObjectSet.end();
3562 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
3563 FloatingObject* r = *it;
3564 if (r->isPlaced() && r->type() & floatType)
3565 lowestFloatBottom = max(lowestFloatBottom, logicalBottomForFloat(r));
3566 }
3567 return lowestFloatBottom;
3568 }
3569
markLinesDirtyInBlockRange(int logicalTop,int logicalBottom,RootInlineBox * highest)3570 void RenderBlock::markLinesDirtyInBlockRange(int logicalTop, int logicalBottom, RootInlineBox* highest)
3571 {
3572 if (logicalTop >= logicalBottom)
3573 return;
3574
3575 RootInlineBox* lowestDirtyLine = lastRootBox();
3576 RootInlineBox* afterLowest = lowestDirtyLine;
3577 while (lowestDirtyLine && lowestDirtyLine->blockLogicalHeight() >= logicalBottom && logicalBottom < numeric_limits<int>::max()) {
3578 afterLowest = lowestDirtyLine;
3579 lowestDirtyLine = lowestDirtyLine->prevRootBox();
3580 }
3581
3582 while (afterLowest && afterLowest != highest && (afterLowest->blockLogicalHeight() >= logicalTop || afterLowest->blockLogicalHeight() < 0)) {
3583 afterLowest->markDirty();
3584 afterLowest = afterLowest->prevRootBox();
3585 }
3586 }
3587
clearFloats()3588 void RenderBlock::clearFloats()
3589 {
3590 // Inline blocks are covered by the isReplaced() check in the avoidFloats method.
3591 if (avoidsFloats() || isRoot() || isRenderView() || isFloatingOrPositioned() || isTableCell()) {
3592 if (m_floatingObjects) {
3593 deleteAllValues(m_floatingObjects->set());
3594 m_floatingObjects->clear();
3595 }
3596 return;
3597 }
3598
3599 typedef HashMap<RenderObject*, FloatingObject*> RendererToFloatInfoMap;
3600 RendererToFloatInfoMap floatMap;
3601
3602 if (m_floatingObjects) {
3603 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3604 if (childrenInline()) {
3605 FloatingObjectSet::iterator end = floatingObjectSet.end();
3606 for (FloatingObjectSet::iterator it = floatingObjectSet.begin(); it != end; ++it) {
3607 FloatingObject* f = *it;
3608 floatMap.add(f->m_renderer, f);
3609 }
3610 } else
3611 deleteAllValues(floatingObjectSet);
3612 m_floatingObjects->clear();
3613 }
3614
3615 // We should not process floats if the parent node is not a RenderBlock. Otherwise, we will add
3616 // floats in an invalid context. This will cause a crash arising from a bad cast on the parent.
3617 // See <rdar://problem/8049753>, where float property is applied on a text node in a SVG.
3618 if (!parent() || !parent()->isRenderBlock())
3619 return;
3620
3621 // Attempt to locate a previous sibling with overhanging floats. We skip any elements that are
3622 // out of flow (like floating/positioned elements), and we also skip over any objects that may have shifted
3623 // to avoid floats.
3624 bool parentHasFloats = false;
3625 RenderBlock* parentBlock = toRenderBlock(parent());
3626 RenderObject* prev = previousSibling();
3627 while (prev && (prev->isFloatingOrPositioned() || !prev->isBox() || !prev->isRenderBlock() || toRenderBlock(prev)->avoidsFloats())) {
3628 if (prev->isFloating())
3629 parentHasFloats = true;
3630 prev = prev->previousSibling();
3631 }
3632
3633 // First add in floats from the parent.
3634 int logicalTopOffset = logicalTop();
3635 if (parentHasFloats)
3636 addIntrudingFloats(parentBlock, parentBlock->logicalLeftOffsetForContent(), logicalTopOffset);
3637
3638 int logicalLeftOffset = 0;
3639 if (prev)
3640 logicalTopOffset -= toRenderBox(prev)->logicalTop();
3641 else {
3642 prev = parentBlock;
3643 logicalLeftOffset += parentBlock->logicalLeftOffsetForContent();
3644 }
3645
3646 // Add overhanging floats from the previous RenderBlock, but only if it has a float that intrudes into our space.
3647 if (!prev || !prev->isRenderBlock())
3648 return;
3649
3650 RenderBlock* block = toRenderBlock(prev);
3651 if (block->m_floatingObjects && block->lowestFloatLogicalBottom() > logicalTopOffset)
3652 addIntrudingFloats(block, logicalLeftOffset, logicalTopOffset);
3653
3654 if (childrenInline()) {
3655 int changeLogicalTop = numeric_limits<int>::max();
3656 int changeLogicalBottom = numeric_limits<int>::min();
3657 if (m_floatingObjects) {
3658 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3659 FloatingObjectSetIterator end = floatingObjectSet.end();
3660 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
3661 FloatingObject* f = *it;
3662 FloatingObject* oldFloatingObject = floatMap.get(f->m_renderer);
3663 int logicalBottom = logicalBottomForFloat(f);
3664 if (oldFloatingObject) {
3665 int oldLogicalBottom = logicalBottomForFloat(oldFloatingObject);
3666 if (logicalWidthForFloat(f) != logicalWidthForFloat(oldFloatingObject) || logicalLeftForFloat(f) != logicalLeftForFloat(oldFloatingObject)) {
3667 changeLogicalTop = 0;
3668 changeLogicalBottom = max(changeLogicalBottom, max(logicalBottom, oldLogicalBottom));
3669 } else if (logicalBottom != oldLogicalBottom) {
3670 changeLogicalTop = min(changeLogicalTop, min(logicalBottom, oldLogicalBottom));
3671 changeLogicalBottom = max(changeLogicalBottom, max(logicalBottom, oldLogicalBottom));
3672 }
3673
3674 floatMap.remove(f->m_renderer);
3675 if (oldFloatingObject->m_originatingLine && !selfNeedsLayout()) {
3676 ASSERT(oldFloatingObject->m_originatingLine->renderer() == this);
3677 oldFloatingObject->m_originatingLine->markDirty();
3678 }
3679 delete oldFloatingObject;
3680 } else {
3681 changeLogicalTop = 0;
3682 changeLogicalBottom = max(changeLogicalBottom, logicalBottom);
3683 }
3684 }
3685 }
3686
3687 RendererToFloatInfoMap::iterator end = floatMap.end();
3688 for (RendererToFloatInfoMap::iterator it = floatMap.begin(); it != end; ++it) {
3689 FloatingObject* floatingObject = (*it).second;
3690 if (!floatingObject->m_isDescendant) {
3691 changeLogicalTop = 0;
3692 changeLogicalBottom = max(changeLogicalBottom, logicalBottomForFloat(floatingObject));
3693 }
3694 }
3695 deleteAllValues(floatMap);
3696
3697 markLinesDirtyInBlockRange(changeLogicalTop, changeLogicalBottom);
3698 }
3699 }
3700
addOverhangingFloats(RenderBlock * child,int logicalLeftOffset,int logicalTopOffset,bool makeChildPaintOtherFloats)3701 int RenderBlock::addOverhangingFloats(RenderBlock* child, int logicalLeftOffset, int logicalTopOffset, bool makeChildPaintOtherFloats)
3702 {
3703 // Prevent floats from being added to the canvas by the root element, e.g., <html>.
3704 if (child->hasOverflowClip() || !child->containsFloats() || child->isRoot() || child->hasColumns() || child->isWritingModeRoot())
3705 return 0;
3706
3707 int childLogicalTop = child->logicalTop();
3708 int lowestFloatLogicalBottom = 0;
3709
3710 // Floats that will remain the child's responsibility to paint should factor into its
3711 // overflow.
3712 FloatingObjectSetIterator childEnd = child->m_floatingObjects->set().end();
3713 for (FloatingObjectSetIterator childIt = child->m_floatingObjects->set().begin(); childIt != childEnd; ++childIt) {
3714 FloatingObject* r = *childIt;
3715 int logicalBottomForFloat = min(this->logicalBottomForFloat(r), numeric_limits<int>::max() - childLogicalTop);
3716 int logicalBottom = childLogicalTop + logicalBottomForFloat;
3717 lowestFloatLogicalBottom = max(lowestFloatLogicalBottom, logicalBottom);
3718
3719 if (logicalBottom > logicalHeight()) {
3720 // If the object is not in the list, we add it now.
3721 if (!containsFloat(r->m_renderer)) {
3722 int leftOffset = isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset;
3723 int topOffset = isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset;
3724 FloatingObject* floatingObj = new FloatingObject(r->type(), IntRect(r->x() - leftOffset, r->y() - topOffset, r->width(), r->height()));
3725 floatingObj->m_renderer = r->m_renderer;
3726
3727 // The nearest enclosing layer always paints the float (so that zindex and stacking
3728 // behaves properly). We always want to propagate the desire to paint the float as
3729 // far out as we can, to the outermost block that overlaps the float, stopping only
3730 // if we hit a self-painting layer boundary.
3731 if (r->m_renderer->enclosingFloatPaintingLayer() == enclosingFloatPaintingLayer())
3732 r->m_shouldPaint = false;
3733 else
3734 floatingObj->m_shouldPaint = false;
3735
3736 floatingObj->m_isDescendant = true;
3737
3738 // We create the floating object list lazily.
3739 if (!m_floatingObjects)
3740 m_floatingObjects = adoptPtr(new FloatingObjects);
3741
3742 m_floatingObjects->increaseObjectsCount(floatingObj->type());
3743 m_floatingObjects->set().add(floatingObj);
3744 }
3745 } else {
3746 if (makeChildPaintOtherFloats && !r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer() &&
3747 r->m_renderer->isDescendantOf(child) && r->m_renderer->enclosingFloatPaintingLayer() == child->enclosingFloatPaintingLayer()) {
3748 // The float is not overhanging from this block, so if it is a descendant of the child, the child should
3749 // paint it (the other case is that it is intruding into the child), unless it has its own layer or enclosing
3750 // layer.
3751 // If makeChildPaintOtherFloats is false, it means that the child must already know about all the floats
3752 // it should paint.
3753 r->m_shouldPaint = true;
3754 }
3755
3756 // Since the float doesn't overhang, it didn't get put into our list. We need to go ahead and add its overflow in to the
3757 // child now.
3758 if (r->m_isDescendant)
3759 child->addOverflowFromChild(r->m_renderer, IntSize(xPositionForFloatIncludingMargin(r), yPositionForFloatIncludingMargin(r)));
3760 }
3761 }
3762 return lowestFloatLogicalBottom;
3763 }
3764
hasOverhangingFloat(RenderBox * renderer)3765 bool RenderBlock::hasOverhangingFloat(RenderBox* renderer)
3766 {
3767 if (!m_floatingObjects || hasColumns() || !parent())
3768 return false;
3769
3770 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3771 FloatingObjectSetIterator it = floatingObjectSet.find<RenderBox*, FloatingObjectHashTranslator>(renderer);
3772 if (it == floatingObjectSet.end())
3773 return false;
3774
3775 return logicalBottomForFloat(*it) > logicalHeight();
3776 }
3777
addIntrudingFloats(RenderBlock * prev,int logicalLeftOffset,int logicalTopOffset)3778 void RenderBlock::addIntrudingFloats(RenderBlock* prev, int logicalLeftOffset, int logicalTopOffset)
3779 {
3780 // If the parent or previous sibling doesn't have any floats to add, don't bother.
3781 if (!prev->m_floatingObjects)
3782 return;
3783
3784 logicalLeftOffset += (isHorizontalWritingMode() ? marginLeft() : marginTop());
3785
3786 FloatingObjectSet& prevSet = prev->m_floatingObjects->set();
3787 FloatingObjectSetIterator prevEnd = prevSet.end();
3788 for (FloatingObjectSetIterator prevIt = prevSet.begin(); prevIt != prevEnd; ++prevIt) {
3789 FloatingObject* r = *prevIt;
3790 if (logicalBottomForFloat(r) > logicalTopOffset) {
3791 if (!m_floatingObjects || !m_floatingObjects->set().contains(r)) {
3792 int leftOffset = isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset;
3793 int topOffset = isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset;
3794
3795 FloatingObject* floatingObj = new FloatingObject(r->type(), IntRect(r->x() - leftOffset, r->y() - topOffset, r->width(), r->height()));
3796
3797 // Applying the child's margin makes no sense in the case where the child was passed in.
3798 // since this margin was added already through the modification of the |logicalLeftOffset| variable
3799 // above. |logicalLeftOffset| will equal the margin in this case, so it's already been taken
3800 // into account. Only apply this code if prev is the parent, since otherwise the left margin
3801 // will get applied twice.
3802 if (prev != parent()) {
3803 if (isHorizontalWritingMode())
3804 floatingObj->setX(floatingObj->x() + prev->marginLeft());
3805 else
3806 floatingObj->setY(floatingObj->y() + prev->marginTop());
3807 }
3808
3809 floatingObj->m_shouldPaint = false; // We are not in the direct inheritance chain for this float. We will never paint it.
3810 floatingObj->m_renderer = r->m_renderer;
3811
3812 // We create the floating object list lazily.
3813 if (!m_floatingObjects)
3814 m_floatingObjects = adoptPtr(new FloatingObjects);
3815 m_floatingObjects->increaseObjectsCount(floatingObj->type());
3816 m_floatingObjects->set().add(floatingObj);
3817 }
3818 }
3819 }
3820 }
3821
avoidsFloats() const3822 bool RenderBlock::avoidsFloats() const
3823 {
3824 // Floats can't intrude into our box if we have a non-auto column count or width.
3825 return RenderBox::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth();
3826 }
3827
containsFloat(RenderBox * renderer)3828 bool RenderBlock::containsFloat(RenderBox* renderer)
3829 {
3830 return m_floatingObjects && m_floatingObjects->set().contains<RenderBox*, FloatingObjectHashTranslator>(renderer);
3831 }
3832
markAllDescendantsWithFloatsForLayout(RenderBox * floatToRemove,bool inLayout)3833 void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove, bool inLayout)
3834 {
3835 if (!m_everHadLayout)
3836 return;
3837
3838 setChildNeedsLayout(true, !inLayout);
3839
3840 if (floatToRemove)
3841 removeFloatingObject(floatToRemove);
3842
3843 // Iterate over our children and mark them as needed.
3844 if (!childrenInline()) {
3845 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
3846 if ((!floatToRemove && child->isFloatingOrPositioned()) || !child->isRenderBlock())
3847 continue;
3848 RenderBlock* childBlock = toRenderBlock(child);
3849 if ((floatToRemove ? childBlock->containsFloat(floatToRemove) : childBlock->containsFloats()) || childBlock->shrinkToAvoidFloats())
3850 childBlock->markAllDescendantsWithFloatsForLayout(floatToRemove, inLayout);
3851 }
3852 }
3853 }
3854
markSiblingsWithFloatsForLayout()3855 void RenderBlock::markSiblingsWithFloatsForLayout()
3856 {
3857 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3858 FloatingObjectSetIterator end = floatingObjectSet.end();
3859 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
3860 if (logicalBottomForFloat(*it) > logicalHeight()) {
3861 RenderBox* floatingBox = (*it)->renderer();
3862
3863 RenderObject* next = nextSibling();
3864 while (next) {
3865 if (next->isRenderBlock() && !next->isFloatingOrPositioned() && !toRenderBlock(next)->avoidsFloats()) {
3866 RenderBlock* nextBlock = toRenderBlock(next);
3867 if (nextBlock->containsFloat(floatingBox))
3868 nextBlock->markAllDescendantsWithFloatsForLayout(floatingBox);
3869 else
3870 break;
3871 }
3872
3873 next = next->nextSibling();
3874 }
3875 }
3876 }
3877 }
3878
getClearDelta(RenderBox * child,int yPos)3879 int RenderBlock::getClearDelta(RenderBox* child, int yPos)
3880 {
3881 // There is no need to compute clearance if we have no floats.
3882 if (!containsFloats())
3883 return 0;
3884
3885 // At least one float is present. We need to perform the clearance computation.
3886 bool clearSet = child->style()->clear() != CNONE;
3887 int bottom = 0;
3888 switch (child->style()->clear()) {
3889 case CNONE:
3890 break;
3891 case CLEFT:
3892 bottom = lowestFloatLogicalBottom(FloatingObject::FloatLeft);
3893 break;
3894 case CRIGHT:
3895 bottom = lowestFloatLogicalBottom(FloatingObject::FloatRight);
3896 break;
3897 case CBOTH:
3898 bottom = lowestFloatLogicalBottom();
3899 break;
3900 }
3901
3902 // We also clear floats if we are too big to sit on the same line as a float (and wish to avoid floats by default).
3903 int result = clearSet ? max(0, bottom - yPos) : 0;
3904 if (!result && child->avoidsFloats()) {
3905 int y = yPos;
3906 while (true) {
3907 int widthAtY = availableLogicalWidthForLine(y, false);
3908 if (widthAtY == availableLogicalWidth())
3909 return y - yPos;
3910
3911 int oldChildY = child->y();
3912 int oldChildWidth = child->width();
3913 child->setY(y);
3914 child->computeLogicalWidth();
3915 int childWidthAtY = child->width();
3916 child->setY(oldChildY);
3917 child->setWidth(oldChildWidth);
3918
3919 if (childWidthAtY <= widthAtY)
3920 return y - yPos;
3921
3922 y = nextFloatLogicalBottomBelow(y);
3923 ASSERT(y >= yPos);
3924 if (y < yPos)
3925 break;
3926 }
3927 ASSERT_NOT_REACHED();
3928 }
3929 return result;
3930 }
3931
isPointInOverflowControl(HitTestResult & result,int _x,int _y,int _tx,int _ty)3932 bool RenderBlock::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty)
3933 {
3934 if (!scrollsOverflow())
3935 return false;
3936
3937 return layer()->hitTestOverflowControls(result, IntPoint(_x - _tx, _y - _ty));
3938 }
3939
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int _x,int _y,int _tx,int _ty,HitTestAction hitTestAction)3940 bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
3941 {
3942 int tx = _tx + x();
3943 int ty = _ty + y();
3944
3945 if (!isRenderView()) {
3946 // Check if we need to do anything at all.
3947 IntRect overflowBox = visualOverflowRect();
3948 overflowBox.move(tx, ty);
3949 if (!overflowBox.intersects(result.rectForPoint(_x, _y)))
3950 return false;
3951 }
3952
3953 if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, _x, _y, tx, ty)) {
3954 updateHitTestResult(result, IntPoint(_x - tx, _y - ty));
3955 // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet.
3956 if (!result.addNodeToRectBasedTestResult(node(), _x, _y))
3957 return true;
3958 }
3959
3960 // If we have clipping, then we can't have any spillout.
3961 bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer();
3962 bool useClip = (hasControlClip() || useOverflowClip);
3963 IntRect hitTestArea(result.rectForPoint(_x, _y));
3964 bool checkChildren = !useClip || (hasControlClip() ? controlClipRect(tx, ty).intersects(hitTestArea) : overflowClipRect(tx, ty, IncludeOverlayScrollbarSize).intersects(hitTestArea));
3965 if (checkChildren) {
3966 // Hit test descendants first.
3967 int scrolledX = tx;
3968 int scrolledY = ty;
3969 if (hasOverflowClip()) {
3970 IntSize offset = layer()->scrolledContentOffset();
3971 scrolledX -= offset.width();
3972 scrolledY -= offset.height();
3973 }
3974
3975 // Hit test contents if we don't have columns.
3976 if (!hasColumns()) {
3977 if (hitTestContents(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) {
3978 updateHitTestResult(result, IntPoint(_x - tx, _y - ty));
3979 return true;
3980 }
3981 if (hitTestAction == HitTestFloat && hitTestFloats(request, result, _x, _y, scrolledX, scrolledY))
3982 return true;
3983 } else if (hitTestColumns(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) {
3984 updateHitTestResult(result, IntPoint(_x - tx, _y - ty));
3985 return true;
3986 }
3987 }
3988
3989 // Now hit test our background
3990 if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) {
3991 IntRect boundsRect(tx, ty, width(), height());
3992 if (visibleToHitTesting() && boundsRect.intersects(result.rectForPoint(_x, _y))) {
3993 updateHitTestResult(result, flipForWritingMode(IntPoint(_x - tx, _y - ty)));
3994 if (!result.addNodeToRectBasedTestResult(node(), _x, _y, boundsRect))
3995 return true;
3996 }
3997 }
3998
3999 return false;
4000 }
4001
hitTestFloats(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty)4002 bool RenderBlock::hitTestFloats(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty)
4003 {
4004 if (!m_floatingObjects)
4005 return false;
4006
4007 if (isRenderView()) {
4008 tx += toRenderView(this)->frameView()->scrollX();
4009 ty += toRenderView(this)->frameView()->scrollY();
4010 }
4011
4012 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
4013 FloatingObjectSetIterator begin = floatingObjectSet.begin();
4014 for (FloatingObjectSetIterator it = floatingObjectSet.end(); it != begin;) {
4015 --it;
4016 FloatingObject* floatingObject = *it;
4017 if (floatingObject->m_shouldPaint && !floatingObject->m_renderer->hasSelfPaintingLayer()) {
4018 int xOffset = xPositionForFloatIncludingMargin(floatingObject) - floatingObject->m_renderer->x();
4019 int yOffset = yPositionForFloatIncludingMargin(floatingObject) - floatingObject->m_renderer->y();
4020 IntPoint childPoint = flipFloatForWritingMode(floatingObject, IntPoint(tx + xOffset, ty + yOffset));
4021 if (floatingObject->m_renderer->hitTest(request, result, IntPoint(x, y), childPoint.x(), childPoint.y())) {
4022 updateHitTestResult(result, IntPoint(x - childPoint.x(), y - childPoint.y()));
4023 return true;
4024 }
4025 }
4026 }
4027
4028 return false;
4029 }
4030
hitTestColumns(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,HitTestAction hitTestAction)4031 bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
4032 {
4033 // We need to do multiple passes, breaking up our hit testing into strips.
4034 ColumnInfo* colInfo = columnInfo();
4035 int colCount = columnCount(colInfo);
4036 if (!colCount)
4037 return false;
4038 int logicalLeft = logicalLeftOffsetForContent();
4039 int currLogicalTopOffset = 0;
4040 int i;
4041 bool isHorizontal = isHorizontalWritingMode();
4042 for (i = 0; i < colCount; i++) {
4043 IntRect colRect = columnRectAt(colInfo, i);
4044 int blockDelta = (isHorizontal ? colRect.height() : colRect.width());
4045 if (style()->isFlippedBlocksWritingMode())
4046 currLogicalTopOffset += blockDelta;
4047 else
4048 currLogicalTopOffset -= blockDelta;
4049 }
4050 for (i = colCount - 1; i >= 0; i--) {
4051 IntRect colRect = columnRectAt(colInfo, i);
4052 flipForWritingMode(colRect);
4053 int currLogicalLeftOffset = (isHorizontal ? colRect.x() : colRect.y()) - logicalLeft;
4054 int blockDelta = (isHorizontal ? colRect.height() : colRect.width());
4055 if (style()->isFlippedBlocksWritingMode())
4056 currLogicalTopOffset -= blockDelta;
4057 else
4058 currLogicalTopOffset += blockDelta;
4059 colRect.move(tx, ty);
4060
4061 if (colRect.intersects(result.rectForPoint(x, y))) {
4062 // The point is inside this column.
4063 // Adjust tx and ty to change where we hit test.
4064
4065 IntSize offset = isHorizontal ? IntSize(currLogicalLeftOffset, currLogicalTopOffset) : IntSize(currLogicalTopOffset, currLogicalLeftOffset);
4066 int finalX = tx + offset.width();
4067 int finalY = ty + offset.height();
4068 if (result.isRectBasedTest() && !colRect.contains(result.rectForPoint(x, y)))
4069 hitTestContents(request, result, x, y, finalX, finalY, hitTestAction);
4070 else
4071 return hitTestContents(request, result, x, y, finalX, finalY, hitTestAction) || (hitTestAction == HitTestFloat && hitTestFloats(request, result, x, y, finalX, finalY));
4072 }
4073 }
4074
4075 return false;
4076 }
4077
hitTestContents(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,HitTestAction hitTestAction)4078 bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
4079 {
4080 if (childrenInline() && !isTable()) {
4081 // We have to hit-test our line boxes.
4082 if (m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction))
4083 return true;
4084 } else {
4085 // Hit test our children.
4086 HitTestAction childHitTest = hitTestAction;
4087 if (hitTestAction == HitTestChildBlockBackgrounds)
4088 childHitTest = HitTestChildBlockBackground;
4089 for (RenderBox* child = lastChildBox(); child; child = child->previousSiblingBox()) {
4090 IntPoint childPoint = flipForWritingMode(child, IntPoint(tx, ty), ParentToChildFlippingAdjustment);
4091 if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, x, y, childPoint.x(), childPoint.y(), childHitTest))
4092 return true;
4093 }
4094 }
4095
4096 return false;
4097 }
4098
positionForBox(InlineBox * box,bool start) const4099 Position RenderBlock::positionForBox(InlineBox *box, bool start) const
4100 {
4101 if (!box)
4102 return Position();
4103
4104 if (!box->renderer()->node())
4105 return Position(node(), start ? caretMinOffset() : caretMaxOffset());
4106
4107 if (!box->isInlineTextBox())
4108 return Position(box->renderer()->node(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset());
4109
4110 InlineTextBox *textBox = static_cast<InlineTextBox *>(box);
4111 return Position(box->renderer()->node(), start ? textBox->start() : textBox->start() + textBox->len());
4112 }
4113
4114 // FIXME: This function should go on RenderObject as an instance method. Then
4115 // all cases in which positionForPoint recurs could call this instead to
4116 // prevent crossing editable boundaries. This would require many tests.
positionForPointRespectingEditingBoundaries(RenderBlock * parent,RenderBox * child,const IntPoint & pointInParentCoordinates)4117 static VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock* parent, RenderBox* child, const IntPoint& pointInParentCoordinates)
4118 {
4119 // FIXME: This is wrong if the child's writing-mode is different from the parent's.
4120 IntPoint pointInChildCoordinates(pointInParentCoordinates - child->location());
4121
4122 // If this is an anonymous renderer, we just recur normally
4123 Node* childNode = child->node();
4124 if (!childNode)
4125 return child->positionForPoint(pointInChildCoordinates);
4126
4127 // Otherwise, first make sure that the editability of the parent and child agree.
4128 // If they don't agree, then we return a visible position just before or after the child
4129 RenderObject* ancestor = parent;
4130 while (ancestor && !ancestor->node())
4131 ancestor = ancestor->parent();
4132
4133 // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal
4134 if (!ancestor || ancestor->node()->rendererIsEditable() == childNode->rendererIsEditable())
4135 return child->positionForPoint(pointInChildCoordinates);
4136
4137 // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child
4138 int childMiddle = parent->logicalWidthForChild(child) / 2;
4139 int logicalLeft = parent->isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y();
4140 if (logicalLeft < childMiddle)
4141 return ancestor->createVisiblePosition(childNode->nodeIndex(), DOWNSTREAM);
4142 return ancestor->createVisiblePosition(childNode->nodeIndex() + 1, UPSTREAM);
4143 }
4144
positionForPointWithInlineChildren(const IntPoint & pointInLogicalContents)4145 VisiblePosition RenderBlock::positionForPointWithInlineChildren(const IntPoint& pointInLogicalContents)
4146 {
4147 ASSERT(childrenInline());
4148
4149 if (!firstRootBox())
4150 return createVisiblePosition(0, DOWNSTREAM);
4151
4152 // look for the closest line box in the root box which is at the passed-in y coordinate
4153 InlineBox* closestBox = 0;
4154 RootInlineBox* firstRootBoxWithChildren = 0;
4155 RootInlineBox* lastRootBoxWithChildren = 0;
4156 for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) {
4157 if (!root->firstLeafChild())
4158 continue;
4159 if (!firstRootBoxWithChildren)
4160 firstRootBoxWithChildren = root;
4161
4162 if (root->isFirstAfterPageBreak() && pointInLogicalContents.y() < root->logicalTop())
4163 break;
4164
4165 lastRootBoxWithChildren = root;
4166
4167 // check if this root line box is located at this y coordinate
4168 if (pointInLogicalContents.y() < root->selectionBottom()) {
4169 closestBox = root->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x());
4170 if (closestBox)
4171 break;
4172 }
4173 }
4174
4175 bool moveCaretToBoundary = document()->frame()->editor()->behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom();
4176
4177 if (!moveCaretToBoundary && !closestBox && lastRootBoxWithChildren) {
4178 // y coordinate is below last root line box, pretend we hit it
4179 closestBox = lastRootBoxWithChildren->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x());
4180 }
4181
4182 if (closestBox) {
4183 if (moveCaretToBoundary && pointInLogicalContents.y() < firstRootBoxWithChildren->selectionTop()
4184 && pointInLogicalContents.y() < firstRootBoxWithChildren->logicalTop()) {
4185 // y coordinate is above first root line box, so return the start of the first
4186 return VisiblePosition(positionForBox(firstRootBoxWithChildren->firstLeafChild(), true), DOWNSTREAM);
4187 }
4188
4189 // pass the box a top position that is inside it
4190 IntPoint point(pointInLogicalContents.x(), closestBox->logicalTop());
4191 if (!isHorizontalWritingMode())
4192 point = point.transposedPoint();
4193 if (closestBox->renderer()->isReplaced())
4194 return positionForPointRespectingEditingBoundaries(this, toRenderBox(closestBox->renderer()), point);
4195 return closestBox->renderer()->positionForPoint(point);
4196 }
4197
4198 if (lastRootBoxWithChildren) {
4199 // We hit this case for Mac behavior when the Y coordinate is below the last box.
4200 ASSERT(moveCaretToBoundary);
4201 InlineBox* logicallyLastBox;
4202 if (lastRootBoxWithChildren->getLogicalEndBoxWithNode(logicallyLastBox))
4203 return VisiblePosition(positionForBox(logicallyLastBox, false), DOWNSTREAM);
4204 }
4205
4206 // Can't reach this. We have a root line box, but it has no kids.
4207 // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text
4208 // seems to hit this code path.
4209 return createVisiblePosition(0, DOWNSTREAM);
4210 }
4211
isChildHitTestCandidate(RenderBox * box)4212 static inline bool isChildHitTestCandidate(RenderBox* box)
4213 {
4214 return box->height() && box->style()->visibility() == VISIBLE && !box->isFloatingOrPositioned();
4215 }
4216
positionForPoint(const IntPoint & point)4217 VisiblePosition RenderBlock::positionForPoint(const IntPoint& point)
4218 {
4219 if (isTable())
4220 return RenderBox::positionForPoint(point);
4221
4222 if (isReplaced()) {
4223 // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode.
4224 int pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.y();
4225 int pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x();
4226
4227 if (pointLogicalTop < 0 || (pointLogicalTop < logicalHeight() && pointLogicalLeft < 0))
4228 return createVisiblePosition(caretMinOffset(), DOWNSTREAM);
4229 if (pointLogicalTop >= logicalHeight() || (pointLogicalTop >= 0 && pointLogicalLeft >= logicalWidth()))
4230 return createVisiblePosition(caretMaxOffset(), DOWNSTREAM);
4231 }
4232
4233 int contentsX = point.x();
4234 int contentsY = point.y();
4235 offsetForContents(contentsX, contentsY);
4236 IntPoint pointInContents(contentsX, contentsY);
4237 IntPoint pointInLogicalContents(pointInContents);
4238 if (!isHorizontalWritingMode())
4239 pointInLogicalContents = pointInLogicalContents.transposedPoint();
4240
4241 if (childrenInline())
4242 return positionForPointWithInlineChildren(pointInLogicalContents);
4243
4244 if (lastChildBox() && pointInContents.y() > lastChildBox()->logicalTop()) {
4245 for (RenderBox* childBox = lastChildBox(); childBox; childBox = childBox->previousSiblingBox()) {
4246 if (isChildHitTestCandidate(childBox))
4247 return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents);
4248 }
4249 } else {
4250 for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) {
4251 // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3).
4252 if (isChildHitTestCandidate(childBox) && pointInContents.y() < childBox->logicalBottom())
4253 return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents);
4254 }
4255 }
4256
4257 // We only get here if there are no hit test candidate children below the click.
4258 return RenderBox::positionForPoint(point);
4259 }
4260
offsetForContents(int & tx,int & ty) const4261 void RenderBlock::offsetForContents(int& tx, int& ty) const
4262 {
4263 IntPoint contentsPoint(tx, ty);
4264
4265 if (hasOverflowClip())
4266 contentsPoint += layer()->scrolledContentOffset();
4267
4268 if (hasColumns())
4269 adjustPointToColumnContents(contentsPoint);
4270
4271 tx = contentsPoint.x();
4272 ty = contentsPoint.y();
4273 }
4274
availableLogicalWidth() const4275 int RenderBlock::availableLogicalWidth() const
4276 {
4277 // If we have multiple columns, then the available logical width is reduced to our column width.
4278 if (hasColumns())
4279 return desiredColumnWidth();
4280 return RenderBox::availableLogicalWidth();
4281 }
4282
columnGap() const4283 int RenderBlock::columnGap() const
4284 {
4285 if (style()->hasNormalColumnGap())
4286 return style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins.
4287 return static_cast<int>(style()->columnGap());
4288 }
4289
calcColumnWidth()4290 void RenderBlock::calcColumnWidth()
4291 {
4292 // Calculate our column width and column count.
4293 unsigned desiredColumnCount = 1;
4294 int desiredColumnWidth = contentLogicalWidth();
4295
4296 // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination.
4297 if (document()->paginated() || (style()->hasAutoColumnCount() && style()->hasAutoColumnWidth())) {
4298 setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth);
4299 return;
4300 }
4301
4302 int availWidth = desiredColumnWidth;
4303 int colGap = columnGap();
4304 int colWidth = max(1, static_cast<int>(style()->columnWidth()));
4305 int colCount = max(1, static_cast<int>(style()->columnCount()));
4306
4307 if (style()->hasAutoColumnWidth() && !style()->hasAutoColumnCount()) {
4308 desiredColumnCount = colCount;
4309 desiredColumnWidth = max<int>(0, (availWidth - ((desiredColumnCount - 1) * colGap)) / desiredColumnCount);
4310 } else if (!style()->hasAutoColumnWidth() && style()->hasAutoColumnCount()) {
4311 desiredColumnCount = max<int>(1, (float)(availWidth + colGap) / (colWidth + colGap));
4312 desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap;
4313 } else {
4314 desiredColumnCount = max(min<int>(colCount, (float)(availWidth + colGap) / (colWidth + colGap)), 1);
4315 desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap;
4316 }
4317 setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth);
4318 }
4319
setDesiredColumnCountAndWidth(int count,int width)4320 void RenderBlock::setDesiredColumnCountAndWidth(int count, int width)
4321 {
4322 bool destroyColumns = !firstChild()
4323 || (count == 1 && style()->hasAutoColumnWidth())
4324 || firstChild()->isAnonymousColumnsBlock()
4325 || firstChild()->isAnonymousColumnSpanBlock();
4326 if (destroyColumns) {
4327 if (hasColumns()) {
4328 delete gColumnInfoMap->take(this);
4329 setHasColumns(false);
4330 }
4331 } else {
4332 ColumnInfo* info;
4333 if (hasColumns())
4334 info = gColumnInfoMap->get(this);
4335 else {
4336 if (!gColumnInfoMap)
4337 gColumnInfoMap = new ColumnInfoMap;
4338 info = new ColumnInfo;
4339 gColumnInfoMap->add(this, info);
4340 setHasColumns(true);
4341 }
4342 info->setDesiredColumnCount(count);
4343 info->setDesiredColumnWidth(width);
4344 }
4345 }
4346
desiredColumnWidth() const4347 int RenderBlock::desiredColumnWidth() const
4348 {
4349 if (!hasColumns())
4350 return contentLogicalWidth();
4351 return gColumnInfoMap->get(this)->desiredColumnWidth();
4352 }
4353
desiredColumnCount() const4354 unsigned RenderBlock::desiredColumnCount() const
4355 {
4356 if (!hasColumns())
4357 return 1;
4358 return gColumnInfoMap->get(this)->desiredColumnCount();
4359 }
4360
columnInfo() const4361 ColumnInfo* RenderBlock::columnInfo() const
4362 {
4363 if (!hasColumns())
4364 return 0;
4365 return gColumnInfoMap->get(this);
4366 }
4367
columnCount(ColumnInfo * colInfo) const4368 unsigned RenderBlock::columnCount(ColumnInfo* colInfo) const
4369 {
4370 ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo);
4371 return colInfo->columnCount();
4372 }
4373
columnRectAt(ColumnInfo * colInfo,unsigned index) const4374 IntRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const
4375 {
4376 ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo);
4377
4378 // Compute the appropriate rect based off our information.
4379 int colLogicalWidth = colInfo->desiredColumnWidth();
4380 int colLogicalHeight = colInfo->columnHeight();
4381 int colLogicalTop = borderBefore() + paddingBefore();
4382 int colGap = columnGap();
4383 int colLogicalLeft = style()->isLeftToRightDirection() ?
4384 logicalLeftOffsetForContent() + (index * (colLogicalWidth + colGap))
4385 : logicalLeftOffsetForContent() + contentLogicalWidth() - colLogicalWidth - (index * (colLogicalWidth + colGap));
4386 IntRect rect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight);
4387 if (isHorizontalWritingMode())
4388 return IntRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight);
4389 return IntRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth);
4390 }
4391
layoutColumns(bool hasSpecifiedPageLogicalHeight,int pageLogicalHeight,LayoutStateMaintainer & statePusher)4392 bool RenderBlock::layoutColumns(bool hasSpecifiedPageLogicalHeight, int pageLogicalHeight, LayoutStateMaintainer& statePusher)
4393 {
4394 if (!hasColumns())
4395 return false;
4396
4397 // FIXME: We don't balance properly at all in the presence of forced page breaks. We need to understand what
4398 // the distance between forced page breaks is so that we can avoid making the minimum column height too tall.
4399 ColumnInfo* colInfo = columnInfo();
4400 int desiredColumnCount = colInfo->desiredColumnCount();
4401 if (!hasSpecifiedPageLogicalHeight) {
4402 int columnHeight = pageLogicalHeight;
4403 int minColumnCount = colInfo->forcedBreaks() + 1;
4404 if (minColumnCount >= desiredColumnCount) {
4405 // The forced page breaks are in control of the balancing. Just set the column height to the
4406 // maximum page break distance.
4407 if (!pageLogicalHeight) {
4408 int distanceBetweenBreaks = max(colInfo->maximumDistanceBetweenForcedBreaks(),
4409 view()->layoutState()->pageLogicalOffset(borderBefore() + paddingBefore() + contentLogicalHeight()) - colInfo->forcedBreakOffset());
4410 columnHeight = max(colInfo->minimumColumnHeight(), distanceBetweenBreaks);
4411 }
4412 } else if (contentLogicalHeight() > pageLogicalHeight * desiredColumnCount) {
4413 // Now that we know the intrinsic height of the columns, we have to rebalance them.
4414 columnHeight = max(colInfo->minimumColumnHeight(), (int)ceilf((float)contentLogicalHeight() / desiredColumnCount));
4415 }
4416
4417 if (columnHeight && columnHeight != pageLogicalHeight) {
4418 statePusher.pop();
4419 m_everHadLayout = true;
4420 layoutBlock(false, columnHeight);
4421 return true;
4422 }
4423 }
4424
4425 if (pageLogicalHeight)
4426 colInfo->setColumnCountAndHeight(ceilf((float)contentLogicalHeight() / pageLogicalHeight), pageLogicalHeight);
4427
4428 if (columnCount(colInfo)) {
4429 setLogicalHeight(borderBefore() + paddingBefore() + colInfo->columnHeight() + borderAfter() + paddingAfter() + scrollbarLogicalHeight());
4430 m_overflow.clear();
4431 }
4432
4433 return false;
4434 }
4435
adjustPointToColumnContents(IntPoint & point) const4436 void RenderBlock::adjustPointToColumnContents(IntPoint& point) const
4437 {
4438 // Just bail if we have no columns.
4439 if (!hasColumns())
4440 return;
4441
4442 ColumnInfo* colInfo = columnInfo();
4443 if (!columnCount(colInfo))
4444 return;
4445
4446 // Determine which columns we intersect.
4447 int colGap = columnGap();
4448 int halfColGap = colGap / 2;
4449 IntPoint columnPoint(columnRectAt(colInfo, 0).location());
4450 int logicalOffset = 0;
4451 for (unsigned i = 0; i < colInfo->columnCount(); i++) {
4452 // Add in half the column gap to the left and right of the rect.
4453 IntRect colRect = columnRectAt(colInfo, i);
4454 if (isHorizontalWritingMode()) {
4455 IntRect gapAndColumnRect(colRect.x() - halfColGap, colRect.y(), colRect.width() + colGap, colRect.height());
4456 if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.maxX()) {
4457 // FIXME: The clamping that follows is not completely right for right-to-left
4458 // content.
4459 // Clamp everything above the column to its top left.
4460 if (point.y() < gapAndColumnRect.y())
4461 point = gapAndColumnRect.location();
4462 // Clamp everything below the column to the next column's top left. If there is
4463 // no next column, this still maps to just after this column.
4464 else if (point.y() >= gapAndColumnRect.maxY()) {
4465 point = gapAndColumnRect.location();
4466 point.move(0, gapAndColumnRect.height());
4467 }
4468
4469 // We're inside the column. Translate the x and y into our column coordinate space.
4470 point.move(columnPoint.x() - colRect.x(), logicalOffset);
4471 return;
4472 }
4473
4474 // Move to the next position.
4475 logicalOffset += colRect.height();
4476 } else {
4477 IntRect gapAndColumnRect(colRect.x(), colRect.y() - halfColGap, colRect.width(), colRect.height() + colGap);
4478 if (point.y() >= gapAndColumnRect.y() && point.y() < gapAndColumnRect.maxY()) {
4479 // FIXME: The clamping that follows is not completely right for right-to-left
4480 // content.
4481 // Clamp everything above the column to its top left.
4482 if (point.x() < gapAndColumnRect.x())
4483 point = gapAndColumnRect.location();
4484 // Clamp everything below the column to the next column's top left. If there is
4485 // no next column, this still maps to just after this column.
4486 else if (point.x() >= gapAndColumnRect.maxX()) {
4487 point = gapAndColumnRect.location();
4488 point.move(gapAndColumnRect.width(), 0);
4489 }
4490
4491 // We're inside the column. Translate the x and y into our column coordinate space.
4492 point.move(logicalOffset, columnPoint.y() - colRect.y());
4493 return;
4494 }
4495
4496 // Move to the next position.
4497 logicalOffset += colRect.width();
4498 }
4499 }
4500 }
4501
adjustRectForColumns(IntRect & r) const4502 void RenderBlock::adjustRectForColumns(IntRect& r) const
4503 {
4504 // Just bail if we have no columns.
4505 if (!hasColumns())
4506 return;
4507
4508 ColumnInfo* colInfo = columnInfo();
4509
4510 // Begin with a result rect that is empty.
4511 IntRect result;
4512
4513 // Determine which columns we intersect.
4514 unsigned colCount = columnCount(colInfo);
4515 if (!colCount)
4516 return;
4517
4518 int logicalLeft = logicalLeftOffsetForContent();
4519 int currLogicalOffset = 0;
4520
4521 for (unsigned i = 0; i < colCount; i++) {
4522 IntRect colRect = columnRectAt(colInfo, i);
4523 IntRect repaintRect = r;
4524 if (isHorizontalWritingMode()) {
4525 int currXOffset = colRect.x() - logicalLeft;
4526 repaintRect.move(currXOffset, currLogicalOffset);
4527 currLogicalOffset -= colRect.height();
4528 } else {
4529 int currYOffset = colRect.y() - logicalLeft;
4530 repaintRect.move(currLogicalOffset, currYOffset);
4531 currLogicalOffset -= colRect.width();
4532 }
4533 repaintRect.intersect(colRect);
4534 result.unite(repaintRect);
4535 }
4536
4537 r = result;
4538 }
4539
flipForWritingModeIncludingColumns(const IntPoint & point) const4540 IntPoint RenderBlock::flipForWritingModeIncludingColumns(const IntPoint& point) const
4541 {
4542 ASSERT(hasColumns());
4543 if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
4544 return point;
4545 ColumnInfo* colInfo = columnInfo();
4546 int columnLogicalHeight = colInfo->columnHeight();
4547 int expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight();
4548 if (isHorizontalWritingMode())
4549 return IntPoint(point.x(), expandedLogicalHeight - point.y());
4550 return IntPoint(expandedLogicalHeight - point.x(), point.y());
4551 }
4552
flipForWritingModeIncludingColumns(IntRect & rect) const4553 void RenderBlock::flipForWritingModeIncludingColumns(IntRect& rect) const
4554 {
4555 ASSERT(hasColumns());
4556 if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
4557 return;
4558
4559 ColumnInfo* colInfo = columnInfo();
4560 int columnLogicalHeight = colInfo->columnHeight();
4561 int expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight();
4562 if (isHorizontalWritingMode())
4563 rect.setY(expandedLogicalHeight - rect.maxY());
4564 else
4565 rect.setX(expandedLogicalHeight - rect.maxX());
4566 }
4567
adjustForColumns(IntSize & offset,const IntPoint & point) const4568 void RenderBlock::adjustForColumns(IntSize& offset, const IntPoint& point) const
4569 {
4570 if (!hasColumns())
4571 return;
4572
4573 ColumnInfo* colInfo = columnInfo();
4574
4575 int logicalLeft = logicalLeftOffsetForContent();
4576 size_t colCount = columnCount(colInfo);
4577 int colLogicalWidth = colInfo->desiredColumnWidth();
4578 int colLogicalHeight = colInfo->columnHeight();
4579
4580 for (size_t i = 0; i < colCount; ++i) {
4581 // Compute the edges for a given column in the block progression direction.
4582 IntRect sliceRect = IntRect(logicalLeft, borderBefore() + paddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight);
4583 if (!isHorizontalWritingMode())
4584 sliceRect = sliceRect.transposedRect();
4585
4586 // If we have a flipped blocks writing mode, then convert the column so that it's coming from the after edge (either top or left edge).
4587 flipForWritingModeIncludingColumns(sliceRect);
4588
4589 int logicalOffset = style()->isFlippedBlocksWritingMode() ? (colCount - 1 - i) * colLogicalHeight : i * colLogicalHeight;
4590
4591 // Now we're in the same coordinate space as the point. See if it is inside the rectangle.
4592 if (isHorizontalWritingMode()) {
4593 if (point.y() >= sliceRect.y() && point.y() < sliceRect.maxY()) {
4594 offset.expand(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset);
4595 return;
4596 }
4597 } else {
4598 if (point.x() >= sliceRect.x() && point.x() < sliceRect.maxX()) {
4599 offset.expand(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft);
4600 return;
4601 }
4602 }
4603 }
4604 }
4605
computePreferredLogicalWidths()4606 void RenderBlock::computePreferredLogicalWidths()
4607 {
4608 ASSERT(preferredLogicalWidthsDirty());
4609
4610 updateFirstLetter();
4611
4612 if (!isTableCell() && style()->logicalWidth().isFixed() && style()->logicalWidth().value() > 0)
4613 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->logicalWidth().value());
4614 else {
4615 m_minPreferredLogicalWidth = 0;
4616 m_maxPreferredLogicalWidth = 0;
4617
4618 if (childrenInline())
4619 computeInlinePreferredLogicalWidths();
4620 else
4621 computeBlockPreferredLogicalWidths();
4622
4623 m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
4624
4625 if (!style()->autoWrap() && childrenInline()) {
4626 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
4627
4628 // A horizontal marquee with inline children has no minimum width.
4629 if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal())
4630 m_minPreferredLogicalWidth = 0;
4631 }
4632
4633 int scrollbarWidth = 0;
4634 if (hasOverflowClip() && style()->overflowY() == OSCROLL) {
4635 layer()->setHasVerticalScrollbar(true);
4636 scrollbarWidth = verticalScrollbarWidth();
4637 m_maxPreferredLogicalWidth += scrollbarWidth;
4638 }
4639
4640 if (isTableCell()) {
4641 Length w = toRenderTableCell(this)->styleOrColLogicalWidth();
4642 if (w.isFixed() && w.value() > 0) {
4643 m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(w.value()));
4644 scrollbarWidth = 0;
4645 }
4646 }
4647
4648 m_minPreferredLogicalWidth += scrollbarWidth;
4649 }
4650
4651 if (style()->logicalMinWidth().isFixed() && style()->logicalMinWidth().value() > 0) {
4652 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMinWidth().value()));
4653 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMinWidth().value()));
4654 }
4655
4656 if (style()->logicalMaxWidth().isFixed() && style()->logicalMaxWidth().value() != undefinedLength) {
4657 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMaxWidth().value()));
4658 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMaxWidth().value()));
4659 }
4660
4661 int borderAndPadding = borderAndPaddingLogicalWidth();
4662 m_minPreferredLogicalWidth += borderAndPadding;
4663 m_maxPreferredLogicalWidth += borderAndPadding;
4664
4665 setPreferredLogicalWidthsDirty(false);
4666 }
4667
4668 struct InlineMinMaxIterator {
4669 /* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to
4670 inline min/max width calculations. Note the following about the way it walks:
4671 (1) Positioned content is skipped (since it does not contribute to min/max width of a block)
4672 (2) We do not drill into the children of floats or replaced elements, since you can't break
4673 in the middle of such an element.
4674 (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have
4675 distinct borders/margin/padding that contribute to the min/max width.
4676 */
4677 RenderObject* parent;
4678 RenderObject* current;
4679 bool endOfInline;
4680
InlineMinMaxIteratorWebCore::InlineMinMaxIterator4681 InlineMinMaxIterator(RenderObject* p, bool end = false)
4682 :parent(p), current(p), endOfInline(end) {}
4683
4684 RenderObject* next();
4685 };
4686
next()4687 RenderObject* InlineMinMaxIterator::next()
4688 {
4689 RenderObject* result = 0;
4690 bool oldEndOfInline = endOfInline;
4691 endOfInline = false;
4692 while (current || current == parent) {
4693 if (!oldEndOfInline &&
4694 (current == parent ||
4695 (!current->isFloating() && !current->isReplaced() && !current->isPositioned())))
4696 result = current->firstChild();
4697 if (!result) {
4698 // We hit the end of our inline. (It was empty, e.g., <span></span>.)
4699 if (!oldEndOfInline && current->isRenderInline()) {
4700 result = current;
4701 endOfInline = true;
4702 break;
4703 }
4704
4705 while (current && current != parent) {
4706 result = current->nextSibling();
4707 if (result) break;
4708 current = current->parent();
4709 if (current && current != parent && current->isRenderInline()) {
4710 result = current;
4711 endOfInline = true;
4712 break;
4713 }
4714 }
4715 }
4716
4717 if (!result)
4718 break;
4719
4720 if (!result->isPositioned() && (result->isText() || result->isFloating() || result->isReplaced() || result->isRenderInline()))
4721 break;
4722
4723 current = result;
4724 result = 0;
4725 }
4726
4727 // Update our position.
4728 current = result;
4729 return current;
4730 }
4731
getBPMWidth(int childValue,Length cssUnit)4732 static int getBPMWidth(int childValue, Length cssUnit)
4733 {
4734 if (cssUnit.type() != Auto)
4735 return (cssUnit.isFixed() ? cssUnit.value() : childValue);
4736 return 0;
4737 }
4738
getBorderPaddingMargin(const RenderBoxModelObject * child,bool endOfInline)4739 static int getBorderPaddingMargin(const RenderBoxModelObject* child, bool endOfInline)
4740 {
4741 RenderStyle* cstyle = child->style();
4742 if (endOfInline)
4743 return getBPMWidth(child->marginEnd(), cstyle->marginEnd()) +
4744 getBPMWidth(child->paddingEnd(), cstyle->paddingEnd()) +
4745 child->borderEnd();
4746 return getBPMWidth(child->marginStart(), cstyle->marginStart()) +
4747 getBPMWidth(child->paddingStart(), cstyle->paddingStart()) +
4748 child->borderStart();
4749 }
4750
stripTrailingSpace(float & inlineMax,float & inlineMin,RenderObject * trailingSpaceChild)4751 static inline void stripTrailingSpace(float& inlineMax, float& inlineMin,
4752 RenderObject* trailingSpaceChild)
4753 {
4754 if (trailingSpaceChild && trailingSpaceChild->isText()) {
4755 // Collapse away the trailing space at the end of a block.
4756 RenderText* t = toRenderText(trailingSpaceChild);
4757 const UChar space = ' ';
4758 const Font& font = t->style()->font(); // FIXME: This ignores first-line.
4759 float spaceWidth = font.width(TextRun(&space, 1));
4760 inlineMax -= spaceWidth + font.wordSpacing();
4761 if (inlineMin > inlineMax)
4762 inlineMin = inlineMax;
4763 }
4764 }
4765
updatePreferredWidth(int & preferredWidth,float & result)4766 static inline void updatePreferredWidth(int& preferredWidth, float& result)
4767 {
4768 int snappedResult = ceilf(result);
4769 preferredWidth = max(snappedResult, preferredWidth);
4770 }
4771
computeInlinePreferredLogicalWidths()4772 void RenderBlock::computeInlinePreferredLogicalWidths()
4773 {
4774 float inlineMax = 0;
4775 float inlineMin = 0;
4776
4777 int cw = containingBlock()->contentLogicalWidth();
4778
4779 // If we are at the start of a line, we want to ignore all white-space.
4780 // Also strip spaces if we previously had text that ended in a trailing space.
4781 bool stripFrontSpaces = true;
4782 RenderObject* trailingSpaceChild = 0;
4783
4784 // Firefox and Opera will allow a table cell to grow to fit an image inside it under
4785 // very specific cirucumstances (in order to match common WinIE renderings).
4786 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
4787 bool allowImagesToBreak = !document()->inQuirksMode() || !isTableCell() || !style()->logicalWidth().isIntrinsicOrAuto();
4788
4789 bool autoWrap, oldAutoWrap;
4790 autoWrap = oldAutoWrap = style()->autoWrap();
4791
4792 InlineMinMaxIterator childIterator(this);
4793 bool addedTextIndent = false; // Only gets added in once.
4794 RenderObject* prevFloat = 0;
4795 while (RenderObject* child = childIterator.next()) {
4796 autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() :
4797 child->style()->autoWrap();
4798
4799 if (!child->isBR()) {
4800 // Step One: determine whether or not we need to go ahead and
4801 // terminate our current line. Each discrete chunk can become
4802 // the new min-width, if it is the widest chunk seen so far, and
4803 // it can also become the max-width.
4804
4805 // Children fall into three categories:
4806 // (1) An inline flow object. These objects always have a min/max of 0,
4807 // and are included in the iteration solely so that their margins can
4808 // be added in.
4809 //
4810 // (2) An inline non-text non-flow object, e.g., an inline replaced element.
4811 // These objects can always be on a line by themselves, so in this situation
4812 // we need to go ahead and break the current line, and then add in our own
4813 // margins and min/max width on its own line, and then terminate the line.
4814 //
4815 // (3) A text object. Text runs can have breakable characters at the start,
4816 // the middle or the end. They may also lose whitespace off the front if
4817 // we're already ignoring whitespace. In order to compute accurate min-width
4818 // information, we need three pieces of information.
4819 // (a) the min-width of the first non-breakable run. Should be 0 if the text string
4820 // starts with whitespace.
4821 // (b) the min-width of the last non-breakable run. Should be 0 if the text string
4822 // ends with whitespace.
4823 // (c) the min/max width of the string (trimmed for whitespace).
4824 //
4825 // If the text string starts with whitespace, then we need to go ahead and
4826 // terminate our current line (unless we're already in a whitespace stripping
4827 // mode.
4828 //
4829 // If the text string has a breakable character in the middle, but didn't start
4830 // with whitespace, then we add the width of the first non-breakable run and
4831 // then end the current line. We then need to use the intermediate min/max width
4832 // values (if any of them are larger than our current min/max). We then look at
4833 // the width of the last non-breakable run and use that to start a new line
4834 // (unless we end in whitespace).
4835 RenderStyle* cstyle = child->style();
4836 float childMin = 0;
4837 float childMax = 0;
4838
4839 if (!child->isText()) {
4840 // Case (1) and (2). Inline replaced and inline flow elements.
4841 if (child->isRenderInline()) {
4842 // Add in padding/border/margin from the appropriate side of
4843 // the element.
4844 float bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline);
4845 childMin += bpm;
4846 childMax += bpm;
4847
4848 inlineMin += childMin;
4849 inlineMax += childMax;
4850
4851 child->setPreferredLogicalWidthsDirty(false);
4852 } else {
4853 // Inline replaced elts add in their margins to their min/max values.
4854 float margins = 0;
4855 Length startMargin = cstyle->marginStart();
4856 Length endMargin = cstyle->marginEnd();
4857 if (startMargin.isFixed())
4858 margins += startMargin.value();
4859 if (endMargin.isFixed())
4860 margins += endMargin.value();
4861 childMin += margins;
4862 childMax += margins;
4863 }
4864 }
4865
4866 if (!child->isRenderInline() && !child->isText()) {
4867 // Case (2). Inline replaced elements and floats.
4868 // Go ahead and terminate the current line as far as
4869 // minwidth is concerned.
4870 childMin += child->minPreferredLogicalWidth();
4871 childMax += child->maxPreferredLogicalWidth();
4872
4873 bool clearPreviousFloat;
4874 if (child->isFloating()) {
4875 clearPreviousFloat = (prevFloat
4876 && ((prevFloat->style()->floating() == FLEFT && (child->style()->clear() & CLEFT))
4877 || (prevFloat->style()->floating() == FRIGHT && (child->style()->clear() & CRIGHT))));
4878 prevFloat = child;
4879 } else
4880 clearPreviousFloat = false;
4881
4882 bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak;
4883 if ((canBreakReplacedElement && (autoWrap || oldAutoWrap)) || clearPreviousFloat) {
4884 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
4885 inlineMin = 0;
4886 }
4887
4888 // If we're supposed to clear the previous float, then terminate maxwidth as well.
4889 if (clearPreviousFloat) {
4890 updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax);
4891 inlineMax = 0;
4892 }
4893
4894 // Add in text-indent. This is added in only once.
4895 int ti = 0;
4896 if (!addedTextIndent) {
4897 addedTextIndent = true;
4898 ti = style()->textIndent().calcMinValue(cw);
4899 childMin += ti;
4900 childMax += ti;
4901 }
4902
4903 // Add our width to the max.
4904 inlineMax += childMax;
4905
4906 if (!autoWrap || !canBreakReplacedElement) {
4907 if (child->isFloating())
4908 updatePreferredWidth(m_minPreferredLogicalWidth, childMin);
4909 else
4910 inlineMin += childMin;
4911 } else {
4912 // Now check our line.
4913 updatePreferredWidth(m_minPreferredLogicalWidth, childMin);
4914
4915 // Now start a new line.
4916 inlineMin = 0;
4917 }
4918
4919 // We are no longer stripping whitespace at the start of
4920 // a line.
4921 if (!child->isFloating()) {
4922 stripFrontSpaces = false;
4923 trailingSpaceChild = 0;
4924 }
4925 } else if (child->isText()) {
4926 // Case (3). Text.
4927 RenderText* t = toRenderText(child);
4928
4929 if (t->isWordBreak()) {
4930 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
4931 inlineMin = 0;
4932 continue;
4933 }
4934
4935 if (t->style()->hasTextCombine() && t->isCombineText())
4936 toRenderCombineText(t)->combineText();
4937
4938 // Determine if we have a breakable character. Pass in
4939 // whether or not we should ignore any spaces at the front
4940 // of the string. If those are going to be stripped out,
4941 // then they shouldn't be considered in the breakable char
4942 // check.
4943 bool hasBreakableChar, hasBreak;
4944 float beginMin, endMin;
4945 bool beginWS, endWS;
4946 float beginMax, endMax;
4947 t->trimmedPrefWidths(inlineMax, beginMin, beginWS, endMin, endWS,
4948 hasBreakableChar, hasBreak, beginMax, endMax,
4949 childMin, childMax, stripFrontSpaces);
4950
4951 // This text object will not be rendered, but it may still provide a breaking opportunity.
4952 if (!hasBreak && childMax == 0) {
4953 if (autoWrap && (beginWS || endWS)) {
4954 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
4955 inlineMin = 0;
4956 }
4957 continue;
4958 }
4959
4960 if (stripFrontSpaces)
4961 trailingSpaceChild = child;
4962 else
4963 trailingSpaceChild = 0;
4964
4965 // Add in text-indent. This is added in only once.
4966 int ti = 0;
4967 if (!addedTextIndent) {
4968 addedTextIndent = true;
4969 ti = style()->textIndent().calcMinValue(cw);
4970 childMin+=ti; beginMin += ti;
4971 childMax+=ti; beginMax += ti;
4972 }
4973
4974 // If we have no breakable characters at all,
4975 // then this is the easy case. We add ourselves to the current
4976 // min and max and continue.
4977 if (!hasBreakableChar) {
4978 inlineMin += childMin;
4979 } else {
4980 // We have a breakable character. Now we need to know if
4981 // we start and end with whitespace.
4982 if (beginWS)
4983 // Go ahead and end the current line.
4984 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
4985 else {
4986 inlineMin += beginMin;
4987 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
4988 childMin -= ti;
4989 }
4990
4991 inlineMin = childMin;
4992
4993 if (endWS) {
4994 // We end in whitespace, which means we can go ahead
4995 // and end our current line.
4996 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
4997 inlineMin = 0;
4998 } else {
4999 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
5000 inlineMin = endMin;
5001 }
5002 }
5003
5004 if (hasBreak) {
5005 inlineMax += beginMax;
5006 updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax);
5007 updatePreferredWidth(m_maxPreferredLogicalWidth, childMax);
5008 inlineMax = endMax;
5009 } else
5010 inlineMax += childMax;
5011 }
5012
5013 // Ignore spaces after a list marker.
5014 if (child->isListMarker())
5015 stripFrontSpaces = true;
5016 } else {
5017 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
5018 updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax);
5019 inlineMin = inlineMax = 0;
5020 stripFrontSpaces = true;
5021 trailingSpaceChild = 0;
5022 }
5023
5024 oldAutoWrap = autoWrap;
5025 }
5026
5027 if (style()->collapseWhiteSpace())
5028 stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
5029
5030 updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin);
5031 updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax);
5032 }
5033
5034 // Use a very large value (in effect infinite).
5035 #define BLOCK_MAX_WIDTH 15000
5036
computeBlockPreferredLogicalWidths()5037 void RenderBlock::computeBlockPreferredLogicalWidths()
5038 {
5039 bool nowrap = style()->whiteSpace() == NOWRAP;
5040
5041 RenderObject *child = firstChild();
5042 int floatLeftWidth = 0, floatRightWidth = 0;
5043 while (child) {
5044 // Positioned children don't affect the min/max width
5045 if (child->isPositioned()) {
5046 child = child->nextSibling();
5047 continue;
5048 }
5049
5050 if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) {
5051 int floatTotalWidth = floatLeftWidth + floatRightWidth;
5052 if (child->style()->clear() & CLEFT) {
5053 m_maxPreferredLogicalWidth = max(floatTotalWidth, m_maxPreferredLogicalWidth);
5054 floatLeftWidth = 0;
5055 }
5056 if (child->style()->clear() & CRIGHT) {
5057 m_maxPreferredLogicalWidth = max(floatTotalWidth, m_maxPreferredLogicalWidth);
5058 floatRightWidth = 0;
5059 }
5060 }
5061
5062 // A margin basically has three types: fixed, percentage, and auto (variable).
5063 // Auto and percentage margins simply become 0 when computing min/max width.
5064 // Fixed margins can be added in as is.
5065 Length startMarginLength = child->style()->marginStart();
5066 Length endMarginLength = child->style()->marginEnd();
5067 int margin = 0;
5068 int marginStart = 0;
5069 int marginEnd = 0;
5070 if (startMarginLength.isFixed())
5071 marginStart += startMarginLength.value();
5072 if (endMarginLength.isFixed())
5073 marginEnd += endMarginLength.value();
5074 margin = marginStart + marginEnd;
5075
5076 int w = child->minPreferredLogicalWidth() + margin;
5077 m_minPreferredLogicalWidth = max(w, m_minPreferredLogicalWidth);
5078
5079 // IE ignores tables for calculation of nowrap. Makes some sense.
5080 if (nowrap && !child->isTable())
5081 m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth);
5082
5083 w = child->maxPreferredLogicalWidth() + margin;
5084
5085 if (!child->isFloating()) {
5086 if (child->isBox() && toRenderBox(child)->avoidsFloats()) {
5087 // Determine a left and right max value based off whether or not the floats can fit in the
5088 // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin
5089 // is smaller than the float width.
5090 bool ltr = containingBlock()->style()->isLeftToRightDirection();
5091 int marginLogicalLeft = ltr ? marginStart : marginEnd;
5092 int marginLogicalRight = ltr ? marginEnd : marginStart;
5093 int maxLeft = marginLogicalLeft > 0 ? max(floatLeftWidth, marginLogicalLeft) : floatLeftWidth + marginLogicalLeft;
5094 int maxRight = marginLogicalRight > 0 ? max(floatRightWidth, marginLogicalRight) : floatRightWidth + marginLogicalRight;
5095 w = child->maxPreferredLogicalWidth() + maxLeft + maxRight;
5096 w = max(w, floatLeftWidth + floatRightWidth);
5097 }
5098 else
5099 m_maxPreferredLogicalWidth = max(floatLeftWidth + floatRightWidth, m_maxPreferredLogicalWidth);
5100 floatLeftWidth = floatRightWidth = 0;
5101 }
5102
5103 if (child->isFloating()) {
5104 if (style()->floating() == FLEFT)
5105 floatLeftWidth += w;
5106 else
5107 floatRightWidth += w;
5108 } else
5109 m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth);
5110
5111 // A very specific WinIE quirk.
5112 // Example:
5113 /*
5114 <div style="position:absolute; width:100px; top:50px;">
5115 <div style="position:absolute;left:0px;top:50px;height:50px;background-color:green">
5116 <table style="width:100%"><tr><td></table>
5117 </div>
5118 </div>
5119 */
5120 // In the above example, the inner absolute positioned block should have a computed width
5121 // of 100px because of the table.
5122 // We can achieve this effect by making the maxwidth of blocks that contain tables
5123 // with percentage widths be infinite (as long as they are not inside a table cell).
5124 if (document()->inQuirksMode() && child->style()->logicalWidth().isPercent() &&
5125 !isTableCell() && child->isTable() && m_maxPreferredLogicalWidth < BLOCK_MAX_WIDTH) {
5126 RenderBlock* cb = containingBlock();
5127 while (!cb->isRenderView() && !cb->isTableCell())
5128 cb = cb->containingBlock();
5129 if (!cb->isTableCell())
5130 m_maxPreferredLogicalWidth = BLOCK_MAX_WIDTH;
5131 }
5132
5133 child = child->nextSibling();
5134 }
5135
5136 // Always make sure these values are non-negative.
5137 m_minPreferredLogicalWidth = max(0, m_minPreferredLogicalWidth);
5138 m_maxPreferredLogicalWidth = max(0, m_maxPreferredLogicalWidth);
5139
5140 m_maxPreferredLogicalWidth = max(floatLeftWidth + floatRightWidth, m_maxPreferredLogicalWidth);
5141 }
5142
hasLineIfEmpty() const5143 bool RenderBlock::hasLineIfEmpty() const
5144 {
5145 if (!node())
5146 return false;
5147
5148 if (node()->rendererIsEditable() && node()->rootEditableElement() == node())
5149 return true;
5150
5151 if (node()->isShadowRoot() && (node()->shadowHost()->hasTagName(inputTag)))
5152 return true;
5153
5154 return false;
5155 }
5156
lineHeight(bool firstLine,LineDirectionMode direction,LinePositionMode linePositionMode) const5157 int RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
5158 {
5159 // Inline blocks are replaced elements. Otherwise, just pass off to
5160 // the base class. If we're being queried as though we're the root line
5161 // box, then the fact that we're an inline-block is irrelevant, and we behave
5162 // just like a block.
5163 if (isReplaced() && linePositionMode == PositionOnContainingLine)
5164 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
5165
5166 if (firstLine && document()->usesFirstLineRules()) {
5167 RenderStyle* s = style(firstLine);
5168 if (s != style())
5169 return s->computedLineHeight();
5170 }
5171
5172 if (m_lineHeight == -1)
5173 m_lineHeight = style()->computedLineHeight();
5174
5175 return m_lineHeight;
5176 }
5177
baselinePosition(FontBaseline baselineType,bool firstLine,LineDirectionMode direction,LinePositionMode linePositionMode) const5178 int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
5179 {
5180 // Inline blocks are replaced elements. Otherwise, just pass off to
5181 // the base class. If we're being queried as though we're the root line
5182 // box, then the fact that we're an inline-block is irrelevant, and we behave
5183 // just like a block.
5184 if (isReplaced() && linePositionMode == PositionOnContainingLine) {
5185 // For "leaf" theme objects, let the theme decide what the baseline position is.
5186 // FIXME: Might be better to have a custom CSS property instead, so that if the theme
5187 // is turned off, checkboxes/radios will still have decent baselines.
5188 // FIXME: Need to patch form controls to deal with vertical lines.
5189 if (style()->hasAppearance() && !theme()->isControlContainer(style()->appearance()))
5190 return theme()->baselinePosition(this);
5191
5192 // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in
5193 // the normal flow. We make an exception for marquees, since their baselines are meaningless
5194 // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them.
5195 // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled
5196 // vertically (e.g., an overflow:hidden block that has had scrollTop moved) or if the baseline is outside
5197 // of our content box.
5198 bool ignoreBaseline = (layer() && (layer()->marquee() || (direction == HorizontalLine ? (layer()->verticalScrollbar() || layer()->scrollYOffset() != 0)
5199 : (layer()->horizontalScrollbar() || layer()->scrollXOffset() != 0)))) || (isWritingModeRoot() && !isRubyRun());
5200
5201 int baselinePos = ignoreBaseline ? -1 : lastLineBoxBaseline();
5202
5203 int bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth();
5204 if (baselinePos != -1 && baselinePos <= bottomOfContent)
5205 return direction == HorizontalLine ? marginTop() + baselinePos : marginRight() + baselinePos;
5206
5207 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
5208 }
5209
5210 const FontMetrics& fontMetrics = style(firstLine)->fontMetrics();
5211 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
5212 }
5213
firstLineBoxBaseline() const5214 int RenderBlock::firstLineBoxBaseline() const
5215 {
5216 if (!isBlockFlow() || (isWritingModeRoot() && !isRubyRun()))
5217 return -1;
5218
5219 if (childrenInline()) {
5220 if (firstLineBox())
5221 return firstLineBox()->logicalTop() + style(true)->fontMetrics().ascent(firstRootBox()->baselineType());
5222 else
5223 return -1;
5224 }
5225 else {
5226 for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) {
5227 if (!curr->isFloatingOrPositioned()) {
5228 int result = curr->firstLineBoxBaseline();
5229 if (result != -1)
5230 return curr->logicalTop() + result; // Translate to our coordinate space.
5231 }
5232 }
5233 }
5234
5235 return -1;
5236 }
5237
lastLineBoxBaseline() const5238 int RenderBlock::lastLineBoxBaseline() const
5239 {
5240 if (!isBlockFlow() || (isWritingModeRoot() && !isRubyRun()))
5241 return -1;
5242
5243 LineDirectionMode lineDirection = isHorizontalWritingMode() ? HorizontalLine : VerticalLine;
5244
5245 if (childrenInline()) {
5246 if (!firstLineBox() && hasLineIfEmpty()) {
5247 const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics();
5248 return fontMetrics.ascent()
5249 + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2
5250 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight());
5251 }
5252 if (lastLineBox())
5253 return lastLineBox()->logicalTop() + style(lastLineBox() == firstLineBox())->fontMetrics().ascent(lastRootBox()->baselineType());
5254 return -1;
5255 } else {
5256 bool haveNormalFlowChild = false;
5257 for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) {
5258 if (!curr->isFloatingOrPositioned()) {
5259 haveNormalFlowChild = true;
5260 int result = curr->lastLineBoxBaseline();
5261 if (result != -1)
5262 return curr->logicalTop() + result; // Translate to our coordinate space.
5263 }
5264 }
5265 if (!haveNormalFlowChild && hasLineIfEmpty()) {
5266 const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics();
5267 return fontMetrics.ascent()
5268 + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2
5269 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight());
5270 }
5271 }
5272
5273 return -1;
5274 }
5275
containsNonZeroBidiLevel() const5276 bool RenderBlock::containsNonZeroBidiLevel() const
5277 {
5278 for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) {
5279 for (InlineBox* box = root->firstLeafChild(); box; box = box->nextLeafChild()) {
5280 if (box->bidiLevel())
5281 return true;
5282 }
5283 }
5284 return false;
5285 }
5286
firstLineBlock() const5287 RenderBlock* RenderBlock::firstLineBlock() const
5288 {
5289 RenderBlock* firstLineBlock = const_cast<RenderBlock*>(this);
5290 bool hasPseudo = false;
5291 while (true) {
5292 hasPseudo = firstLineBlock->style()->hasPseudoStyle(FIRST_LINE);
5293 if (hasPseudo)
5294 break;
5295 RenderObject* parentBlock = firstLineBlock->parent();
5296 if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() ||
5297 !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow())
5298 break;
5299 ASSERT(parentBlock->isRenderBlock());
5300 firstLineBlock = toRenderBlock(parentBlock);
5301 }
5302
5303 if (!hasPseudo)
5304 return 0;
5305
5306 return firstLineBlock;
5307 }
5308
styleForFirstLetter(RenderObject * firstLetterBlock,RenderObject * firstLetterContainer)5309 static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer)
5310 {
5311 RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle());
5312 // Force inline display (except for floating first-letters).
5313 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE);
5314 // CSS2 says first-letter can't be positioned.
5315 pseudoStyle->setPosition(StaticPosition);
5316 return pseudoStyle;
5317 }
5318
5319 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter
5320 // "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe),
5321 // "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included"
isPunctuationForFirstLetter(UChar c)5322 static inline bool isPunctuationForFirstLetter(UChar c)
5323 {
5324 CharCategory charCategory = category(c);
5325 return charCategory == Punctuation_Open
5326 || charCategory == Punctuation_Close
5327 || charCategory == Punctuation_InitialQuote
5328 || charCategory == Punctuation_FinalQuote
5329 || charCategory == Punctuation_Other;
5330 }
5331
shouldSkipForFirstLetter(UChar c)5332 static inline bool shouldSkipForFirstLetter(UChar c)
5333 {
5334 return isSpaceOrNewline(c) || c == noBreakSpace || isPunctuationForFirstLetter(c);
5335 }
5336
updateFirstLetter()5337 void RenderBlock::updateFirstLetter()
5338 {
5339 if (!document()->usesFirstLetterRules())
5340 return;
5341 // Don't recur
5342 if (style()->styleType() == FIRST_LETTER)
5343 return;
5344
5345 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find
5346 // an efficient way to check for that situation though before implementing anything.
5347 RenderObject* firstLetterBlock = this;
5348 bool hasPseudoStyle = false;
5349 while (true) {
5350 // We only honor first-letter if the firstLetterBlock can have children in the DOM. This correctly
5351 // prevents form controls from honoring first-letter.
5352 hasPseudoStyle = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER)
5353 && firstLetterBlock->canHaveChildren();
5354 if (hasPseudoStyle)
5355 break;
5356 RenderObject* parentBlock = firstLetterBlock->parent();
5357 if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock ||
5358 !parentBlock->isBlockFlow())
5359 break;
5360 firstLetterBlock = parentBlock;
5361 }
5362
5363 if (!hasPseudoStyle)
5364 return;
5365
5366 // Drill into inlines looking for our first text child.
5367 RenderObject* currChild = firstLetterBlock->firstChild();
5368 while (currChild && ((!currChild->isReplaced() && !currChild->isRenderButton() && !currChild->isMenuList()) || currChild->isFloatingOrPositioned()) && !currChild->isText()) {
5369 if (currChild->isFloatingOrPositioned()) {
5370 if (currChild->style()->styleType() == FIRST_LETTER) {
5371 currChild = currChild->firstChild();
5372 break;
5373 }
5374 currChild = currChild->nextSibling();
5375 } else
5376 currChild = currChild->firstChild();
5377 }
5378
5379 // Get list markers out of the way.
5380 while (currChild && currChild->isListMarker())
5381 currChild = currChild->nextSibling();
5382
5383 if (!currChild)
5384 return;
5385
5386 // If the child already has style, then it has already been created, so we just want
5387 // to update it.
5388 if (currChild->parent()->style()->styleType() == FIRST_LETTER) {
5389 RenderObject* firstLetter = currChild->parent();
5390 RenderObject* firstLetterContainer = firstLetter->parent();
5391 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
5392
5393 if (Node::diff(firstLetter->style(), pseudoStyle) == Node::Detach) {
5394 // The first-letter renderer needs to be replaced. Create a new renderer of the right type.
5395 RenderObject* newFirstLetter;
5396 if (pseudoStyle->display() == INLINE)
5397 newFirstLetter = new (renderArena()) RenderInline(document());
5398 else
5399 newFirstLetter = new (renderArena()) RenderBlock(document());
5400 newFirstLetter->setStyle(pseudoStyle);
5401
5402 // Move the first letter into the new renderer.
5403 view()->disableLayoutState();
5404 while (RenderObject* child = firstLetter->firstChild()) {
5405 if (child->isText())
5406 toRenderText(child)->removeAndDestroyTextBoxes();
5407 firstLetter->removeChild(child);
5408 newFirstLetter->addChild(child, 0);
5409 }
5410
5411 RenderTextFragment* remainingText = 0;
5412 RenderObject* nextSibling = firstLetter->nextSibling();
5413 RenderObject* next = nextSibling;
5414 while (next) {
5415 if (next->isText() && toRenderText(next)->isTextFragment()) {
5416 remainingText = toRenderTextFragment(next);
5417 break;
5418 }
5419 next = next->nextSibling();
5420 }
5421 if (remainingText) {
5422 ASSERT(remainingText->node()->renderer() == remainingText);
5423 // Replace the old renderer with the new one.
5424 remainingText->setFirstLetter(newFirstLetter);
5425 }
5426 firstLetter->destroy();
5427 firstLetter = newFirstLetter;
5428 firstLetterContainer->addChild(firstLetter, nextSibling);
5429 view()->enableLayoutState();
5430 } else
5431 firstLetter->setStyle(pseudoStyle);
5432
5433 for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) {
5434 if (genChild->isText())
5435 genChild->setStyle(pseudoStyle);
5436 }
5437
5438 return;
5439 }
5440
5441 if (!currChild->isText() || currChild->isBR())
5442 return;
5443
5444 // If the child does not already have style, we create it here.
5445 RenderObject* firstLetterContainer = currChild->parent();
5446
5447 // Our layout state is not valid for the repaints we are going to trigger by
5448 // adding and removing children of firstLetterContainer.
5449 view()->disableLayoutState();
5450
5451 RenderText* textObj = toRenderText(currChild);
5452
5453 // Create our pseudo style now that we have our firstLetterContainer determined.
5454 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
5455
5456 RenderObject* firstLetter = 0;
5457 if (pseudoStyle->display() == INLINE)
5458 firstLetter = new (renderArena()) RenderInline(document());
5459 else
5460 firstLetter = new (renderArena()) RenderBlock(document());
5461 firstLetter->setStyle(pseudoStyle);
5462 firstLetterContainer->addChild(firstLetter, currChild);
5463
5464 // The original string is going to be either a generated content string or a DOM node's
5465 // string. We want the original string before it got transformed in case first-letter has
5466 // no text-transform or a different text-transform applied to it.
5467 RefPtr<StringImpl> oldText = textObj->originalText();
5468 ASSERT(oldText);
5469
5470 if (oldText && oldText->length() > 0) {
5471 unsigned length = 0;
5472
5473 // Account for leading spaces and punctuation.
5474 while (length < oldText->length() && shouldSkipForFirstLetter((*oldText)[length]))
5475 length++;
5476
5477 // Account for first letter.
5478 length++;
5479
5480 // Keep looking for whitespace and allowed punctuation, but avoid
5481 // accumulating just whitespace into the :first-letter.
5482 for (unsigned scanLength = length; scanLength < oldText->length(); ++scanLength) {
5483 UChar c = (*oldText)[scanLength];
5484
5485 if (!shouldSkipForFirstLetter(c))
5486 break;
5487
5488 if (isPunctuationForFirstLetter(c))
5489 length = scanLength + 1;
5490 }
5491
5492 // Construct a text fragment for the text after the first letter.
5493 // This text fragment might be empty.
5494 RenderTextFragment* remainingText =
5495 new (renderArena()) RenderTextFragment(textObj->node() ? textObj->node() : textObj->document(), oldText.get(), length, oldText->length() - length);
5496 remainingText->setStyle(textObj->style());
5497 if (remainingText->node())
5498 remainingText->node()->setRenderer(remainingText);
5499
5500 firstLetterContainer->addChild(remainingText, textObj);
5501 firstLetterContainer->removeChild(textObj);
5502 remainingText->setFirstLetter(firstLetter);
5503
5504 // construct text fragment for the first letter
5505 RenderTextFragment* letter =
5506 new (renderArena()) RenderTextFragment(remainingText->node() ? remainingText->node() : remainingText->document(), oldText.get(), 0, length);
5507 letter->setStyle(pseudoStyle);
5508 firstLetter->addChild(letter);
5509
5510 textObj->destroy();
5511 }
5512 view()->enableLayoutState();
5513 }
5514
5515 // Helper methods for obtaining the last line, computing line counts and heights for line counts
5516 // (crawling into blocks).
shouldCheckLines(RenderObject * obj)5517 static bool shouldCheckLines(RenderObject* obj)
5518 {
5519 return !obj->isFloatingOrPositioned() && !obj->isRunIn() &&
5520 obj->isBlockFlow() && obj->style()->height().isAuto() &&
5521 (!obj->isFlexibleBox() || obj->style()->boxOrient() == VERTICAL);
5522 }
5523
getLineAtIndex(RenderBlock * block,int i,int & count)5524 static RootInlineBox* getLineAtIndex(RenderBlock* block, int i, int& count)
5525 {
5526 if (block->style()->visibility() == VISIBLE) {
5527 if (block->childrenInline()) {
5528 for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) {
5529 if (count++ == i)
5530 return box;
5531 }
5532 }
5533 else {
5534 for (RenderObject* obj = block->firstChild(); obj; obj = obj->nextSibling()) {
5535 if (shouldCheckLines(obj)) {
5536 RootInlineBox *box = getLineAtIndex(toRenderBlock(obj), i, count);
5537 if (box)
5538 return box;
5539 }
5540 }
5541 }
5542 }
5543 return 0;
5544 }
5545
getHeightForLineCount(RenderBlock * block,int l,bool includeBottom,int & count)5546 static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, int& count)
5547 {
5548 if (block->style()->visibility() == VISIBLE) {
5549 if (block->childrenInline()) {
5550 for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) {
5551 if (++count == l)
5552 return box->lineBottom() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0);
5553 }
5554 }
5555 else {
5556 RenderBox* normalFlowChildWithoutLines = 0;
5557 for (RenderBox* obj = block->firstChildBox(); obj; obj = obj->nextSiblingBox()) {
5558 if (shouldCheckLines(obj)) {
5559 int result = getHeightForLineCount(toRenderBlock(obj), l, false, count);
5560 if (result != -1)
5561 return result + obj->y() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0);
5562 }
5563 else if (!obj->isFloatingOrPositioned() && !obj->isRunIn())
5564 normalFlowChildWithoutLines = obj;
5565 }
5566 if (normalFlowChildWithoutLines && l == 0)
5567 return normalFlowChildWithoutLines->y() + normalFlowChildWithoutLines->height();
5568 }
5569 }
5570
5571 return -1;
5572 }
5573
lineAtIndex(int i)5574 RootInlineBox* RenderBlock::lineAtIndex(int i)
5575 {
5576 int count = 0;
5577 return getLineAtIndex(this, i, count);
5578 }
5579
lineCount()5580 int RenderBlock::lineCount()
5581 {
5582 int count = 0;
5583 if (style()->visibility() == VISIBLE) {
5584 if (childrenInline())
5585 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox())
5586 count++;
5587 else
5588 for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling())
5589 if (shouldCheckLines(obj))
5590 count += toRenderBlock(obj)->lineCount();
5591 }
5592 return count;
5593 }
5594
heightForLineCount(int l)5595 int RenderBlock::heightForLineCount(int l)
5596 {
5597 int count = 0;
5598 return getHeightForLineCount(this, l, true, count);
5599 }
5600
adjustForBorderFit(int x,int & left,int & right) const5601 void RenderBlock::adjustForBorderFit(int x, int& left, int& right) const
5602 {
5603 // We don't deal with relative positioning. Our assumption is that you shrink to fit the lines without accounting
5604 // for either overflow or translations via relative positioning.
5605 if (style()->visibility() == VISIBLE) {
5606 if (childrenInline()) {
5607 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) {
5608 if (box->firstChild())
5609 left = min(left, x + static_cast<int>(box->firstChild()->x()));
5610 if (box->lastChild())
5611 right = max(right, x + static_cast<int>(ceilf(box->lastChild()->logicalRight())));
5612 }
5613 }
5614 else {
5615 for (RenderBox* obj = firstChildBox(); obj; obj = obj->nextSiblingBox()) {
5616 if (!obj->isFloatingOrPositioned()) {
5617 if (obj->isBlockFlow() && !obj->hasOverflowClip())
5618 toRenderBlock(obj)->adjustForBorderFit(x + obj->x(), left, right);
5619 else if (obj->style()->visibility() == VISIBLE) {
5620 // We are a replaced element or some kind of non-block-flow object.
5621 left = min(left, x + obj->x());
5622 right = max(right, x + obj->x() + obj->width());
5623 }
5624 }
5625 }
5626 }
5627
5628 if (m_floatingObjects) {
5629 FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
5630 FloatingObjectSetIterator end = floatingObjectSet.end();
5631 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
5632 FloatingObject* r = *it;
5633 // Only examine the object if our m_shouldPaint flag is set.
5634 if (r->m_shouldPaint) {
5635 int floatLeft = xPositionForFloatIncludingMargin(r) - r->m_renderer->x();
5636 int floatRight = floatLeft + r->m_renderer->width();
5637 left = min(left, floatLeft);
5638 right = max(right, floatRight);
5639 }
5640 }
5641 }
5642 }
5643 }
5644
borderFitAdjust(int & x,int & w) const5645 void RenderBlock::borderFitAdjust(int& x, int& w) const
5646 {
5647 if (style()->borderFit() == BorderFitBorder)
5648 return;
5649
5650 // Walk any normal flow lines to snugly fit.
5651 int left = INT_MAX;
5652 int right = INT_MIN;
5653 int oldWidth = w;
5654 adjustForBorderFit(0, left, right);
5655 if (left != INT_MAX) {
5656 left -= (borderLeft() + paddingLeft());
5657 if (left > 0) {
5658 x += left;
5659 w -= left;
5660 }
5661 }
5662 if (right != INT_MIN) {
5663 right += (borderRight() + paddingRight());
5664 if (right < oldWidth)
5665 w -= (oldWidth - right);
5666 }
5667 }
5668
clearTruncation()5669 void RenderBlock::clearTruncation()
5670 {
5671 if (style()->visibility() == VISIBLE) {
5672 if (childrenInline() && hasMarkupTruncation()) {
5673 setHasMarkupTruncation(false);
5674 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox())
5675 box->clearTruncation();
5676 }
5677 else
5678 for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling())
5679 if (shouldCheckLines(obj))
5680 toRenderBlock(obj)->clearTruncation();
5681 }
5682 }
5683
setMaxMarginBeforeValues(int pos,int neg)5684 void RenderBlock::setMaxMarginBeforeValues(int pos, int neg)
5685 {
5686 if (!m_rareData) {
5687 if (pos == RenderBlockRareData::positiveMarginBeforeDefault(this) && neg == RenderBlockRareData::negativeMarginBeforeDefault(this))
5688 return;
5689 m_rareData = adoptPtr(new RenderBlockRareData(this));
5690 }
5691 m_rareData->m_margins.setPositiveMarginBefore(pos);
5692 m_rareData->m_margins.setNegativeMarginBefore(neg);
5693 }
5694
setMaxMarginAfterValues(int pos,int neg)5695 void RenderBlock::setMaxMarginAfterValues(int pos, int neg)
5696 {
5697 if (!m_rareData) {
5698 if (pos == RenderBlockRareData::positiveMarginAfterDefault(this) && neg == RenderBlockRareData::negativeMarginAfterDefault(this))
5699 return;
5700 m_rareData = adoptPtr(new RenderBlockRareData(this));
5701 }
5702 m_rareData->m_margins.setPositiveMarginAfter(pos);
5703 m_rareData->m_margins.setNegativeMarginAfter(neg);
5704 }
5705
setPaginationStrut(int strut)5706 void RenderBlock::setPaginationStrut(int strut)
5707 {
5708 if (!m_rareData) {
5709 if (!strut)
5710 return;
5711 m_rareData = adoptPtr(new RenderBlockRareData(this));
5712 }
5713 m_rareData->m_paginationStrut = strut;
5714 }
5715
setPageLogicalOffset(int logicalOffset)5716 void RenderBlock::setPageLogicalOffset(int logicalOffset)
5717 {
5718 if (!m_rareData) {
5719 if (!logicalOffset)
5720 return;
5721 m_rareData = adoptPtr(new RenderBlockRareData(this));
5722 }
5723 m_rareData->m_pageLogicalOffset = logicalOffset;
5724 }
5725
setBreakAtLineToAvoidWidow(RootInlineBox * lineToBreak)5726 void RenderBlock::setBreakAtLineToAvoidWidow(RootInlineBox* lineToBreak)
5727 {
5728 ASSERT(lineToBreak);
5729 if (!m_rareData)
5730 m_rareData = adoptPtr(new RenderBlockRareData(this));
5731 m_rareData->m_shouldBreakAtLineToAvoidWidow = true;
5732 m_rareData->m_lineBreakToAvoidWidow = lineToBreak;
5733 }
5734
clearShouldBreakAtLineToAvoidWidow() const5735 void RenderBlock::clearShouldBreakAtLineToAvoidWidow() const
5736 {
5737 if (!m_rareData)
5738 return;
5739 m_rareData->m_shouldBreakAtLineToAvoidWidow = false;
5740 m_rareData->m_lineBreakToAvoidWidow = 0;
5741 }
5742
absoluteRects(Vector<IntRect> & rects,int tx,int ty)5743 void RenderBlock::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
5744 {
5745 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
5746 // inline boxes above and below us (thus getting merged with them to form a single irregular
5747 // shape).
5748 if (isAnonymousBlockContinuation()) {
5749 // FIXME: This is wrong for block-flows that are horizontal.
5750 // https://bugs.webkit.org/show_bug.cgi?id=46781
5751 rects.append(IntRect(tx, ty - collapsedMarginBefore(),
5752 width(), height() + collapsedMarginBefore() + collapsedMarginAfter()));
5753 continuation()->absoluteRects(rects,
5754 tx - x() + inlineElementContinuation()->containingBlock()->x(),
5755 ty - y() + inlineElementContinuation()->containingBlock()->y());
5756 } else
5757 rects.append(IntRect(tx, ty, width(), height()));
5758 }
5759
absoluteQuads(Vector<FloatQuad> & quads)5760 void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads)
5761 {
5762 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
5763 // inline boxes above and below us (thus getting merged with them to form a single irregular
5764 // shape).
5765 if (isAnonymousBlockContinuation()) {
5766 // FIXME: This is wrong for block-flows that are horizontal.
5767 // https://bugs.webkit.org/show_bug.cgi?id=46781
5768 FloatRect localRect(0, -collapsedMarginBefore(),
5769 width(), height() + collapsedMarginBefore() + collapsedMarginAfter());
5770 quads.append(localToAbsoluteQuad(localRect));
5771 continuation()->absoluteQuads(quads);
5772 } else
5773 quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width(), height())));
5774 }
5775
rectWithOutlineForRepaint(RenderBoxModelObject * repaintContainer,int outlineWidth)5776 IntRect RenderBlock::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth)
5777 {
5778 IntRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
5779 if (isAnonymousBlockContinuation())
5780 r.inflateY(collapsedMarginBefore()); // FIXME: This is wrong for block-flows that are horizontal.
5781 return r;
5782 }
5783
hoverAncestor() const5784 RenderObject* RenderBlock::hoverAncestor() const
5785 {
5786 return isAnonymousBlockContinuation() ? continuation() : RenderBox::hoverAncestor();
5787 }
5788
updateDragState(bool dragOn)5789 void RenderBlock::updateDragState(bool dragOn)
5790 {
5791 RenderBox::updateDragState(dragOn);
5792 if (continuation())
5793 continuation()->updateDragState(dragOn);
5794 }
5795
outlineStyleForRepaint() const5796 RenderStyle* RenderBlock::outlineStyleForRepaint() const
5797 {
5798 return isAnonymousBlockContinuation() ? continuation()->style() : style();
5799 }
5800
childBecameNonInline(RenderObject *)5801 void RenderBlock::childBecameNonInline(RenderObject*)
5802 {
5803 makeChildrenNonInline();
5804 if (isAnonymousBlock() && parent() && parent()->isRenderBlock())
5805 toRenderBlock(parent())->removeLeftoverAnonymousBlock(this);
5806 // |this| may be dead here
5807 }
5808
updateHitTestResult(HitTestResult & result,const IntPoint & point)5809 void RenderBlock::updateHitTestResult(HitTestResult& result, const IntPoint& point)
5810 {
5811 if (result.innerNode())
5812 return;
5813
5814 Node* n = node();
5815 if (isAnonymousBlockContinuation())
5816 // We are in the margins of block elements that are part of a continuation. In
5817 // this case we're actually still inside the enclosing element that was
5818 // split. Go ahead and set our inner node accordingly.
5819 n = continuation()->node();
5820
5821 if (n) {
5822 result.setInnerNode(n);
5823 if (!result.innerNonSharedNode())
5824 result.setInnerNonSharedNode(n);
5825 result.setLocalPoint(point);
5826 }
5827 }
5828
localCaretRect(InlineBox * inlineBox,int caretOffset,int * extraWidthToEndOfLine)5829 IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
5830 {
5831 // Do the normal calculation in most cases.
5832 if (firstChild())
5833 return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine);
5834
5835 // This is a special case:
5836 // The element is not an inline element, and it's empty. So we have to
5837 // calculate a fake position to indicate where objects are to be inserted.
5838
5839 // FIXME: This does not take into account either :first-line or :first-letter
5840 // However, as soon as some content is entered, the line boxes will be
5841 // constructed and this kludge is not called any more. So only the caret size
5842 // of an empty :first-line'd block is wrong. I think we can live with that.
5843 RenderStyle* currentStyle = firstLineStyle();
5844 int height = lineHeight(true, currentStyle->isHorizontalWritingMode() ? HorizontalLine : VerticalLine);
5845
5846 enum CaretAlignment { alignLeft, alignRight, alignCenter };
5847
5848 CaretAlignment alignment = alignLeft;
5849
5850 switch (currentStyle->textAlign()) {
5851 case TAAUTO:
5852 case JUSTIFY:
5853 if (!currentStyle->isLeftToRightDirection())
5854 alignment = alignRight;
5855 break;
5856 case LEFT:
5857 case WEBKIT_LEFT:
5858 break;
5859 case CENTER:
5860 case WEBKIT_CENTER:
5861 alignment = alignCenter;
5862 break;
5863 case RIGHT:
5864 case WEBKIT_RIGHT:
5865 alignment = alignRight;
5866 break;
5867 case TASTART:
5868 if (!currentStyle->isLeftToRightDirection())
5869 alignment = alignRight;
5870 break;
5871 case TAEND:
5872 if (currentStyle->isLeftToRightDirection())
5873 alignment = alignRight;
5874 break;
5875 }
5876
5877 int x = borderLeft() + paddingLeft();
5878 int w = width();
5879
5880 switch (alignment) {
5881 case alignLeft:
5882 break;
5883 case alignCenter:
5884 x = (x + w - (borderRight() + paddingRight())) / 2;
5885 break;
5886 case alignRight:
5887 x = w - (borderRight() + paddingRight()) - caretWidth;
5888 break;
5889 }
5890
5891 if (extraWidthToEndOfLine) {
5892 if (isRenderBlock()) {
5893 *extraWidthToEndOfLine = w - (x + caretWidth);
5894 } else {
5895 // FIXME: This code looks wrong.
5896 // myRight and containerRight are set up, but then clobbered.
5897 // So *extraWidthToEndOfLine will always be 0 here.
5898
5899 int myRight = x + caretWidth;
5900 // FIXME: why call localToAbsoluteForContent() twice here, too?
5901 FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0));
5902
5903 int containerRight = containingBlock()->x() + containingBlockLogicalWidthForContent();
5904 FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0));
5905
5906 *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x();
5907 }
5908 }
5909
5910 int y = paddingTop() + borderTop();
5911
5912 return IntRect(x, y, caretWidth, height);
5913 }
5914
addFocusRingRects(Vector<IntRect> & rects,int tx,int ty)5915 void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
5916 {
5917 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
5918 // inline boxes above and below us (thus getting merged with them to form a single irregular
5919 // shape).
5920 if (inlineElementContinuation()) {
5921 // FIXME: This check really isn't accurate.
5922 bool nextInlineHasLineBox = inlineElementContinuation()->firstLineBox();
5923 // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block.
5924 // FIXME: This is wrong for block-flows that are horizontal.
5925 // https://bugs.webkit.org/show_bug.cgi?id=46781
5926 bool prevInlineHasLineBox = toRenderInline(inlineElementContinuation()->node()->renderer())->firstLineBox();
5927 int topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : 0;
5928 int bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : 0;
5929 IntRect rect(tx, ty - topMargin, width(), height() + topMargin + bottomMargin);
5930 if (!rect.isEmpty())
5931 rects.append(rect);
5932 } else if (width() && height())
5933 rects.append(IntRect(tx, ty, width(), height()));
5934
5935 if (!hasOverflowClip() && !hasControlClip()) {
5936 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
5937 int top = max(curr->lineTop(), curr->logicalTop());
5938 int bottom = min(curr->lineBottom(), curr->logicalTop() + curr->logicalHeight());
5939 IntRect rect(tx + curr->x(), ty + top, curr->logicalWidth(), bottom - top);
5940 if (!rect.isEmpty())
5941 rects.append(rect);
5942 }
5943
5944 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
5945 if (!curr->isText() && !curr->isListMarker() && curr->isBox()) {
5946 RenderBox* box = toRenderBox(curr);
5947 FloatPoint pos;
5948 // FIXME: This doesn't work correctly with transforms.
5949 if (box->layer())
5950 pos = curr->localToAbsolute();
5951 else
5952 pos = FloatPoint(tx + box->x(), ty + box->y());
5953 box->addFocusRingRects(rects, pos.x(), pos.y());
5954 }
5955 }
5956 }
5957
5958 if (inlineElementContinuation())
5959 inlineElementContinuation()->addFocusRingRects(rects,
5960 tx - x() + inlineElementContinuation()->containingBlock()->x(),
5961 ty - y() + inlineElementContinuation()->containingBlock()->y());
5962 }
5963
createAnonymousBlock(bool isFlexibleBox) const5964 RenderBlock* RenderBlock::createAnonymousBlock(bool isFlexibleBox) const
5965 {
5966 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style());
5967
5968 RenderBlock* newBox = 0;
5969 if (isFlexibleBox) {
5970 newStyle->setDisplay(BOX);
5971 newBox = new (renderArena()) RenderFlexibleBox(document() /* anonymous box */);
5972 } else {
5973 newStyle->setDisplay(BLOCK);
5974 newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
5975 }
5976
5977 newBox->setStyle(newStyle.release());
5978 return newBox;
5979 }
5980
createAnonymousBlockWithSameTypeAs(RenderBlock * otherAnonymousBlock) const5981 RenderBlock* RenderBlock::createAnonymousBlockWithSameTypeAs(RenderBlock* otherAnonymousBlock) const
5982 {
5983 if (otherAnonymousBlock->isAnonymousColumnsBlock())
5984 return createAnonymousColumnsBlock();
5985 if (otherAnonymousBlock->isAnonymousColumnSpanBlock())
5986 return createAnonymousColumnSpanBlock();
5987 return createAnonymousBlock(otherAnonymousBlock->style()->display() == BOX);
5988 }
5989
createAnonymousColumnsBlock() const5990 RenderBlock* RenderBlock::createAnonymousColumnsBlock() const
5991 {
5992 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style());
5993 newStyle->inheritColumnPropertiesFrom(style());
5994 newStyle->setDisplay(BLOCK);
5995
5996 RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
5997 newBox->setStyle(newStyle.release());
5998 return newBox;
5999 }
6000
createAnonymousColumnSpanBlock() const6001 RenderBlock* RenderBlock::createAnonymousColumnSpanBlock() const
6002 {
6003 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style());
6004 newStyle->setColumnSpan(true);
6005 newStyle->setDisplay(BLOCK);
6006
6007 RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
6008 newBox->setStyle(newStyle.release());
6009 return newBox;
6010 }
6011
nextPageLogicalTop(int logicalOffset) const6012 int RenderBlock::nextPageLogicalTop(int logicalOffset) const
6013 {
6014 LayoutState* layoutState = view()->layoutState();
6015 if (!layoutState->m_pageLogicalHeight)
6016 return logicalOffset;
6017
6018 // The logicalOffset is in our coordinate space. We can add in our pushed offset.
6019 int pageLogicalHeight = layoutState->m_pageLogicalHeight;
6020 IntSize delta = layoutState->m_layoutOffset - layoutState->m_pageOffset;
6021 int offset = isHorizontalWritingMode() ? delta.height() : delta.width();
6022 int remainingLogicalHeight = (pageLogicalHeight - (offset + logicalOffset) % pageLogicalHeight) % pageLogicalHeight;
6023 return logicalOffset + remainingLogicalHeight;
6024 }
6025
inNormalFlow(RenderBox * child)6026 static bool inNormalFlow(RenderBox* child)
6027 {
6028 RenderBlock* curr = child->containingBlock();
6029 RenderBlock* initialBlock = child->view();
6030 while (curr && curr != initialBlock) {
6031 if (curr->hasColumns())
6032 return true;
6033 if (curr->isFloatingOrPositioned())
6034 return false;
6035 curr = curr->containingBlock();
6036 }
6037 return true;
6038 }
6039
applyBeforeBreak(RenderBox * child,int logicalOffset)6040 int RenderBlock::applyBeforeBreak(RenderBox* child, int logicalOffset)
6041 {
6042 // FIXME: Add page break checking here when we support printing.
6043 bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns();
6044 bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this.
6045 bool checkBeforeAlways = (checkColumnBreaks && child->style()->columnBreakBefore() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakBefore() == PBALWAYS);
6046 if (checkBeforeAlways && inNormalFlow(child)) {
6047 if (checkColumnBreaks)
6048 view()->layoutState()->addForcedColumnBreak(logicalOffset);
6049 return nextPageLogicalTop(logicalOffset);
6050 }
6051 return logicalOffset;
6052 }
6053
applyAfterBreak(RenderBox * child,int logicalOffset,MarginInfo & marginInfo)6054 int RenderBlock::applyAfterBreak(RenderBox* child, int logicalOffset, MarginInfo& marginInfo)
6055 {
6056 // FIXME: Add page break checking here when we support printing.
6057 bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns();
6058 bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this.
6059 bool checkAfterAlways = (checkColumnBreaks && child->style()->columnBreakAfter() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakAfter() == PBALWAYS);
6060 if (checkAfterAlways && inNormalFlow(child)) {
6061 marginInfo.setMarginAfterQuirk(true); // Cause margins to be discarded for any following content.
6062 if (checkColumnBreaks)
6063 view()->layoutState()->addForcedColumnBreak(logicalOffset);
6064 return nextPageLogicalTop(logicalOffset);
6065 }
6066 return logicalOffset;
6067 }
6068
adjustForUnsplittableChild(RenderBox * child,int logicalOffset,bool includeMargins)6069 int RenderBlock::adjustForUnsplittableChild(RenderBox* child, int logicalOffset, bool includeMargins)
6070 {
6071 bool isUnsplittable = child->isReplaced() || child->scrollsOverflow() ||
6072 child->style()->pageBreakInside() == PBAVOID;
6073 if (!isUnsplittable)
6074 return logicalOffset;
6075 int childLogicalHeight = logicalHeightForChild(child) + (includeMargins ? marginBeforeForChild(child) + marginAfterForChild(child) : 0);
6076 LayoutState* layoutState = view()->layoutState();
6077 if (layoutState->m_columnInfo)
6078 layoutState->m_columnInfo->updateMinimumColumnHeight(childLogicalHeight);
6079 int pageLogicalHeight = layoutState->m_pageLogicalHeight;
6080 if (!pageLogicalHeight || childLogicalHeight > pageLogicalHeight)
6081 return logicalOffset;
6082 IntSize delta = layoutState->m_layoutOffset - layoutState->m_pageOffset;
6083 int offset = isHorizontalWritingMode() ? delta.height() : delta.width();
6084 int remainingLogicalHeight = (pageLogicalHeight - (offset + logicalOffset) % pageLogicalHeight) % pageLogicalHeight;
6085 if (remainingLogicalHeight < childLogicalHeight)
6086 return logicalOffset + remainingLogicalHeight;
6087 return logicalOffset;
6088 }
6089
adjustLinePositionForPagination(RootInlineBox * lineBox,int & delta)6090 void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, int& delta)
6091 {
6092 // FIXME: For now we paginate using line overflow. This ensures that lines don't overlap at all when we
6093 // put a strut between them for pagination purposes. However, this really isn't the desired rendering, since
6094 // the line on the top of the next page will appear too far down relative to the same kind of line at the top
6095 // of the first column.
6096 //
6097 // The rendering we would like to see is one where the lineTop is at the top of the column, and any line overflow
6098 // simply spills out above the top of the column. This effect would match what happens at the top of the first column.
6099 // We can't achieve this rendering, however, until we stop columns from clipping to the column bounds (thus allowing
6100 // for overflow to occur), and then cache visible overflow for each column rect.
6101 //
6102 // Furthermore, the paint we have to do when a column has overflow has to be special. We need to exclude
6103 // content that paints in a previous column (and content that paints in the following column).
6104 //
6105 // FIXME: Another problem with simply moving lines is that the available line width may change (because of floats).
6106 // Technically if the location we move the line to has a different line width than our old position, then we need to dirty the
6107 // line and all following lines.
6108 LayoutState* layoutState = view()->layoutState();
6109 int pageLogicalHeight = layoutState->m_pageLogicalHeight;
6110 IntRect logicalVisualOverflow = lineBox->logicalVisualOverflowRect(lineBox->lineTop(), lineBox->lineBottom());
6111 int logicalOffset = logicalVisualOverflow.y();
6112 int lineHeight = logicalVisualOverflow.maxY() - logicalOffset;
6113 if (layoutState->m_columnInfo)
6114 layoutState->m_columnInfo->updateMinimumColumnHeight(lineHeight);
6115 logicalOffset += delta;
6116 lineBox->setPaginationStrut(0);
6117 lineBox->setIsFirstAfterPageBreak(false);
6118 if (!pageLogicalHeight || lineHeight > pageLogicalHeight)
6119 return;
6120 IntSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset;
6121 int offset = isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
6122 int remainingLogicalHeight = pageLogicalHeight - (offset + logicalOffset) % pageLogicalHeight;
6123 if (remainingLogicalHeight < lineHeight || (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineBox)) {
6124 if (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineBox)
6125 clearShouldBreakAtLineToAvoidWidow();
6126 int totalLogicalHeight = lineHeight + max(0, logicalOffset);
6127 if (((lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeight) || (!style()->hasAutoOrphans() && style()->orphans() >= lineCount()))
6128 && !isPositioned() && !isTableCell())
6129 setPaginationStrut(remainingLogicalHeight + max(0, logicalOffset));
6130 else {
6131 delta += remainingLogicalHeight;
6132 lineBox->setPaginationStrut(remainingLogicalHeight);
6133 lineBox->setIsFirstAfterPageBreak(true);
6134 }
6135 } else if (remainingLogicalHeight == pageLogicalHeight && lineBox != firstRootBox())
6136 lineBox->setIsFirstAfterPageBreak(true);
6137 }
6138
collapsedMarginBeforeForChild(RenderBox * child) const6139 int RenderBlock::collapsedMarginBeforeForChild(RenderBox* child) const
6140 {
6141 // If the child has the same directionality as we do, then we can just return its
6142 // collapsed margin.
6143 if (!child->isWritingModeRoot())
6144 return child->collapsedMarginBefore();
6145
6146 // The child has a different directionality. If the child is parallel, then it's just
6147 // flipped relative to us. We can use the collapsed margin for the opposite edge.
6148 if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
6149 return child->collapsedMarginAfter();
6150
6151 // The child is perpendicular to us, which means its margins don't collapse but are on the
6152 // "logical left/right" sides of the child box. We can just return the raw margin in this case.
6153 return marginBeforeForChild(child);
6154 }
6155
collapsedMarginAfterForChild(RenderBox * child) const6156 int RenderBlock::collapsedMarginAfterForChild(RenderBox* child) const
6157 {
6158 // If the child has the same directionality as we do, then we can just return its
6159 // collapsed margin.
6160 if (!child->isWritingModeRoot())
6161 return child->collapsedMarginAfter();
6162
6163 // The child has a different directionality. If the child is parallel, then it's just
6164 // flipped relative to us. We can use the collapsed margin for the opposite edge.
6165 if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
6166 return child->collapsedMarginBefore();
6167
6168 // The child is perpendicular to us, which means its margins don't collapse but are on the
6169 // "logical left/right" side of the child box. We can just return the raw margin in this case.
6170 return marginAfterForChild(child);
6171 }
6172
marginBeforeForChild(RenderBoxModelObject * child) const6173 int RenderBlock::marginBeforeForChild(RenderBoxModelObject* child) const
6174 {
6175 switch (style()->writingMode()) {
6176 case TopToBottomWritingMode:
6177 return child->marginTop();
6178 case BottomToTopWritingMode:
6179 return child->marginBottom();
6180 case LeftToRightWritingMode:
6181 return child->marginLeft();
6182 case RightToLeftWritingMode:
6183 return child->marginRight();
6184 }
6185 ASSERT_NOT_REACHED();
6186 return child->marginTop();
6187 }
6188
marginAfterForChild(RenderBoxModelObject * child) const6189 int RenderBlock::marginAfterForChild(RenderBoxModelObject* child) const
6190 {
6191 switch (style()->writingMode()) {
6192 case TopToBottomWritingMode:
6193 return child->marginBottom();
6194 case BottomToTopWritingMode:
6195 return child->marginTop();
6196 case LeftToRightWritingMode:
6197 return child->marginRight();
6198 case RightToLeftWritingMode:
6199 return child->marginLeft();
6200 }
6201 ASSERT_NOT_REACHED();
6202 return child->marginBottom();
6203 }
6204
marginStartForChild(RenderBoxModelObject * child) const6205 int RenderBlock::marginStartForChild(RenderBoxModelObject* child) const
6206 {
6207 if (isHorizontalWritingMode())
6208 return style()->isLeftToRightDirection() ? child->marginLeft() : child->marginRight();
6209 return style()->isLeftToRightDirection() ? child->marginTop() : child->marginBottom();
6210 }
6211
marginEndForChild(RenderBoxModelObject * child) const6212 int RenderBlock::marginEndForChild(RenderBoxModelObject* child) const
6213 {
6214 if (isHorizontalWritingMode())
6215 return style()->isLeftToRightDirection() ? child->marginRight() : child->marginLeft();
6216 return style()->isLeftToRightDirection() ? child->marginBottom() : child->marginTop();
6217 }
6218
setMarginStartForChild(RenderBox * child,int margin)6219 void RenderBlock::setMarginStartForChild(RenderBox* child, int margin)
6220 {
6221 if (isHorizontalWritingMode()) {
6222 if (style()->isLeftToRightDirection())
6223 child->setMarginLeft(margin);
6224 else
6225 child->setMarginRight(margin);
6226 } else {
6227 if (style()->isLeftToRightDirection())
6228 child->setMarginTop(margin);
6229 else
6230 child->setMarginBottom(margin);
6231 }
6232 }
6233
setMarginEndForChild(RenderBox * child,int margin)6234 void RenderBlock::setMarginEndForChild(RenderBox* child, int margin)
6235 {
6236 if (isHorizontalWritingMode()) {
6237 if (style()->isLeftToRightDirection())
6238 child->setMarginRight(margin);
6239 else
6240 child->setMarginLeft(margin);
6241 } else {
6242 if (style()->isLeftToRightDirection())
6243 child->setMarginBottom(margin);
6244 else
6245 child->setMarginTop(margin);
6246 }
6247 }
6248
setMarginBeforeForChild(RenderBox * child,int margin)6249 void RenderBlock::setMarginBeforeForChild(RenderBox* child, int margin)
6250 {
6251 switch (style()->writingMode()) {
6252 case TopToBottomWritingMode:
6253 child->setMarginTop(margin);
6254 break;
6255 case BottomToTopWritingMode:
6256 child->setMarginBottom(margin);
6257 break;
6258 case LeftToRightWritingMode:
6259 child->setMarginLeft(margin);
6260 break;
6261 case RightToLeftWritingMode:
6262 child->setMarginRight(margin);
6263 break;
6264 }
6265 }
6266
setMarginAfterForChild(RenderBox * child,int margin)6267 void RenderBlock::setMarginAfterForChild(RenderBox* child, int margin)
6268 {
6269 switch (style()->writingMode()) {
6270 case TopToBottomWritingMode:
6271 child->setMarginBottom(margin);
6272 break;
6273 case BottomToTopWritingMode:
6274 child->setMarginTop(margin);
6275 break;
6276 case LeftToRightWritingMode:
6277 child->setMarginRight(margin);
6278 break;
6279 case RightToLeftWritingMode:
6280 child->setMarginLeft(margin);
6281 break;
6282 }
6283 }
6284
marginValuesForChild(RenderBox * child)6285 RenderBlock::MarginValues RenderBlock::marginValuesForChild(RenderBox* child)
6286 {
6287 int childBeforePositive = 0;
6288 int childBeforeNegative = 0;
6289 int childAfterPositive = 0;
6290 int childAfterNegative = 0;
6291
6292 int beforeMargin = 0;
6293 int afterMargin = 0;
6294
6295 RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0;
6296
6297 // If the child has the same directionality as we do, then we can just return its
6298 // margins in the same direction.
6299 if (!child->isWritingModeRoot()) {
6300 if (childRenderBlock) {
6301 childBeforePositive = childRenderBlock->maxPositiveMarginBefore();
6302 childBeforeNegative = childRenderBlock->maxNegativeMarginBefore();
6303 childAfterPositive = childRenderBlock->maxPositiveMarginAfter();
6304 childAfterNegative = childRenderBlock->maxNegativeMarginAfter();
6305 } else {
6306 beforeMargin = child->marginBefore();
6307 afterMargin = child->marginAfter();
6308 }
6309 } else if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) {
6310 // The child has a different directionality. If the child is parallel, then it's just
6311 // flipped relative to us. We can use the margins for the opposite edges.
6312 if (childRenderBlock) {
6313 childBeforePositive = childRenderBlock->maxPositiveMarginAfter();
6314 childBeforeNegative = childRenderBlock->maxNegativeMarginAfter();
6315 childAfterPositive = childRenderBlock->maxPositiveMarginBefore();
6316 childAfterNegative = childRenderBlock->maxNegativeMarginBefore();
6317 } else {
6318 beforeMargin = child->marginAfter();
6319 afterMargin = child->marginBefore();
6320 }
6321 } else {
6322 // The child is perpendicular to us, which means its margins don't collapse but are on the
6323 // "logical left/right" sides of the child box. We can just return the raw margin in this case.
6324 beforeMargin = marginBeforeForChild(child);
6325 afterMargin = marginAfterForChild(child);
6326 }
6327
6328 // Resolve uncollapsing margins into their positive/negative buckets.
6329 if (beforeMargin) {
6330 if (beforeMargin > 0)
6331 childBeforePositive = beforeMargin;
6332 else
6333 childBeforeNegative = -beforeMargin;
6334 }
6335 if (afterMargin) {
6336 if (afterMargin > 0)
6337 childAfterPositive = afterMargin;
6338 else
6339 childAfterNegative = -afterMargin;
6340 }
6341
6342 return MarginValues(childBeforePositive, childBeforeNegative, childAfterPositive, childAfterNegative);
6343 }
6344
renderName() const6345 const char* RenderBlock::renderName() const
6346 {
6347 if (isBody())
6348 return "RenderBody"; // FIXME: Temporary hack until we know that the regression tests pass.
6349
6350 if (isFloating())
6351 return "RenderBlock (floating)";
6352 if (isPositioned())
6353 return "RenderBlock (positioned)";
6354 if (isAnonymousColumnsBlock())
6355 return "RenderBlock (anonymous multi-column)";
6356 if (isAnonymousColumnSpanBlock())
6357 return "RenderBlock (anonymous multi-column span)";
6358 if (isAnonymousBlock())
6359 return "RenderBlock (anonymous)";
6360 else if (isAnonymous())
6361 return "RenderBlock (generated)";
6362 if (isRelPositioned())
6363 return "RenderBlock (relative positioned)";
6364 if (isRunIn())
6365 return "RenderBlock (run-in)";
6366 return "RenderBlock";
6367 }
6368
clear()6369 inline void RenderBlock::FloatingObjects::clear()
6370 {
6371 m_set.clear();
6372 m_leftObjectsCount = 0;
6373 m_rightObjectsCount = 0;
6374 }
6375
increaseObjectsCount(FloatingObject::Type type)6376 inline void RenderBlock::FloatingObjects::increaseObjectsCount(FloatingObject::Type type)
6377 {
6378 if (type == FloatingObject::FloatLeft)
6379 m_leftObjectsCount++;
6380 else
6381 m_rightObjectsCount++;
6382 }
6383
decreaseObjectsCount(FloatingObject::Type type)6384 inline void RenderBlock::FloatingObjects::decreaseObjectsCount(FloatingObject::Type type)
6385 {
6386 if (type == FloatingObject::FloatLeft)
6387 m_leftObjectsCount--;
6388 else
6389 m_rightObjectsCount--;
6390 }
6391
6392
6393 #ifndef NDEBUG
6394
showLineTreeAndMark(const InlineBox * markedBox1,const char * markedLabel1,const InlineBox * markedBox2,const char * markedLabel2,const RenderObject * obj) const6395 void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj) const
6396 {
6397 showRenderObject();
6398 for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox())
6399 root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, 1);
6400 }
6401
6402 #endif
6403
6404 } // namespace WebCore
6405