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, 2011 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 "core/rendering/RenderBlock.h"
26
27 #include "HTMLNames.h"
28 #include "core/accessibility/AXObjectCache.h"
29 #include "core/dom/Document.h"
30 #include "core/dom/Element.h"
31 #include "core/events/OverflowEvent.h"
32 #include "core/dom/shadow/ShadowRoot.h"
33 #include "core/editing/Editor.h"
34 #include "core/editing/FrameSelection.h"
35 #include "core/fetch/ResourceLoadPriorityOptimizer.h"
36 #include "core/frame/Frame.h"
37 #include "core/frame/FrameView.h"
38 #include "core/page/Page.h"
39 #include "core/frame/Settings.h"
40 #include "core/rendering/FastTextAutosizer.h"
41 #include "core/rendering/GraphicsContextAnnotator.h"
42 #include "core/rendering/HitTestLocation.h"
43 #include "core/rendering/HitTestResult.h"
44 #include "core/rendering/InlineIterator.h"
45 #include "core/rendering/InlineTextBox.h"
46 #include "core/rendering/LayoutRectRecorder.h"
47 #include "core/rendering/LayoutRepainter.h"
48 #include "core/rendering/PaintInfo.h"
49 #include "core/rendering/RenderCombineText.h"
50 #include "core/rendering/RenderDeprecatedFlexibleBox.h"
51 #include "core/rendering/RenderFlexibleBox.h"
52 #include "core/rendering/RenderInline.h"
53 #include "core/rendering/RenderLayer.h"
54 #include "core/rendering/RenderMarquee.h"
55 #include "core/rendering/RenderNamedFlowThread.h"
56 #include "core/rendering/RenderRegion.h"
57 #include "core/rendering/RenderTableCell.h"
58 #include "core/rendering/RenderTextFragment.h"
59 #include "core/rendering/RenderTheme.h"
60 #include "core/rendering/RenderView.h"
61 #include "core/rendering/shapes/ShapeOutsideInfo.h"
62 #include "core/rendering/style/ContentData.h"
63 #include "core/rendering/style/RenderStyle.h"
64 #include "platform/geometry/FloatQuad.h"
65 #include "platform/geometry/TransformState.h"
66 #include "platform/graphics/GraphicsContextStateSaver.h"
67 #include "wtf/StdLibExtras.h"
68 #include "wtf/TemporaryChange.h"
69
70 using namespace std;
71 using namespace WTF;
72 using namespace Unicode;
73
74 namespace WebCore {
75
76 using namespace HTMLNames;
77
78 struct SameSizeAsRenderBlock : public RenderBox {
79 void* pointers[1];
80 RenderObjectChildList children;
81 RenderLineBoxList lineBoxes;
82 uint32_t bitfields;
83 };
84
85 COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small);
86
87 typedef WTF::HashMap<const RenderBox*, OwnPtr<ColumnInfo> > ColumnInfoMap;
88 static ColumnInfoMap* gColumnInfoMap = 0;
89
90 static TrackedDescendantsMap* gPositionedDescendantsMap = 0;
91 static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0;
92
93 static TrackedContainerMap* gPositionedContainerMap = 0;
94 static TrackedContainerMap* gPercentHeightContainerMap = 0;
95
96 typedef WTF::HashMap<RenderBlock*, OwnPtr<ListHashSet<RenderInline*> > > ContinuationOutlineTableMap;
97
98 typedef WTF::HashSet<RenderBlock*> DelayedUpdateScrollInfoSet;
99 static int gDelayUpdateScrollInfo = 0;
100 static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0;
101
102 static bool gColumnFlowSplitEnabled = true;
103
104 // This class helps dispatching the 'overflow' event on layout change. overflow can be set on RenderBoxes, yet the existing code
105 // only works on RenderBlocks. If this changes, this class should be shared with other RenderBoxes.
106 class OverflowEventDispatcher {
107 WTF_MAKE_NONCOPYABLE(OverflowEventDispatcher);
108 public:
OverflowEventDispatcher(const RenderBlock * block)109 OverflowEventDispatcher(const RenderBlock* block)
110 : m_block(block)
111 {
112 m_shouldDispatchEvent = !m_block->isAnonymous() && m_block->hasOverflowClip() && m_block->document().hasListenerType(Document::OVERFLOWCHANGED_LISTENER);
113 if (m_shouldDispatchEvent) {
114 m_hadHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow();
115 m_hadVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow();
116 }
117 }
118
~OverflowEventDispatcher()119 ~OverflowEventDispatcher()
120 {
121 if (!m_shouldDispatchEvent)
122 return;
123
124 bool hasHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow();
125 bool hasVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow();
126
127 bool horizontalLayoutOverflowChanged = hasHorizontalLayoutOverflow != m_hadHorizontalLayoutOverflow;
128 bool verticalLayoutOverflowChanged = hasVerticalLayoutOverflow != m_hadVerticalLayoutOverflow;
129
130 if (!horizontalLayoutOverflowChanged && !verticalLayoutOverflowChanged)
131 return;
132
133 RefPtr<OverflowEvent> event = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow);
134 event->setTarget(m_block->node());
135 m_block->document().enqueueAnimationFrameEvent(event.release());
136 }
137
138 private:
139 const RenderBlock* m_block;
140 bool m_shouldDispatchEvent;
141 bool m_hadHorizontalLayoutOverflow;
142 bool m_hadVerticalLayoutOverflow;
143 };
144
RenderBlock(ContainerNode * node)145 RenderBlock::RenderBlock(ContainerNode* node)
146 : RenderBox(node)
147 , m_lineHeight(-1)
148 , m_hasMarginBeforeQuirk(false)
149 , m_hasMarginAfterQuirk(false)
150 , m_beingDestroyed(false)
151 , m_hasMarkupTruncation(false)
152 , m_hasBorderOrPaddingLogicalWidthChanged(false)
153 {
154 setChildrenInline(true);
155 }
156
removeBlockFromDescendantAndContainerMaps(RenderBlock * block,TrackedDescendantsMap * & descendantMap,TrackedContainerMap * & containerMap)157 static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap)
158 {
159 if (OwnPtr<TrackedRendererListHashSet> descendantSet = descendantMap->take(block)) {
160 TrackedRendererListHashSet::iterator end = descendantSet->end();
161 for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) {
162 TrackedContainerMap::iterator it = containerMap->find(*descendant);
163 ASSERT(it != containerMap->end());
164 if (it == containerMap->end())
165 continue;
166 HashSet<RenderBlock*>* containerSet = it->value.get();
167 ASSERT(containerSet->contains(block));
168 containerSet->remove(block);
169 if (containerSet->isEmpty())
170 containerMap->remove(it);
171 }
172 }
173 }
174
appendImageIfNotNull(Vector<ImageResource * > & imageResources,const StyleImage * styleImage)175 static void appendImageIfNotNull(Vector<ImageResource*>& imageResources, const StyleImage* styleImage)
176 {
177 if (styleImage && styleImage->cachedImage())
178 imageResources.append(styleImage->cachedImage());
179 }
180
appendLayers(Vector<ImageResource * > & images,const FillLayer * styleLayer)181 static void appendLayers(Vector<ImageResource*>& images, const FillLayer* styleLayer)
182 {
183 for (const FillLayer* layer = styleLayer; layer; layer = layer->next()) {
184 appendImageIfNotNull(images, layer->image());
185 }
186 }
187
~RenderBlock()188 RenderBlock::~RenderBlock()
189 {
190 if (hasColumns())
191 gColumnInfoMap->take(this);
192 if (gPercentHeightDescendantsMap)
193 removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap);
194 if (gPositionedDescendantsMap)
195 removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, gPositionedContainerMap);
196 }
197
willBeDestroyed()198 void RenderBlock::willBeDestroyed()
199 {
200 // Mark as being destroyed to avoid trouble with merges in removeChild().
201 m_beingDestroyed = true;
202
203 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
204 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
205 children()->destroyLeftoverChildren();
206
207 // Destroy our continuation before anything other than anonymous children.
208 // The reason we don't destroy it before anonymous children is that they may
209 // have continuations of their own that are anonymous children of our continuation.
210 RenderBoxModelObject* continuation = this->continuation();
211 if (continuation) {
212 continuation->destroy();
213 setContinuation(0);
214 }
215
216 if (!documentBeingDestroyed()) {
217 if (firstLineBox()) {
218 // We can't wait for RenderBox::destroy to clear the selection,
219 // because by then we will have nuked the line boxes.
220 // FIXME: The FrameSelection should be responsible for this when it
221 // is notified of DOM mutations.
222 if (isSelectionBorder())
223 view()->clearSelection();
224
225 // If we are an anonymous block, then our line boxes might have children
226 // that will outlast this block. In the non-anonymous block case those
227 // children will be destroyed by the time we return from this function.
228 if (isAnonymousBlock()) {
229 for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) {
230 while (InlineBox* childBox = box->firstChild())
231 childBox->remove();
232 }
233 }
234 } else if (parent())
235 parent()->dirtyLinesFromChangedChild(this);
236 }
237
238 m_lineBoxes.deleteLineBoxes();
239
240 if (UNLIKELY(gDelayedUpdateScrollInfoSet != 0))
241 gDelayedUpdateScrollInfoSet->remove(this);
242
243 FastTextAutosizer* textAutosizer = document().fastTextAutosizer();
244 if (textAutosizer)
245 textAutosizer->destroy(this);
246
247 RenderBox::willBeDestroyed();
248 }
249
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)250 void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
251 {
252 RenderStyle* oldStyle = style();
253
254 setReplaced(newStyle->isDisplayInlineType());
255
256 if (oldStyle && parent() && diff == StyleDifferenceLayout && oldStyle->position() != newStyle->position()) {
257 if (newStyle->position() == StaticPosition)
258 // Clear our positioned objects list. Our absolutely positioned descendants will be
259 // inserted into our containing block's positioned objects list during layout.
260 removePositionedObjects(0, NewContainingBlock);
261 else if (oldStyle->position() == StaticPosition) {
262 // Remove our absolutely positioned descendants from their current containing block.
263 // They will be inserted into our positioned objects list during layout.
264 RenderObject* cb = parent();
265 while (cb && (cb->style()->position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) {
266 if (cb->style()->position() == RelativePosition && cb->isInline() && !cb->isReplaced()) {
267 cb = cb->containingBlock();
268 break;
269 }
270 cb = cb->parent();
271 }
272
273 if (cb->isRenderBlock())
274 toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock);
275 }
276 }
277
278 RenderBox::styleWillChange(diff, newStyle);
279 }
280
borderOrPaddingLogicalWidthChanged(const RenderStyle * oldStyle,const RenderStyle * newStyle)281 static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle)
282 {
283 if (newStyle->isHorizontalWritingMode())
284 return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth()
285 || oldStyle->borderRightWidth() != newStyle->borderRightWidth()
286 || oldStyle->paddingLeft() != newStyle->paddingLeft()
287 || oldStyle->paddingRight() != newStyle->paddingRight();
288
289 return oldStyle->borderTopWidth() != newStyle->borderTopWidth()
290 || oldStyle->borderBottomWidth() != newStyle->borderBottomWidth()
291 || oldStyle->paddingTop() != newStyle->paddingTop()
292 || oldStyle->paddingBottom() != newStyle->paddingBottom();
293 }
294
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)295 void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
296 {
297 RenderBox::styleDidChange(diff, oldStyle);
298
299 RenderStyle* newStyle = style();
300
301 updateShapeInsideInfoAfterStyleChange(newStyle->resolvedShapeInside(), oldStyle ? oldStyle->resolvedShapeInside() : RenderStyle::initialShapeInside());
302
303 if (!isAnonymousBlock()) {
304 // Ensure that all of our continuation blocks pick up the new style.
305 for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) {
306 RenderBoxModelObject* nextCont = currCont->continuation();
307 currCont->setContinuation(0);
308 currCont->setStyle(newStyle);
309 currCont->setContinuation(nextCont);
310 }
311 }
312
313 FastTextAutosizer* textAutosizer = document().fastTextAutosizer();
314 if (textAutosizer)
315 textAutosizer->record(this);
316
317 propagateStyleToAnonymousChildren(true);
318 m_lineHeight = -1;
319
320 // It's possible for our border/padding to change, but for the overall logical width of the block to
321 // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true.
322 m_hasBorderOrPaddingLogicalWidthChanged = oldStyle && diff == StyleDifferenceLayout && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, newStyle);
323 }
324
continuationBefore(RenderObject * beforeChild)325 RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild)
326 {
327 if (beforeChild && beforeChild->parent() == this)
328 return this;
329
330 RenderBlock* curr = toRenderBlock(continuation());
331 RenderBlock* nextToLast = this;
332 RenderBlock* last = this;
333 while (curr) {
334 if (beforeChild && beforeChild->parent() == curr) {
335 if (curr->firstChild() == beforeChild)
336 return last;
337 return curr;
338 }
339
340 nextToLast = last;
341 last = curr;
342 curr = toRenderBlock(curr->continuation());
343 }
344
345 if (!beforeChild && !last->firstChild())
346 return nextToLast;
347 return last;
348 }
349
addChildToContinuation(RenderObject * newChild,RenderObject * beforeChild)350 void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
351 {
352 RenderBlock* flow = continuationBefore(beforeChild);
353 ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock());
354 RenderBoxModelObject* beforeChildParent = 0;
355 if (beforeChild)
356 beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
357 else {
358 RenderBoxModelObject* cont = flow->continuation();
359 if (cont)
360 beforeChildParent = cont;
361 else
362 beforeChildParent = flow;
363 }
364
365 if (newChild->isFloatingOrOutOfFlowPositioned()) {
366 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
367 return;
368 }
369
370 // A continuation always consists of two potential candidates: a block or an anonymous
371 // column span box holding column span children.
372 bool childIsNormal = newChild->isInline() || !newChild->style()->columnSpan();
373 bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style()->columnSpan();
374 bool flowIsNormal = flow->isInline() || !flow->style()->columnSpan();
375
376 if (flow == beforeChildParent) {
377 flow->addChildIgnoringContinuation(newChild, beforeChild);
378 return;
379 }
380
381 // The goal here is to match up if we can, so that we can coalesce and create the
382 // minimal # of continuations needed for the inline.
383 if (childIsNormal == bcpIsNormal) {
384 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
385 return;
386 }
387 if (flowIsNormal == childIsNormal) {
388 flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append.
389 return;
390 }
391 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
392 }
393
394
addChildToAnonymousColumnBlocks(RenderObject * newChild,RenderObject * beforeChild)395 void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild)
396 {
397 ASSERT(!continuation()); // We don't yet support column spans that aren't immediate children of the multi-column block.
398
399 // The goal is to locate a suitable box in which to place our child.
400 RenderBlock* beforeChildParent = 0;
401 if (beforeChild) {
402 RenderObject* curr = beforeChild;
403 while (curr && curr->parent() != this)
404 curr = curr->parent();
405 beforeChildParent = toRenderBlock(curr);
406 ASSERT(beforeChildParent);
407 ASSERT(beforeChildParent->isAnonymousColumnsBlock() || beforeChildParent->isAnonymousColumnSpanBlock());
408 } else
409 beforeChildParent = toRenderBlock(lastChild());
410
411 // If the new child is floating or positioned it can just go in that block.
412 if (newChild->isFloatingOrOutOfFlowPositioned()) {
413 beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
414 return;
415 }
416
417 // See if the child can be placed in the box.
418 bool newChildHasColumnSpan = newChild->style()->columnSpan() && !newChild->isInline();
419 bool beforeChildParentHoldsColumnSpans = beforeChildParent->isAnonymousColumnSpanBlock();
420
421 if (newChildHasColumnSpan == beforeChildParentHoldsColumnSpans) {
422 beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
423 return;
424 }
425
426 if (!beforeChild) {
427 // Create a new block of the correct type.
428 RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock();
429 children()->appendChildNode(this, newBox);
430 newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0);
431 return;
432 }
433
434 RenderObject* immediateChild = beforeChild;
435 bool isPreviousBlockViable = true;
436 while (immediateChild->parent() != this) {
437 if (isPreviousBlockViable)
438 isPreviousBlockViable = !immediateChild->previousSibling();
439 immediateChild = immediateChild->parent();
440 }
441 if (isPreviousBlockViable && immediateChild->previousSibling()) {
442 toRenderBlock(immediateChild->previousSibling())->addChildIgnoringAnonymousColumnBlocks(newChild, 0); // Treat like an append.
443 return;
444 }
445
446 // Split our anonymous blocks.
447 RenderObject* newBeforeChild = splitAnonymousBoxesAroundChild(beforeChild);
448
449
450 // Create a new anonymous box of the appropriate type.
451 RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock();
452 children()->insertChildNode(this, newBox, newBeforeChild);
453 newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0);
454 return;
455 }
456
containingColumnsBlock(bool allowAnonymousColumnBlock)457 RenderBlockFlow* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBlock)
458 {
459 RenderBlock* firstChildIgnoringAnonymousWrappers = 0;
460 for (RenderObject* curr = this; curr; curr = curr->parent()) {
461 if (!curr->isRenderBlock() || curr->isFloatingOrOutOfFlowPositioned() || curr->isTableCell() || curr->isRoot() || curr->isRenderView() || curr->hasOverflowClip()
462 || curr->isInlineBlockOrInlineTable())
463 return 0;
464
465 // FIXME: Renderers that do special management of their children (tables, buttons,
466 // lists, flexboxes, etc.) breaks when the flow is split through them. Disabling
467 // multi-column for them to avoid this problem.)
468 if (!curr->isRenderBlockFlow() || curr->isListItem())
469 return 0;
470
471 RenderBlockFlow* currBlock = toRenderBlockFlow(curr);
472 if (!currBlock->createsAnonymousWrapper())
473 firstChildIgnoringAnonymousWrappers = currBlock;
474
475 if (currBlock->style()->specifiesColumns() && (allowAnonymousColumnBlock || !currBlock->isAnonymousColumnsBlock()))
476 return toRenderBlockFlow(firstChildIgnoringAnonymousWrappers);
477
478 if (currBlock->isAnonymousColumnSpanBlock())
479 return 0;
480 }
481 return 0;
482 }
483
clone() const484 RenderBlock* RenderBlock::clone() const
485 {
486 RenderBlock* cloneBlock;
487 if (isAnonymousBlock()) {
488 cloneBlock = createAnonymousBlock();
489 cloneBlock->setChildrenInline(childrenInline());
490 }
491 else {
492 RenderObject* cloneRenderer = toElement(node())->createRenderer(style());
493 cloneBlock = toRenderBlock(cloneRenderer);
494 cloneBlock->setStyle(style());
495
496 // This takes care of setting the right value of childrenInline in case
497 // generated content is added to cloneBlock and 'this' does not have
498 // generated content added yet.
499 cloneBlock->setChildrenInline(cloneBlock->firstChild() ? cloneBlock->firstChild()->isInline() : childrenInline());
500 }
501 cloneBlock->setFlowThreadState(flowThreadState());
502 return cloneBlock;
503 }
504
splitBlocks(RenderBlock * fromBlock,RenderBlock * toBlock,RenderBlock * middleBlock,RenderObject * beforeChild,RenderBoxModelObject * oldCont)505 void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock,
506 RenderBlock* middleBlock,
507 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
508 {
509 // Create a clone of this inline.
510 RenderBlock* cloneBlock = clone();
511 if (!isAnonymousBlock())
512 cloneBlock->setContinuation(oldCont);
513
514 if (!beforeChild && isAfterContent(lastChild()))
515 beforeChild = lastChild();
516
517 // If we are moving inline children from |this| to cloneBlock, then we need
518 // to clear our line box tree.
519 if (beforeChild && childrenInline())
520 deleteLineBoxTree();
521
522 // Now take all of the children from beforeChild to the end and remove
523 // them from |this| and place them in the clone.
524 moveChildrenTo(cloneBlock, beforeChild, 0, true);
525
526 // Hook |clone| up as the continuation of the middle block.
527 if (!cloneBlock->isAnonymousBlock())
528 middleBlock->setContinuation(cloneBlock);
529
530 // We have been reparented and are now under the fromBlock. We need
531 // to walk up our block parent chain until we hit the containing anonymous columns block.
532 // Once we hit the anonymous columns block we're done.
533 RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
534 RenderBoxModelObject* currChild = this;
535 RenderObject* currChildNextSibling = currChild->nextSibling();
536
537 while (curr && curr->isDescendantOf(fromBlock) && curr != fromBlock) {
538 ASSERT_WITH_SECURITY_IMPLICATION(curr->isRenderBlock());
539
540 RenderBlock* blockCurr = toRenderBlock(curr);
541
542 // Create a new clone.
543 RenderBlock* cloneChild = cloneBlock;
544 cloneBlock = blockCurr->clone();
545
546 // Insert our child clone as the first child.
547 cloneBlock->addChildIgnoringContinuation(cloneChild, 0);
548
549 // Hook the clone up as a continuation of |curr|. Note we do encounter
550 // anonymous blocks possibly as we walk up the block chain. When we split an
551 // anonymous block, there's no need to do any continuation hookup, since we haven't
552 // actually split a real element.
553 if (!blockCurr->isAnonymousBlock()) {
554 oldCont = blockCurr->continuation();
555 blockCurr->setContinuation(cloneBlock);
556 cloneBlock->setContinuation(oldCont);
557 }
558
559 // Now we need to take all of the children starting from the first child
560 // *after* currChild and append them all to the clone.
561 blockCurr->moveChildrenTo(cloneBlock, currChildNextSibling, 0, true);
562
563 // Keep walking up the chain.
564 currChild = curr;
565 currChildNextSibling = currChild->nextSibling();
566 curr = toRenderBoxModelObject(curr->parent());
567 }
568
569 // Now we are at the columns block level. We need to put the clone into the toBlock.
570 toBlock->children()->appendChildNode(toBlock, cloneBlock);
571
572 // Now take all the children after currChild and remove them from the fromBlock
573 // and put them in the toBlock.
574 fromBlock->moveChildrenTo(toBlock, currChildNextSibling, 0, true);
575 }
576
splitFlow(RenderObject * beforeChild,RenderBlock * newBlockBox,RenderObject * newChild,RenderBoxModelObject * oldCont)577 void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
578 RenderObject* newChild, RenderBoxModelObject* oldCont)
579 {
580 RenderBlock* pre = 0;
581 RenderBlock* block = containingColumnsBlock();
582
583 // Delete our line boxes before we do the inline split into continuations.
584 block->deleteLineBoxTree();
585
586 bool madeNewBeforeBlock = false;
587 if (block->isAnonymousColumnsBlock()) {
588 // We can reuse this block and make it the preBlock of the next continuation.
589 pre = block;
590 pre->removePositionedObjects(0);
591 if (block->isRenderBlockFlow())
592 toRenderBlockFlow(pre)->removeFloatingObjects();
593 block = toRenderBlock(block->parent());
594 } else {
595 // No anonymous block available for use. Make one.
596 pre = block->createAnonymousColumnsBlock();
597 pre->setChildrenInline(false);
598 madeNewBeforeBlock = true;
599 }
600
601 RenderBlock* post = block->createAnonymousColumnsBlock();
602 post->setChildrenInline(false);
603
604 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
605 if (madeNewBeforeBlock)
606 block->children()->insertChildNode(block, pre, boxFirst);
607 block->children()->insertChildNode(block, newBlockBox, boxFirst);
608 block->children()->insertChildNode(block, post, boxFirst);
609 block->setChildrenInline(false);
610
611 if (madeNewBeforeBlock)
612 block->moveChildrenTo(pre, boxFirst, 0, true);
613
614 splitBlocks(pre, post, newBlockBox, beforeChild, oldCont);
615
616 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
617 // time in makeChildrenNonInline by just setting this explicitly up front.
618 newBlockBox->setChildrenInline(false);
619
620 newBlockBox->addChild(newChild);
621
622 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
623 // get deleted properly. Because objects moves from the pre block into the post block, we want to
624 // make new line boxes instead of leaving the old line boxes around.
625 pre->setNeedsLayoutAndPrefWidthsRecalc();
626 block->setNeedsLayoutAndPrefWidthsRecalc();
627 post->setNeedsLayoutAndPrefWidthsRecalc();
628 }
629
makeChildrenAnonymousColumnBlocks(RenderObject * beforeChild,RenderBlockFlow * newBlockBox,RenderObject * newChild)630 void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlockFlow* newBlockBox, RenderObject* newChild)
631 {
632 RenderBlockFlow* pre = 0;
633 RenderBlockFlow* post = 0;
634 RenderBlock* block = this; // Eventually block will not just be |this|, but will also be a block nested inside |this|. Assign to a variable
635 // so that we don't have to patch all of the rest of the code later on.
636
637 // Delete the block's line boxes before we do the split.
638 block->deleteLineBoxTree();
639
640 if (beforeChild && beforeChild->parent() != this)
641 beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
642
643 if (beforeChild != firstChild()) {
644 pre = block->createAnonymousColumnsBlock();
645 pre->setChildrenInline(block->childrenInline());
646 }
647
648 if (beforeChild) {
649 post = block->createAnonymousColumnsBlock();
650 post->setChildrenInline(block->childrenInline());
651 }
652
653 RenderObject* boxFirst = block->firstChild();
654 if (pre)
655 block->children()->insertChildNode(block, pre, boxFirst);
656 block->children()->insertChildNode(block, newBlockBox, boxFirst);
657 if (post)
658 block->children()->insertChildNode(block, post, boxFirst);
659 block->setChildrenInline(false);
660
661 // 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).
662 block->moveChildrenTo(pre, boxFirst, beforeChild, true);
663 block->moveChildrenTo(post, beforeChild, 0, true);
664
665 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
666 // time in makeChildrenNonInline by just setting this explicitly up front.
667 newBlockBox->setChildrenInline(false);
668
669 newBlockBox->addChild(newChild);
670
671 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
672 // get deleted properly. Because objects moved from the pre block into the post block, we want to
673 // make new line boxes instead of leaving the old line boxes around.
674 if (pre)
675 pre->setNeedsLayoutAndPrefWidthsRecalc();
676 block->setNeedsLayoutAndPrefWidthsRecalc();
677 if (post)
678 post->setNeedsLayoutAndPrefWidthsRecalc();
679 }
680
columnsBlockForSpanningElement(RenderObject * newChild)681 RenderBlockFlow* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild)
682 {
683 // FIXME: This function is the gateway for the addition of column-span support. It will
684 // be added to in three stages:
685 // (1) Immediate children of a multi-column block can span.
686 // (2) Nested block-level children with only block-level ancestors between them and the multi-column block can span.
687 // (3) Nested children with block or inline ancestors between them and the multi-column block can span (this is when we
688 // cross the streams and have to cope with both types of continuations mixed together).
689 // This function currently supports (1) and (2).
690 RenderBlockFlow* columnsBlockAncestor = 0;
691 if (!newChild->isText() && newChild->style()->columnSpan() && !newChild->isBeforeOrAfterContent()
692 && !newChild->isFloatingOrOutOfFlowPositioned() && !newChild->isInline() && !isAnonymousColumnSpanBlock()) {
693 columnsBlockAncestor = containingColumnsBlock(false);
694 if (columnsBlockAncestor) {
695 // Make sure that none of the parent ancestors have a continuation.
696 // If yes, we do not want split the block into continuations.
697 RenderObject* curr = this;
698 while (curr && curr != columnsBlockAncestor) {
699 if (curr->isRenderBlock() && toRenderBlock(curr)->continuation()) {
700 columnsBlockAncestor = 0;
701 break;
702 }
703 curr = curr->parent();
704 }
705 }
706 }
707 return columnsBlockAncestor;
708 }
709
addChildIgnoringAnonymousColumnBlocks(RenderObject * newChild,RenderObject * beforeChild)710 void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild)
711 {
712 if (beforeChild && beforeChild->parent() != this) {
713 RenderObject* beforeChildContainer = beforeChild->parent();
714 while (beforeChildContainer->parent() != this)
715 beforeChildContainer = beforeChildContainer->parent();
716 ASSERT(beforeChildContainer);
717
718 if (beforeChildContainer->isAnonymous()) {
719 // If the requested beforeChild is not one of our children, then this is because
720 // there is an anonymous container within this object that contains the beforeChild.
721 RenderObject* beforeChildAnonymousContainer = beforeChildContainer;
722 if (beforeChildAnonymousContainer->isAnonymousBlock()
723 // Full screen renderers and full screen placeholders act as anonymous blocks, not tables:
724 || beforeChildAnonymousContainer->isRenderFullScreen()
725 || beforeChildAnonymousContainer->isRenderFullScreenPlaceholder()
726 ) {
727 // Insert the child into the anonymous block box instead of here.
728 if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild)
729 beforeChild->parent()->addChild(newChild, beforeChild);
730 else
731 addChild(newChild, beforeChild->parent());
732 return;
733 }
734
735 ASSERT(beforeChildAnonymousContainer->isTable());
736 if (newChild->isTablePart()) {
737 // Insert into the anonymous table.
738 beforeChildAnonymousContainer->addChild(newChild, beforeChild);
739 return;
740 }
741
742 beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
743
744 ASSERT(beforeChild->parent() == this);
745 if (beforeChild->parent() != this) {
746 // We should never reach here. If we do, we need to use the
747 // safe fallback to use the topmost beforeChild container.
748 beforeChild = beforeChildContainer;
749 }
750 }
751 }
752
753 // Check for a spanning element in columns.
754 if (gColumnFlowSplitEnabled) {
755 RenderBlockFlow* columnsBlockAncestor = columnsBlockForSpanningElement(newChild);
756 if (columnsBlockAncestor) {
757 TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false);
758 // We are placing a column-span element inside a block.
759 RenderBlockFlow* newBox = createAnonymousColumnSpanBlock();
760
761 if (columnsBlockAncestor != this && !isRenderFlowThread()) {
762 // We are nested inside a multi-column element and are being split by the span. We have to break up
763 // our block into continuations.
764 RenderBoxModelObject* oldContinuation = continuation();
765
766 // When we split an anonymous block, there's no need to do any continuation hookup,
767 // since we haven't actually split a real element.
768 if (!isAnonymousBlock())
769 setContinuation(newBox);
770
771 splitFlow(beforeChild, newBox, newChild, oldContinuation);
772 return;
773 }
774
775 // We have to perform a split of this block's children. This involves creating an anonymous block box to hold
776 // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into
777 // one anonymous columns block, and all of the children after |newChild| go into another anonymous block.
778 makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild);
779 return;
780 }
781 }
782
783 bool madeBoxesNonInline = false;
784
785 // A block has to either have all of its children inline, or all of its children as blocks.
786 // So, if our children are currently inline and a block child has to be inserted, we move all our
787 // inline children into anonymous block boxes.
788 if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned()) {
789 // This is a block with inline content. Wrap the inline content in anonymous blocks.
790 makeChildrenNonInline(beforeChild);
791 madeBoxesNonInline = true;
792
793 if (beforeChild && beforeChild->parent() != this) {
794 beforeChild = beforeChild->parent();
795 ASSERT(beforeChild->isAnonymousBlock());
796 ASSERT(beforeChild->parent() == this);
797 }
798 } else if (!childrenInline() && (newChild->isFloatingOrOutOfFlowPositioned() || newChild->isInline())) {
799 // If we're inserting an inline child but all of our children are blocks, then we have to make sure
800 // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise
801 // a new one is created and inserted into our list of children in the appropriate position.
802 RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild();
803
804 if (afterChild && afterChild->isAnonymousBlock()) {
805 afterChild->addChild(newChild);
806 return;
807 }
808
809 if (newChild->isInline()) {
810 // No suitable existing anonymous box - create a new one.
811 RenderBlock* newBox = createAnonymousBlock();
812 RenderBox::addChild(newBox, beforeChild);
813 newBox->addChild(newChild);
814 return;
815 }
816 }
817
818 RenderBox::addChild(newChild, beforeChild);
819
820 if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock())
821 toRenderBlock(parent())->removeLeftoverAnonymousBlock(this);
822 // this object may be dead here
823 }
824
addChild(RenderObject * newChild,RenderObject * beforeChild)825 void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild)
826 {
827 if (continuation() && !isAnonymousBlock())
828 addChildToContinuation(newChild, beforeChild);
829 else
830 addChildIgnoringContinuation(newChild, beforeChild);
831 }
832
addChildIgnoringContinuation(RenderObject * newChild,RenderObject * beforeChild)833 void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
834 {
835 if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock()))
836 addChildToAnonymousColumnBlocks(newChild, beforeChild);
837 else
838 addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
839 }
840
getInlineRun(RenderObject * start,RenderObject * boundary,RenderObject * & inlineRunStart,RenderObject * & inlineRunEnd)841 static void getInlineRun(RenderObject* start, RenderObject* boundary,
842 RenderObject*& inlineRunStart,
843 RenderObject*& inlineRunEnd)
844 {
845 // Beginning at |start| we find the largest contiguous run of inlines that
846 // we can. We denote the run with start and end points, |inlineRunStart|
847 // and |inlineRunEnd|. Note that these two values may be the same if
848 // we encounter only one inline.
849 //
850 // We skip any non-inlines we encounter as long as we haven't found any
851 // inlines yet.
852 //
853 // |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary|
854 // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered
855 // a non-inline.
856
857 // Start by skipping as many non-inlines as we can.
858 RenderObject * curr = start;
859 bool sawInline;
860 do {
861 while (curr && !(curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()))
862 curr = curr->nextSibling();
863
864 inlineRunStart = inlineRunEnd = curr;
865
866 if (!curr)
867 return; // No more inline children to be found.
868
869 sawInline = curr->isInline();
870
871 curr = curr->nextSibling();
872 while (curr && (curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()) && (curr != boundary)) {
873 inlineRunEnd = curr;
874 if (curr->isInline())
875 sawInline = true;
876 curr = curr->nextSibling();
877 }
878 } while (!sawInline);
879 }
880
deleteLineBoxTree()881 void RenderBlock::deleteLineBoxTree()
882 {
883 m_lineBoxes.deleteLineBoxTree();
884
885 if (AXObjectCache* cache = document().existingAXObjectCache())
886 cache->recomputeIsIgnored(this);
887 }
888
createAndAppendRootInlineBox()889 RootInlineBox* RenderBlock::createAndAppendRootInlineBox()
890 {
891 RootInlineBox* rootBox = createRootInlineBox();
892 m_lineBoxes.appendLineBox(rootBox);
893
894 if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && m_lineBoxes.firstLineBox() == rootBox) {
895 if (AXObjectCache* cache = document().existingAXObjectCache())
896 cache->recomputeIsIgnored(this);
897 }
898
899 return rootBox;
900 }
901
makeChildrenNonInline(RenderObject * insertionPoint)902 void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint)
903 {
904 // makeChildrenNonInline takes a block whose children are *all* inline and it
905 // makes sure that inline children are coalesced under anonymous
906 // blocks. If |insertionPoint| is defined, then it represents the insertion point for
907 // the new block child that is causing us to have to wrap all the inlines. This
908 // means that we cannot coalesce inlines before |insertionPoint| with inlines following
909 // |insertionPoint|, because the new child is going to be inserted in between the inlines,
910 // splitting them.
911 ASSERT(isInlineBlockOrInlineTable() || !isInline());
912 ASSERT(!insertionPoint || insertionPoint->parent() == this);
913
914 setChildrenInline(false);
915
916 RenderObject *child = firstChild();
917 if (!child)
918 return;
919
920 deleteLineBoxTree();
921
922 while (child) {
923 RenderObject *inlineRunStart, *inlineRunEnd;
924 getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd);
925
926 if (!inlineRunStart)
927 break;
928
929 child = inlineRunEnd->nextSibling();
930
931 RenderBlock* block = createAnonymousBlock();
932 children()->insertChildNode(this, block, inlineRunStart);
933 moveChildrenTo(block, inlineRunStart, child);
934 }
935
936 #ifndef NDEBUG
937 for (RenderObject *c = firstChild(); c; c = c->nextSibling())
938 ASSERT(!c->isInline());
939 #endif
940
941 repaint();
942 }
943
removeLeftoverAnonymousBlock(RenderBlock * child)944 void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child)
945 {
946 ASSERT(child->isAnonymousBlock());
947 ASSERT(!child->childrenInline());
948
949 if (child->continuation() || (child->firstChild() && (child->isAnonymousColumnSpanBlock() || child->isAnonymousColumnsBlock())))
950 return;
951
952 RenderObject* firstAnChild = child->m_children.firstChild();
953 RenderObject* lastAnChild = child->m_children.lastChild();
954 if (firstAnChild) {
955 RenderObject* o = firstAnChild;
956 while (o) {
957 o->setParent(this);
958 o = o->nextSibling();
959 }
960 firstAnChild->setPreviousSibling(child->previousSibling());
961 lastAnChild->setNextSibling(child->nextSibling());
962 if (child->previousSibling())
963 child->previousSibling()->setNextSibling(firstAnChild);
964 if (child->nextSibling())
965 child->nextSibling()->setPreviousSibling(lastAnChild);
966
967 if (child == m_children.firstChild())
968 m_children.setFirstChild(firstAnChild);
969 if (child == m_children.lastChild())
970 m_children.setLastChild(lastAnChild);
971 } else {
972 if (child == m_children.firstChild())
973 m_children.setFirstChild(child->nextSibling());
974 if (child == m_children.lastChild())
975 m_children.setLastChild(child->previousSibling());
976
977 if (child->previousSibling())
978 child->previousSibling()->setNextSibling(child->nextSibling());
979 if (child->nextSibling())
980 child->nextSibling()->setPreviousSibling(child->previousSibling());
981 }
982
983 child->children()->setFirstChild(0);
984 child->m_next = 0;
985
986 // Remove all the information in the flow thread associated with the leftover anonymous block.
987 child->removeFromRenderFlowThread();
988
989 child->setParent(0);
990 child->setPreviousSibling(0);
991 child->setNextSibling(0);
992
993 child->destroy();
994 }
995
canMergeContiguousAnonymousBlocks(RenderObject * oldChild,RenderObject * prev,RenderObject * next)996 static bool canMergeContiguousAnonymousBlocks(RenderObject* oldChild, RenderObject* prev, RenderObject* next)
997 {
998 if (oldChild->documentBeingDestroyed() || oldChild->isInline() || oldChild->virtualContinuation())
999 return false;
1000
1001 if ((prev && (!prev->isAnonymousBlock() || toRenderBlock(prev)->continuation() || toRenderBlock(prev)->beingDestroyed()))
1002 || (next && (!next->isAnonymousBlock() || toRenderBlock(next)->continuation() || toRenderBlock(next)->beingDestroyed())))
1003 return false;
1004
1005 if ((prev && (prev->isRubyRun() || prev->isRubyBase()))
1006 || (next && (next->isRubyRun() || next->isRubyBase())))
1007 return false;
1008
1009 if (!prev || !next)
1010 return true;
1011
1012 // Make sure the types of the anonymous blocks match up.
1013 return prev->isAnonymousColumnsBlock() == next->isAnonymousColumnsBlock()
1014 && prev->isAnonymousColumnSpanBlock() == next->isAnonymousColumnSpanBlock();
1015 }
1016
collapseAnonymousBlockChild(RenderBlock * parent,RenderBlock * child)1017 void RenderBlock::collapseAnonymousBlockChild(RenderBlock* parent, RenderBlock* child)
1018 {
1019 // It's possible that this block's destruction may have been triggered by the
1020 // child's removal. Just bail if the anonymous child block is already being
1021 // destroyed. See crbug.com/282088
1022 if (child->beingDestroyed())
1023 return;
1024 parent->setNeedsLayoutAndPrefWidthsRecalc();
1025 parent->setChildrenInline(child->childrenInline());
1026 RenderObject* nextSibling = child->nextSibling();
1027
1028 RenderFlowThread* childFlowThread = child->flowThreadContainingBlock();
1029 CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread);
1030
1031 parent->children()->removeChildNode(parent, child, child->hasLayer());
1032 child->moveAllChildrenTo(parent, nextSibling, child->hasLayer());
1033 // Explicitly delete the child's line box tree, or the special anonymous
1034 // block handling in willBeDestroyed will cause problems.
1035 child->deleteLineBoxTree();
1036 if (childFlowThread && childFlowThread->isRenderNamedFlowThread())
1037 toRenderNamedFlowThread(childFlowThread)->removeFlowChildInfo(child);
1038 child->destroy();
1039 }
1040
removeChild(RenderObject * oldChild)1041 void RenderBlock::removeChild(RenderObject* oldChild)
1042 {
1043 // No need to waste time in merging or removing empty anonymous blocks.
1044 // We can just bail out if our document is getting destroyed.
1045 if (documentBeingDestroyed()) {
1046 RenderBox::removeChild(oldChild);
1047 return;
1048 }
1049
1050 // This protects against column split flows when anonymous blocks are getting merged.
1051 TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false);
1052
1053 // If this child is a block, and if our previous and next siblings are
1054 // both anonymous blocks with inline content, then we can go ahead and
1055 // fold the inline content back together.
1056 RenderObject* prev = oldChild->previousSibling();
1057 RenderObject* next = oldChild->nextSibling();
1058 bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next);
1059 if (canMergeAnonymousBlocks && prev && next) {
1060 prev->setNeedsLayoutAndPrefWidthsRecalc();
1061 RenderBlock* nextBlock = toRenderBlock(next);
1062 RenderBlock* prevBlock = toRenderBlock(prev);
1063
1064 if (prev->childrenInline() != next->childrenInline()) {
1065 RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock;
1066 RenderBlock* blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock;
1067
1068 // Place the inline children block inside of the block children block instead of deleting it.
1069 // In order to reuse it, we have to reset it to just be a generic anonymous block. Make sure
1070 // to clear out inherited column properties by just making a new style, and to also clear the
1071 // column span flag if it is set.
1072 ASSERT(!inlineChildrenBlock->continuation());
1073 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
1074 // Cache this value as it might get changed in setStyle() call.
1075 bool inlineChildrenBlockHasLayer = inlineChildrenBlock->hasLayer();
1076 inlineChildrenBlock->setStyle(newStyle);
1077 children()->removeChildNode(this, inlineChildrenBlock, inlineChildrenBlockHasLayer);
1078
1079 // Now just put the inlineChildrenBlock inside the blockChildrenBlock.
1080 blockChildrenBlock->children()->insertChildNode(blockChildrenBlock, inlineChildrenBlock, prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : 0,
1081 inlineChildrenBlockHasLayer || blockChildrenBlock->hasLayer());
1082 next->setNeedsLayoutAndPrefWidthsRecalc();
1083
1084 // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child
1085 // of "this". we null out prev or next so that is not used later in the function.
1086 if (inlineChildrenBlock == prevBlock)
1087 prev = 0;
1088 else
1089 next = 0;
1090 } else {
1091 // Take all the children out of the |next| block and put them in
1092 // the |prev| block.
1093 nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer());
1094
1095 // Delete the now-empty block's lines and nuke it.
1096 nextBlock->deleteLineBoxTree();
1097 nextBlock->destroy();
1098 next = 0;
1099 }
1100 }
1101
1102 RenderBox::removeChild(oldChild);
1103
1104 RenderObject* child = prev ? prev : next;
1105 if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canCollapseAnonymousBlockChild()) {
1106 // The removal has knocked us down to containing only a single anonymous
1107 // box. We can go ahead and pull the content right back up into our
1108 // box.
1109 collapseAnonymousBlockChild(this, toRenderBlock(child));
1110 } else if (((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) && canCollapseAnonymousBlockChild()) {
1111 // It's possible that the removal has knocked us down to a single anonymous
1112 // block with pseudo-style element siblings (e.g. first-letter). If these
1113 // are floating, then we need to pull the content up also.
1114 RenderBlock* anonymousBlock = toRenderBlock((prev && prev->isAnonymousBlock()) ? prev : next);
1115 if ((anonymousBlock->previousSibling() || anonymousBlock->nextSibling())
1116 && (!anonymousBlock->previousSibling() || (anonymousBlock->previousSibling()->style()->styleType() != NOPSEUDO && anonymousBlock->previousSibling()->isFloating() && !anonymousBlock->previousSibling()->previousSibling()))
1117 && (!anonymousBlock->nextSibling() || (anonymousBlock->nextSibling()->style()->styleType() != NOPSEUDO && anonymousBlock->nextSibling()->isFloating() && !anonymousBlock->nextSibling()->nextSibling()))) {
1118 collapseAnonymousBlockChild(this, anonymousBlock);
1119 }
1120 }
1121
1122 if (!firstChild()) {
1123 // If this was our last child be sure to clear out our line boxes.
1124 if (childrenInline())
1125 deleteLineBoxTree();
1126
1127 // If we are an empty anonymous block in the continuation chain,
1128 // we need to remove ourself and fix the continuation chain.
1129 if (!beingDestroyed() && isAnonymousBlockContinuation() && !oldChild->isListMarker()) {
1130 RenderObject* containingBlockIgnoringAnonymous = containingBlock();
1131 while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymousBlock())
1132 containingBlockIgnoringAnonymous = containingBlockIgnoringAnonymous->containingBlock();
1133 for (RenderObject* curr = this; curr; curr = curr->previousInPreOrder(containingBlockIgnoringAnonymous)) {
1134 if (curr->virtualContinuation() != this)
1135 continue;
1136
1137 // Found our previous continuation. We just need to point it to
1138 // |this|'s next continuation.
1139 RenderBoxModelObject* nextContinuation = continuation();
1140 if (curr->isRenderInline())
1141 toRenderInline(curr)->setContinuation(nextContinuation);
1142 else if (curr->isRenderBlock())
1143 toRenderBlock(curr)->setContinuation(nextContinuation);
1144 else
1145 ASSERT_NOT_REACHED();
1146
1147 break;
1148 }
1149 setContinuation(0);
1150 destroy();
1151 }
1152 }
1153 }
1154
isSelfCollapsingBlock() const1155 bool RenderBlock::isSelfCollapsingBlock() const
1156 {
1157 // We are not self-collapsing if we
1158 // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
1159 // (b) are a table,
1160 // (c) have border/padding,
1161 // (d) have a min-height
1162 // (e) have specified that one of our margins can't collapse using a CSS extension
1163 if (logicalHeight() > 0
1164 || isTable() || borderAndPaddingLogicalHeight()
1165 || style()->logicalMinHeight().isPositive()
1166 || style()->marginBeforeCollapse() == MSEPARATE || style()->marginAfterCollapse() == MSEPARATE)
1167 return false;
1168
1169 Length logicalHeightLength = style()->logicalHeight();
1170 bool hasAutoHeight = logicalHeightLength.isAuto();
1171 if (logicalHeightLength.isPercent() && !document().inQuirksMode()) {
1172 hasAutoHeight = true;
1173 for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) {
1174 if (cb->style()->logicalHeight().isFixed() || cb->isTableCell())
1175 hasAutoHeight = false;
1176 }
1177 }
1178
1179 // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
1180 // on whether we have content that is all self-collapsing or not.
1181 if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) {
1182 // If the block has inline children, see if we generated any line boxes. If we have any
1183 // line boxes, then we can't be self-collapsing, since we have content.
1184 if (childrenInline())
1185 return !firstLineBox();
1186
1187 // Whether or not we collapse is dependent on whether all our normal flow children
1188 // are also self-collapsing.
1189 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
1190 if (child->isFloatingOrOutOfFlowPositioned())
1191 continue;
1192 if (!child->isSelfCollapsingBlock())
1193 return false;
1194 }
1195 return true;
1196 }
1197 return false;
1198 }
1199
startDelayUpdateScrollInfo()1200 void RenderBlock::startDelayUpdateScrollInfo()
1201 {
1202 if (gDelayUpdateScrollInfo == 0) {
1203 ASSERT(!gDelayedUpdateScrollInfoSet);
1204 gDelayedUpdateScrollInfoSet = new DelayedUpdateScrollInfoSet;
1205 }
1206 ASSERT(gDelayedUpdateScrollInfoSet);
1207 ++gDelayUpdateScrollInfo;
1208 }
1209
finishDelayUpdateScrollInfo()1210 void RenderBlock::finishDelayUpdateScrollInfo()
1211 {
1212 --gDelayUpdateScrollInfo;
1213 ASSERT(gDelayUpdateScrollInfo >= 0);
1214 if (gDelayUpdateScrollInfo == 0) {
1215 ASSERT(gDelayedUpdateScrollInfoSet);
1216
1217 OwnPtr<DelayedUpdateScrollInfoSet> infoSet(adoptPtr(gDelayedUpdateScrollInfoSet));
1218 gDelayedUpdateScrollInfoSet = 0;
1219
1220 for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) {
1221 RenderBlock* block = *it;
1222 if (block->hasOverflowClip()) {
1223 block->layer()->scrollableArea()->updateAfterLayout();
1224 }
1225 }
1226 }
1227 }
1228
updateScrollInfoAfterLayout()1229 void RenderBlock::updateScrollInfoAfterLayout()
1230 {
1231 if (hasOverflowClip()) {
1232 if (style()->isFlippedBlocksWritingMode()) {
1233 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937
1234 // Workaround for now. We cannot delay the scroll info for overflow
1235 // for items with opposite writing directions, as the contents needs
1236 // to overflow in that direction
1237 layer()->scrollableArea()->updateAfterLayout();
1238 return;
1239 }
1240
1241 if (gDelayUpdateScrollInfo)
1242 gDelayedUpdateScrollInfoSet->add(this);
1243 else
1244 layer()->scrollableArea()->updateAfterLayout();
1245 }
1246 }
1247
layout()1248 void RenderBlock::layout()
1249 {
1250 OverflowEventDispatcher dispatcher(this);
1251 LayoutRectRecorder recorder(*this);
1252
1253 // Update our first letter info now.
1254 updateFirstLetter();
1255
1256 // Table cells call layoutBlock directly, so don't add any logic here. Put code into
1257 // layoutBlock().
1258 layoutBlock(false);
1259
1260 if (frameView()->partialLayout().isStopping())
1261 return;
1262
1263 // It's safe to check for control clip here, since controls can never be table cells.
1264 // If we have a lightweight clip, there can never be any overflow from children.
1265 if (hasControlClip() && m_overflow)
1266 clearLayoutOverflow();
1267
1268 invalidateBackgroundObscurationStatus();
1269 }
1270
didLayout(ResourceLoadPriorityOptimizer & optimizer)1271 void RenderBlock::didLayout(ResourceLoadPriorityOptimizer& optimizer)
1272 {
1273 RenderBox::didLayout(optimizer);
1274 updateStyleImageLoadingPriorities(optimizer);
1275 }
1276
didScroll(ResourceLoadPriorityOptimizer & optimizer)1277 void RenderBlock::didScroll(ResourceLoadPriorityOptimizer& optimizer)
1278 {
1279 RenderBox::didScroll(optimizer);
1280 updateStyleImageLoadingPriorities(optimizer);
1281 }
1282
updateStyleImageLoadingPriorities(ResourceLoadPriorityOptimizer & optimizer)1283 void RenderBlock::updateStyleImageLoadingPriorities(ResourceLoadPriorityOptimizer& optimizer)
1284 {
1285 RenderStyle* blockStyle = style();
1286 if (!blockStyle)
1287 return;
1288
1289 Vector<ImageResource*> images;
1290
1291 appendLayers(images, blockStyle->backgroundLayers());
1292 appendLayers(images, blockStyle->maskLayers());
1293
1294 const ContentData* contentData = blockStyle->contentData();
1295 if (contentData && contentData->isImage()) {
1296 const ImageContentData* imageContentData = static_cast<const ImageContentData*>(contentData);
1297 appendImageIfNotNull(images, imageContentData->image());
1298 }
1299 if (blockStyle->boxReflect())
1300 appendImageIfNotNull(images, blockStyle->boxReflect()->mask().image());
1301 appendImageIfNotNull(images, blockStyle->listStyleImage());
1302 appendImageIfNotNull(images, blockStyle->borderImageSource());
1303 appendImageIfNotNull(images, blockStyle->maskBoxImageSource());
1304
1305 if (images.isEmpty())
1306 return;
1307
1308 LayoutRect viewBounds = viewRect();
1309 LayoutRect objectBounds = absoluteContentBox();
1310 // The object bounds might be empty right now, so intersects will fail since it doesn't deal
1311 // with empty rects. Use LayoutRect::contains in that case.
1312 bool isVisible;
1313 if (!objectBounds.isEmpty())
1314 isVisible = viewBounds.intersects(objectBounds);
1315 else
1316 isVisible = viewBounds.contains(objectBounds);
1317
1318 ResourceLoadPriorityOptimizer::VisibilityStatus status = isVisible ?
1319 ResourceLoadPriorityOptimizer::Visible : ResourceLoadPriorityOptimizer::NotVisible;
1320
1321 for (Vector<ImageResource*>::iterator it = images.begin(), end = images.end(); it != end; ++it)
1322 optimizer.notifyImageResourceVisibility(*it, status);
1323 }
1324
relayoutShapeDescendantIfMoved(RenderBlock * child,LayoutSize offset)1325 void RenderBlock::relayoutShapeDescendantIfMoved(RenderBlock* child, LayoutSize offset)
1326 {
1327 LayoutUnit left = isHorizontalWritingMode() ? offset.width() : offset.height();
1328 if (!left || !child || child->shapeInsideInfo() || !layoutShapeInsideInfo())
1329 return;
1330 // Propagate layout markers only up to the child, as we are still in the middle
1331 // of a layout pass
1332 child->setNormalChildNeedsLayout(true);
1333 child->markShapeInsideDescendantsForLayout();
1334 child->layoutIfNeeded();
1335 }
1336
layoutShapeInsideInfo() const1337 ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const
1338 {
1339 if (ShapeInsideInfo* shapeInsideInfo = view()->layoutState()->shapeInsideInfo())
1340 return shapeInsideInfo;
1341
1342 RenderFlowThread* flowThread = flowThreadContainingBlock();
1343 if (allowsShapeInsideInfoSharing(flowThread)) {
1344 LayoutUnit lineHeight = this->lineHeight(false, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
1345 // regionAtBlockOffset returns regions like an array first={0,N-1}, second={N,M-1}, ...
1346 LayoutUnit offset = logicalHeight() + lineHeight - LayoutUnit(1);
1347 RenderRegion* region = regionAtBlockOffset(offset);
1348 if (region && region->logicalHeight())
1349 return region->shapeInsideInfo();
1350 }
1351
1352 return 0;
1353 }
1354
logicalOffsetFromShapeAncestorContainer(const RenderBlock * container) const1355 LayoutSize RenderBlock::logicalOffsetFromShapeAncestorContainer(const RenderBlock* container) const
1356 {
1357 const RenderBlock* currentBlock = this;
1358 LayoutRect blockRect(currentBlock->borderBoxRect());
1359 while (currentBlock && !currentBlock->isRenderFlowThread() && currentBlock != container) {
1360 RenderBlock* containerBlock = currentBlock->containingBlock();
1361 ASSERT(containerBlock);
1362 if (!containerBlock)
1363 return LayoutSize();
1364
1365 if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) {
1366 // We have to put the block rect in container coordinates
1367 // and we have to take into account both the container and current block flipping modes
1368 // Bug: Flipping inline and block directions at the same time will not work,
1369 // as one of the flipped dimensions will not yet have been set to its final size
1370 if (containerBlock->style()->isFlippedBlocksWritingMode()) {
1371 if (containerBlock->isHorizontalWritingMode())
1372 blockRect.setY(currentBlock->height() - blockRect.maxY());
1373 else
1374 blockRect.setX(currentBlock->width() - blockRect.maxX());
1375 }
1376 currentBlock->flipForWritingMode(blockRect);
1377 }
1378
1379 blockRect.moveBy(currentBlock->location());
1380 currentBlock = containerBlock;
1381 }
1382
1383 LayoutSize result = isHorizontalWritingMode() ? LayoutSize(blockRect.x(), blockRect.y()) : LayoutSize(blockRect.y(), blockRect.x());
1384 return result;
1385 }
1386
imageChanged(WrappedImagePtr image,const IntRect *)1387 void RenderBlock::imageChanged(WrappedImagePtr image, const IntRect*)
1388 {
1389 RenderBox::imageChanged(image);
1390
1391 if (!parent() || !everHadLayout())
1392 return;
1393
1394 ShapeValue* shapeValue = style()->shapeInside();
1395 if (shapeValue && shapeValue->image() && shapeValue->image()->data() == image) {
1396 ShapeInsideInfo* shapeInsideInfo = ensureShapeInsideInfo();
1397 shapeInsideInfo->dirtyShapeSize();
1398 markShapeInsideDescendantsForLayout();
1399 }
1400
1401 ShapeValue* shapeOutsideValue = style()->shapeOutside();
1402 if (isFloating() && shapeOutsideValue && shapeOutsideValue->image() && shapeOutsideValue->image()->data() == image)
1403 parent()->setNeedsLayoutAndPrefWidthsRecalc();
1404 }
1405
updateShapeInsideInfoAfterStyleChange(const ShapeValue * shapeInside,const ShapeValue * oldShapeInside)1406 void RenderBlock::updateShapeInsideInfoAfterStyleChange(const ShapeValue* shapeInside, const ShapeValue* oldShapeInside)
1407 {
1408 // FIXME: A future optimization would do a deep comparison for equality.
1409 if (shapeInside == oldShapeInside)
1410 return;
1411
1412 if (shapeInside) {
1413 ShapeInsideInfo* shapeInsideInfo = ensureShapeInsideInfo();
1414 shapeInsideInfo->dirtyShapeSize();
1415 } else {
1416 setShapeInsideInfo(nullptr);
1417 markShapeInsideDescendantsForLayout();
1418 }
1419 }
1420
shapeInfoRequiresRelayout(const RenderBlock * block)1421 static inline bool shapeInfoRequiresRelayout(const RenderBlock* block)
1422 {
1423 ShapeInsideInfo* info = block->shapeInsideInfo();
1424 if (info)
1425 info->setNeedsLayout(info->shapeSizeDirty());
1426 else
1427 info = block->layoutShapeInsideInfo();
1428 return info && info->needsLayout();
1429 }
1430
updateRegionsAndShapesLogicalSize(RenderFlowThread * flowThread)1431 bool RenderBlock::updateRegionsAndShapesLogicalSize(RenderFlowThread* flowThread)
1432 {
1433 if (!flowThread && !shapeInsideInfo())
1434 return shapeInfoRequiresRelayout(this);
1435
1436 LayoutUnit oldHeight = logicalHeight();
1437 LayoutUnit oldTop = logicalTop();
1438
1439 // Compute the maximum logical height content may cause this block to expand to
1440 // FIXME: These should eventually use the const computeLogicalHeight rather than updateLogicalHeight
1441 setLogicalHeight(RenderFlowThread::maxLogicalHeight());
1442 updateLogicalHeight();
1443
1444 computeShapeSize();
1445
1446 // Set our start and end regions. No regions above or below us will be considered by our children. They are
1447 // effectively clamped to our region range.
1448 computeRegionRangeForBlock(flowThread);
1449
1450 setLogicalHeight(oldHeight);
1451 setLogicalTop(oldTop);
1452
1453 return shapeInfoRequiresRelayout(this);
1454 }
1455
computeShapeSize()1456 void RenderBlock::computeShapeSize()
1457 {
1458 ShapeInsideInfo* shapeInsideInfo = this->shapeInsideInfo();
1459 if (!shapeInsideInfo)
1460 return;
1461
1462 if (isRenderNamedFlowFragment()) {
1463 ShapeInsideInfo* parentShapeInsideInfo = toRenderBlock(parent())->shapeInsideInfo();
1464 ASSERT(parentShapeInsideInfo);
1465 shapeInsideInfo->setShapeSize(parentShapeInsideInfo->shapeSize().width(), parentShapeInsideInfo->shapeSize().height());
1466 } else {
1467 bool percentageLogicalHeightResolvable = percentageLogicalHeightIsResolvableFromBlock(this, false);
1468 shapeInsideInfo->setShapeSize(logicalWidth(), percentageLogicalHeightResolvable ? logicalHeight() : LayoutUnit());
1469 }
1470 }
1471
updateRegionsAndShapesAfterChildLayout(RenderFlowThread * flowThread,bool heightChanged)1472 void RenderBlock::updateRegionsAndShapesAfterChildLayout(RenderFlowThread* flowThread, bool heightChanged)
1473 {
1474 // A previous sibling has changed dimension, so we need to relayout the shape with the content
1475 ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo();
1476 if (heightChanged && shapeInsideInfo)
1477 shapeInsideInfo->dirtyShapeSize();
1478
1479 computeRegionRangeForBlock(flowThread);
1480 }
1481
computeRegionRangeForBlock(RenderFlowThread * flowThread)1482 void RenderBlock::computeRegionRangeForBlock(RenderFlowThread* flowThread)
1483 {
1484 if (flowThread)
1485 flowThread->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage());
1486 }
1487
updateLogicalWidthAndColumnWidth()1488 bool RenderBlock::updateLogicalWidthAndColumnWidth()
1489 {
1490 LayoutUnit oldWidth = logicalWidth();
1491 LayoutUnit oldColumnWidth = desiredColumnWidth();
1492
1493 updateLogicalWidth();
1494 calcColumnWidth();
1495
1496 bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged;
1497 m_hasBorderOrPaddingLogicalWidthChanged = false;
1498
1499 return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth() || hasBorderOrPaddingLogicalWidthChanged;
1500 }
1501
checkForPaginationLogicalHeightChange(LayoutUnit & pageLogicalHeight,bool & pageLogicalHeightChanged,bool & hasSpecifiedPageLogicalHeight)1502 void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight)
1503 {
1504 ColumnInfo* colInfo = columnInfo();
1505 if (hasColumns()) {
1506 if (!pageLogicalHeight) {
1507 // We need to go ahead and set our explicit page height if one exists, so that we can
1508 // avoid doing two layout passes.
1509 updateLogicalHeight();
1510 LayoutUnit columnHeight = contentLogicalHeight();
1511 if (columnHeight > 0) {
1512 pageLogicalHeight = columnHeight;
1513 hasSpecifiedPageLogicalHeight = true;
1514 }
1515 setLogicalHeight(0);
1516 }
1517 if (colInfo->columnHeight() != pageLogicalHeight && everHadLayout()) {
1518 colInfo->setColumnHeight(pageLogicalHeight);
1519 pageLogicalHeightChanged = true;
1520 }
1521
1522 if (!hasSpecifiedPageLogicalHeight && !pageLogicalHeight)
1523 colInfo->clearForcedBreaks();
1524
1525 colInfo->setPaginationUnit(paginationUnit());
1526 } else if (isRenderFlowThread()) {
1527 pageLogicalHeight = 1; // This is just a hack to always make sure we have a page logical height.
1528 pageLogicalHeightChanged = toRenderFlowThread(this)->pageLogicalSizeChanged();
1529 }
1530 }
1531
layoutBlock(bool,LayoutUnit)1532 void RenderBlock::layoutBlock(bool, LayoutUnit)
1533 {
1534 ASSERT_NOT_REACHED();
1535 clearNeedsLayout();
1536 }
1537
addOverflowFromChildren()1538 void RenderBlock::addOverflowFromChildren()
1539 {
1540 if (!hasColumns()) {
1541 if (childrenInline())
1542 toRenderBlockFlow(this)->addOverflowFromInlineChildren();
1543 else
1544 addOverflowFromBlockChildren();
1545 } else {
1546 ColumnInfo* colInfo = columnInfo();
1547 if (columnCount(colInfo)) {
1548 LayoutRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1);
1549 addLayoutOverflow(lastRect);
1550 addContentsVisualOverflow(lastRect);
1551 }
1552 }
1553 }
1554
computeOverflow(LayoutUnit oldClientAfterEdge,bool)1555 void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool)
1556 {
1557 m_overflow.clear();
1558
1559 // Add overflow from children.
1560 addOverflowFromChildren();
1561
1562 // Add in the overflow from positioned objects.
1563 addOverflowFromPositionedObjects();
1564
1565 if (hasOverflowClip()) {
1566 // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins
1567 // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always
1568 // be considered reachable.
1569 LayoutRect clientRect(noOverflowRect());
1570 LayoutRect rectToApply;
1571 if (isHorizontalWritingMode())
1572 rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, max<LayoutUnit>(0, oldClientAfterEdge - clientRect.y()));
1573 else
1574 rectToApply = LayoutRect(clientRect.x(), clientRect.y(), max<LayoutUnit>(0, oldClientAfterEdge - clientRect.x()), 1);
1575 addLayoutOverflow(rectToApply);
1576 if (hasRenderOverflow())
1577 m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge);
1578 }
1579
1580 // Add visual overflow from box-shadow and border-image-outset.
1581 addVisualEffectOverflow();
1582
1583 // Add visual overflow from theme.
1584 addVisualOverflowFromTheme();
1585
1586 if (isRenderNamedFlowThread())
1587 toRenderNamedFlowThread(this)->computeOversetStateForRegions(oldClientAfterEdge);
1588 }
1589
addOverflowFromBlockChildren()1590 void RenderBlock::addOverflowFromBlockChildren()
1591 {
1592 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
1593 if (!child->isFloatingOrOutOfFlowPositioned())
1594 addOverflowFromChild(child);
1595 }
1596 }
1597
addOverflowFromPositionedObjects()1598 void RenderBlock::addOverflowFromPositionedObjects()
1599 {
1600 TrackedRendererListHashSet* positionedDescendants = positionedObjects();
1601 if (!positionedDescendants)
1602 return;
1603
1604 RenderBox* positionedObject;
1605 TrackedRendererListHashSet::iterator end = positionedDescendants->end();
1606 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) {
1607 positionedObject = *it;
1608
1609 // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content.
1610 if (positionedObject->style()->position() != FixedPosition)
1611 addOverflowFromChild(positionedObject, LayoutSize(positionedObject->x(), positionedObject->y()));
1612 }
1613 }
1614
addVisualOverflowFromTheme()1615 void RenderBlock::addVisualOverflowFromTheme()
1616 {
1617 if (!style()->hasAppearance())
1618 return;
1619
1620 IntRect inflatedRect = pixelSnappedBorderBoxRect();
1621 RenderTheme::theme().adjustRepaintRect(this, inflatedRect);
1622 addVisualOverflow(inflatedRect);
1623 }
1624
expandsToEncloseOverhangingFloats() const1625 bool RenderBlock::expandsToEncloseOverhangingFloats() const
1626 {
1627 return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated())
1628 || hasColumns() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot();
1629 }
1630
computeStartPositionDeltaForChildAvoidingFloats(const RenderBox * child,LayoutUnit childMarginStart,RenderRegion * region)1631 LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, RenderRegion* region)
1632 {
1633 LayoutUnit startPosition = startOffsetForContent(region);
1634
1635 // Add in our start margin.
1636 LayoutUnit oldPosition = startPosition + childMarginStart;
1637 LayoutUnit newPosition = oldPosition;
1638
1639 LayoutUnit blockOffset = logicalTopForChild(child);
1640 if (region)
1641 blockOffset = max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage()));
1642
1643 LayoutUnit startOff = startOffsetForLineInRegion(blockOffset, false, region, logicalHeightForChild(child));
1644
1645 if (style()->textAlign() != WEBKIT_CENTER && !child->style()->marginStartUsing(style()).isAuto()) {
1646 if (childMarginStart < 0)
1647 startOff += childMarginStart;
1648 newPosition = max(newPosition, startOff); // Let the float sit in the child's margin if it can fit.
1649 } else if (startOff != startPosition)
1650 newPosition = startOff + childMarginStart;
1651
1652 return newPosition - oldPosition;
1653 }
1654
determineLogicalLeftPositionForChild(RenderBox * child,ApplyLayoutDeltaMode applyDelta)1655 void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child, ApplyLayoutDeltaMode applyDelta)
1656 {
1657 LayoutUnit startPosition = borderStart() + paddingStart();
1658 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1659 startPosition -= verticalScrollbarWidth();
1660 LayoutUnit totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + availableLogicalWidth();
1661
1662 // Add in our start margin.
1663 LayoutUnit childMarginStart = marginStartForChild(child);
1664 LayoutUnit newPosition = startPosition + childMarginStart;
1665
1666 // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need
1667 // to shift over as necessary to dodge any floats that might get in the way.
1668 if (child->avoidsFloats() && containsFloats() && !flowThreadContainingBlock())
1669 newPosition += computeStartPositionDeltaForChildAvoidingFloats(child, marginStartForChild(child));
1670
1671 setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), applyDelta);
1672 }
1673
setLogicalLeftForChild(RenderBox * child,LayoutUnit logicalLeft,ApplyLayoutDeltaMode applyDelta)1674 void RenderBlock::setLogicalLeftForChild(RenderBox* child, LayoutUnit logicalLeft, ApplyLayoutDeltaMode applyDelta)
1675 {
1676 if (isHorizontalWritingMode()) {
1677 if (applyDelta == ApplyLayoutDelta)
1678 view()->addLayoutDelta(LayoutSize(child->x() - logicalLeft, 0));
1679 child->setX(logicalLeft);
1680 } else {
1681 if (applyDelta == ApplyLayoutDelta)
1682 view()->addLayoutDelta(LayoutSize(0, child->y() - logicalLeft));
1683 child->setY(logicalLeft);
1684 }
1685 }
1686
setLogicalTopForChild(RenderBox * child,LayoutUnit logicalTop,ApplyLayoutDeltaMode applyDelta)1687 void RenderBlock::setLogicalTopForChild(RenderBox* child, LayoutUnit logicalTop, ApplyLayoutDeltaMode applyDelta)
1688 {
1689 if (isHorizontalWritingMode()) {
1690 if (applyDelta == ApplyLayoutDelta)
1691 view()->addLayoutDelta(LayoutSize(0, child->y() - logicalTop));
1692 child->setY(logicalTop);
1693 } else {
1694 if (applyDelta == ApplyLayoutDelta)
1695 view()->addLayoutDelta(LayoutSize(child->x() - logicalTop, 0));
1696 child->setX(logicalTop);
1697 }
1698 }
1699
updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren,RenderBox * child)1700 void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox* child)
1701 {
1702 // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into
1703 // an auto value. Add a method to determine this, so that we can avoid the relayout.
1704 if (relayoutChildren || (child->hasRelativeLogicalHeight() && !isRenderView()))
1705 child->setChildNeedsLayout(MarkOnlyThis);
1706
1707 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
1708 if (relayoutChildren && child->needsPreferredWidthsRecalculation())
1709 child->setPreferredLogicalWidthsDirty(MarkOnlyThis);
1710 }
1711
simplifiedNormalFlowLayout()1712 void RenderBlock::simplifiedNormalFlowLayout()
1713 {
1714 if (childrenInline()) {
1715 ListHashSet<RootInlineBox*> lineBoxes;
1716 for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) {
1717 RenderObject* o = walker.current();
1718 if (!o->isOutOfFlowPositioned() && (o->isReplaced() || o->isFloating())) {
1719 o->layoutIfNeeded();
1720 if (toRenderBox(o)->inlineBoxWrapper()) {
1721 RootInlineBox* box = toRenderBox(o)->inlineBoxWrapper()->root();
1722 lineBoxes.add(box);
1723 }
1724 } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) {
1725 o->clearNeedsLayout();
1726 }
1727 }
1728
1729 // FIXME: Glyph overflow will get lost in this case, but not really a big deal.
1730 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
1731 for (ListHashSet<RootInlineBox*>::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) {
1732 RootInlineBox* box = *it;
1733 box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap);
1734 }
1735 } else {
1736 for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) {
1737 if (!box->isOutOfFlowPositioned())
1738 box->layoutIfNeeded();
1739 }
1740 }
1741 }
1742
simplifiedLayout()1743 bool RenderBlock::simplifiedLayout()
1744 {
1745 if ((!posChildNeedsLayout() && !needsSimplifiedNormalFlowLayout()) || normalChildNeedsLayout() || selfNeedsLayout())
1746 return false;
1747
1748 LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasColumns() || hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode());
1749
1750 if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly())
1751 return false;
1752
1753 // Lay out positioned descendants or objects that just need to recompute overflow.
1754 if (needsSimplifiedNormalFlowLayout())
1755 simplifiedNormalFlowLayout();
1756
1757 // Make sure a forced break is applied after the content if we are a flow thread in a simplified layout.
1758 // This ensures the size information is correctly computed for the last auto-height region receiving content.
1759 if (isRenderFlowThread())
1760 toRenderFlowThread(this)->applyBreakAfterContent(clientLogicalBottom());
1761
1762 // Lay out our positioned objects if our positioned child bit is set.
1763 // Also, if an absolute position element inside a relative positioned container moves, and the absolute element has a fixed position
1764 // child, neither the fixed element nor its container learn of the movement since posChildNeedsLayout() is only marked as far as the
1765 // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them
1766 // are statically positioned and thus need to move with their absolute ancestors.
1767 bool canContainFixedPosObjects = canContainFixedPositionObjects();
1768 if (posChildNeedsLayout() || canContainFixedPosObjects)
1769 layoutPositionedObjects(false, !posChildNeedsLayout() && canContainFixedPosObjects);
1770
1771 // Recompute our overflow information.
1772 // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only
1773 // updating our overflow if we either used to have overflow or if the new temporary object has overflow.
1774 // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and
1775 // lowestPosition on every relayout so it's not a regression.
1776 // computeOverflow expects the bottom edge before we clamp our height. Since this information isn't available during
1777 // simplifiedLayout, we cache the value in m_overflow.
1778 LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom();
1779 computeOverflow(oldClientAfterEdge, true);
1780
1781 statePusher.pop();
1782
1783 updateLayerTransform();
1784
1785 updateScrollInfoAfterLayout();
1786
1787 clearNeedsLayout();
1788 return true;
1789 }
1790
markFixedPositionObjectForLayoutIfNeeded(RenderObject * child,SubtreeLayoutScope & layoutScope)1791 void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderObject* child, SubtreeLayoutScope& layoutScope)
1792 {
1793 if (child->style()->position() != FixedPosition)
1794 return;
1795
1796 bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontalWritingMode());
1797 bool hasStaticInlinePosition = child->style()->hasStaticInlinePosition(isHorizontalWritingMode());
1798 if (!hasStaticBlockPosition && !hasStaticInlinePosition)
1799 return;
1800
1801 RenderObject* o = child->parent();
1802 while (o && !o->isRenderView() && o->style()->position() != AbsolutePosition)
1803 o = o->parent();
1804 if (o->style()->position() != AbsolutePosition)
1805 return;
1806
1807 RenderBox* box = toRenderBox(child);
1808 if (hasStaticInlinePosition) {
1809 LayoutUnit oldLeft = box->logicalLeft();
1810 box->updateLogicalWidth();
1811 if (box->logicalLeft() != oldLeft)
1812 layoutScope.setChildNeedsLayout(child);
1813 } else if (hasStaticBlockPosition) {
1814 LayoutUnit oldTop = box->logicalTop();
1815 box->updateLogicalHeight();
1816 if (box->logicalTop() != oldTop)
1817 layoutScope.setChildNeedsLayout(child);
1818 }
1819 }
1820
marginIntrinsicLogicalWidthForChild(RenderBox * child) const1821 LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox* child) const
1822 {
1823 // A margin has three types: fixed, percentage, and auto (variable).
1824 // Auto and percentage margins become 0 when computing min/max width.
1825 // Fixed margins can be added in as is.
1826 Length marginLeft = child->style()->marginStartUsing(style());
1827 Length marginRight = child->style()->marginEndUsing(style());
1828 LayoutUnit margin = 0;
1829 if (marginLeft.isFixed())
1830 margin += marginLeft.value();
1831 if (marginRight.isFixed())
1832 margin += marginRight.value();
1833 return margin;
1834 }
1835
layoutPositionedObjects(bool relayoutChildren,bool fixedPositionObjectsOnly)1836 void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly)
1837 {
1838 TrackedRendererListHashSet* positionedDescendants = positionedObjects();
1839 if (!positionedDescendants)
1840 return;
1841
1842 if (hasColumns())
1843 view()->layoutState()->clearPaginationInformation(); // Positioned objects are not part of the column flow, so they don't paginate with the columns.
1844
1845 RenderBox* r;
1846 TrackedRendererListHashSet::iterator end = positionedDescendants->end();
1847 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) {
1848 r = *it;
1849
1850 SubtreeLayoutScope layoutScope(r);
1851 // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So
1852 // if this is a fixed position element, mark it for layout if it has an abspos ancestor and needs to move with that ancestor, i.e.
1853 // it has static position.
1854 markFixedPositionObjectForLayoutIfNeeded(r, layoutScope);
1855 if (fixedPositionObjectsOnly) {
1856 r->layoutIfNeeded();
1857 continue;
1858 }
1859
1860 // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the
1861 // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned
1862 // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is
1863 // positioned explicitly) this should not incur a performance penalty.
1864 if (relayoutChildren || (r->style()->hasStaticBlockPosition(isHorizontalWritingMode()) && r->parent() != this))
1865 layoutScope.setChildNeedsLayout(r);
1866
1867 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
1868 if (relayoutChildren && r->needsPreferredWidthsRecalculation())
1869 r->setPreferredLogicalWidthsDirty(MarkOnlyThis);
1870
1871 if (!r->needsLayout())
1872 r->markForPaginationRelayoutIfNeeded(layoutScope);
1873
1874 // 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
1875 // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout.
1876 if (r->needsPositionedMovementLayoutOnly() && r->tryLayoutDoingPositionedMovementOnly())
1877 r->clearNeedsLayout();
1878
1879 // If we are paginated or in a line grid, go ahead and compute a vertical position for our object now.
1880 // If it's wrong we'll lay out again.
1881 LayoutUnit oldLogicalTop = 0;
1882 bool needsBlockDirectionLocationSetBeforeLayout = r->needsLayout() && view()->layoutState()->needsBlockDirectionLocationSetBeforeLayout();
1883 if (needsBlockDirectionLocationSetBeforeLayout) {
1884 if (isHorizontalWritingMode() == r->isHorizontalWritingMode())
1885 r->updateLogicalHeight();
1886 else
1887 r->updateLogicalWidth();
1888 oldLogicalTop = logicalTopForChild(r);
1889 }
1890
1891 r->layoutIfNeeded();
1892
1893 // Lay out again if our estimate was wrong.
1894 if (needsBlockDirectionLocationSetBeforeLayout && logicalTopForChild(r) != oldLogicalTop)
1895 r->forceChildLayout();
1896 }
1897
1898 if (hasColumns())
1899 view()->layoutState()->m_columnInfo = columnInfo(); // FIXME: Kind of gross. We just put this back into the layout state so that pop() will work.
1900 }
1901
markPositionedObjectsForLayout()1902 void RenderBlock::markPositionedObjectsForLayout()
1903 {
1904 TrackedRendererListHashSet* positionedDescendants = positionedObjects();
1905 if (positionedDescendants) {
1906 RenderBox* r;
1907 TrackedRendererListHashSet::iterator end = positionedDescendants->end();
1908 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) {
1909 r = *it;
1910 r->setChildNeedsLayout();
1911 }
1912 }
1913 }
1914
markForPaginationRelayoutIfNeeded(SubtreeLayoutScope & layoutScope)1915 void RenderBlock::markForPaginationRelayoutIfNeeded(SubtreeLayoutScope& layoutScope)
1916 {
1917 ASSERT(!needsLayout());
1918 if (needsLayout())
1919 return;
1920
1921 if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset()))
1922 layoutScope.setChildNeedsLayout(this);
1923 }
1924
paint(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1925 void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1926 {
1927 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1928
1929 LayoutPoint adjustedPaintOffset = paintOffset + location();
1930
1931 PaintPhase phase = paintInfo.phase;
1932
1933 // Check if we need to do anything at all.
1934 // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView
1935 // paints the root's background.
1936 if (!isRoot()) {
1937 LayoutRect overflowBox = overflowRectForPaintRejection();
1938 flipForWritingMode(overflowBox);
1939 overflowBox.inflate(maximalOutlineSize(paintInfo.phase));
1940 overflowBox.moveBy(adjustedPaintOffset);
1941 if (!overflowBox.intersects(paintInfo.rect))
1942 return;
1943 }
1944
1945 // There are some cases where not all clipped visual overflow is accounted for.
1946 // FIXME: reduce the number of such cases.
1947 ContentsClipBehavior contentsClipBehavior = ForceContentsClip;
1948 if (hasOverflowClip() && !hasControlClip() && !(shouldPaintSelectionGaps() && phase == PaintPhaseForeground) && !hasCaret())
1949 contentsClipBehavior = SkipContentsClipIfPossible;
1950
1951 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, contentsClipBehavior);
1952 paintObject(paintInfo, adjustedPaintOffset);
1953 if (pushedClip)
1954 popContentsClip(paintInfo, phase, adjustedPaintOffset);
1955
1956 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
1957 // z-index. We paint after we painted the background/border, so that the scrollbars will
1958 // sit above the background/border.
1959 if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this) && !paintInfo.paintRootBackgroundOnly())
1960 layer()->scrollableArea()->paintOverflowControls(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect, false /* paintingOverlayControls */);
1961 }
1962
paintColumnRules(PaintInfo & paintInfo,const LayoutPoint & paintOffset)1963 void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1964 {
1965 if (paintInfo.context->paintingDisabled())
1966 return;
1967
1968 const Color& ruleColor = resolveColor(CSSPropertyWebkitColumnRuleColor);
1969 bool ruleTransparent = style()->columnRuleIsTransparent();
1970 EBorderStyle ruleStyle = style()->columnRuleStyle();
1971 LayoutUnit ruleThickness = style()->columnRuleWidth();
1972 LayoutUnit colGap = columnGap();
1973 bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent;
1974 if (!renderRule)
1975 return;
1976
1977 ColumnInfo* colInfo = columnInfo();
1978 unsigned colCount = columnCount(colInfo);
1979
1980 bool antialias = shouldAntialiasLines(paintInfo.context);
1981
1982 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
1983 bool leftToRight = style()->isLeftToRightDirection() ^ colInfo->progressionIsReversed();
1984 LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth();
1985 LayoutUnit ruleAdd = logicalLeftOffsetForContent();
1986 LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth();
1987 LayoutUnit inlineDirectionSize = colInfo->desiredColumnWidth();
1988 BoxSide boxSide = isHorizontalWritingMode()
1989 ? leftToRight ? BSLeft : BSRight
1990 : leftToRight ? BSTop : BSBottom;
1991
1992 for (unsigned i = 0; i < colCount; i++) {
1993 // Move to the next position.
1994 if (leftToRight) {
1995 ruleLogicalLeft += inlineDirectionSize + colGap / 2;
1996 currLogicalLeftOffset += inlineDirectionSize + colGap;
1997 } else {
1998 ruleLogicalLeft -= (inlineDirectionSize + colGap / 2);
1999 currLogicalLeftOffset -= (inlineDirectionSize + colGap);
2000 }
2001
2002 // Now paint the column rule.
2003 if (i < colCount - 1) {
2004 LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft();
2005 LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth();
2006 LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd;
2007 LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness;
2008 IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom);
2009 drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
2010 }
2011
2012 ruleLogicalLeft = currLogicalLeftOffset;
2013 }
2014 } else {
2015 bool topToBottom = !style()->isFlippedBlocksWritingMode() ^ colInfo->progressionIsReversed();
2016 LayoutUnit ruleLeft = isHorizontalWritingMode()
2017 ? borderLeft() + paddingLeft()
2018 : colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderBefore() + paddingBefore() : borderAfter() + paddingAfter());
2019 LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness;
2020 LayoutUnit ruleTop = isHorizontalWritingMode()
2021 ? colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderBefore() + paddingBefore() : borderAfter() + paddingAfter())
2022 : borderStart() + paddingStart();
2023 LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight();
2024 LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight);
2025
2026 if (!topToBottom) {
2027 if (isHorizontalWritingMode())
2028 ruleRect.setY(height() - ruleRect.maxY());
2029 else
2030 ruleRect.setX(width() - ruleRect.maxX());
2031 }
2032
2033 ruleRect.moveBy(paintOffset);
2034
2035 BoxSide boxSide = isHorizontalWritingMode()
2036 ? topToBottom ? BSTop : BSBottom
2037 : topToBottom ? BSLeft : BSRight;
2038
2039 LayoutSize step(0, topToBottom ? colInfo->columnHeight() + colGap : -(colInfo->columnHeight() + colGap));
2040 if (!isHorizontalWritingMode())
2041 step = step.transposedSize();
2042
2043 for (unsigned i = 1; i < colCount; i++) {
2044 ruleRect.move(step);
2045 IntRect pixelSnappedRuleRect = pixelSnappedIntRect(ruleRect);
2046 drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
2047 }
2048 }
2049 }
2050
paintColumnContents(PaintInfo & paintInfo,const LayoutPoint & paintOffset,bool paintingFloats)2051 void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool paintingFloats)
2052 {
2053 // We need to do multiple passes, breaking up our child painting into strips.
2054 GraphicsContext* context = paintInfo.context;
2055 ColumnInfo* colInfo = columnInfo();
2056 unsigned colCount = columnCount(colInfo);
2057 if (!colCount)
2058 return;
2059 LayoutUnit currLogicalTopOffset = 0;
2060 LayoutUnit colGap = columnGap();
2061 for (unsigned i = 0; i < colCount; i++) {
2062 // For each rect, we clip to the rect, and then we adjust our coords.
2063 LayoutRect colRect = columnRectAt(colInfo, i);
2064 flipForWritingMode(colRect);
2065 LayoutUnit logicalLeftOffset = (isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent();
2066 LayoutSize offset = isHorizontalWritingMode() ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset);
2067 if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) {
2068 if (isHorizontalWritingMode())
2069 offset.expand(0, colRect.y() - borderTop() - paddingTop());
2070 else
2071 offset.expand(colRect.x() - borderLeft() - paddingLeft(), 0);
2072 }
2073 colRect.moveBy(paintOffset);
2074 PaintInfo info(paintInfo);
2075 info.rect.intersect(pixelSnappedIntRect(colRect));
2076
2077 if (!info.rect.isEmpty()) {
2078 GraphicsContextStateSaver stateSaver(*context);
2079 LayoutRect clipRect(colRect);
2080
2081 if (i < colCount - 1) {
2082 if (isHorizontalWritingMode())
2083 clipRect.expand(colGap / 2, 0);
2084 else
2085 clipRect.expand(0, colGap / 2);
2086 }
2087 // Each strip pushes a clip, since column boxes are specified as being
2088 // like overflow:hidden.
2089 // FIXME: Content and column rules that extend outside column boxes at the edges of the multi-column element
2090 // are clipped according to the 'overflow' property.
2091 context->clip(pixelSnappedIntRect(clipRect));
2092
2093 // Adjust our x and y when painting.
2094 LayoutPoint adjustedPaintOffset = paintOffset + offset;
2095 if (paintingFloats)
2096 paintFloats(info, adjustedPaintOffset, paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip);
2097 else
2098 paintContents(info, adjustedPaintOffset);
2099 }
2100
2101 LayoutUnit blockDelta = (isHorizontalWritingMode() ? colRect.height() : colRect.width());
2102 if (style()->isFlippedBlocksWritingMode())
2103 currLogicalTopOffset += blockDelta;
2104 else
2105 currLogicalTopOffset -= blockDelta;
2106 }
2107 }
2108
paintContents(PaintInfo & paintInfo,const LayoutPoint & paintOffset)2109 void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
2110 {
2111 // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC.
2112 // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverChanged() on the Document
2113 // will do a full repaint.
2114 if (document().didLayoutWithPendingStylesheets() && !isRenderView())
2115 return;
2116
2117 if (childrenInline())
2118 m_lineBoxes.paint(this, paintInfo, paintOffset);
2119 else {
2120 PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase;
2121 newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase;
2122
2123 // We don't paint our own background, but we do let the kids paint their backgrounds.
2124 PaintInfo paintInfoForChild(paintInfo);
2125 paintInfoForChild.phase = newPhase;
2126 paintInfoForChild.updatePaintingRootForChildren(this);
2127 paintChildren(paintInfoForChild, paintOffset);
2128 }
2129 }
2130
paintChildren(PaintInfo & paintInfo,const LayoutPoint & paintOffset)2131 void RenderBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
2132 {
2133 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox())
2134 paintChild(child, paintInfo, paintOffset);
2135 }
2136
paintChild(RenderBox * child,PaintInfo & paintInfo,const LayoutPoint & paintOffset)2137 void RenderBlock::paintChild(RenderBox* child, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
2138 {
2139 LayoutPoint childPoint = flipForWritingModeForChild(child, paintOffset);
2140 if (!child->hasSelfPaintingLayer() && !child->isFloating())
2141 child->paint(paintInfo, childPoint);
2142 }
2143
paintChildAsInlineBlock(RenderBox * child,PaintInfo & paintInfo,const LayoutPoint & paintOffset)2144 void RenderBlock::paintChildAsInlineBlock(RenderBox* child, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
2145 {
2146 LayoutPoint childPoint = flipForWritingModeForChild(child, paintOffset);
2147 if (!child->hasSelfPaintingLayer() && !child->isFloating())
2148 paintAsInlineBlock(child, paintInfo, childPoint);
2149 }
2150
paintAsInlineBlock(RenderObject * renderer,PaintInfo & paintInfo,const LayoutPoint & childPoint)2151 void RenderBlock::paintAsInlineBlock(RenderObject* renderer, PaintInfo& paintInfo, const LayoutPoint& childPoint)
2152 {
2153 if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
2154 return;
2155
2156 // Paint all phases atomically, as though the element established its own
2157 // stacking context. (See Appendix E.2, section 7.2.1.4 on
2158 // inline block/table/replaced elements in the CSS2.1 specification.)
2159 // This is also used by other elements (e.g. flex items and grid items).
2160 bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip;
2161 PaintInfo info(paintInfo);
2162 info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground;
2163 renderer->paint(info, childPoint);
2164 if (!preservePhase) {
2165 info.phase = PaintPhaseChildBlockBackgrounds;
2166 renderer->paint(info, childPoint);
2167 info.phase = PaintPhaseFloat;
2168 renderer->paint(info, childPoint);
2169 info.phase = PaintPhaseForeground;
2170 renderer->paint(info, childPoint);
2171 info.phase = PaintPhaseOutline;
2172 renderer->paint(info, childPoint);
2173 }
2174 }
2175
hasCaret(CaretType type) const2176 bool RenderBlock::hasCaret(CaretType type) const
2177 {
2178 // Paint the caret if the FrameSelection says so or if caret browsing is enabled
2179 bool caretBrowsing = frame()->settings() && frame()->settings()->caretBrowsingEnabled();
2180 RenderObject* caretPainter;
2181 bool isContentEditable;
2182 if (type == CursorCaret) {
2183 caretPainter = frame()->selection().caretRenderer();
2184 isContentEditable = frame()->selection().rendererIsEditable();
2185 } else {
2186 caretPainter = frame()->page()->dragCaretController().caretRenderer();
2187 isContentEditable = frame()->page()->dragCaretController().isContentEditable();
2188 }
2189 return caretPainter == this && (isContentEditable || caretBrowsing);
2190 }
2191
paintCaret(PaintInfo & paintInfo,const LayoutPoint & paintOffset,CaretType type)2192 void RenderBlock::paintCaret(PaintInfo& paintInfo, const LayoutPoint& paintOffset, CaretType type)
2193 {
2194 if (!hasCaret(type))
2195 return;
2196
2197 if (type == CursorCaret)
2198 frame()->selection().paintCaret(paintInfo.context, paintOffset, paintInfo.rect);
2199 else
2200 frame()->page()->dragCaretController().paintDragCaret(frame(), paintInfo.context, paintOffset, paintInfo.rect);
2201 }
2202
paintObject(PaintInfo & paintInfo,const LayoutPoint & paintOffset)2203 void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
2204 {
2205 PaintPhase paintPhase = paintInfo.phase;
2206
2207 // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div).
2208 LayoutPoint scrolledOffset = paintOffset;
2209 if (hasOverflowClip())
2210 scrolledOffset.move(-scrolledContentOffset());
2211
2212 // 1. paint background, borders etc
2213 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) {
2214 if (hasBoxDecorations())
2215 paintBoxDecorations(paintInfo, paintOffset);
2216 if (hasColumns() && !paintInfo.paintRootBackgroundOnly())
2217 paintColumnRules(paintInfo, scrolledOffset);
2218 }
2219
2220 if (paintPhase == PaintPhaseMask && style()->visibility() == VISIBLE) {
2221 paintMask(paintInfo, paintOffset);
2222 return;
2223 }
2224
2225 if (paintPhase == PaintPhaseClippingMask && style()->visibility() == VISIBLE) {
2226 paintClippingMask(paintInfo, paintOffset);
2227 return;
2228 }
2229
2230 // We're done. We don't bother painting any children.
2231 if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly())
2232 return;
2233
2234 // 2. paint contents
2235 if (paintPhase != PaintPhaseSelfOutline) {
2236 if (hasColumns())
2237 paintColumnContents(paintInfo, scrolledOffset);
2238 else
2239 paintContents(paintInfo, scrolledOffset);
2240 }
2241
2242 // 3. paint selection
2243 // FIXME: Make this work with multi column layouts. For now don't fill gaps.
2244 bool isPrinting = document().printing();
2245 if (!isPrinting && !hasColumns())
2246 paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks.
2247
2248 // 4. paint floats.
2249 if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) {
2250 if (hasColumns())
2251 paintColumnContents(paintInfo, scrolledOffset, true);
2252 else
2253 paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip);
2254 }
2255
2256 // 5. paint outline.
2257 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE)
2258 paintOutline(paintInfo, LayoutRect(paintOffset, size()));
2259
2260 // 6. paint continuation outlines.
2261 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) {
2262 RenderInline* inlineCont = inlineElementContinuation();
2263 if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE) {
2264 RenderInline* inlineRenderer = toRenderInline(inlineCont->node()->renderer());
2265 RenderBlock* cb = containingBlock();
2266
2267 bool inlineEnclosedInSelfPaintingLayer = false;
2268 for (RenderBoxModelObject* box = inlineRenderer; box != cb; box = box->parent()->enclosingBoxModelObject()) {
2269 if (box->hasSelfPaintingLayer()) {
2270 inlineEnclosedInSelfPaintingLayer = true;
2271 break;
2272 }
2273 }
2274
2275 // Do not add continuations for outline painting by our containing block if we are a relative positioned
2276 // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being
2277 // in the same layer.
2278 if (!inlineEnclosedInSelfPaintingLayer && !hasLayer())
2279 cb->addContinuationWithOutline(inlineRenderer);
2280 else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && hasLayer()))
2281 inlineRenderer->paintOutline(paintInfo, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location());
2282 }
2283 paintContinuationOutlines(paintInfo, paintOffset);
2284 }
2285
2286 // 7. paint caret.
2287 // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground,
2288 // then paint the caret.
2289 if (paintPhase == PaintPhaseForeground) {
2290 paintCaret(paintInfo, paintOffset, CursorCaret);
2291 paintCaret(paintInfo, paintOffset, DragCaret);
2292 }
2293 }
2294
inlineElementContinuation() const2295 RenderInline* RenderBlock::inlineElementContinuation() const
2296 {
2297 RenderBoxModelObject* continuation = this->continuation();
2298 return continuation && continuation->isInline() ? toRenderInline(continuation) : 0;
2299 }
2300
blockElementContinuation() const2301 RenderBlock* RenderBlock::blockElementContinuation() const
2302 {
2303 RenderBoxModelObject* currentContinuation = continuation();
2304 if (!currentContinuation || currentContinuation->isInline())
2305 return 0;
2306 RenderBlock* nextContinuation = toRenderBlock(currentContinuation);
2307 if (nextContinuation->isAnonymousBlock())
2308 return nextContinuation->blockElementContinuation();
2309 return nextContinuation;
2310 }
2311
continuationOutlineTable()2312 static ContinuationOutlineTableMap* continuationOutlineTable()
2313 {
2314 DEFINE_STATIC_LOCAL(ContinuationOutlineTableMap, table, ());
2315 return &table;
2316 }
2317
addContinuationWithOutline(RenderInline * flow)2318 void RenderBlock::addContinuationWithOutline(RenderInline* flow)
2319 {
2320 // We can't make this work if the inline is in a layer. We'll just rely on the broken
2321 // way of painting.
2322 ASSERT(!flow->layer() && !flow->isInlineElementContinuation());
2323
2324 ContinuationOutlineTableMap* table = continuationOutlineTable();
2325 ListHashSet<RenderInline*>* continuations = table->get(this);
2326 if (!continuations) {
2327 continuations = new ListHashSet<RenderInline*>;
2328 table->set(this, adoptPtr(continuations));
2329 }
2330
2331 continuations->add(flow);
2332 }
2333
paintsContinuationOutline(RenderInline * flow)2334 bool RenderBlock::paintsContinuationOutline(RenderInline* flow)
2335 {
2336 ContinuationOutlineTableMap* table = continuationOutlineTable();
2337 if (table->isEmpty())
2338 return false;
2339
2340 ListHashSet<RenderInline*>* continuations = table->get(this);
2341 if (!continuations)
2342 return false;
2343
2344 return continuations->contains(flow);
2345 }
2346
paintContinuationOutlines(PaintInfo & info,const LayoutPoint & paintOffset)2347 void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& paintOffset)
2348 {
2349 ContinuationOutlineTableMap* table = continuationOutlineTable();
2350 if (table->isEmpty())
2351 return;
2352
2353 OwnPtr<ListHashSet<RenderInline*> > continuations = table->take(this);
2354 if (!continuations)
2355 return;
2356
2357 LayoutPoint accumulatedPaintOffset = paintOffset;
2358 // Paint each continuation outline.
2359 ListHashSet<RenderInline*>::iterator end = continuations->end();
2360 for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) {
2361 // Need to add in the coordinates of the intervening blocks.
2362 RenderInline* flow = *it;
2363 RenderBlock* block = flow->containingBlock();
2364 for ( ; block && block != this; block = block->containingBlock())
2365 accumulatedPaintOffset.moveBy(block->location());
2366 ASSERT(block);
2367 flow->paintOutline(info, accumulatedPaintOffset);
2368 }
2369 }
2370
shouldPaintSelectionGaps() const2371 bool RenderBlock::shouldPaintSelectionGaps() const
2372 {
2373 return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot();
2374 }
2375
isSelectionRoot() const2376 bool RenderBlock::isSelectionRoot() const
2377 {
2378 if (isPseudoElement())
2379 return false;
2380 ASSERT(node() || isAnonymous());
2381
2382 // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases.
2383 if (isTable())
2384 return false;
2385
2386 if (isBody() || isRoot() || hasOverflowClip()
2387 || isPositioned() || isFloating()
2388 || isTableCell() || isInlineBlockOrInlineTable()
2389 || hasTransform() || hasReflection() || hasMask() || isWritingModeRoot()
2390 || isRenderFlowThread())
2391 return true;
2392
2393 if (view() && view()->selectionStart()) {
2394 Node* startElement = view()->selectionStart()->node();
2395 if (startElement && startElement->rootEditableElement() == node())
2396 return true;
2397 }
2398
2399 return false;
2400 }
2401
selectionGapRectsForRepaint(const RenderLayerModelObject * repaintContainer)2402 GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* repaintContainer)
2403 {
2404 ASSERT(!needsLayout());
2405
2406 if (!shouldPaintSelectionGaps())
2407 return GapRects();
2408
2409 TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint());
2410 mapLocalToContainer(repaintContainer, transformState, ApplyContainerFlip | UseTransforms);
2411 LayoutPoint offsetFromRepaintContainer = roundedLayoutPoint(transformState.mappedPoint());
2412
2413 if (hasOverflowClip())
2414 offsetFromRepaintContainer -= scrolledContentOffset();
2415
2416 LayoutUnit lastTop = 0;
2417 LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop);
2418 LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop);
2419
2420 return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight);
2421 }
2422
paintSelection(PaintInfo & paintInfo,const LayoutPoint & paintOffset)2423 void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
2424 {
2425 if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) {
2426 LayoutUnit lastTop = 0;
2427 LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop);
2428 LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop);
2429 GraphicsContextStateSaver stateSaver(*paintInfo.context);
2430
2431 LayoutRect gapRectsBounds = selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo);
2432 if (!gapRectsBounds.isEmpty()) {
2433 if (RenderLayer* layer = enclosingLayer()) {
2434 gapRectsBounds.moveBy(-paintOffset);
2435 if (!hasLayer()) {
2436 LayoutRect localBounds(gapRectsBounds);
2437 flipForWritingMode(localBounds);
2438 gapRectsBounds = localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox();
2439 if (layer->renderer()->hasOverflowClip())
2440 gapRectsBounds.move(layer->renderBox()->scrolledContentOffset());
2441 }
2442 layer->addBlockSelectionGapsBounds(gapRectsBounds);
2443 }
2444 }
2445 }
2446 }
2447
clipOutPositionedObjects(const PaintInfo * paintInfo,const LayoutPoint & offset,TrackedRendererListHashSet * positionedObjects)2448 static void clipOutPositionedObjects(const PaintInfo* paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* positionedObjects)
2449 {
2450 if (!positionedObjects)
2451 return;
2452
2453 TrackedRendererListHashSet::const_iterator end = positionedObjects->end();
2454 for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) {
2455 RenderBox* r = *it;
2456 paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height()));
2457 }
2458 }
2459
blockDirectionOffset(const LayoutSize & offsetFromBlock) const2460 LayoutUnit RenderBlock::blockDirectionOffset(const LayoutSize& offsetFromBlock) const
2461 {
2462 return isHorizontalWritingMode() ? offsetFromBlock.height() : offsetFromBlock.width();
2463 }
2464
inlineDirectionOffset(const LayoutSize & offsetFromBlock) const2465 LayoutUnit RenderBlock::inlineDirectionOffset(const LayoutSize& offsetFromBlock) const
2466 {
2467 return isHorizontalWritingMode() ? offsetFromBlock.width() : offsetFromBlock.height();
2468 }
2469
logicalRectToPhysicalRect(const LayoutPoint & rootBlockPhysicalPosition,const LayoutRect & logicalRect)2470 LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPhysicalPosition, const LayoutRect& logicalRect)
2471 {
2472 LayoutRect result;
2473 if (isHorizontalWritingMode())
2474 result = logicalRect;
2475 else
2476 result = LayoutRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width());
2477 flipForWritingMode(result);
2478 result.moveBy(rootBlockPhysicalPosition);
2479 return result;
2480 }
2481
selectionGaps(RenderBlock * rootBlock,const LayoutPoint & rootBlockPhysicalPosition,const LayoutSize & offsetFromRootBlock,LayoutUnit & lastLogicalTop,LayoutUnit & lastLogicalLeft,LayoutUnit & lastLogicalRight,const PaintInfo * paintInfo)2482 GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
2483 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo)
2484 {
2485 // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore.
2486 // Clip out floating and positioned objects when painting selection gaps.
2487 if (paintInfo) {
2488 // Note that we don't clip out overflow for positioned objects. We just stick to the border box.
2489 LayoutRect flippedBlockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height());
2490 rootBlock->flipForWritingMode(flippedBlockRect);
2491 flippedBlockRect.moveBy(rootBlockPhysicalPosition);
2492 clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), positionedObjects());
2493 if (isBody() || isRoot()) // The <body> must make sure to examine its containingBlock's positioned objects.
2494 for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock())
2495 clipOutPositionedObjects(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects()); // FIXME: Not right for flipped writing modes.
2496 clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock);
2497 }
2498
2499 // 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
2500 // fixed).
2501 GapRects result;
2502 if (!isRenderBlockFlow() && !isFlexibleBoxIncludingDeprecated()) // FIXME: Make multi-column selection gap filling work someday.
2503 return result;
2504
2505 if (hasColumns() || hasTransform() || style()->columnSpan()) {
2506 // FIXME: We should learn how to gap fill multiple columns and transforms eventually.
2507 lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalHeight();
2508 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight());
2509 lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight());
2510 return result;
2511 }
2512
2513 if (childrenInline())
2514 result = toRenderBlockFlow(this)->inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
2515 else
2516 result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo);
2517
2518 // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block.
2519 if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd))
2520 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
2521 logicalHeight(), paintInfo));
2522 return result;
2523 }
2524
blockSelectionGaps(RenderBlock * rootBlock,const LayoutPoint & rootBlockPhysicalPosition,const LayoutSize & offsetFromRootBlock,LayoutUnit & lastLogicalTop,LayoutUnit & lastLogicalLeft,LayoutUnit & lastLogicalRight,const PaintInfo * paintInfo)2525 GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
2526 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo)
2527 {
2528 GapRects result;
2529
2530 // Go ahead and jump right to the first block child that contains some selected objects.
2531 RenderBox* curr;
2532 for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { }
2533
2534 for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) {
2535 SelectionState childState = curr->selectionState();
2536 if (childState == SelectionBoth || childState == SelectionEnd)
2537 sawSelectionEnd = true;
2538
2539 if (curr->isFloatingOrOutOfFlowPositioned())
2540 continue; // We must be a normal flow object in order to even be considered.
2541
2542 if (curr->isInFlowPositioned() && curr->hasLayer()) {
2543 // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element.
2544 // Just disregard it completely.
2545 LayoutSize relOffset = curr->layer()->offsetForInFlowPosition();
2546 if (relOffset.width() || relOffset.height())
2547 continue;
2548 }
2549
2550 bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this.
2551 bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone);
2552 if (fillBlockGaps) {
2553 // We need to fill the vertical gap above this object.
2554 if (childState == SelectionEnd || childState == SelectionInside)
2555 // Fill the gap above the object.
2556 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight,
2557 curr->logicalTop(), paintInfo));
2558
2559 // 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*
2560 // our object. We know this if the selection did not end inside our object.
2561 if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd))
2562 childState = SelectionNone;
2563
2564 // Fill side gaps on this object based off its state.
2565 bool leftGap, rightGap;
2566 getSelectionGapInfo(childState, leftGap, rightGap);
2567
2568 if (leftGap)
2569 result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
2570 if (rightGap)
2571 result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo));
2572
2573 // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as
2574 // they can without bumping into floating or positioned objects. Ideally they will go right up
2575 // to the border of the root selection block.
2576 lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + curr->logicalBottom();
2577 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom());
2578 lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom());
2579 } else if (childState != SelectionNone)
2580 // We must be a block that has some selected object inside it. Go ahead and recur.
2581 result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()),
2582 lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo));
2583 }
2584 return result;
2585 }
2586
blockSelectionGap(RenderBlock * rootBlock,const LayoutPoint & rootBlockPhysicalPosition,const LayoutSize & offsetFromRootBlock,LayoutUnit lastLogicalTop,LayoutUnit lastLogicalLeft,LayoutUnit lastLogicalRight,LayoutUnit logicalBottom,const PaintInfo * paintInfo)2587 LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
2588 LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo* paintInfo)
2589 {
2590 LayoutUnit logicalTop = lastLogicalTop;
2591 LayoutUnit logicalHeight = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalBottom - logicalTop;
2592 if (logicalHeight <= 0)
2593 return LayoutRect();
2594
2595 // Get the selection offsets for the bottom of the gap
2596 LayoutUnit logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom));
2597 LayoutUnit logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom));
2598 LayoutUnit logicalWidth = logicalRight - logicalLeft;
2599 if (logicalWidth <= 0)
2600 return LayoutRect();
2601
2602 LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight));
2603 if (paintInfo)
2604 paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selectionBackgroundColor());
2605 return gapRect;
2606 }
2607
logicalLeftSelectionGap(RenderBlock * rootBlock,const LayoutPoint & rootBlockPhysicalPosition,const LayoutSize & offsetFromRootBlock,RenderObject * selObj,LayoutUnit logicalLeft,LayoutUnit logicalTop,LayoutUnit logicalHeight,const PaintInfo * paintInfo)2608 LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
2609 RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo)
2610 {
2611 LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop;
2612 LayoutUnit rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight));
2613 LayoutUnit rootBlockLogicalRight = min(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + floorToInt(logicalLeft), min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)));
2614 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
2615 if (rootBlockLogicalWidth <= 0)
2616 return LayoutRect();
2617
2618 LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
2619 if (paintInfo)
2620 paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selObj->selectionBackgroundColor());
2621 return gapRect;
2622 }
2623
logicalRightSelectionGap(RenderBlock * rootBlock,const LayoutPoint & rootBlockPhysicalPosition,const LayoutSize & offsetFromRootBlock,RenderObject * selObj,LayoutUnit logicalRight,LayoutUnit logicalTop,LayoutUnit logicalHeight,const PaintInfo * paintInfo)2624 LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
2625 RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo)
2626 {
2627 LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop;
2628 LayoutUnit rootBlockLogicalLeft = max(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + floorToInt(logicalRight), max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)));
2629 LayoutUnit rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight));
2630 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft;
2631 if (rootBlockLogicalWidth <= 0)
2632 return LayoutRect();
2633
2634 LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight));
2635 if (paintInfo)
2636 paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selObj->selectionBackgroundColor());
2637 return gapRect;
2638 }
2639
getSelectionGapInfo(SelectionState state,bool & leftGap,bool & rightGap)2640 void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap)
2641 {
2642 bool ltr = style()->isLeftToRightDirection();
2643 leftGap = (state == RenderObject::SelectionInside) ||
2644 (state == RenderObject::SelectionEnd && ltr) ||
2645 (state == RenderObject::SelectionStart && !ltr);
2646 rightGap = (state == RenderObject::SelectionInside) ||
2647 (state == RenderObject::SelectionStart && ltr) ||
2648 (state == RenderObject::SelectionEnd && !ltr);
2649 }
2650
logicalLeftSelectionOffset(RenderBlock * rootBlock,LayoutUnit position)2651 LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position)
2652 {
2653 LayoutUnit logicalLeft = logicalLeftOffsetForLine(position, false);
2654 if (logicalLeft == logicalLeftOffsetForContent()) {
2655 if (rootBlock != this)
2656 // The border can potentially be further extended by our containingBlock().
2657 return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop());
2658 return logicalLeft;
2659 } else {
2660 RenderBlock* cb = this;
2661 while (cb != rootBlock) {
2662 logicalLeft += cb->logicalLeft();
2663 cb = cb->containingBlock();
2664 }
2665 }
2666 return logicalLeft;
2667 }
2668
logicalRightSelectionOffset(RenderBlock * rootBlock,LayoutUnit position)2669 LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position)
2670 {
2671 LayoutUnit logicalRight = logicalRightOffsetForLine(position, false);
2672 if (logicalRight == logicalRightOffsetForContent()) {
2673 if (rootBlock != this)
2674 // The border can potentially be further extended by our containingBlock().
2675 return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop());
2676 return logicalRight;
2677 } else {
2678 RenderBlock* cb = this;
2679 while (cb != rootBlock) {
2680 logicalRight += cb->logicalLeft();
2681 cb = cb->containingBlock();
2682 }
2683 }
2684 return logicalRight;
2685 }
2686
blockBeforeWithinSelectionRoot(LayoutSize & offset) const2687 RenderBlock* RenderBlock::blockBeforeWithinSelectionRoot(LayoutSize& offset) const
2688 {
2689 if (isSelectionRoot())
2690 return 0;
2691
2692 const RenderObject* object = this;
2693 RenderObject* sibling;
2694 do {
2695 sibling = object->previousSibling();
2696 while (sibling && (!sibling->isRenderBlock() || toRenderBlock(sibling)->isSelectionRoot()))
2697 sibling = sibling->previousSibling();
2698
2699 offset -= LayoutSize(toRenderBlock(object)->logicalLeft(), toRenderBlock(object)->logicalTop());
2700 object = object->parent();
2701 } while (!sibling && object && object->isRenderBlock() && !toRenderBlock(object)->isSelectionRoot());
2702
2703 if (!sibling)
2704 return 0;
2705
2706 RenderBlock* beforeBlock = toRenderBlock(sibling);
2707
2708 offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop());
2709
2710 RenderObject* child = beforeBlock->lastChild();
2711 while (child && child->isRenderBlock()) {
2712 beforeBlock = toRenderBlock(child);
2713 offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop());
2714 child = beforeBlock->lastChild();
2715 }
2716 return beforeBlock;
2717 }
2718
insertIntoTrackedRendererMaps(RenderBox * descendant,TrackedDescendantsMap * & descendantsMap,TrackedContainerMap * & containerMap)2719 void RenderBlock::insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap)
2720 {
2721 if (!descendantsMap) {
2722 descendantsMap = new TrackedDescendantsMap;
2723 containerMap = new TrackedContainerMap;
2724 }
2725
2726 TrackedRendererListHashSet* descendantSet = descendantsMap->get(this);
2727 if (!descendantSet) {
2728 descendantSet = new TrackedRendererListHashSet;
2729 descendantsMap->set(this, adoptPtr(descendantSet));
2730 }
2731 bool added = descendantSet->add(descendant).isNewEntry;
2732 if (!added) {
2733 ASSERT(containerMap->get(descendant));
2734 ASSERT(containerMap->get(descendant)->contains(this));
2735 return;
2736 }
2737
2738 HashSet<RenderBlock*>* containerSet = containerMap->get(descendant);
2739 if (!containerSet) {
2740 containerSet = new HashSet<RenderBlock*>;
2741 containerMap->set(descendant, adoptPtr(containerSet));
2742 }
2743 ASSERT(!containerSet->contains(this));
2744 containerSet->add(this);
2745 }
2746
removeFromTrackedRendererMaps(RenderBox * descendant,TrackedDescendantsMap * & descendantsMap,TrackedContainerMap * & containerMap)2747 void RenderBlock::removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap)
2748 {
2749 if (!descendantsMap)
2750 return;
2751
2752 OwnPtr<HashSet<RenderBlock*> > containerSet = containerMap->take(descendant);
2753 if (!containerSet)
2754 return;
2755
2756 HashSet<RenderBlock*>::iterator end = containerSet->end();
2757 for (HashSet<RenderBlock*>::iterator it = containerSet->begin(); it != end; ++it) {
2758 RenderBlock* container = *it;
2759
2760 // FIXME: Disabling this assert temporarily until we fix the layout
2761 // bugs associated with positioned objects not properly cleared from
2762 // their ancestor chain before being moved. See webkit bug 93766.
2763 // ASSERT(descendant->isDescendantOf(container));
2764
2765 TrackedDescendantsMap::iterator descendantsMapIterator = descendantsMap->find(container);
2766 ASSERT(descendantsMapIterator != descendantsMap->end());
2767 if (descendantsMapIterator == descendantsMap->end())
2768 continue;
2769 TrackedRendererListHashSet* descendantSet = descendantsMapIterator->value.get();
2770 ASSERT(descendantSet->contains(descendant));
2771 descendantSet->remove(descendant);
2772 if (descendantSet->isEmpty())
2773 descendantsMap->remove(descendantsMapIterator);
2774 }
2775 }
2776
positionedObjects() const2777 TrackedRendererListHashSet* RenderBlock::positionedObjects() const
2778 {
2779 if (gPositionedDescendantsMap)
2780 return gPositionedDescendantsMap->get(this);
2781 return 0;
2782 }
2783
insertPositionedObject(RenderBox * o)2784 void RenderBlock::insertPositionedObject(RenderBox* o)
2785 {
2786 ASSERT(!isAnonymousBlock());
2787
2788 if (o->isRenderFlowThread())
2789 return;
2790
2791 insertIntoTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap);
2792 }
2793
removePositionedObject(RenderBox * o)2794 void RenderBlock::removePositionedObject(RenderBox* o)
2795 {
2796 removeFromTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap);
2797 }
2798
removePositionedObjects(RenderBlock * o,ContainingBlockState containingBlockState)2799 void RenderBlock::removePositionedObjects(RenderBlock* o, ContainingBlockState containingBlockState)
2800 {
2801 TrackedRendererListHashSet* positionedDescendants = positionedObjects();
2802 if (!positionedDescendants)
2803 return;
2804
2805 RenderBox* r;
2806
2807 TrackedRendererListHashSet::iterator end = positionedDescendants->end();
2808
2809 Vector<RenderBox*, 16> deadObjects;
2810
2811 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) {
2812 r = *it;
2813 if (!o || r->isDescendantOf(o)) {
2814 if (containingBlockState == NewContainingBlock)
2815 r->setChildNeedsLayout(MarkOnlyThis);
2816
2817 // It is parent blocks job to add positioned child to positioned objects list of its containing block
2818 // Parent layout needs to be invalidated to ensure this happens.
2819 RenderObject* p = r->parent();
2820 while (p && !p->isRenderBlock())
2821 p = p->parent();
2822 if (p)
2823 p->setChildNeedsLayout();
2824
2825 deadObjects.append(r);
2826 }
2827 }
2828
2829 for (unsigned i = 0; i < deadObjects.size(); i++)
2830 removePositionedObject(deadObjects.at(i));
2831 }
2832
addPercentHeightDescendant(RenderBox * descendant)2833 void RenderBlock::addPercentHeightDescendant(RenderBox* descendant)
2834 {
2835 insertIntoTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap);
2836 }
2837
removePercentHeightDescendant(RenderBox * descendant)2838 void RenderBlock::removePercentHeightDescendant(RenderBox* descendant)
2839 {
2840 removeFromTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap);
2841 }
2842
percentHeightDescendants() const2843 TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const
2844 {
2845 return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0;
2846 }
2847
hasPercentHeightContainerMap()2848 bool RenderBlock::hasPercentHeightContainerMap()
2849 {
2850 return gPercentHeightContainerMap;
2851 }
2852
hasPercentHeightDescendant(RenderBox * descendant)2853 bool RenderBlock::hasPercentHeightDescendant(RenderBox* descendant)
2854 {
2855 // We don't null check gPercentHeightContainerMap since the caller
2856 // already ensures this and we need to call this function on every
2857 // descendant in clearPercentHeightDescendantsFrom().
2858 ASSERT(gPercentHeightContainerMap);
2859 return gPercentHeightContainerMap->contains(descendant);
2860 }
2861
dirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope & layoutScope)2862 void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope& layoutScope)
2863 {
2864 if (!gPercentHeightDescendantsMap)
2865 return;
2866
2867 TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this);
2868 if (!descendants)
2869 return;
2870
2871 TrackedRendererListHashSet::iterator end = descendants->end();
2872 for (TrackedRendererListHashSet::iterator it = descendants->begin(); it != end; ++it) {
2873 RenderBox* box = *it;
2874 while (box != this) {
2875 if (box->normalChildNeedsLayout())
2876 break;
2877 layoutScope.setChildNeedsLayout(box);
2878 box = box->containingBlock();
2879 ASSERT(box);
2880 if (!box)
2881 break;
2882 }
2883 }
2884 }
2885
removePercentHeightDescendantIfNeeded(RenderBox * descendant)2886 void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox* descendant)
2887 {
2888 // We query the map directly, rather than looking at style's
2889 // logicalHeight()/logicalMinHeight()/logicalMaxHeight() since those
2890 // can change with writing mode/directional changes.
2891 if (!hasPercentHeightContainerMap())
2892 return;
2893
2894 if (!hasPercentHeightDescendant(descendant))
2895 return;
2896
2897 removePercentHeightDescendant(descendant);
2898 }
2899
clearPercentHeightDescendantsFrom(RenderBox * parent)2900 void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox* parent)
2901 {
2902 ASSERT(gPercentHeightContainerMap);
2903 for (RenderObject* curr = parent->firstChild(); curr; curr = curr->nextInPreOrder(parent)) {
2904 if (!curr->isBox())
2905 continue;
2906
2907 RenderBox* box = toRenderBox(curr);
2908 if (!hasPercentHeightDescendant(box))
2909 continue;
2910
2911 removePercentHeightDescendant(box);
2912 }
2913 }
2914
textIndentOffset() const2915 LayoutUnit RenderBlock::textIndentOffset() const
2916 {
2917 LayoutUnit cw = 0;
2918 RenderView* renderView = 0;
2919 if (style()->textIndent().isPercent())
2920 cw = containingBlock()->availableLogicalWidth();
2921 else if (style()->textIndent().isViewportPercentage())
2922 renderView = view();
2923 return minimumValueForLength(style()->textIndent(), cw, renderView);
2924 }
2925
logicalLeftOffsetForContent(RenderRegion * region) const2926 LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region) const
2927 {
2928 LayoutUnit logicalLeftOffset = style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop();
2929 if (!region)
2930 return logicalLeftOffset;
2931 LayoutRect boxRect = borderBoxRectInRegion(region);
2932 return logicalLeftOffset + (isHorizontalWritingMode() ? boxRect.x() : boxRect.y());
2933 }
2934
logicalRightOffsetForContent(RenderRegion * region) const2935 LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderRegion* region) const
2936 {
2937 LayoutUnit logicalRightOffset = style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop();
2938 logicalRightOffset += availableLogicalWidth();
2939 if (!region)
2940 return logicalRightOffset;
2941 LayoutRect boxRect = borderBoxRectInRegion(region);
2942 return logicalRightOffset - (logicalWidth() - (isHorizontalWritingMode() ? boxRect.maxX() : boxRect.maxY()));
2943 }
2944
adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats,bool applyTextIndent) const2945 LayoutUnit RenderBlock::adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const
2946 {
2947 LayoutUnit left = offsetFromFloats;
2948
2949 if (applyTextIndent && style()->isLeftToRightDirection())
2950 left += textIndentOffset();
2951
2952 if (style()->lineAlign() == LineAlignNone)
2953 return left;
2954
2955 // Push in our left offset so that it is aligned with the character grid.
2956 LayoutState* layoutState = view()->layoutState();
2957 if (!layoutState)
2958 return left;
2959
2960 RenderBlock* lineGrid = layoutState->lineGrid();
2961 if (!lineGrid || lineGrid->style()->writingMode() != style()->writingMode())
2962 return left;
2963
2964 // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge?
2965 float maxCharWidth = lineGrid->style()->font().primaryFont()->maxCharWidth();
2966 if (!maxCharWidth)
2967 return left;
2968
2969 LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height();
2970 LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height();
2971
2972 // Push in to the nearest character width.
2973 // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945).
2974 // FIXME: This doesn't work with columns or regions (https://bugs.webkit.org/show_bug.cgi?id=79942).
2975 // FIXME: This doesn't work when the inline position of the object isn't set ahead of time.
2976 // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout.
2977 // (https://bugs.webkit.org/show_bug.cgi?id=79944)
2978 float remainder = fmodf(maxCharWidth - fmodf(left + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth);
2979 left += remainder;
2980 return left;
2981 }
2982
adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats,bool applyTextIndent) const2983 LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const
2984 {
2985 LayoutUnit right = offsetFromFloats;
2986
2987 if (applyTextIndent && !style()->isLeftToRightDirection())
2988 right -= textIndentOffset();
2989
2990 if (style()->lineAlign() == LineAlignNone)
2991 return right;
2992
2993 // Push in our right offset so that it is aligned with the character grid.
2994 LayoutState* layoutState = view()->layoutState();
2995 if (!layoutState)
2996 return right;
2997
2998 RenderBlock* lineGrid = layoutState->lineGrid();
2999 if (!lineGrid || lineGrid->style()->writingMode() != style()->writingMode())
3000 return right;
3001
3002 // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge?
3003 float maxCharWidth = lineGrid->style()->font().primaryFont()->maxCharWidth();
3004 if (!maxCharWidth)
3005 return right;
3006
3007 LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height();
3008 LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height();
3009
3010 // Push in to the nearest character width.
3011 // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945).
3012 // FIXME: This doesn't work with columns or regions (https://bugs.webkit.org/show_bug.cgi?id=79942).
3013 // FIXME: This doesn't work when the inline position of the object isn't set ahead of time.
3014 // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout.
3015 // (https://bugs.webkit.org/show_bug.cgi?id=79944)
3016 float remainder = fmodf(fmodf(right + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth);
3017 right -= LayoutUnit::fromFloatCeil(remainder);
3018 return right;
3019 }
3020
markLinesDirtyInBlockRange(LayoutUnit logicalTop,LayoutUnit logicalBottom,RootInlineBox * highest)3021 void RenderBlock::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest)
3022 {
3023 if (logicalTop >= logicalBottom)
3024 return;
3025
3026 RootInlineBox* lowestDirtyLine = lastRootBox();
3027 RootInlineBox* afterLowest = lowestDirtyLine;
3028 while (lowestDirtyLine && lowestDirtyLine->lineBottomWithLeading() >= logicalBottom && logicalBottom < LayoutUnit::max()) {
3029 afterLowest = lowestDirtyLine;
3030 lowestDirtyLine = lowestDirtyLine->prevRootBox();
3031 }
3032
3033 while (afterLowest && afterLowest != highest && (afterLowest->lineBottomWithLeading() >= logicalTop || afterLowest->lineBottomWithLeading() < 0)) {
3034 afterLowest->markDirty();
3035 afterLowest = afterLowest->prevRootBox();
3036 }
3037 }
3038
avoidsFloats() const3039 bool RenderBlock::avoidsFloats() const
3040 {
3041 // Floats can't intrude into our box if we have a non-auto column count or width.
3042 return RenderBox::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth();
3043 }
3044
markShapeInsideDescendantsForLayout()3045 void RenderBlock::markShapeInsideDescendantsForLayout()
3046 {
3047 if (!everHadLayout())
3048 return;
3049 if (childrenInline()) {
3050 setNeedsLayout();
3051 return;
3052 }
3053 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
3054 if (!child->isRenderBlock())
3055 continue;
3056 RenderBlock* childBlock = toRenderBlock(child);
3057 childBlock->markShapeInsideDescendantsForLayout();
3058 }
3059 }
3060
isPointInOverflowControl(HitTestResult & result,const LayoutPoint & locationInContainer,const LayoutPoint & accumulatedOffset)3061 bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset)
3062 {
3063 if (!scrollsOverflow())
3064 return false;
3065
3066 return layer()->scrollableArea()->hitTestOverflowControls(result, roundedIntPoint(locationInContainer - toLayoutSize(accumulatedOffset)));
3067 }
3068
nodeForHitTest() const3069 Node* RenderBlock::nodeForHitTest() const
3070 {
3071 // If we are in the margins of block elements that are part of a
3072 // continuation we're actually still inside the enclosing element
3073 // that was split. Use the appropriate inner node.
3074 return isAnonymousBlockContinuation() ? continuation()->node() : node();
3075 }
3076
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction hitTestAction)3077 bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
3078 {
3079 LayoutPoint adjustedLocation(accumulatedOffset + location());
3080 LayoutSize localOffset = toLayoutSize(adjustedLocation);
3081
3082 if (!isRenderView()) {
3083 // Check if we need to do anything at all.
3084 LayoutRect overflowBox = visualOverflowRect();
3085 flipForWritingMode(overflowBox);
3086 overflowBox.moveBy(adjustedLocation);
3087 if (!locationInContainer.intersects(overflowBox))
3088 return false;
3089 }
3090
3091 if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) {
3092 updateHitTestResult(result, locationInContainer.point() - localOffset);
3093 // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet.
3094 if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer))
3095 return true;
3096 }
3097
3098 // If we have clipping, then we can't have any spillout.
3099 bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer();
3100 bool useClip = (hasControlClip() || useOverflowClip);
3101 bool checkChildren = !useClip || (hasControlClip() ? locationInContainer.intersects(controlClipRect(adjustedLocation)) : locationInContainer.intersects(overflowClipRect(adjustedLocation, locationInContainer.region(), IncludeOverlayScrollbarSize)));
3102 if (checkChildren) {
3103 // Hit test descendants first.
3104 LayoutSize scrolledOffset(localOffset);
3105 if (hasOverflowClip())
3106 scrolledOffset -= scrolledContentOffset();
3107
3108 // Hit test contents if we don't have columns.
3109 if (!hasColumns()) {
3110 if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) {
3111 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
3112 return true;
3113 }
3114 if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset)))
3115 return true;
3116 } else if (hitTestColumns(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) {
3117 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
3118 return true;
3119 }
3120 }
3121
3122 // Check if the point is outside radii.
3123 if (!isRenderView() && style()->hasBorderRadius()) {
3124 LayoutRect borderRect = borderBoxRect();
3125 borderRect.moveBy(adjustedLocation);
3126 RoundedRect border = style()->getRoundedBorderFor(borderRect, view());
3127 if (!locationInContainer.intersects(border))
3128 return false;
3129 }
3130
3131 // Now hit test our background
3132 if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) {
3133 LayoutRect boundsRect(adjustedLocation, size());
3134 if (visibleToHitTestRequest(request) && locationInContainer.intersects(boundsRect)) {
3135 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
3136 if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect))
3137 return true;
3138 }
3139 }
3140
3141 return false;
3142 }
3143
3144 class ColumnRectIterator {
3145 WTF_MAKE_NONCOPYABLE(ColumnRectIterator);
3146 public:
ColumnRectIterator(const RenderBlock & block)3147 ColumnRectIterator(const RenderBlock& block)
3148 : m_block(block)
3149 , m_colInfo(block.columnInfo())
3150 , m_direction(m_block.style()->isFlippedBlocksWritingMode() ? 1 : -1)
3151 , m_isHorizontal(block.isHorizontalWritingMode())
3152 , m_logicalLeft(block.logicalLeftOffsetForContent())
3153 {
3154 int colCount = m_colInfo->columnCount();
3155 m_colIndex = colCount - 1;
3156 m_currLogicalTopOffset = colCount * m_colInfo->columnHeight() * m_direction;
3157 update();
3158 }
3159
advance()3160 void advance()
3161 {
3162 ASSERT(hasMore());
3163 m_colIndex--;
3164 update();
3165 }
3166
columnRect() const3167 LayoutRect columnRect() const { return m_colRect; }
hasMore() const3168 bool hasMore() const { return m_colIndex >= 0; }
3169
adjust(LayoutSize & offset) const3170 void adjust(LayoutSize& offset) const
3171 {
3172 LayoutUnit currLogicalLeftOffset = (m_isHorizontal ? m_colRect.x() : m_colRect.y()) - m_logicalLeft;
3173 offset += m_isHorizontal ? LayoutSize(currLogicalLeftOffset, m_currLogicalTopOffset) : LayoutSize(m_currLogicalTopOffset, currLogicalLeftOffset);
3174 if (m_colInfo->progressionAxis() == ColumnInfo::BlockAxis) {
3175 if (m_isHorizontal)
3176 offset.expand(0, m_colRect.y() - m_block.borderTop() - m_block.paddingTop());
3177 else
3178 offset.expand(m_colRect.x() - m_block.borderLeft() - m_block.paddingLeft(), 0);
3179 }
3180 }
3181
3182 private:
update()3183 void update()
3184 {
3185 if (m_colIndex < 0)
3186 return;
3187
3188 m_colRect = m_block.columnRectAt(const_cast<ColumnInfo*>(m_colInfo), m_colIndex);
3189 m_block.flipForWritingMode(m_colRect);
3190 m_currLogicalTopOffset -= (m_isHorizontal ? m_colRect.height() : m_colRect.width()) * m_direction;
3191 }
3192
3193 const RenderBlock& m_block;
3194 const ColumnInfo* const m_colInfo;
3195 const int m_direction;
3196 const bool m_isHorizontal;
3197 const LayoutUnit m_logicalLeft;
3198 int m_colIndex;
3199 LayoutUnit m_currLogicalTopOffset;
3200 LayoutRect m_colRect;
3201 };
3202
hitTestColumns(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction hitTestAction)3203 bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
3204 {
3205 // We need to do multiple passes, breaking up our hit testing into strips.
3206 if (!hasColumns())
3207 return false;
3208
3209 for (ColumnRectIterator it(*this); it.hasMore(); it.advance()) {
3210 LayoutRect hitRect = locationInContainer.boundingBox();
3211 LayoutRect colRect = it.columnRect();
3212 colRect.moveBy(accumulatedOffset);
3213 if (locationInContainer.intersects(colRect)) {
3214 // The point is inside this column.
3215 // Adjust accumulatedOffset to change where we hit test.
3216 LayoutSize offset;
3217 it.adjust(offset);
3218 LayoutPoint finalLocation = accumulatedOffset + offset;
3219 if (!result.isRectBasedTest() || colRect.contains(hitRect))
3220 return hitTestContents(request, result, locationInContainer, finalLocation, hitTestAction) || (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, finalLocation));
3221
3222 hitTestContents(request, result, locationInContainer, finalLocation, hitTestAction);
3223 }
3224 }
3225
3226 return false;
3227 }
3228
adjustForColumnRect(LayoutSize & offset,const LayoutPoint & locationInContainer) const3229 void RenderBlock::adjustForColumnRect(LayoutSize& offset, const LayoutPoint& locationInContainer) const
3230 {
3231 for (ColumnRectIterator it(*this); it.hasMore(); it.advance()) {
3232 LayoutRect colRect = it.columnRect();
3233 if (colRect.contains(locationInContainer)) {
3234 it.adjust(offset);
3235 return;
3236 }
3237 }
3238 }
3239
hitTestContents(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction hitTestAction)3240 bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
3241 {
3242 if (isRenderRegion())
3243 return toRenderRegion(this)->hitTestFlowThreadContents(request, result, locationInContainer, accumulatedOffset, hitTestAction);
3244
3245 if (childrenInline() && !isTable()) {
3246 // We have to hit-test our line boxes.
3247 if (m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction))
3248 return true;
3249 } else {
3250 // Hit test our children.
3251 HitTestAction childHitTest = hitTestAction;
3252 if (hitTestAction == HitTestChildBlockBackgrounds)
3253 childHitTest = HitTestChildBlockBackground;
3254 for (RenderBox* child = lastChildBox(); child; child = child->previousSiblingBox()) {
3255 LayoutPoint childPoint = flipForWritingModeForChild(child, accumulatedOffset);
3256 if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest))
3257 return true;
3258 }
3259 }
3260
3261 return false;
3262 }
3263
positionForBox(InlineBox * box,bool start) const3264 Position RenderBlock::positionForBox(InlineBox *box, bool start) const
3265 {
3266 if (!box)
3267 return Position();
3268
3269 if (!box->renderer()->nonPseudoNode())
3270 return createLegacyEditingPosition(nonPseudoNode(), start ? caretMinOffset() : caretMaxOffset());
3271
3272 if (!box->isInlineTextBox())
3273 return createLegacyEditingPosition(box->renderer()->nonPseudoNode(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset());
3274
3275 InlineTextBox* textBox = toInlineTextBox(box);
3276 return createLegacyEditingPosition(box->renderer()->nonPseudoNode(), start ? textBox->start() : textBox->start() + textBox->len());
3277 }
3278
isEditingBoundary(RenderObject * ancestor,RenderObject * child)3279 static inline bool isEditingBoundary(RenderObject* ancestor, RenderObject* child)
3280 {
3281 ASSERT(!ancestor || ancestor->nonPseudoNode());
3282 ASSERT(child && child->nonPseudoNode());
3283 return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView())
3284 || ancestor->nonPseudoNode()->rendererIsEditable() == child->nonPseudoNode()->rendererIsEditable();
3285 }
3286
3287 // FIXME: This function should go on RenderObject as an instance method. Then
3288 // all cases in which positionForPoint recurs could call this instead to
3289 // prevent crossing editable boundaries. This would require many tests.
positionForPointRespectingEditingBoundaries(RenderBlock * parent,RenderBox * child,const LayoutPoint & pointInParentCoordinates)3290 static PositionWithAffinity positionForPointRespectingEditingBoundaries(RenderBlock* parent, RenderBox* child, const LayoutPoint& pointInParentCoordinates)
3291 {
3292 LayoutPoint childLocation = child->location();
3293 if (child->isInFlowPositioned())
3294 childLocation += child->offsetForInFlowPosition();
3295
3296 // FIXME: This is wrong if the child's writing-mode is different from the parent's.
3297 LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation));
3298
3299 // If this is an anonymous renderer, we just recur normally
3300 Node* childNode = child->nonPseudoNode();
3301 if (!childNode)
3302 return child->positionForPoint(pointInChildCoordinates);
3303
3304 // Otherwise, first make sure that the editability of the parent and child agree.
3305 // If they don't agree, then we return a visible position just before or after the child
3306 RenderObject* ancestor = parent;
3307 while (ancestor && !ancestor->nonPseudoNode())
3308 ancestor = ancestor->parent();
3309
3310 // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal
3311 if (isEditingBoundary(ancestor, child))
3312 return child->positionForPoint(pointInChildCoordinates);
3313
3314 // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child
3315 LayoutUnit childMiddle = parent->logicalWidthForChild(child) / 2;
3316 LayoutUnit logicalLeft = parent->isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y();
3317 if (logicalLeft < childMiddle)
3318 return ancestor->createPositionWithAffinity(childNode->nodeIndex(), DOWNSTREAM);
3319 return ancestor->createPositionWithAffinity(childNode->nodeIndex() + 1, UPSTREAM);
3320 }
3321
positionForPointWithInlineChildren(const LayoutPoint & pointInLogicalContents)3322 PositionWithAffinity RenderBlock::positionForPointWithInlineChildren(const LayoutPoint& pointInLogicalContents)
3323 {
3324 ASSERT(childrenInline());
3325
3326 if (!firstRootBox())
3327 return createPositionWithAffinity(0, DOWNSTREAM);
3328
3329 bool linesAreFlipped = style()->isFlippedLinesWritingMode();
3330 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode();
3331
3332 // look for the closest line box in the root box which is at the passed-in y coordinate
3333 InlineBox* closestBox = 0;
3334 RootInlineBox* firstRootBoxWithChildren = 0;
3335 RootInlineBox* lastRootBoxWithChildren = 0;
3336 for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) {
3337 if (!root->firstLeafChild())
3338 continue;
3339 if (!firstRootBoxWithChildren)
3340 firstRootBoxWithChildren = root;
3341
3342 if (!linesAreFlipped && root->isFirstAfterPageBreak() && (pointInLogicalContents.y() < root->lineTopWithLeading()
3343 || (blocksAreFlipped && pointInLogicalContents.y() == root->lineTopWithLeading())))
3344 break;
3345
3346 lastRootBoxWithChildren = root;
3347
3348 // check if this root line box is located at this y coordinate
3349 if (pointInLogicalContents.y() < root->selectionBottom() || (blocksAreFlipped && pointInLogicalContents.y() == root->selectionBottom())) {
3350 if (linesAreFlipped) {
3351 RootInlineBox* nextRootBoxWithChildren = root->nextRootBox();
3352 while (nextRootBoxWithChildren && !nextRootBoxWithChildren->firstLeafChild())
3353 nextRootBoxWithChildren = nextRootBoxWithChildren->nextRootBox();
3354
3355 if (nextRootBoxWithChildren && nextRootBoxWithChildren->isFirstAfterPageBreak() && (pointInLogicalContents.y() > nextRootBoxWithChildren->lineTopWithLeading()
3356 || (!blocksAreFlipped && pointInLogicalContents.y() == nextRootBoxWithChildren->lineTopWithLeading())))
3357 continue;
3358 }
3359 closestBox = root->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x());
3360 if (closestBox)
3361 break;
3362 }
3363 }
3364
3365 bool moveCaretToBoundary = document().frame()->editor().behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom();
3366
3367 if (!moveCaretToBoundary && !closestBox && lastRootBoxWithChildren) {
3368 // y coordinate is below last root line box, pretend we hit it
3369 closestBox = lastRootBoxWithChildren->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x());
3370 }
3371
3372 if (closestBox) {
3373 if (moveCaretToBoundary) {
3374 LayoutUnit firstRootBoxWithChildrenTop = min<LayoutUnit>(firstRootBoxWithChildren->selectionTop(), firstRootBoxWithChildren->logicalTop());
3375 if (pointInLogicalContents.y() < firstRootBoxWithChildrenTop
3376 || (blocksAreFlipped && pointInLogicalContents.y() == firstRootBoxWithChildrenTop)) {
3377 InlineBox* box = firstRootBoxWithChildren->firstLeafChild();
3378 if (box->isLineBreak()) {
3379 if (InlineBox* newBox = box->nextLeafChildIgnoringLineBreak())
3380 box = newBox;
3381 }
3382 // y coordinate is above first root line box, so return the start of the first
3383 return PositionWithAffinity(positionForBox(box, true), DOWNSTREAM);
3384 }
3385 }
3386
3387 // pass the box a top position that is inside it
3388 LayoutPoint point(pointInLogicalContents.x(), closestBox->root()->blockDirectionPointInLine());
3389 if (!isHorizontalWritingMode())
3390 point = point.transposedPoint();
3391 if (closestBox->renderer()->isReplaced())
3392 return positionForPointRespectingEditingBoundaries(this, toRenderBox(closestBox->renderer()), point);
3393 return closestBox->renderer()->positionForPoint(point);
3394 }
3395
3396 if (lastRootBoxWithChildren) {
3397 // We hit this case for Mac behavior when the Y coordinate is below the last box.
3398 ASSERT(moveCaretToBoundary);
3399 InlineBox* logicallyLastBox;
3400 if (lastRootBoxWithChildren->getLogicalEndBoxWithNode(logicallyLastBox))
3401 return PositionWithAffinity(positionForBox(logicallyLastBox, false), DOWNSTREAM);
3402 }
3403
3404 // Can't reach this. We have a root line box, but it has no kids.
3405 // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text
3406 // seems to hit this code path.
3407 return createPositionWithAffinity(0, DOWNSTREAM);
3408 }
3409
isChildHitTestCandidate(RenderBox * box)3410 static inline bool isChildHitTestCandidate(RenderBox* box)
3411 {
3412 return box->height() && box->style()->visibility() == VISIBLE && !box->isFloatingOrOutOfFlowPositioned();
3413 }
3414
positionForPoint(const LayoutPoint & point)3415 PositionWithAffinity RenderBlock::positionForPoint(const LayoutPoint& point)
3416 {
3417 if (isTable())
3418 return RenderBox::positionForPoint(point);
3419
3420 if (isReplaced()) {
3421 // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode.
3422 LayoutUnit pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.y();
3423 LayoutUnit pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x();
3424
3425 if (pointLogicalLeft < 0)
3426 return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM);
3427 if (pointLogicalLeft >= logicalWidth())
3428 return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM);
3429 if (pointLogicalTop < 0)
3430 return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM);
3431 if (pointLogicalTop >= logicalHeight())
3432 return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM);
3433 }
3434
3435 LayoutPoint pointInContents = point;
3436 offsetForContents(pointInContents);
3437 LayoutPoint pointInLogicalContents(pointInContents);
3438 if (!isHorizontalWritingMode())
3439 pointInLogicalContents = pointInLogicalContents.transposedPoint();
3440
3441 if (childrenInline())
3442 return positionForPointWithInlineChildren(pointInLogicalContents);
3443
3444 RenderBox* lastCandidateBox = lastChildBox();
3445 while (lastCandidateBox && !isChildHitTestCandidate(lastCandidateBox))
3446 lastCandidateBox = lastCandidateBox->previousSiblingBox();
3447
3448 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode();
3449 if (lastCandidateBox) {
3450 if (pointInLogicalContents.y() > logicalTopForChild(lastCandidateBox)
3451 || (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopForChild(lastCandidateBox)))
3452 return positionForPointRespectingEditingBoundaries(this, lastCandidateBox, pointInContents);
3453
3454 for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) {
3455 if (!isChildHitTestCandidate(childBox))
3456 continue;
3457 LayoutUnit childLogicalBottom = logicalTopForChild(childBox) + logicalHeightForChild(childBox);
3458 // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3).
3459 if (isChildHitTestCandidate(childBox) && (pointInLogicalContents.y() < childLogicalBottom
3460 || (blocksAreFlipped && pointInLogicalContents.y() == childLogicalBottom)))
3461 return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents);
3462 }
3463 }
3464
3465 // We only get here if there are no hit test candidate children below the click.
3466 return RenderBox::positionForPoint(point);
3467 }
3468
offsetForContents(LayoutPoint & offset) const3469 void RenderBlock::offsetForContents(LayoutPoint& offset) const
3470 {
3471 offset = flipForWritingMode(offset);
3472
3473 if (hasOverflowClip())
3474 offset += scrolledContentOffset();
3475
3476 if (hasColumns())
3477 adjustPointToColumnContents(offset);
3478
3479 offset = flipForWritingMode(offset);
3480 }
3481
availableLogicalWidth() const3482 LayoutUnit RenderBlock::availableLogicalWidth() const
3483 {
3484 // If we have multiple columns, then the available logical width is reduced to our column width.
3485 if (hasColumns())
3486 return desiredColumnWidth();
3487 return RenderBox::availableLogicalWidth();
3488 }
3489
columnGap() const3490 int RenderBlock::columnGap() const
3491 {
3492 if (style()->hasNormalColumnGap())
3493 return style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins.
3494 return static_cast<int>(style()->columnGap());
3495 }
3496
calcColumnWidth()3497 void RenderBlock::calcColumnWidth()
3498 {
3499 if (document().regionBasedColumnsEnabled())
3500 return;
3501
3502 // Calculate our column width and column count.
3503 // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744
3504 unsigned desiredColumnCount = 1;
3505 LayoutUnit desiredColumnWidth = contentLogicalWidth();
3506
3507 // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination.
3508 if (document().paginated() || (style()->hasAutoColumnCount() && style()->hasAutoColumnWidth()) || !style()->hasInlineColumnAxis()) {
3509 setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth);
3510 return;
3511 }
3512
3513 LayoutUnit availWidth = desiredColumnWidth;
3514 LayoutUnit colGap = columnGap();
3515 LayoutUnit colWidth = max<LayoutUnit>(1, LayoutUnit(style()->columnWidth()));
3516 int colCount = max<int>(1, style()->columnCount());
3517
3518 if (style()->hasAutoColumnWidth() && !style()->hasAutoColumnCount()) {
3519 desiredColumnCount = colCount;
3520 desiredColumnWidth = max<LayoutUnit>(0, (availWidth - ((desiredColumnCount - 1) * colGap)) / desiredColumnCount);
3521 } else if (!style()->hasAutoColumnWidth() && style()->hasAutoColumnCount()) {
3522 desiredColumnCount = max<LayoutUnit>(1, (availWidth + colGap) / (colWidth + colGap));
3523 desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap;
3524 } else {
3525 desiredColumnCount = max<LayoutUnit>(min<LayoutUnit>(colCount, (availWidth + colGap) / (colWidth + colGap)), 1);
3526 desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap;
3527 }
3528 setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth);
3529 }
3530
requiresColumns(int desiredColumnCount) const3531 bool RenderBlock::requiresColumns(int desiredColumnCount) const
3532 {
3533 // If overflow-y is set to paged-x or paged-y on the body or html element, we'll handle the paginating
3534 // in the RenderView instead.
3535 bool isPaginated = (style()->overflowY() == OPAGEDX || style()->overflowY() == OPAGEDY) && !(isRoot() || isBody());
3536
3537 return firstChild()
3538 && (desiredColumnCount != 1 || !style()->hasAutoColumnWidth() || !style()->hasInlineColumnAxis() || isPaginated)
3539 && !firstChild()->isAnonymousColumnsBlock()
3540 && !firstChild()->isAnonymousColumnSpanBlock();
3541 }
3542
setDesiredColumnCountAndWidth(int count,LayoutUnit width)3543 void RenderBlock::setDesiredColumnCountAndWidth(int count, LayoutUnit width)
3544 {
3545 bool destroyColumns = !requiresColumns(count);
3546 if (destroyColumns) {
3547 if (hasColumns()) {
3548 gColumnInfoMap->take(this);
3549 setHasColumns(false);
3550 }
3551 } else {
3552 ColumnInfo* info;
3553 if (hasColumns())
3554 info = gColumnInfoMap->get(this);
3555 else {
3556 if (!gColumnInfoMap)
3557 gColumnInfoMap = new ColumnInfoMap;
3558 info = new ColumnInfo;
3559 gColumnInfoMap->add(this, adoptPtr(info));
3560 setHasColumns(true);
3561 }
3562 info->setDesiredColumnCount(count);
3563 info->setDesiredColumnWidth(width);
3564 info->setProgressionAxis(style()->hasInlineColumnAxis() ? ColumnInfo::InlineAxis : ColumnInfo::BlockAxis);
3565 info->setProgressionIsReversed(style()->columnProgression() == ReverseColumnProgression);
3566 }
3567 }
3568
updateColumnInfoFromStyle(RenderStyle * style)3569 void RenderBlock::updateColumnInfoFromStyle(RenderStyle* style)
3570 {
3571 if (!hasColumns())
3572 return;
3573
3574 ColumnInfo* info = gColumnInfoMap->get(this);
3575
3576 bool needsLayout = false;
3577 ColumnInfo::Axis oldAxis = info->progressionAxis();
3578 ColumnInfo::Axis newAxis = style->hasInlineColumnAxis() ? ColumnInfo::InlineAxis : ColumnInfo::BlockAxis;
3579 if (oldAxis != newAxis) {
3580 info->setProgressionAxis(newAxis);
3581 needsLayout = true;
3582 }
3583
3584 bool oldProgressionIsReversed = info->progressionIsReversed();
3585 bool newProgressionIsReversed = style->columnProgression() == ReverseColumnProgression;
3586 if (oldProgressionIsReversed != newProgressionIsReversed) {
3587 info->setProgressionIsReversed(newProgressionIsReversed);
3588 needsLayout = true;
3589 }
3590
3591 if (needsLayout)
3592 setNeedsLayoutAndPrefWidthsRecalc();
3593 }
3594
desiredColumnWidth() const3595 LayoutUnit RenderBlock::desiredColumnWidth() const
3596 {
3597 if (!hasColumns())
3598 return contentLogicalWidth();
3599 return gColumnInfoMap->get(this)->desiredColumnWidth();
3600 }
3601
columnInfo() const3602 ColumnInfo* RenderBlock::columnInfo() const
3603 {
3604 if (!hasColumns())
3605 return 0;
3606 return gColumnInfoMap->get(this);
3607 }
3608
columnCount(ColumnInfo * colInfo) const3609 unsigned RenderBlock::columnCount(ColumnInfo* colInfo) const
3610 {
3611 ASSERT(hasColumns());
3612 ASSERT(gColumnInfoMap->get(this) == colInfo);
3613 return colInfo->columnCount();
3614 }
3615
columnRectAt(ColumnInfo * colInfo,unsigned index) const3616 LayoutRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const
3617 {
3618 ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo);
3619
3620 // Compute the appropriate rect based off our information.
3621 LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth();
3622 LayoutUnit colLogicalHeight = colInfo->columnHeight();
3623 LayoutUnit colLogicalTop = borderBefore() + paddingBefore();
3624 LayoutUnit colLogicalLeft = logicalLeftOffsetForContent();
3625 LayoutUnit colGap = columnGap();
3626 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
3627 if (style()->isLeftToRightDirection() ^ colInfo->progressionIsReversed())
3628 colLogicalLeft += index * (colLogicalWidth + colGap);
3629 else
3630 colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap);
3631 } else {
3632 if (!colInfo->progressionIsReversed())
3633 colLogicalTop += index * (colLogicalHeight + colGap);
3634 else
3635 colLogicalTop += contentLogicalHeight() - colLogicalHeight - index * (colLogicalHeight + colGap);
3636 }
3637
3638 if (isHorizontalWritingMode())
3639 return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight);
3640 return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth);
3641 }
3642
relayoutToAvoidWidows(LayoutStateMaintainer & statePusher)3643 bool RenderBlock::relayoutToAvoidWidows(LayoutStateMaintainer& statePusher)
3644 {
3645 if (!shouldBreakAtLineToAvoidWidow())
3646 return false;
3647
3648 statePusher.pop();
3649 setEverHadLayout(true);
3650 layoutBlock(false);
3651 return true;
3652 }
3653
adjustPointToColumnContents(LayoutPoint & point) const3654 void RenderBlock::adjustPointToColumnContents(LayoutPoint& point) const
3655 {
3656 // Just bail if we have no columns.
3657 if (!hasColumns())
3658 return;
3659
3660 ColumnInfo* colInfo = columnInfo();
3661 if (!columnCount(colInfo))
3662 return;
3663
3664 // Determine which columns we intersect.
3665 LayoutUnit colGap = columnGap();
3666 LayoutUnit halfColGap = colGap / 2;
3667 LayoutPoint columnPoint(columnRectAt(colInfo, 0).location());
3668 LayoutUnit logicalOffset = 0;
3669 for (unsigned i = 0; i < colInfo->columnCount(); i++) {
3670 // Add in half the column gap to the left and right of the rect.
3671 LayoutRect colRect = columnRectAt(colInfo, i);
3672 flipForWritingMode(colRect);
3673 if (isHorizontalWritingMode() == (colInfo->progressionAxis() == ColumnInfo::InlineAxis)) {
3674 LayoutRect gapAndColumnRect(colRect.x() - halfColGap, colRect.y(), colRect.width() + colGap, colRect.height());
3675 if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.maxX()) {
3676 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
3677 // FIXME: The clamping that follows is not completely right for right-to-left
3678 // content.
3679 // Clamp everything above the column to its top left.
3680 if (point.y() < gapAndColumnRect.y())
3681 point = gapAndColumnRect.location();
3682 // Clamp everything below the column to the next column's top left. If there is
3683 // no next column, this still maps to just after this column.
3684 else if (point.y() >= gapAndColumnRect.maxY()) {
3685 point = gapAndColumnRect.location();
3686 point.move(0, gapAndColumnRect.height());
3687 }
3688 } else {
3689 if (point.x() < colRect.x())
3690 point.setX(colRect.x());
3691 else if (point.x() >= colRect.maxX())
3692 point.setX(colRect.maxX() - 1);
3693 }
3694
3695 // We're inside the column. Translate the x and y into our column coordinate space.
3696 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis)
3697 point.move(columnPoint.x() - colRect.x(), (!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset));
3698 else
3699 point.move((!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset) - colRect.x() + borderLeft() + paddingLeft(), 0);
3700 return;
3701 }
3702
3703 // Move to the next position.
3704 logicalOffset += colInfo->progressionAxis() == ColumnInfo::InlineAxis ? colRect.height() : colRect.width();
3705 } else {
3706 LayoutRect gapAndColumnRect(colRect.x(), colRect.y() - halfColGap, colRect.width(), colRect.height() + colGap);
3707 if (point.y() >= gapAndColumnRect.y() && point.y() < gapAndColumnRect.maxY()) {
3708 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
3709 // FIXME: The clamping that follows is not completely right for right-to-left
3710 // content.
3711 // Clamp everything above the column to its top left.
3712 if (point.x() < gapAndColumnRect.x())
3713 point = gapAndColumnRect.location();
3714 // Clamp everything below the column to the next column's top left. If there is
3715 // no next column, this still maps to just after this column.
3716 else if (point.x() >= gapAndColumnRect.maxX()) {
3717 point = gapAndColumnRect.location();
3718 point.move(gapAndColumnRect.width(), 0);
3719 }
3720 } else {
3721 if (point.y() < colRect.y())
3722 point.setY(colRect.y());
3723 else if (point.y() >= colRect.maxY())
3724 point.setY(colRect.maxY() - 1);
3725 }
3726
3727 // We're inside the column. Translate the x and y into our column coordinate space.
3728 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis)
3729 point.move((!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset), columnPoint.y() - colRect.y());
3730 else
3731 point.move(0, (!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset) - colRect.y() + borderTop() + paddingTop());
3732 return;
3733 }
3734
3735 // Move to the next position.
3736 logicalOffset += colInfo->progressionAxis() == ColumnInfo::InlineAxis ? colRect.width() : colRect.height();
3737 }
3738 }
3739 }
3740
adjustRectForColumns(LayoutRect & r) const3741 void RenderBlock::adjustRectForColumns(LayoutRect& r) const
3742 {
3743 // Just bail if we have no columns.
3744 if (!hasColumns())
3745 return;
3746
3747 ColumnInfo* colInfo = columnInfo();
3748
3749 // Determine which columns we intersect.
3750 unsigned colCount = columnCount(colInfo);
3751 if (!colCount)
3752 return;
3753
3754 // Begin with a result rect that is empty.
3755 LayoutRect result;
3756
3757 bool isHorizontal = isHorizontalWritingMode();
3758 LayoutUnit beforeBorderPadding = borderBefore() + paddingBefore();
3759 LayoutUnit colHeight = colInfo->columnHeight();
3760 if (!colHeight)
3761 return;
3762
3763 LayoutUnit startOffset = max(isHorizontal ? r.y() : r.x(), beforeBorderPadding);
3764 LayoutUnit endOffset = max(min<LayoutUnit>(isHorizontal ? r.maxY() : r.maxX(), beforeBorderPadding + colCount * colHeight), beforeBorderPadding);
3765
3766 // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744
3767 unsigned startColumn = (startOffset - beforeBorderPadding) / colHeight;
3768 unsigned endColumn = (endOffset - beforeBorderPadding) / colHeight;
3769
3770 if (startColumn == endColumn) {
3771 // The rect is fully contained within one column. Adjust for our offsets
3772 // and repaint only that portion.
3773 LayoutUnit logicalLeftOffset = logicalLeftOffsetForContent();
3774 LayoutRect colRect = columnRectAt(colInfo, startColumn);
3775 LayoutRect repaintRect = r;
3776
3777 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) {
3778 if (isHorizontal)
3779 repaintRect.move(colRect.x() - logicalLeftOffset, - static_cast<int>(startColumn) * colHeight);
3780 else
3781 repaintRect.move(- static_cast<int>(startColumn) * colHeight, colRect.y() - logicalLeftOffset);
3782 } else {
3783 if (isHorizontal)
3784 repaintRect.move(0, colRect.y() - startColumn * colHeight - beforeBorderPadding);
3785 else
3786 repaintRect.move(colRect.x() - startColumn * colHeight - beforeBorderPadding, 0);
3787 }
3788 repaintRect.intersect(colRect);
3789 result.unite(repaintRect);
3790 } else {
3791 // We span multiple columns. We can just unite the start and end column to get the final
3792 // repaint rect.
3793 result.unite(columnRectAt(colInfo, startColumn));
3794 result.unite(columnRectAt(colInfo, endColumn));
3795 }
3796
3797 r = result;
3798 }
3799
flipForWritingModeIncludingColumns(const LayoutPoint & point) const3800 LayoutPoint RenderBlock::flipForWritingModeIncludingColumns(const LayoutPoint& point) const
3801 {
3802 ASSERT(hasColumns());
3803 if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
3804 return point;
3805 ColumnInfo* colInfo = columnInfo();
3806 LayoutUnit columnLogicalHeight = colInfo->columnHeight();
3807 LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight();
3808 if (isHorizontalWritingMode())
3809 return LayoutPoint(point.x(), expandedLogicalHeight - point.y());
3810 return LayoutPoint(expandedLogicalHeight - point.x(), point.y());
3811 }
3812
adjustStartEdgeForWritingModeIncludingColumns(LayoutRect & rect) const3813 void RenderBlock::adjustStartEdgeForWritingModeIncludingColumns(LayoutRect& rect) const
3814 {
3815 ASSERT(hasColumns());
3816 if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
3817 return;
3818
3819 ColumnInfo* colInfo = columnInfo();
3820 LayoutUnit columnLogicalHeight = colInfo->columnHeight();
3821 LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight();
3822
3823 if (isHorizontalWritingMode())
3824 rect.setY(expandedLogicalHeight - rect.maxY());
3825 else
3826 rect.setX(expandedLogicalHeight - rect.maxX());
3827 }
3828
adjustForColumns(LayoutSize & offset,const LayoutPoint & point) const3829 void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) const
3830 {
3831 if (!hasColumns())
3832 return;
3833
3834 ColumnInfo* colInfo = columnInfo();
3835
3836 LayoutUnit logicalLeft = logicalLeftOffsetForContent();
3837 unsigned colCount = columnCount(colInfo);
3838 LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth();
3839 LayoutUnit colLogicalHeight = colInfo->columnHeight();
3840
3841 for (unsigned i = 0; i < colCount; ++i) {
3842 // Compute the edges for a given column in the block progression direction.
3843 LayoutRect sliceRect = LayoutRect(logicalLeft, borderBefore() + paddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight);
3844 if (!isHorizontalWritingMode())
3845 sliceRect = sliceRect.transposedRect();
3846
3847 LayoutUnit logicalOffset = i * colLogicalHeight;
3848
3849 // Now we're in the same coordinate space as the point. See if it is inside the rectangle.
3850 if (isHorizontalWritingMode()) {
3851 if (point.y() >= sliceRect.y() && point.y() < sliceRect.maxY()) {
3852 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis)
3853 offset.expand(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset);
3854 else
3855 offset.expand(0, columnRectAt(colInfo, i).y() - logicalOffset - borderBefore() - paddingBefore());
3856 return;
3857 }
3858 } else {
3859 if (point.x() >= sliceRect.x() && point.x() < sliceRect.maxX()) {
3860 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis)
3861 offset.expand(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft);
3862 else
3863 offset.expand(columnRectAt(colInfo, i).x() - logicalOffset - borderBefore() - paddingBefore(), 0);
3864 return;
3865 }
3866 }
3867 }
3868 }
3869
computeIntrinsicLogicalWidths(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth) const3870 void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
3871 {
3872 if (childrenInline()) {
3873 // FIXME: Remove this const_cast.
3874 const_cast<RenderBlock*>(this)->computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth);
3875 } else
3876 computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth);
3877
3878 maxLogicalWidth = max(minLogicalWidth, maxLogicalWidth);
3879
3880 adjustIntrinsicLogicalWidthsForColumns(minLogicalWidth, maxLogicalWidth);
3881
3882 // A horizontal marquee with inline children has no minimum width.
3883 if (childrenInline() && isMarquee() && toRenderMarquee(this)->isHorizontal())
3884 minLogicalWidth = 0;
3885
3886 if (isTableCell()) {
3887 Length tableCellWidth = toRenderTableCell(this)->styleOrColLogicalWidth();
3888 if (tableCellWidth.isFixed() && tableCellWidth.value() > 0)
3889 maxLogicalWidth = max(minLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(tableCellWidth.value()));
3890 }
3891
3892 int scrollbarWidth = instrinsicScrollbarLogicalWidth();
3893 maxLogicalWidth += scrollbarWidth;
3894 minLogicalWidth += scrollbarWidth;
3895 }
3896
computePreferredLogicalWidths()3897 void RenderBlock::computePreferredLogicalWidths()
3898 {
3899 ASSERT(preferredLogicalWidthsDirty());
3900
3901 updateFirstLetter();
3902
3903 m_minPreferredLogicalWidth = 0;
3904 m_maxPreferredLogicalWidth = 0;
3905
3906 // FIXME: The isFixed() calls here should probably be checking for isSpecified since you
3907 // should be able to use percentage, calc or viewport relative values for width.
3908 RenderStyle* styleToUse = style();
3909 if (!isTableCell() && styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() >= 0
3910 && !(isDeprecatedFlexItem() && !styleToUse->logicalWidth().intValue()))
3911 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value());
3912 else
3913 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
3914
3915 if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) {
3916 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
3917 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
3918 }
3919
3920 if (styleToUse->logicalMaxWidth().isFixed()) {
3921 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
3922 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
3923 }
3924
3925 // Table layout uses integers, ceil the preferred widths to ensure that they can contain the contents.
3926 if (isTableCell()) {
3927 m_minPreferredLogicalWidth = m_minPreferredLogicalWidth.ceil();
3928 m_maxPreferredLogicalWidth = m_maxPreferredLogicalWidth.ceil();
3929 }
3930
3931 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
3932 m_minPreferredLogicalWidth += borderAndPadding;
3933 m_maxPreferredLogicalWidth += borderAndPadding;
3934
3935 clearPreferredLogicalWidthsDirty();
3936 }
3937
adjustIntrinsicLogicalWidthsForColumns(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth) const3938 void RenderBlock::adjustIntrinsicLogicalWidthsForColumns(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
3939 {
3940 // FIXME: make this method virtual and move the code to RenderMultiColumnBlock once the old
3941 // multicol code is gone.
3942
3943 if (!style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth()) {
3944 // The min/max intrinsic widths calculated really tell how much space elements need when
3945 // laid out inside the columns. In order to eventually end up with the desired column width,
3946 // we need to convert them to values pertaining to the multicol container.
3947 int columnCount = style()->hasAutoColumnCount() ? 1 : style()->columnCount();
3948 LayoutUnit columnWidth;
3949 LayoutUnit gapExtra = (columnCount - 1) * columnGap();
3950 if (style()->hasAutoColumnWidth()) {
3951 minLogicalWidth = minLogicalWidth * columnCount + gapExtra;
3952 } else {
3953 columnWidth = style()->columnWidth();
3954 minLogicalWidth = min(minLogicalWidth, columnWidth);
3955 }
3956 // FIXME: If column-count is auto here, we should resolve it to calculate the maximum
3957 // intrinsic width, instead of pretending that it's 1. The only way to do that is by
3958 // performing a layout pass, but this is not an appropriate time or place for layout. The
3959 // good news is that if height is unconstrained and there are no explicit breaks, the
3960 // resolved column-count really should be 1.
3961 maxLogicalWidth = max(maxLogicalWidth, columnWidth) * columnCount + gapExtra;
3962 }
3963 }
3964
3965 struct InlineMinMaxIterator {
3966 /* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to
3967 inline min/max width calculations. Note the following about the way it walks:
3968 (1) Positioned content is skipped (since it does not contribute to min/max width of a block)
3969 (2) We do not drill into the children of floats or replaced elements, since you can't break
3970 in the middle of such an element.
3971 (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have
3972 distinct borders/margin/padding that contribute to the min/max width.
3973 */
3974 RenderObject* parent;
3975 RenderObject* current;
3976 bool endOfInline;
3977
InlineMinMaxIteratorWebCore::InlineMinMaxIterator3978 InlineMinMaxIterator(RenderObject* p, bool end = false)
3979 :parent(p), current(p), endOfInline(end) {}
3980
3981 RenderObject* next();
3982 };
3983
next()3984 RenderObject* InlineMinMaxIterator::next()
3985 {
3986 RenderObject* result = 0;
3987 bool oldEndOfInline = endOfInline;
3988 endOfInline = false;
3989 while (current || current == parent) {
3990 if (!oldEndOfInline &&
3991 (current == parent ||
3992 (!current->isFloating() && !current->isReplaced() && !current->isOutOfFlowPositioned())))
3993 result = current->firstChild();
3994 if (!result) {
3995 // We hit the end of our inline. (It was empty, e.g., <span></span>.)
3996 if (!oldEndOfInline && current->isRenderInline()) {
3997 result = current;
3998 endOfInline = true;
3999 break;
4000 }
4001
4002 while (current && current != parent) {
4003 result = current->nextSibling();
4004 if (result) break;
4005 current = current->parent();
4006 if (current && current != parent && current->isRenderInline()) {
4007 result = current;
4008 endOfInline = true;
4009 break;
4010 }
4011 }
4012 }
4013
4014 if (!result)
4015 break;
4016
4017 if (!result->isOutOfFlowPositioned() && (result->isText() || result->isFloating() || result->isReplaced() || result->isRenderInline()))
4018 break;
4019
4020 current = result;
4021 result = 0;
4022 }
4023
4024 // Update our position.
4025 current = result;
4026 return current;
4027 }
4028
getBPMWidth(LayoutUnit childValue,Length cssUnit)4029 static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit)
4030 {
4031 if (cssUnit.type() != Auto)
4032 return (cssUnit.isFixed() ? static_cast<LayoutUnit>(cssUnit.value()) : childValue);
4033 return 0;
4034 }
4035
getBorderPaddingMargin(const RenderBoxModelObject * child,bool endOfInline)4036 static LayoutUnit getBorderPaddingMargin(const RenderBoxModelObject* child, bool endOfInline)
4037 {
4038 RenderStyle* childStyle = child->style();
4039 if (endOfInline)
4040 return getBPMWidth(child->marginEnd(), childStyle->marginEnd()) +
4041 getBPMWidth(child->paddingEnd(), childStyle->paddingEnd()) +
4042 child->borderEnd();
4043 return getBPMWidth(child->marginStart(), childStyle->marginStart()) +
4044 getBPMWidth(child->paddingStart(), childStyle->paddingStart()) +
4045 child->borderStart();
4046 }
4047
stripTrailingSpace(float & inlineMax,float & inlineMin,RenderObject * trailingSpaceChild)4048 static inline void stripTrailingSpace(float& inlineMax, float& inlineMin,
4049 RenderObject* trailingSpaceChild)
4050 {
4051 if (trailingSpaceChild && trailingSpaceChild->isText()) {
4052 // Collapse away the trailing space at the end of a block.
4053 RenderText* t = toRenderText(trailingSpaceChild);
4054 const UChar space = ' ';
4055 const Font& font = t->style()->font(); // FIXME: This ignores first-line.
4056 float spaceWidth = font.width(RenderBlockFlow::constructTextRun(t, font, &space, 1, t->style()));
4057 inlineMax -= spaceWidth + font.wordSpacing();
4058 if (inlineMin > inlineMax)
4059 inlineMin = inlineMax;
4060 }
4061 }
4062
updatePreferredWidth(LayoutUnit & preferredWidth,float & result)4063 static inline void updatePreferredWidth(LayoutUnit& preferredWidth, float& result)
4064 {
4065 LayoutUnit snappedResult = LayoutUnit::fromFloatCeil(result);
4066 preferredWidth = max(snappedResult, preferredWidth);
4067 }
4068
4069 // When converting between floating point and LayoutUnits we risk losing precision
4070 // with each conversion. When this occurs while accumulating our preferred widths,
4071 // we can wind up with a line width that's larger than our maxPreferredWidth due to
4072 // pure float accumulation.
adjustFloatForSubPixelLayout(float value)4073 static inline LayoutUnit adjustFloatForSubPixelLayout(float value)
4074 {
4075 return LayoutUnit::fromFloatCeil(value);
4076 }
4077
4078
computeInlinePreferredLogicalWidths(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth)4079 void RenderBlock::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth)
4080 {
4081 float inlineMax = 0;
4082 float inlineMin = 0;
4083
4084 RenderStyle* styleToUse = style();
4085 RenderBlock* containingBlock = this->containingBlock();
4086 LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit();
4087
4088 // If we are at the start of a line, we want to ignore all white-space.
4089 // Also strip spaces if we previously had text that ended in a trailing space.
4090 bool stripFrontSpaces = true;
4091 RenderObject* trailingSpaceChild = 0;
4092
4093 // Firefox and Opera will allow a table cell to grow to fit an image inside it under
4094 // very specific cirucumstances (in order to match common WinIE renderings).
4095 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
4096 bool allowImagesToBreak = !document().inQuirksMode() || !isTableCell() || !styleToUse->logicalWidth().isIntrinsicOrAuto();
4097
4098 bool autoWrap, oldAutoWrap;
4099 autoWrap = oldAutoWrap = styleToUse->autoWrap();
4100
4101 InlineMinMaxIterator childIterator(this);
4102
4103 // Only gets added to the max preffered width once.
4104 bool addedTextIndent = false;
4105 // Signals the text indent was more negative than the min preferred width
4106 bool hasRemainingNegativeTextIndent = false;
4107
4108 LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw, view());
4109 RenderObject* prevFloat = 0;
4110 bool isPrevChildInlineFlow = false;
4111 bool shouldBreakLineAfterText = false;
4112 while (RenderObject* child = childIterator.next()) {
4113 autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() :
4114 child->style()->autoWrap();
4115
4116 if (!child->isBR()) {
4117 // Step One: determine whether or not we need to go ahead and
4118 // terminate our current line. Each discrete chunk can become
4119 // the new min-width, if it is the widest chunk seen so far, and
4120 // it can also become the max-width.
4121
4122 // Children fall into three categories:
4123 // (1) An inline flow object. These objects always have a min/max of 0,
4124 // and are included in the iteration solely so that their margins can
4125 // be added in.
4126 //
4127 // (2) An inline non-text non-flow object, e.g., an inline replaced element.
4128 // These objects can always be on a line by themselves, so in this situation
4129 // we need to go ahead and break the current line, and then add in our own
4130 // margins and min/max width on its own line, and then terminate the line.
4131 //
4132 // (3) A text object. Text runs can have breakable characters at the start,
4133 // the middle or the end. They may also lose whitespace off the front if
4134 // we're already ignoring whitespace. In order to compute accurate min-width
4135 // information, we need three pieces of information.
4136 // (a) the min-width of the first non-breakable run. Should be 0 if the text string
4137 // starts with whitespace.
4138 // (b) the min-width of the last non-breakable run. Should be 0 if the text string
4139 // ends with whitespace.
4140 // (c) the min/max width of the string (trimmed for whitespace).
4141 //
4142 // If the text string starts with whitespace, then we need to go ahead and
4143 // terminate our current line (unless we're already in a whitespace stripping
4144 // mode.
4145 //
4146 // If the text string has a breakable character in the middle, but didn't start
4147 // with whitespace, then we add the width of the first non-breakable run and
4148 // then end the current line. We then need to use the intermediate min/max width
4149 // values (if any of them are larger than our current min/max). We then look at
4150 // the width of the last non-breakable run and use that to start a new line
4151 // (unless we end in whitespace).
4152 RenderStyle* childStyle = child->style();
4153 float childMin = 0;
4154 float childMax = 0;
4155
4156 if (!child->isText()) {
4157 // Case (1) and (2). Inline replaced and inline flow elements.
4158 if (child->isRenderInline()) {
4159 // Add in padding/border/margin from the appropriate side of
4160 // the element.
4161 float bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline);
4162 childMin += bpm;
4163 childMax += bpm;
4164
4165 inlineMin += childMin;
4166 inlineMax += childMax;
4167
4168 child->clearPreferredLogicalWidthsDirty();
4169 } else {
4170 // Inline replaced elts add in their margins to their min/max values.
4171 LayoutUnit margins = 0;
4172 Length startMargin = childStyle->marginStart();
4173 Length endMargin = childStyle->marginEnd();
4174 if (startMargin.isFixed())
4175 margins += adjustFloatForSubPixelLayout(startMargin.value());
4176 if (endMargin.isFixed())
4177 margins += adjustFloatForSubPixelLayout(endMargin.value());
4178 childMin += margins.ceilToFloat();
4179 childMax += margins.ceilToFloat();
4180 }
4181 }
4182
4183 if (!child->isRenderInline() && !child->isText()) {
4184 // Case (2). Inline replaced elements and floats.
4185 // Go ahead and terminate the current line as far as
4186 // minwidth is concerned.
4187 LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth;
4188 if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) {
4189 RenderBox* childBox = toRenderBox(child);
4190 LogicalExtentComputedValues computedValues;
4191 childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues);
4192 childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent;
4193 } else {
4194 childMinPreferredLogicalWidth = child->minPreferredLogicalWidth();
4195 childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth();
4196 }
4197 childMin += childMinPreferredLogicalWidth.ceilToFloat();
4198 childMax += childMaxPreferredLogicalWidth.ceilToFloat();
4199
4200 bool clearPreviousFloat;
4201 if (child->isFloating()) {
4202 clearPreviousFloat = (prevFloat
4203 && ((prevFloat->style()->floating() == LeftFloat && (childStyle->clear() & CLEFT))
4204 || (prevFloat->style()->floating() == RightFloat && (childStyle->clear() & CRIGHT))));
4205 prevFloat = child;
4206 } else
4207 clearPreviousFloat = false;
4208
4209 bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak;
4210 if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) {
4211 updatePreferredWidth(minLogicalWidth, inlineMin);
4212 inlineMin = 0;
4213 }
4214
4215 // If we're supposed to clear the previous float, then terminate maxwidth as well.
4216 if (clearPreviousFloat) {
4217 updatePreferredWidth(maxLogicalWidth, inlineMax);
4218 inlineMax = 0;
4219 }
4220
4221 // Add in text-indent. This is added in only once.
4222 if (!addedTextIndent && !child->isFloating()) {
4223 float ceiledTextIndent = textIndent.ceilToFloat();
4224 childMin += ceiledTextIndent;
4225 childMax += ceiledTextIndent;
4226
4227 if (childMin < 0)
4228 textIndent = adjustFloatForSubPixelLayout(childMin);
4229 else
4230 addedTextIndent = true;
4231 }
4232
4233 // Add our width to the max.
4234 inlineMax += max<float>(0, childMax);
4235
4236 if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) {
4237 if (child->isFloating())
4238 updatePreferredWidth(minLogicalWidth, childMin);
4239 else
4240 inlineMin += childMin;
4241 } else {
4242 // Now check our line.
4243 updatePreferredWidth(minLogicalWidth, childMin);
4244
4245 // Now start a new line.
4246 inlineMin = 0;
4247 }
4248
4249 if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) {
4250 updatePreferredWidth(minLogicalWidth, inlineMin);
4251 inlineMin = 0;
4252 }
4253
4254 // We are no longer stripping whitespace at the start of
4255 // a line.
4256 if (!child->isFloating()) {
4257 stripFrontSpaces = false;
4258 trailingSpaceChild = 0;
4259 }
4260 } else if (child->isText()) {
4261 // Case (3). Text.
4262 RenderText* t = toRenderText(child);
4263
4264 if (t->isWordBreak()) {
4265 updatePreferredWidth(minLogicalWidth, inlineMin);
4266 inlineMin = 0;
4267 continue;
4268 }
4269
4270 if (t->style()->hasTextCombine() && t->isCombineText())
4271 toRenderCombineText(t)->combineText();
4272
4273 // Determine if we have a breakable character. Pass in
4274 // whether or not we should ignore any spaces at the front
4275 // of the string. If those are going to be stripped out,
4276 // then they shouldn't be considered in the breakable char
4277 // check.
4278 bool hasBreakableChar, hasBreak;
4279 float firstLineMinWidth, lastLineMinWidth;
4280 bool hasBreakableStart, hasBreakableEnd;
4281 float firstLineMaxWidth, lastLineMaxWidth;
4282 t->trimmedPrefWidths(inlineMax,
4283 firstLineMinWidth, hasBreakableStart, lastLineMinWidth, hasBreakableEnd,
4284 hasBreakableChar, hasBreak, firstLineMaxWidth, lastLineMaxWidth,
4285 childMin, childMax, stripFrontSpaces);
4286
4287 // This text object will not be rendered, but it may still provide a breaking opportunity.
4288 if (!hasBreak && childMax == 0) {
4289 if (autoWrap && (hasBreakableStart || hasBreakableEnd)) {
4290 updatePreferredWidth(minLogicalWidth, inlineMin);
4291 inlineMin = 0;
4292 }
4293 continue;
4294 }
4295
4296 if (stripFrontSpaces)
4297 trailingSpaceChild = child;
4298 else
4299 trailingSpaceChild = 0;
4300
4301 // Add in text-indent. This is added in only once.
4302 float ti = 0;
4303 if (!addedTextIndent || hasRemainingNegativeTextIndent) {
4304 ti = textIndent.ceilToFloat();
4305 childMin += ti;
4306 firstLineMinWidth += ti;
4307
4308 // It the text indent negative and larger than the child minimum, we re-use the remainder
4309 // in future minimum calculations, but using the negative value again on the maximum
4310 // will lead to under-counting the max pref width.
4311 if (!addedTextIndent) {
4312 childMax += ti;
4313 firstLineMaxWidth += ti;
4314 addedTextIndent = true;
4315 }
4316
4317 if (childMin < 0) {
4318 textIndent = childMin;
4319 hasRemainingNegativeTextIndent = true;
4320 }
4321 }
4322
4323 // If we have no breakable characters at all,
4324 // then this is the easy case. We add ourselves to the current
4325 // min and max and continue.
4326 if (!hasBreakableChar) {
4327 inlineMin += childMin;
4328 } else {
4329 if (hasBreakableStart) {
4330 updatePreferredWidth(minLogicalWidth, inlineMin);
4331 } else {
4332 inlineMin += firstLineMinWidth;
4333 updatePreferredWidth(minLogicalWidth, inlineMin);
4334 childMin -= ti;
4335 }
4336
4337 inlineMin = childMin;
4338
4339 if (hasBreakableEnd) {
4340 updatePreferredWidth(minLogicalWidth, inlineMin);
4341 inlineMin = 0;
4342 shouldBreakLineAfterText = false;
4343 } else {
4344 updatePreferredWidth(minLogicalWidth, inlineMin);
4345 inlineMin = lastLineMinWidth;
4346 shouldBreakLineAfterText = true;
4347 }
4348 }
4349
4350 if (hasBreak) {
4351 inlineMax += firstLineMaxWidth;
4352 updatePreferredWidth(maxLogicalWidth, inlineMax);
4353 updatePreferredWidth(maxLogicalWidth, childMax);
4354 inlineMax = lastLineMaxWidth;
4355 addedTextIndent = true;
4356 } else {
4357 inlineMax += max<float>(0, childMax);
4358 }
4359 }
4360
4361 // Ignore spaces after a list marker.
4362 if (child->isListMarker())
4363 stripFrontSpaces = true;
4364 } else {
4365 updatePreferredWidth(minLogicalWidth, inlineMin);
4366 updatePreferredWidth(maxLogicalWidth, inlineMax);
4367 inlineMin = inlineMax = 0;
4368 stripFrontSpaces = true;
4369 trailingSpaceChild = 0;
4370 addedTextIndent = true;
4371 }
4372
4373 if (!child->isText() && child->isRenderInline())
4374 isPrevChildInlineFlow = true;
4375 else
4376 isPrevChildInlineFlow = false;
4377
4378 oldAutoWrap = autoWrap;
4379 }
4380
4381 if (styleToUse->collapseWhiteSpace())
4382 stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
4383
4384 updatePreferredWidth(minLogicalWidth, inlineMin);
4385 updatePreferredWidth(maxLogicalWidth, inlineMax);
4386 }
4387
computeBlockPreferredLogicalWidths(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth) const4388 void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
4389 {
4390 RenderStyle* styleToUse = style();
4391 bool nowrap = styleToUse->whiteSpace() == NOWRAP;
4392
4393 RenderObject* child = firstChild();
4394 RenderBlock* containingBlock = this->containingBlock();
4395 LayoutUnit floatLeftWidth = 0, floatRightWidth = 0;
4396 while (child) {
4397 // Positioned children don't affect the min/max width
4398 if (child->isOutOfFlowPositioned()) {
4399 child = child->nextSibling();
4400 continue;
4401 }
4402
4403 RenderStyle* childStyle = child->style();
4404 if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) {
4405 LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth;
4406 if (childStyle->clear() & CLEFT) {
4407 maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth);
4408 floatLeftWidth = 0;
4409 }
4410 if (childStyle->clear() & CRIGHT) {
4411 maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth);
4412 floatRightWidth = 0;
4413 }
4414 }
4415
4416 // A margin basically has three types: fixed, percentage, and auto (variable).
4417 // Auto and percentage margins simply become 0 when computing min/max width.
4418 // Fixed margins can be added in as is.
4419 Length startMarginLength = childStyle->marginStartUsing(styleToUse);
4420 Length endMarginLength = childStyle->marginEndUsing(styleToUse);
4421 LayoutUnit margin = 0;
4422 LayoutUnit marginStart = 0;
4423 LayoutUnit marginEnd = 0;
4424 if (startMarginLength.isFixed())
4425 marginStart += startMarginLength.value();
4426 if (endMarginLength.isFixed())
4427 marginEnd += endMarginLength.value();
4428 margin = marginStart + marginEnd;
4429
4430 LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth;
4431 if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) {
4432 RenderBox* childBox = toRenderBox(child);
4433 LogicalExtentComputedValues computedValues;
4434 childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues);
4435 childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent;
4436 } else {
4437 childMinPreferredLogicalWidth = child->minPreferredLogicalWidth();
4438 childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth();
4439 }
4440
4441 LayoutUnit w = childMinPreferredLogicalWidth + margin;
4442 minLogicalWidth = max(w, minLogicalWidth);
4443
4444 // IE ignores tables for calculation of nowrap. Makes some sense.
4445 if (nowrap && !child->isTable())
4446 maxLogicalWidth = max(w, maxLogicalWidth);
4447
4448 w = childMaxPreferredLogicalWidth + margin;
4449
4450 if (!child->isFloating()) {
4451 if (child->isBox() && toRenderBox(child)->avoidsFloats()) {
4452 // Determine a left and right max value based off whether or not the floats can fit in the
4453 // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin
4454 // is smaller than the float width.
4455 bool ltr = containingBlock ? containingBlock->style()->isLeftToRightDirection() : styleToUse->isLeftToRightDirection();
4456 LayoutUnit marginLogicalLeft = ltr ? marginStart : marginEnd;
4457 LayoutUnit marginLogicalRight = ltr ? marginEnd : marginStart;
4458 LayoutUnit maxLeft = marginLogicalLeft > 0 ? max(floatLeftWidth, marginLogicalLeft) : floatLeftWidth + marginLogicalLeft;
4459 LayoutUnit maxRight = marginLogicalRight > 0 ? max(floatRightWidth, marginLogicalRight) : floatRightWidth + marginLogicalRight;
4460 w = childMaxPreferredLogicalWidth + maxLeft + maxRight;
4461 w = max(w, floatLeftWidth + floatRightWidth);
4462 }
4463 else
4464 maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth);
4465 floatLeftWidth = floatRightWidth = 0;
4466 }
4467
4468 if (child->isFloating()) {
4469 if (childStyle->floating() == LeftFloat)
4470 floatLeftWidth += w;
4471 else
4472 floatRightWidth += w;
4473 } else
4474 maxLogicalWidth = max(w, maxLogicalWidth);
4475
4476 child = child->nextSibling();
4477 }
4478
4479 // Always make sure these values are non-negative.
4480 minLogicalWidth = max<LayoutUnit>(0, minLogicalWidth);
4481 maxLogicalWidth = max<LayoutUnit>(0, maxLogicalWidth);
4482
4483 maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth);
4484 }
4485
hasLineIfEmpty() const4486 bool RenderBlock::hasLineIfEmpty() const
4487 {
4488 if (!node())
4489 return false;
4490
4491 if (node()->isRootEditableElement())
4492 return true;
4493
4494 if (node()->isShadowRoot() && toShadowRoot(node())->host()->hasTagName(inputTag))
4495 return true;
4496
4497 return false;
4498 }
4499
lineHeight(bool firstLine,LineDirectionMode direction,LinePositionMode linePositionMode) const4500 LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
4501 {
4502 // Inline blocks are replaced elements. Otherwise, just pass off to
4503 // the base class. If we're being queried as though we're the root line
4504 // box, then the fact that we're an inline-block is irrelevant, and we behave
4505 // just like a block.
4506 if (isReplaced() && linePositionMode == PositionOnContainingLine)
4507 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
4508
4509 if (firstLine && document().styleEngine()->usesFirstLineRules()) {
4510 RenderStyle* s = style(firstLine);
4511 if (s != style())
4512 return s->computedLineHeight(view());
4513 }
4514
4515 if (m_lineHeight == -1)
4516 m_lineHeight = style()->computedLineHeight(view());
4517
4518 return m_lineHeight;
4519 }
4520
baselinePosition(FontBaseline baselineType,bool firstLine,LineDirectionMode direction,LinePositionMode linePositionMode) const4521 int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
4522 {
4523 // Inline blocks are replaced elements. Otherwise, just pass off to
4524 // the base class. If we're being queried as though we're the root line
4525 // box, then the fact that we're an inline-block is irrelevant, and we behave
4526 // just like a block.
4527 if (isReplaced() && linePositionMode == PositionOnContainingLine) {
4528 // For "leaf" theme objects, let the theme decide what the baseline position is.
4529 // FIXME: Might be better to have a custom CSS property instead, so that if the theme
4530 // is turned off, checkboxes/radios will still have decent baselines.
4531 // FIXME: Need to patch form controls to deal with vertical lines.
4532 if (style()->hasAppearance() && !RenderTheme::theme().isControlContainer(style()->appearance()))
4533 return RenderTheme::theme().baselinePosition(this);
4534
4535 // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in
4536 // the normal flow. We make an exception for marquees, since their baselines are meaningless
4537 // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them.
4538 // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled
4539 // vertically (e.g., an overflow:hidden block that has had scrollTop moved).
4540 bool ignoreBaseline = (layer() && layer()->scrollableArea() && (isMarquee() || (direction == HorizontalLine ? (layer()->scrollableArea()->verticalScrollbar() || layer()->scrollableArea()->scrollYOffset())
4541 : (layer()->scrollableArea()->horizontalScrollbar() || layer()->scrollableArea()->scrollXOffset())))) || (isWritingModeRoot() && !isRubyRun());
4542
4543 int baselinePos = ignoreBaseline ? -1 : inlineBlockBaseline(direction);
4544
4545 if (isDeprecatedFlexibleBox()) {
4546 // Historically, we did this check for all baselines. But we can't
4547 // remove this code from deprecated flexbox, because it effectively
4548 // breaks -webkit-line-clamp, which is used in the wild -- we would
4549 // calculate the baseline as if -webkit-line-clamp wasn't used.
4550 // For simplicity, we use this for all uses of deprecated flexbox.
4551 LayoutUnit bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth();
4552 if (baselinePos > bottomOfContent)
4553 baselinePos = -1;
4554 }
4555 if (baselinePos != -1)
4556 return direction == HorizontalLine ? marginTop() + baselinePos : marginRight() + baselinePos;
4557
4558 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
4559 }
4560
4561 // If we're not replaced, we'll only get called with PositionOfInteriorLineBoxes.
4562 // Note that inline-block counts as replaced here.
4563 ASSERT(linePositionMode == PositionOfInteriorLineBoxes);
4564
4565 const FontMetrics& fontMetrics = style(firstLine)->fontMetrics();
4566 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
4567 }
4568
minLineHeightForReplacedRenderer(bool isFirstLine,LayoutUnit replacedHeight) const4569 LayoutUnit RenderBlock::minLineHeightForReplacedRenderer(bool isFirstLine, LayoutUnit replacedHeight) const
4570 {
4571 if (!document().inNoQuirksMode() && replacedHeight)
4572 return replacedHeight;
4573
4574 if (!(style(isFirstLine)->lineBoxContain() & LineBoxContainBlock))
4575 return 0;
4576
4577 return std::max<LayoutUnit>(replacedHeight, lineHeight(isFirstLine, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
4578 }
4579
firstLineBoxBaseline() const4580 int RenderBlock::firstLineBoxBaseline() const
4581 {
4582 if (isWritingModeRoot() && !isRubyRun())
4583 return -1;
4584
4585 if (childrenInline()) {
4586 if (firstLineBox())
4587 return firstLineBox()->logicalTop() + style(true)->fontMetrics().ascent(firstRootBox()->baselineType());
4588 else
4589 return -1;
4590 }
4591 else {
4592 for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) {
4593 if (!curr->isFloatingOrOutOfFlowPositioned()) {
4594 int result = curr->firstLineBoxBaseline();
4595 if (result != -1)
4596 return curr->logicalTop() + result; // Translate to our coordinate space.
4597 }
4598 }
4599 }
4600
4601 return -1;
4602 }
4603
inlineBlockBaseline(LineDirectionMode direction) const4604 int RenderBlock::inlineBlockBaseline(LineDirectionMode direction) const
4605 {
4606 if (style()->overflowY() != OVISIBLE) {
4607 // We are not calling RenderBox::baselinePosition here because the caller should add the margin-top/margin-right, not us.
4608 return direction == HorizontalLine ? height() + m_marginBox.bottom() : width() + m_marginBox.left();
4609 }
4610
4611 return lastLineBoxBaseline(direction);
4612 }
4613
lastLineBoxBaseline(LineDirectionMode lineDirection) const4614 int RenderBlock::lastLineBoxBaseline(LineDirectionMode lineDirection) const
4615 {
4616 if (isWritingModeRoot() && !isRubyRun())
4617 return -1;
4618
4619 if (childrenInline()) {
4620 if (!firstLineBox() && hasLineIfEmpty()) {
4621 const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics();
4622 return fontMetrics.ascent()
4623 + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2
4624 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight());
4625 }
4626 if (lastLineBox())
4627 return lastLineBox()->logicalTop() + style(lastLineBox() == firstLineBox())->fontMetrics().ascent(lastRootBox()->baselineType());
4628 return -1;
4629 } else {
4630 bool haveNormalFlowChild = false;
4631 for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) {
4632 if (!curr->isFloatingOrOutOfFlowPositioned()) {
4633 haveNormalFlowChild = true;
4634 int result = curr->inlineBlockBaseline(lineDirection);
4635 if (result != -1)
4636 return curr->logicalTop() + result; // Translate to our coordinate space.
4637 }
4638 }
4639 if (!haveNormalFlowChild && hasLineIfEmpty()) {
4640 const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics();
4641 return fontMetrics.ascent()
4642 + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2
4643 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight());
4644 }
4645 }
4646
4647 return -1;
4648 }
4649
firstLineBlock() const4650 RenderBlock* RenderBlock::firstLineBlock() const
4651 {
4652 RenderBlock* firstLineBlock = const_cast<RenderBlock*>(this);
4653 bool hasPseudo = false;
4654 while (true) {
4655 hasPseudo = firstLineBlock->style()->hasPseudoStyle(FIRST_LINE);
4656 if (hasPseudo)
4657 break;
4658 RenderObject* parentBlock = firstLineBlock->parent();
4659 // We include isRenderButton in this check because buttons are
4660 // implemented using flex box but should still support first-line. The
4661 // flex box spec requires that flex box does not support first-line,
4662 // though.
4663 // FIXME: Remove when buttons are implemented with align-items instead
4664 // of flexbox.
4665 if (firstLineBlock->isReplaced() || firstLineBlock->isFloating()
4666 || !parentBlock || parentBlock->firstChild() != firstLineBlock
4667 || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton()))
4668 break;
4669 ASSERT_WITH_SECURITY_IMPLICATION(parentBlock->isRenderBlock());
4670 firstLineBlock = toRenderBlock(parentBlock);
4671 }
4672
4673 if (!hasPseudo)
4674 return 0;
4675
4676 return firstLineBlock;
4677 }
4678
styleForFirstLetter(RenderObject * firstLetterBlock,RenderObject * firstLetterContainer)4679 static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer)
4680 {
4681 RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle());
4682 // Force inline display (except for floating first-letters).
4683 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE);
4684 // CSS2 says first-letter can't be positioned.
4685 pseudoStyle->setPosition(StaticPosition);
4686 return pseudoStyle;
4687 }
4688
4689 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter
4690 // "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe),
4691 // "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included"
isPunctuationForFirstLetter(UChar c)4692 static inline bool isPunctuationForFirstLetter(UChar c)
4693 {
4694 CharCategory charCategory = category(c);
4695 return charCategory == Punctuation_Open
4696 || charCategory == Punctuation_Close
4697 || charCategory == Punctuation_InitialQuote
4698 || charCategory == Punctuation_FinalQuote
4699 || charCategory == Punctuation_Other;
4700 }
4701
shouldSkipForFirstLetter(UChar c)4702 static inline bool shouldSkipForFirstLetter(UChar c)
4703 {
4704 return isSpaceOrNewline(c) || c == noBreakSpace || isPunctuationForFirstLetter(c);
4705 }
4706
findFirstLetterBlock(RenderBlock * start)4707 static inline RenderObject* findFirstLetterBlock(RenderBlock* start)
4708 {
4709 RenderObject* firstLetterBlock = start;
4710 while (true) {
4711 // We include isRenderButton in these two checks because buttons are
4712 // implemented using flex box but should still support first-letter.
4713 // The flex box spec requires that flex box does not support
4714 // first-letter, though.
4715 // FIXME: Remove when buttons are implemented with align-items instead
4716 // of flexbox.
4717 bool canHaveFirstLetterRenderer = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER)
4718 && firstLetterBlock->canHaveGeneratedChildren()
4719 && (!firstLetterBlock->isFlexibleBox() || firstLetterBlock->isRenderButton());
4720 if (canHaveFirstLetterRenderer)
4721 return firstLetterBlock;
4722
4723 RenderObject* parentBlock = firstLetterBlock->parent();
4724 if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock ||
4725 (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton()))
4726 return 0;
4727 firstLetterBlock = parentBlock;
4728 }
4729
4730 return 0;
4731 }
4732
updateFirstLetterStyle(RenderObject * firstLetterBlock,RenderObject * currentChild)4733 void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderObject* currentChild)
4734 {
4735 RenderObject* firstLetter = currentChild->parent();
4736 RenderObject* firstLetterContainer = firstLetter->parent();
4737 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
4738 ASSERT(firstLetter->isFloating() || firstLetter->isInline());
4739
4740 if (RenderStyle::compare(firstLetter->style(), pseudoStyle) == Reattach) {
4741 // The first-letter renderer needs to be replaced. Create a new renderer of the right type.
4742 RenderBoxModelObject* newFirstLetter;
4743 if (pseudoStyle->display() == INLINE)
4744 newFirstLetter = RenderInline::createAnonymous(&document());
4745 else
4746 newFirstLetter = RenderBlockFlow::createAnonymous(&document());
4747 newFirstLetter->setStyle(pseudoStyle);
4748
4749 // Move the first letter into the new renderer.
4750 LayoutStateDisabler layoutStateDisabler(view());
4751 while (RenderObject* child = firstLetter->firstChild()) {
4752 if (child->isText())
4753 toRenderText(child)->removeAndDestroyTextBoxes();
4754 firstLetter->removeChild(child);
4755 newFirstLetter->addChild(child, 0);
4756 }
4757
4758 RenderObject* nextSibling = firstLetter->nextSibling();
4759 if (RenderTextFragment* remainingText = toRenderBoxModelObject(firstLetter)->firstLetterRemainingText()) {
4760 ASSERT(remainingText->isAnonymous() || remainingText->node()->renderer() == remainingText);
4761 // Replace the old renderer with the new one.
4762 remainingText->setFirstLetter(newFirstLetter);
4763 newFirstLetter->setFirstLetterRemainingText(remainingText);
4764 }
4765 // To prevent removal of single anonymous block in RenderBlock::removeChild and causing
4766 // |nextSibling| to go stale, we remove the old first letter using removeChildNode first.
4767 firstLetterContainer->virtualChildren()->removeChildNode(firstLetterContainer, firstLetter);
4768 firstLetter->destroy();
4769 firstLetter = newFirstLetter;
4770 firstLetterContainer->addChild(firstLetter, nextSibling);
4771 } else
4772 firstLetter->setStyle(pseudoStyle);
4773
4774 for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) {
4775 if (genChild->isText())
4776 genChild->setStyle(pseudoStyle);
4777 }
4778 }
4779
firstLetterLength(const String & text)4780 static inline unsigned firstLetterLength(const String& text)
4781 {
4782 unsigned length = 0;
4783 bool punctuationOpen = false;
4784
4785 // Account for leading spaces and punctuation.
4786 while (length < text.length() && shouldSkipForFirstLetter((text)[length])) {
4787 if (isPunctuationForFirstLetter((text)[length]))
4788 punctuationOpen = true;
4789
4790 length++;
4791 }
4792
4793 // Bail if we didn't find a letter
4794 if (text.length() && length == text.length())
4795 return 0;
4796
4797 // Account for first letter.
4798 length++;
4799
4800 if (!punctuationOpen)
4801 return length;
4802
4803 // Keep looking for whitespace and allowed punctuation, but avoid
4804 // accumulating just whitespace into the :first-letter.
4805 for (unsigned scanLength = length; scanLength < text.length(); ++scanLength) {
4806 UChar c = (text)[scanLength];
4807
4808 if (!shouldSkipForFirstLetter(c))
4809 break;
4810
4811 if (isPunctuationForFirstLetter(c))
4812 length = scanLength + 1;
4813 }
4814
4815 return length;
4816 }
4817
createFirstLetterRenderer(RenderObject * firstLetterBlock,RenderObject * currentChild,unsigned length)4818 void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, RenderObject* currentChild, unsigned length)
4819 {
4820 ASSERT(length && currentChild->isText());
4821
4822 RenderObject* firstLetterContainer = currentChild->parent();
4823 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
4824 RenderObject* firstLetter = 0;
4825 if (pseudoStyle->display() == INLINE)
4826 firstLetter = RenderInline::createAnonymous(&document());
4827 else
4828 firstLetter = RenderBlockFlow::createAnonymous(&document());
4829 firstLetter->setStyle(pseudoStyle);
4830 firstLetterContainer->addChild(firstLetter, currentChild);
4831
4832 RenderText* textObj = toRenderText(currentChild);
4833
4834 // The original string is going to be either a generated content string or a DOM node's
4835 // string. We want the original string before it got transformed in case first-letter has
4836 // no text-transform or a different text-transform applied to it.
4837 String oldText = textObj->originalText();
4838 ASSERT(oldText.impl());
4839
4840 // Construct a text fragment for the text after the first letter.
4841 // This text fragment might be empty.
4842 RenderTextFragment* remainingText =
4843 new RenderTextFragment(textObj->node() ? textObj->node() : &textObj->document(), oldText.impl(), length, oldText.length() - length);
4844 remainingText->setStyle(textObj->style());
4845 if (remainingText->node())
4846 remainingText->node()->setRenderer(remainingText);
4847
4848 firstLetterContainer->addChild(remainingText, textObj);
4849 firstLetterContainer->removeChild(textObj);
4850 remainingText->setFirstLetter(firstLetter);
4851 toRenderBoxModelObject(firstLetter)->setFirstLetterRemainingText(remainingText);
4852
4853 // construct text fragment for the first letter
4854 RenderTextFragment* letter =
4855 new RenderTextFragment(remainingText->node() ? remainingText->node() : &remainingText->document(), oldText.impl(), 0, length);
4856 letter->setStyle(pseudoStyle);
4857 firstLetter->addChild(letter);
4858
4859 textObj->destroy();
4860 }
4861
updateFirstLetter()4862 void RenderBlock::updateFirstLetter()
4863 {
4864 if (!document().styleEngine()->usesFirstLetterRules())
4865 return;
4866 // Don't recur
4867 if (style()->styleType() == FIRST_LETTER)
4868 return;
4869
4870 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find
4871 // an efficient way to check for that situation though before implementing anything.
4872 RenderObject* firstLetterBlock = findFirstLetterBlock(this);
4873 if (!firstLetterBlock)
4874 return;
4875
4876 // Drill into inlines looking for our first text child.
4877 RenderObject* currChild = firstLetterBlock->firstChild();
4878 unsigned length = 0;
4879 while (currChild) {
4880 if (currChild->isText()) {
4881 // FIXME: If there is leading punctuation in a different RenderText than
4882 // the first letter, we'll not apply the correct style to it.
4883 length = firstLetterLength(toRenderText(currChild)->originalText());
4884 if (length)
4885 break;
4886 currChild = currChild->nextSibling();
4887 } else if (currChild->isListMarker()) {
4888 currChild = currChild->nextSibling();
4889 } else if (currChild->isFloatingOrOutOfFlowPositioned()) {
4890 if (currChild->style()->styleType() == FIRST_LETTER) {
4891 currChild = currChild->firstChild();
4892 break;
4893 }
4894 currChild = currChild->nextSibling();
4895 } else if (currChild->isReplaced() || currChild->isRenderButton() || currChild->isMenuList())
4896 break;
4897 else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild->canHaveGeneratedChildren()) {
4898 // We found a lower-level node with first-letter, which supersedes the higher-level style
4899 firstLetterBlock = currChild;
4900 currChild = currChild->firstChild();
4901 } else
4902 currChild = currChild->firstChild();
4903 }
4904
4905 if (!currChild)
4906 return;
4907
4908 // If the child already has style, then it has already been created, so we just want
4909 // to update it.
4910 if (currChild->parent()->style()->styleType() == FIRST_LETTER) {
4911 updateFirstLetterStyle(firstLetterBlock, currChild);
4912 return;
4913 }
4914
4915 if (!currChild->isText() || currChild->isBR())
4916 return;
4917
4918 // Our layout state is not valid for the repaints we are going to trigger by
4919 // adding and removing children of firstLetterContainer.
4920 LayoutStateDisabler layoutStateDisabler(view());
4921
4922 createFirstLetterRenderer(firstLetterBlock, currChild, length);
4923 }
4924
4925 // Helper methods for obtaining the last line, computing line counts and heights for line counts
4926 // (crawling into blocks).
shouldCheckLines(RenderObject * obj)4927 static bool shouldCheckLines(RenderObject* obj)
4928 {
4929 return !obj->isFloatingOrOutOfFlowPositioned()
4930 && obj->isRenderBlock() && obj->style()->height().isAuto()
4931 && (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERTICAL);
4932 }
4933
getHeightForLineCount(RenderBlock * block,int l,bool includeBottom,int & count)4934 static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, int& count)
4935 {
4936 if (block->style()->visibility() == VISIBLE) {
4937 if (block->childrenInline()) {
4938 for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) {
4939 if (++count == l)
4940 return box->lineBottom() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : LayoutUnit());
4941 }
4942 }
4943 else {
4944 RenderBox* normalFlowChildWithoutLines = 0;
4945 for (RenderBox* obj = block->firstChildBox(); obj; obj = obj->nextSiblingBox()) {
4946 if (shouldCheckLines(obj)) {
4947 int result = getHeightForLineCount(toRenderBlock(obj), l, false, count);
4948 if (result != -1)
4949 return result + obj->y() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : LayoutUnit());
4950 } else if (!obj->isFloatingOrOutOfFlowPositioned())
4951 normalFlowChildWithoutLines = obj;
4952 }
4953 if (normalFlowChildWithoutLines && l == 0)
4954 return normalFlowChildWithoutLines->y() + normalFlowChildWithoutLines->height();
4955 }
4956 }
4957
4958 return -1;
4959 }
4960
lineAtIndex(int i) const4961 RootInlineBox* RenderBlock::lineAtIndex(int i) const
4962 {
4963 ASSERT(i >= 0);
4964
4965 if (style()->visibility() != VISIBLE)
4966 return 0;
4967
4968 if (childrenInline()) {
4969 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox())
4970 if (!i--)
4971 return box;
4972 } else {
4973 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
4974 if (!shouldCheckLines(child))
4975 continue;
4976 if (RootInlineBox* box = toRenderBlock(child)->lineAtIndex(i))
4977 return box;
4978 }
4979 }
4980
4981 return 0;
4982 }
4983
lineCount(const RootInlineBox * stopRootInlineBox,bool * found) const4984 int RenderBlock::lineCount(const RootInlineBox* stopRootInlineBox, bool* found) const
4985 {
4986 int count = 0;
4987
4988 if (style()->visibility() == VISIBLE) {
4989 if (childrenInline())
4990 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) {
4991 count++;
4992 if (box == stopRootInlineBox) {
4993 if (found)
4994 *found = true;
4995 break;
4996 }
4997 }
4998 else
4999 for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling())
5000 if (shouldCheckLines(obj)) {
5001 bool recursiveFound = false;
5002 count += toRenderBlock(obj)->lineCount(stopRootInlineBox, &recursiveFound);
5003 if (recursiveFound) {
5004 if (found)
5005 *found = true;
5006 break;
5007 }
5008 }
5009 }
5010 return count;
5011 }
5012
heightForLineCount(int l)5013 int RenderBlock::heightForLineCount(int l)
5014 {
5015 int count = 0;
5016 return getHeightForLineCount(this, l, true, count);
5017 }
5018
adjustForBorderFit(LayoutUnit x,LayoutUnit & left,LayoutUnit & right) const5019 void RenderBlock::adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutUnit& right) const
5020 {
5021 // We don't deal with relative positioning. Our assumption is that you shrink to fit the lines without accounting
5022 // for either overflow or translations via relative positioning.
5023 if (style()->visibility() == VISIBLE) {
5024 if (childrenInline()) {
5025 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) {
5026 if (box->firstChild())
5027 left = min(left, x + static_cast<LayoutUnit>(box->firstChild()->x()));
5028 if (box->lastChild())
5029 right = max(right, x + static_cast<LayoutUnit>(ceilf(box->lastChild()->logicalRight())));
5030 }
5031 } else {
5032 for (RenderBox* obj = firstChildBox(); obj; obj = obj->nextSiblingBox()) {
5033 if (!obj->isFloatingOrOutOfFlowPositioned()) {
5034 if (obj->isRenderBlockFlow() && !obj->hasOverflowClip())
5035 toRenderBlock(obj)->adjustForBorderFit(x + obj->x(), left, right);
5036 else if (obj->style()->visibility() == VISIBLE) {
5037 // We are a replaced element or some kind of non-block-flow object.
5038 left = min(left, x + obj->x());
5039 right = max(right, x + obj->x() + obj->width());
5040 }
5041 }
5042 }
5043 }
5044 }
5045 }
5046
fitBorderToLinesIfNeeded()5047 void RenderBlock::fitBorderToLinesIfNeeded()
5048 {
5049 if (style()->borderFit() == BorderFitBorder || hasOverrideWidth())
5050 return;
5051
5052 // Walk any normal flow lines to snugly fit.
5053 LayoutUnit left = LayoutUnit::max();
5054 LayoutUnit right = LayoutUnit::min();
5055 LayoutUnit oldWidth = contentWidth();
5056 adjustForBorderFit(0, left, right);
5057
5058 // Clamp to our existing edges. We can never grow. We only shrink.
5059 LayoutUnit leftEdge = borderLeft() + paddingLeft();
5060 LayoutUnit rightEdge = leftEdge + oldWidth;
5061 left = min(rightEdge, max(leftEdge, left));
5062 right = max(leftEdge, min(rightEdge, right));
5063
5064 LayoutUnit newContentWidth = right - left;
5065 if (newContentWidth == oldWidth)
5066 return;
5067
5068 setOverrideLogicalContentWidth(newContentWidth);
5069 layoutBlock(false);
5070 clearOverrideLogicalContentWidth();
5071 }
5072
clearTruncation()5073 void RenderBlock::clearTruncation()
5074 {
5075 if (style()->visibility() == VISIBLE) {
5076 if (childrenInline() && hasMarkupTruncation()) {
5077 setHasMarkupTruncation(false);
5078 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox())
5079 box->clearTruncation();
5080 } else {
5081 for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) {
5082 if (shouldCheckLines(obj))
5083 toRenderBlock(obj)->clearTruncation();
5084 }
5085 }
5086 }
5087 }
5088
setPaginationStrut(LayoutUnit strut)5089 void RenderBlock::setPaginationStrut(LayoutUnit strut)
5090 {
5091 if (!m_rareData) {
5092 if (!strut)
5093 return;
5094 m_rareData = adoptPtr(new RenderBlockRareData());
5095 }
5096 m_rareData->m_paginationStrut = strut;
5097 }
5098
setPageLogicalOffset(LayoutUnit logicalOffset)5099 void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset)
5100 {
5101 if (!m_rareData) {
5102 if (!logicalOffset)
5103 return;
5104 m_rareData = adoptPtr(new RenderBlockRareData());
5105 }
5106 m_rareData->m_pageLogicalOffset = logicalOffset;
5107 }
5108
setBreakAtLineToAvoidWidow(int lineToBreak)5109 void RenderBlock::setBreakAtLineToAvoidWidow(int lineToBreak)
5110 {
5111 ASSERT(lineToBreak >= 0);
5112 if (!m_rareData)
5113 m_rareData = adoptPtr(new RenderBlockRareData());
5114
5115 ASSERT(!m_rareData->m_didBreakAtLineToAvoidWidow);
5116 m_rareData->m_lineBreakToAvoidWidow = lineToBreak;
5117 }
5118
setDidBreakAtLineToAvoidWidow()5119 void RenderBlock::setDidBreakAtLineToAvoidWidow()
5120 {
5121 ASSERT(!shouldBreakAtLineToAvoidWidow());
5122
5123 // This function should be called only after a break was applied to avoid widows
5124 // so assert |m_rareData| exists.
5125 ASSERT(m_rareData);
5126
5127 m_rareData->m_didBreakAtLineToAvoidWidow = true;
5128 }
5129
clearDidBreakAtLineToAvoidWidow()5130 void RenderBlock::clearDidBreakAtLineToAvoidWidow()
5131 {
5132 if (!m_rareData)
5133 return;
5134
5135 m_rareData->m_didBreakAtLineToAvoidWidow = false;
5136 }
5137
clearShouldBreakAtLineToAvoidWidow() const5138 void RenderBlock::clearShouldBreakAtLineToAvoidWidow() const
5139 {
5140 ASSERT(shouldBreakAtLineToAvoidWidow());
5141 if (!m_rareData)
5142 return;
5143
5144 m_rareData->m_lineBreakToAvoidWidow = -1;
5145 }
5146
absoluteRects(Vector<IntRect> & rects,const LayoutPoint & accumulatedOffset) const5147 void RenderBlock::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
5148 {
5149 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
5150 // inline boxes above and below us (thus getting merged with them to form a single irregular
5151 // shape).
5152 if (isAnonymousBlockContinuation()) {
5153 // FIXME: This is wrong for block-flows that are horizontal.
5154 // https://bugs.webkit.org/show_bug.cgi?id=46781
5155 rects.append(pixelSnappedIntRect(accumulatedOffset.x(), accumulatedOffset.y() - collapsedMarginBefore(),
5156 width(), height() + collapsedMarginBefore() + collapsedMarginAfter()));
5157 continuation()->absoluteRects(rects, accumulatedOffset - toLayoutSize(location() +
5158 inlineElementContinuation()->containingBlock()->location()));
5159 } else
5160 rects.append(pixelSnappedIntRect(accumulatedOffset, size()));
5161 }
5162
absoluteQuads(Vector<FloatQuad> & quads,bool * wasFixed) const5163 void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
5164 {
5165 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
5166 // inline boxes above and below us (thus getting merged with them to form a single irregular
5167 // shape).
5168 if (isAnonymousBlockContinuation()) {
5169 // FIXME: This is wrong for block-flows that are horizontal.
5170 // https://bugs.webkit.org/show_bug.cgi?id=46781
5171 FloatRect localRect(0, -collapsedMarginBefore(),
5172 width(), height() + collapsedMarginBefore() + collapsedMarginAfter());
5173 quads.append(localToAbsoluteQuad(localRect, 0 /* mode */, wasFixed));
5174 continuation()->absoluteQuads(quads, wasFixed);
5175 } else
5176 quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width(), height()), 0 /* mode */, wasFixed));
5177 }
5178
rectWithOutlineForRepaint(const RenderLayerModelObject * repaintContainer,LayoutUnit outlineWidth) const5179 LayoutRect RenderBlock::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
5180 {
5181 LayoutRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
5182 if (isAnonymousBlockContinuation())
5183 r.inflateY(collapsedMarginBefore()); // FIXME: This is wrong for block-flows that are horizontal.
5184 return r;
5185 }
5186
hoverAncestor() const5187 RenderObject* RenderBlock::hoverAncestor() const
5188 {
5189 return isAnonymousBlockContinuation() ? continuation() : RenderBox::hoverAncestor();
5190 }
5191
updateDragState(bool dragOn)5192 void RenderBlock::updateDragState(bool dragOn)
5193 {
5194 RenderBox::updateDragState(dragOn);
5195 if (continuation())
5196 continuation()->updateDragState(dragOn);
5197 }
5198
outlineStyleForRepaint() const5199 RenderStyle* RenderBlock::outlineStyleForRepaint() const
5200 {
5201 return isAnonymousBlockContinuation() ? continuation()->style() : style();
5202 }
5203
childBecameNonInline(RenderObject *)5204 void RenderBlock::childBecameNonInline(RenderObject*)
5205 {
5206 makeChildrenNonInline();
5207 if (isAnonymousBlock() && parent() && parent()->isRenderBlock())
5208 toRenderBlock(parent())->removeLeftoverAnonymousBlock(this);
5209 // |this| may be dead here
5210 }
5211
updateHitTestResult(HitTestResult & result,const LayoutPoint & point)5212 void RenderBlock::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
5213 {
5214 if (result.innerNode())
5215 return;
5216
5217 if (Node* n = nodeForHitTest()) {
5218 result.setInnerNode(n);
5219 if (!result.innerNonSharedNode())
5220 result.setInnerNonSharedNode(n);
5221 result.setLocalPoint(point);
5222 }
5223 }
5224
localCaretRect(InlineBox * inlineBox,int caretOffset,LayoutUnit * extraWidthToEndOfLine)5225 LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
5226 {
5227 // Do the normal calculation in most cases.
5228 if (firstChild())
5229 return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine);
5230
5231 LayoutRect caretRect = localCaretRectForEmptyElement(width(), textIndentOffset());
5232
5233 if (extraWidthToEndOfLine) {
5234 if (isRenderBlock()) {
5235 *extraWidthToEndOfLine = width() - caretRect.maxX();
5236 } else {
5237 // FIXME: This code looks wrong.
5238 // myRight and containerRight are set up, but then clobbered.
5239 // So *extraWidthToEndOfLine will always be 0 here.
5240
5241 LayoutUnit myRight = caretRect.maxX();
5242 // FIXME: why call localToAbsoluteForContent() twice here, too?
5243 FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0));
5244
5245 LayoutUnit containerRight = containingBlock()->x() + containingBlockLogicalWidthForContent();
5246 FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0));
5247
5248 *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x();
5249 }
5250 }
5251
5252 return caretRect;
5253 }
5254
addFocusRingRects(Vector<IntRect> & rects,const LayoutPoint & additionalOffset,const RenderLayerModelObject * paintContainer)5255 void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
5256 {
5257 // For blocks inside inlines, we go ahead and include margins so that we run right up to the
5258 // inline boxes above and below us (thus getting merged with them to form a single irregular
5259 // shape).
5260 if (inlineElementContinuation()) {
5261 // FIXME: This check really isn't accurate.
5262 bool nextInlineHasLineBox = inlineElementContinuation()->firstLineBox();
5263 // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block.
5264 // FIXME: This is wrong for block-flows that are horizontal.
5265 // https://bugs.webkit.org/show_bug.cgi?id=46781
5266 bool prevInlineHasLineBox = toRenderInline(inlineElementContinuation()->node()->renderer())->firstLineBox();
5267 float topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : LayoutUnit();
5268 float bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : LayoutUnit();
5269 LayoutRect rect(additionalOffset.x(), additionalOffset.y() - topMargin, width(), height() + topMargin + bottomMargin);
5270 if (!rect.isEmpty())
5271 rects.append(pixelSnappedIntRect(rect));
5272 } else if (width() && height())
5273 rects.append(pixelSnappedIntRect(additionalOffset, size()));
5274
5275 if (!hasOverflowClip() && !hasControlClip()) {
5276 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
5277 LayoutUnit top = max<LayoutUnit>(curr->lineTop(), curr->top());
5278 LayoutUnit bottom = min<LayoutUnit>(curr->lineBottom(), curr->top() + curr->height());
5279 LayoutRect rect(additionalOffset.x() + curr->x(), additionalOffset.y() + top, curr->width(), bottom - top);
5280 if (!rect.isEmpty())
5281 rects.append(pixelSnappedIntRect(rect));
5282 }
5283
5284 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
5285 if (!curr->isText() && !curr->isListMarker() && curr->isBox()) {
5286 RenderBox* box = toRenderBox(curr);
5287 FloatPoint pos;
5288 // FIXME: This doesn't work correctly with transforms.
5289 if (box->layer())
5290 pos = curr->localToContainerPoint(FloatPoint(), paintContainer);
5291 else
5292 pos = FloatPoint(additionalOffset.x() + box->x(), additionalOffset.y() + box->y());
5293 box->addFocusRingRects(rects, flooredLayoutPoint(pos), paintContainer);
5294 }
5295 }
5296 }
5297
5298 if (inlineElementContinuation())
5299 inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location()), paintContainer);
5300 }
5301
computeSelfHitTestRects(Vector<LayoutRect> & rects,const LayoutPoint & layerOffset) const5302 void RenderBlock::computeSelfHitTestRects(Vector<LayoutRect>& rects, const LayoutPoint& layerOffset) const
5303 {
5304 RenderBox::computeSelfHitTestRects(rects, layerOffset);
5305
5306 if (hasHorizontalLayoutOverflow() || hasVerticalLayoutOverflow()) {
5307 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
5308 LayoutUnit top = max<LayoutUnit>(curr->lineTop(), curr->top());
5309 LayoutUnit bottom = min<LayoutUnit>(curr->lineBottom(), curr->top() + curr->height());
5310 LayoutRect rect(layerOffset.x() + curr->x(), layerOffset.y() + top, curr->width(), bottom - top);
5311 // It's common for this rect to be entirely contained in our box, so exclude that simple case.
5312 if (!rect.isEmpty() && (rects.isEmpty() || !rects[0].contains(rect)))
5313 rects.append(rect);
5314 }
5315 }
5316 }
5317
createAnonymousBoxWithSameTypeAs(const RenderObject * parent) const5318 RenderBox* RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const
5319 {
5320 if (isAnonymousColumnsBlock())
5321 return createAnonymousColumnsWithParentRenderer(parent);
5322 if (isAnonymousColumnSpanBlock())
5323 return createAnonymousColumnSpanWithParentRenderer(parent);
5324 return createAnonymousWithParentRendererAndDisplay(parent, style()->display());
5325 }
5326
hasNextPage(LayoutUnit logicalOffset,PageBoundaryRule pageBoundaryRule) const5327 bool RenderBlock::hasNextPage(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const
5328 {
5329 ASSERT(view()->layoutState() && view()->layoutState()->isPaginated());
5330
5331 RenderFlowThread* flowThread = flowThreadContainingBlock();
5332 if (!flowThread)
5333 return true; // Printing and multi-column both make new pages to accommodate content.
5334
5335 // See if we're in the last region.
5336 LayoutUnit pageOffset = offsetFromLogicalTopOfFirstPage() + logicalOffset;
5337 RenderRegion* region = flowThread->regionAtBlockOffset(pageOffset, this);
5338 if (!region)
5339 return false;
5340 if (region->isLastRegion())
5341 return region->isRenderRegionSet() || region->style()->regionFragment() == BreakRegionFragment
5342 || (pageBoundaryRule == IncludePageBoundary && pageOffset == region->logicalTopForFlowThreadContent());
5343 return true;
5344 }
5345
nextPageLogicalTop(LayoutUnit logicalOffset,PageBoundaryRule pageBoundaryRule) const5346 LayoutUnit RenderBlock::nextPageLogicalTop(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const
5347 {
5348 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
5349 if (!pageLogicalHeight)
5350 return logicalOffset;
5351
5352 // The logicalOffset is in our coordinate space. We can add in our pushed offset.
5353 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset);
5354 if (pageBoundaryRule == ExcludePageBoundary)
5355 return logicalOffset + (remainingLogicalHeight ? remainingLogicalHeight : pageLogicalHeight);
5356 return logicalOffset + remainingLogicalHeight;
5357 }
5358
paginationUnit() const5359 ColumnInfo::PaginationUnit RenderBlock::paginationUnit() const
5360 {
5361 return ColumnInfo::Column;
5362 }
5363
pageLogicalTopForOffset(LayoutUnit offset) const5364 LayoutUnit RenderBlock::pageLogicalTopForOffset(LayoutUnit offset) const
5365 {
5366 RenderView* renderView = view();
5367 LayoutUnit firstPageLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->m_pageOffset.height() : renderView->layoutState()->m_pageOffset.width();
5368 LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->m_layoutOffset.height() : renderView->layoutState()->m_layoutOffset.width();
5369
5370 LayoutUnit cumulativeOffset = offset + blockLogicalTop;
5371 RenderFlowThread* flowThread = flowThreadContainingBlock();
5372 if (!flowThread) {
5373 LayoutUnit pageLogicalHeight = renderView->layoutState()->pageLogicalHeight();
5374 if (!pageLogicalHeight)
5375 return 0;
5376 return cumulativeOffset - roundToInt(cumulativeOffset - firstPageLogicalTop) % roundToInt(pageLogicalHeight);
5377 }
5378 return flowThread->pageLogicalTopForOffset(cumulativeOffset);
5379 }
5380
pageLogicalHeightForOffset(LayoutUnit offset) const5381 LayoutUnit RenderBlock::pageLogicalHeightForOffset(LayoutUnit offset) const
5382 {
5383 RenderView* renderView = view();
5384 RenderFlowThread* flowThread = flowThreadContainingBlock();
5385 if (!flowThread)
5386 return renderView->layoutState()->m_pageLogicalHeight;
5387 return flowThread->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage());
5388 }
5389
pageRemainingLogicalHeightForOffset(LayoutUnit offset,PageBoundaryRule pageBoundaryRule) const5390 LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) const
5391 {
5392 RenderView* renderView = view();
5393 offset += offsetFromLogicalTopOfFirstPage();
5394
5395 RenderFlowThread* flowThread = flowThreadContainingBlock();
5396 if (!flowThread) {
5397 LayoutUnit pageLogicalHeight = renderView->layoutState()->m_pageLogicalHeight;
5398 LayoutUnit remainingHeight = pageLogicalHeight - intMod(offset, pageLogicalHeight);
5399 if (pageBoundaryRule == IncludePageBoundary) {
5400 // If includeBoundaryPoint is true the line exactly on the top edge of a
5401 // column will act as being part of the previous column.
5402 remainingHeight = intMod(remainingHeight, pageLogicalHeight);
5403 }
5404 return remainingHeight;
5405 }
5406
5407 return flowThread->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule);
5408 }
5409
adjustForUnsplittableChild(RenderBox * child,LayoutUnit logicalOffset,bool includeMargins)5410 LayoutUnit RenderBlock::adjustForUnsplittableChild(RenderBox* child, LayoutUnit logicalOffset, bool includeMargins)
5411 {
5412 bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns();
5413 bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight;
5414 RenderFlowThread* flowThread = flowThreadContainingBlock();
5415 bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread();
5416 bool isUnsplittable = child->isUnsplittableForPagination() || (checkColumnBreaks && child->style()->columnBreakInside() == PBAVOID)
5417 || (checkPageBreaks && child->style()->pageBreakInside() == PBAVOID)
5418 || (checkRegionBreaks && child->style()->regionBreakInside() == PBAVOID);
5419 if (!isUnsplittable)
5420 return logicalOffset;
5421 LayoutUnit childLogicalHeight = logicalHeightForChild(child) + (includeMargins ? marginBeforeForChild(child) + marginAfterForChild(child) : LayoutUnit());
5422 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
5423 bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight();
5424 updateMinimumPageHeight(logicalOffset, childLogicalHeight);
5425 if (!pageLogicalHeight || (hasUniformPageLogicalHeight && childLogicalHeight > pageLogicalHeight)
5426 || !hasNextPage(logicalOffset))
5427 return logicalOffset;
5428 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary);
5429 if (remainingLogicalHeight < childLogicalHeight) {
5430 if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, childLogicalHeight))
5431 return logicalOffset;
5432 return logicalOffset + remainingLogicalHeight;
5433 }
5434 return logicalOffset;
5435 }
5436
pushToNextPageWithMinimumLogicalHeight(LayoutUnit & adjustment,LayoutUnit logicalOffset,LayoutUnit minimumLogicalHeight) const5437 bool RenderBlock::pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, LayoutUnit logicalOffset, LayoutUnit minimumLogicalHeight) const
5438 {
5439 bool checkRegion = false;
5440 for (LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset + adjustment); pageLogicalHeight;
5441 pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset + adjustment)) {
5442 if (minimumLogicalHeight <= pageLogicalHeight)
5443 return true;
5444 if (!hasNextPage(logicalOffset + adjustment))
5445 return false;
5446 adjustment += pageLogicalHeight;
5447 checkRegion = true;
5448 }
5449 return !checkRegion;
5450 }
5451
setPageBreak(LayoutUnit offset,LayoutUnit spaceShortage)5452 void RenderBlock::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage)
5453 {
5454 if (RenderFlowThread* flowThread = flowThreadContainingBlock())
5455 flowThread->setPageBreak(offsetFromLogicalTopOfFirstPage() + offset, spaceShortage);
5456 }
5457
updateMinimumPageHeight(LayoutUnit offset,LayoutUnit minHeight)5458 void RenderBlock::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight)
5459 {
5460 if (RenderFlowThread* flowThread = flowThreadContainingBlock())
5461 flowThread->updateMinimumPageHeight(offsetFromLogicalTopOfFirstPage() + offset, minHeight);
5462 else if (ColumnInfo* colInfo = view()->layoutState()->m_columnInfo)
5463 colInfo->updateMinimumColumnHeight(minHeight);
5464 }
5465
calculateMinimumPageHeight(RenderStyle * renderStyle,RootInlineBox * lastLine,LayoutUnit lineTop,LayoutUnit lineBottom)5466 static inline LayoutUnit calculateMinimumPageHeight(RenderStyle* renderStyle, RootInlineBox* lastLine, LayoutUnit lineTop, LayoutUnit lineBottom)
5467 {
5468 // We may require a certain minimum number of lines per page in order to satisfy
5469 // orphans and widows, and that may affect the minimum page height.
5470 unsigned lineCount = max<unsigned>(renderStyle->hasAutoOrphans() ? 1 : renderStyle->orphans(), renderStyle->hasAutoWidows() ? 1 : renderStyle->widows());
5471 if (lineCount > 1) {
5472 RootInlineBox* line = lastLine;
5473 for (unsigned i = 1; i < lineCount && line->prevRootBox(); i++)
5474 line = line->prevRootBox();
5475
5476 // FIXME: Paginating using line overflow isn't all fine. See FIXME in
5477 // adjustLinePositionForPagination() for more details.
5478 LayoutRect overflow = line->logicalVisualOverflowRect(line->lineTop(), line->lineBottom());
5479 lineTop = min(line->lineTopWithLeading(), overflow.y());
5480 }
5481 return lineBottom - lineTop;
5482 }
5483
adjustLinePositionForPagination(RootInlineBox * lineBox,LayoutUnit & delta,RenderFlowThread * flowThread)5484 void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta, RenderFlowThread* flowThread)
5485 {
5486 // FIXME: For now we paginate using line overflow. This ensures that lines don't overlap at all when we
5487 // put a strut between them for pagination purposes. However, this really isn't the desired rendering, since
5488 // the line on the top of the next page will appear too far down relative to the same kind of line at the top
5489 // of the first column.
5490 //
5491 // The rendering we would like to see is one where the lineTopWithLeading is at the top of the column, and any line overflow
5492 // simply spills out above the top of the column. This effect would match what happens at the top of the first column.
5493 // We can't achieve this rendering, however, until we stop columns from clipping to the column bounds (thus allowing
5494 // for overflow to occur), and then cache visible overflow for each column rect.
5495 //
5496 // Furthermore, the paint we have to do when a column has overflow has to be special. We need to exclude
5497 // content that paints in a previous column (and content that paints in the following column).
5498 //
5499 // For now we'll at least honor the lineTopWithLeading when paginating if it is above the logical top overflow. This will
5500 // at least make positive leading work in typical cases.
5501 //
5502 // FIXME: Another problem with simply moving lines is that the available line width may change (because of floats).
5503 // Technically if the location we move the line to has a different line width than our old position, then we need to dirty the
5504 // line and all following lines.
5505 LayoutRect logicalVisualOverflow = lineBox->logicalVisualOverflowRect(lineBox->lineTop(), lineBox->lineBottom());
5506 LayoutUnit logicalOffset = min(lineBox->lineTopWithLeading(), logicalVisualOverflow.y());
5507 LayoutUnit logicalBottom = max(lineBox->lineBottomWithLeading(), logicalVisualOverflow.maxY());
5508 LayoutUnit lineHeight = logicalBottom - logicalOffset;
5509 updateMinimumPageHeight(logicalOffset, calculateMinimumPageHeight(style(), lineBox, logicalOffset, logicalBottom));
5510 logicalOffset += delta;
5511 lineBox->setPaginationStrut(0);
5512 lineBox->setIsFirstAfterPageBreak(false);
5513 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset);
5514 bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight();
5515 // If lineHeight is greater than pageLogicalHeight, but logicalVisualOverflow.height() still fits, we are
5516 // still going to add a strut, so that the visible overflow fits on a single page.
5517 if (!pageLogicalHeight || (hasUniformPageLogicalHeight && logicalVisualOverflow.height() > pageLogicalHeight)
5518 || !hasNextPage(logicalOffset))
5519 // FIXME: In case the line aligns with the top of the page (or it's slightly shifted downwards) it will not be marked as the first line in the page.
5520 // From here, the fix is not straightforward because it's not easy to always determine when the current line is the first in the page.
5521 return;
5522 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary);
5523
5524 int lineIndex = lineCount(lineBox);
5525 if (remainingLogicalHeight < lineHeight || (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineIndex)) {
5526 if (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineIndex) {
5527 clearShouldBreakAtLineToAvoidWidow();
5528 setDidBreakAtLineToAvoidWidow();
5529 }
5530 // If we have a non-uniform page height, then we have to shift further possibly.
5531 if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, lineHeight))
5532 return;
5533 if (lineHeight > pageLogicalHeight) {
5534 // Split the top margin in order to avoid splitting the visible part of the line.
5535 remainingLogicalHeight -= min(lineHeight - pageLogicalHeight, max<LayoutUnit>(0, logicalVisualOverflow.y() - lineBox->lineTopWithLeading()));
5536 }
5537 LayoutUnit totalLogicalHeight = lineHeight + max<LayoutUnit>(0, logicalOffset);
5538 LayoutUnit pageLogicalHeightAtNewOffset = hasUniformPageLogicalHeight ? pageLogicalHeight : pageLogicalHeightForOffset(logicalOffset + remainingLogicalHeight);
5539 setPageBreak(logicalOffset, lineHeight - remainingLogicalHeight);
5540 if (((lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeightAtNewOffset) || (!style()->hasAutoOrphans() && style()->orphans() >= lineIndex))
5541 && !isOutOfFlowPositioned() && !isTableCell())
5542 setPaginationStrut(remainingLogicalHeight + max<LayoutUnit>(0, logicalOffset));
5543 else {
5544 delta += remainingLogicalHeight;
5545 lineBox->setPaginationStrut(remainingLogicalHeight);
5546 lineBox->setIsFirstAfterPageBreak(true);
5547 }
5548 } else if (remainingLogicalHeight == pageLogicalHeight) {
5549 // We're at the very top of a page or column.
5550 if (lineBox != firstRootBox())
5551 lineBox->setIsFirstAfterPageBreak(true);
5552 if (lineBox != firstRootBox() || offsetFromLogicalTopOfFirstPage())
5553 setPageBreak(logicalOffset, lineHeight);
5554 }
5555 }
5556
updateRegionForLine(RootInlineBox * lineBox) const5557 void RenderBlock::updateRegionForLine(RootInlineBox* lineBox) const
5558 {
5559 ASSERT(lineBox);
5560 lineBox->setContainingRegion(regionAtBlockOffset(lineBox->lineTopWithLeading()));
5561
5562 RootInlineBox* prevLineBox = lineBox->prevRootBox();
5563 if (!prevLineBox)
5564 return;
5565
5566 // This check is more accurate than the one in |adjustLinePositionForPagination| because it takes into
5567 // account just the container changes between lines. The before mentioned function doesn't set the flag
5568 // correctly if the line is positioned at the top of the last fragment container.
5569 if (lineBox->containingRegion() != prevLineBox->containingRegion())
5570 lineBox->setIsFirstAfterPageBreak(true);
5571 }
5572
lineWidthForPaginatedLineChanged(RootInlineBox * rootBox,LayoutUnit lineDelta,RenderFlowThread * flowThread) const5573 bool RenderBlock::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta, RenderFlowThread* flowThread) const
5574 {
5575 if (!flowThread)
5576 return false;
5577
5578 RenderRegion* currentRegion = regionAtBlockOffset(rootBox->lineTopWithLeading() + lineDelta);
5579 // Just bail if the region didn't change.
5580 if (rootBox->containingRegion() == currentRegion)
5581 return false;
5582 return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion);
5583 }
5584
offsetFromLogicalTopOfFirstPage() const5585 LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const
5586 {
5587 LayoutState* layoutState = view()->layoutState();
5588 if (layoutState && !layoutState->isPaginated())
5589 return 0;
5590
5591 RenderFlowThread* flowThread = flowThreadContainingBlock();
5592 if (flowThread)
5593 return flowThread->offsetFromLogicalTopOfFirstRegion(this);
5594
5595 if (layoutState) {
5596 ASSERT(layoutState->renderer() == this);
5597
5598 LayoutSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset;
5599 return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
5600 }
5601
5602 ASSERT_NOT_REACHED();
5603 return 0;
5604 }
5605
regionAtBlockOffset(LayoutUnit blockOffset) const5606 RenderRegion* RenderBlock::regionAtBlockOffset(LayoutUnit blockOffset) const
5607 {
5608 RenderFlowThread* flowThread = flowThreadContainingBlock();
5609 if (!flowThread || !flowThread->hasValidRegionInfo())
5610 return 0;
5611
5612 return flowThread->regionAtBlockOffset(offsetFromLogicalTopOfFirstPage() + blockOffset, true);
5613 }
5614
logicalWidthChangedInRegions(RenderFlowThread * flowThread) const5615 bool RenderBlock::logicalWidthChangedInRegions(RenderFlowThread* flowThread) const
5616 {
5617 if (!flowThread || !flowThread->hasValidRegionInfo())
5618 return false;
5619
5620 return flowThread->logicalWidthChangedInRegionsForBlock(this);
5621 }
5622
clampToStartAndEndRegions(RenderRegion * region) const5623 RenderRegion* RenderBlock::clampToStartAndEndRegions(RenderRegion* region) const
5624 {
5625 RenderFlowThread* flowThread = flowThreadContainingBlock();
5626
5627 ASSERT(isRenderView() || (region && flowThread));
5628 if (isRenderView())
5629 return region;
5630
5631 // We need to clamp to the block, since we want any lines or blocks that overflow out of the
5632 // logical top or logical bottom of the block to size as though the border box in the first and
5633 // last regions extended infinitely. Otherwise the lines are going to size according to the regions
5634 // they overflow into, which makes no sense when this block doesn't exist in |region| at all.
5635 RenderRegion* startRegion;
5636 RenderRegion* endRegion;
5637 flowThread->getRegionRangeForBox(this, startRegion, endRegion);
5638
5639 if (startRegion && region->logicalTopForFlowThreadContent() < startRegion->logicalTopForFlowThreadContent())
5640 return startRegion;
5641 if (endRegion && region->logicalTopForFlowThreadContent() > endRegion->logicalTopForFlowThreadContent())
5642 return endRegion;
5643
5644 return region;
5645 }
5646
collapsedMarginBeforeForChild(const RenderBox * child) const5647 LayoutUnit RenderBlock::collapsedMarginBeforeForChild(const RenderBox* child) const
5648 {
5649 // If the child has the same directionality as we do, then we can just return its
5650 // collapsed margin.
5651 if (!child->isWritingModeRoot())
5652 return child->collapsedMarginBefore();
5653
5654 // The child has a different directionality. If the child is parallel, then it's just
5655 // flipped relative to us. We can use the collapsed margin for the opposite edge.
5656 if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
5657 return child->collapsedMarginAfter();
5658
5659 // The child is perpendicular to us, which means its margins don't collapse but are on the
5660 // "logical left/right" sides of the child box. We can just return the raw margin in this case.
5661 return marginBeforeForChild(child);
5662 }
5663
collapsedMarginAfterForChild(const RenderBox * child) const5664 LayoutUnit RenderBlock::collapsedMarginAfterForChild(const RenderBox* child) const
5665 {
5666 // If the child has the same directionality as we do, then we can just return its
5667 // collapsed margin.
5668 if (!child->isWritingModeRoot())
5669 return child->collapsedMarginAfter();
5670
5671 // The child has a different directionality. If the child is parallel, then it's just
5672 // flipped relative to us. We can use the collapsed margin for the opposite edge.
5673 if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
5674 return child->collapsedMarginBefore();
5675
5676 // The child is perpendicular to us, which means its margins don't collapse but are on the
5677 // "logical left/right" side of the child box. We can just return the raw margin in this case.
5678 return marginAfterForChild(child);
5679 }
5680
hasMarginBeforeQuirk(const RenderBox * child) const5681 bool RenderBlock::hasMarginBeforeQuirk(const RenderBox* child) const
5682 {
5683 // If the child has the same directionality as we do, then we can just return its
5684 // margin quirk.
5685 if (!child->isWritingModeRoot())
5686 return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk();
5687
5688 // The child has a different directionality. If the child is parallel, then it's just
5689 // flipped relative to us. We can use the opposite edge.
5690 if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
5691 return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk();
5692
5693 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about
5694 // whether or not authors specified quirky ems, since they're an implementation detail.
5695 return false;
5696 }
5697
hasMarginAfterQuirk(const RenderBox * child) const5698 bool RenderBlock::hasMarginAfterQuirk(const RenderBox* child) const
5699 {
5700 // If the child has the same directionality as we do, then we can just return its
5701 // margin quirk.
5702 if (!child->isWritingModeRoot())
5703 return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk();
5704
5705 // The child has a different directionality. If the child is parallel, then it's just
5706 // flipped relative to us. We can use the opposite edge.
5707 if (child->isHorizontalWritingMode() == isHorizontalWritingMode())
5708 return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk();
5709
5710 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about
5711 // whether or not authors specified quirky ems, since they're an implementation detail.
5712 return false;
5713 }
5714
renderName() const5715 const char* RenderBlock::renderName() const
5716 {
5717 if (isBody())
5718 return "RenderBody"; // FIXME: Temporary hack until we know that the regression tests pass.
5719
5720 if (isFloating())
5721 return "RenderBlock (floating)";
5722 if (isOutOfFlowPositioned())
5723 return "RenderBlock (positioned)";
5724 if (isAnonymousColumnsBlock())
5725 return "RenderBlock (anonymous multi-column)";
5726 if (isAnonymousColumnSpanBlock())
5727 return "RenderBlock (anonymous multi-column span)";
5728 if (isAnonymousBlock())
5729 return "RenderBlock (anonymous)";
5730 // FIXME: Temporary hack while the new generated content system is being implemented.
5731 if (isPseudoElement())
5732 return "RenderBlock (generated)";
5733 if (isAnonymous())
5734 return "RenderBlock (generated)";
5735 if (isRelPositioned())
5736 return "RenderBlock (relative positioned)";
5737 if (isStickyPositioned())
5738 return "RenderBlock (sticky positioned)";
5739 return "RenderBlock";
5740 }
5741
createAnonymousWithParentRendererAndDisplay(const RenderObject * parent,EDisplay display)5742 RenderBlock* RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderObject* parent, EDisplay display)
5743 {
5744 // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ?
5745 EDisplay newDisplay;
5746 RenderBlock* newBox = 0;
5747 if (display == BOX || display == INLINE_BOX) {
5748 // FIXME: Remove this case once we have eliminated all internal users of old flexbox
5749 newBox = RenderDeprecatedFlexibleBox::createAnonymous(&parent->document());
5750 newDisplay = BOX;
5751 } else if (display == FLEX || display == INLINE_FLEX) {
5752 newBox = RenderFlexibleBox::createAnonymous(&parent->document());
5753 newDisplay = FLEX;
5754 } else {
5755 newBox = RenderBlockFlow::createAnonymous(&parent->document());
5756 newDisplay = BLOCK;
5757 }
5758
5759 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), newDisplay);
5760 newBox->setStyle(newStyle.release());
5761 return newBox;
5762 }
5763
createAnonymousColumnsWithParentRenderer(const RenderObject * parent)5764 RenderBlockFlow* RenderBlock::createAnonymousColumnsWithParentRenderer(const RenderObject* parent)
5765 {
5766 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK);
5767 newStyle->inheritColumnPropertiesFrom(parent->style());
5768
5769 RenderBlockFlow* newBox = RenderBlockFlow::createAnonymous(&parent->document());
5770 newBox->setStyle(newStyle.release());
5771 return newBox;
5772 }
5773
createAnonymousColumnSpanWithParentRenderer(const RenderObject * parent)5774 RenderBlockFlow* RenderBlock::createAnonymousColumnSpanWithParentRenderer(const RenderObject* parent)
5775 {
5776 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK);
5777 newStyle->setColumnSpan(ColumnSpanAll);
5778
5779 RenderBlockFlow* newBox = RenderBlockFlow::createAnonymous(&parent->document());
5780 newBox->setStyle(newStyle.release());
5781 return newBox;
5782 }
5783
5784 #ifndef NDEBUG
checkPositionedObjectsNeedLayout()5785 void RenderBlock::checkPositionedObjectsNeedLayout()
5786 {
5787 if (!gPositionedDescendantsMap)
5788 return;
5789
5790 if (TrackedRendererListHashSet* positionedDescendantSet = positionedObjects()) {
5791 TrackedRendererListHashSet::const_iterator end = positionedDescendantSet->end();
5792 for (TrackedRendererListHashSet::const_iterator it = positionedDescendantSet->begin(); it != end; ++it) {
5793 RenderBox* currBox = *it;
5794 ASSERT(!currBox->needsLayout());
5795 }
5796 }
5797 }
5798
showLineTreeAndMark(const InlineBox * markedBox1,const char * markedLabel1,const InlineBox * markedBox2,const char * markedLabel2,const RenderObject * obj) const5799 void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj) const
5800 {
5801 showRenderObject();
5802 for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox())
5803 root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, 1);
5804 }
5805
5806 #endif
5807
5808 } // namespace WebCore
5809