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