1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "config.h"
24 #include "core/rendering/RenderInline.h"
25
26 #include "core/dom/Fullscreen.h"
27 #include "core/dom/StyleEngine.h"
28 #include "core/page/Chrome.h"
29 #include "core/page/Page.h"
30 #include "core/paint/BoxPainter.h"
31 #include "core/paint/InlinePainter.h"
32 #include "core/paint/ObjectPainter.h"
33 #include "core/rendering/GraphicsContextAnnotator.h"
34 #include "core/rendering/HitTestResult.h"
35 #include "core/rendering/InlineTextBox.h"
36 #include "core/rendering/RenderBlock.h"
37 #include "core/rendering/RenderFlowThread.h"
38 #include "core/rendering/RenderFullScreen.h"
39 #include "core/rendering/RenderGeometryMap.h"
40 #include "core/rendering/RenderLayer.h"
41 #include "core/rendering/RenderTheme.h"
42 #include "core/rendering/RenderView.h"
43 #include "core/rendering/style/StyleInheritedData.h"
44 #include "platform/geometry/FloatQuad.h"
45 #include "platform/geometry/TransformState.h"
46 #include "platform/graphics/GraphicsContext.h"
47
48 namespace blink {
49
50 struct SameSizeAsRenderInline : public RenderBoxModelObject {
~SameSizeAsRenderInlineblink::SameSizeAsRenderInline51 virtual ~SameSizeAsRenderInline() { }
52 RenderObjectChildList m_children;
53 RenderLineBoxList m_lineBoxes;
54 };
55
56 COMPILE_ASSERT(sizeof(RenderInline) == sizeof(SameSizeAsRenderInline), RenderInline_should_stay_small);
57
RenderInline(Element * element)58 RenderInline::RenderInline(Element* element)
59 : RenderBoxModelObject(element)
60 {
61 setChildrenInline(true);
62 }
63
trace(Visitor * visitor)64 void RenderInline::trace(Visitor* visitor)
65 {
66 visitor->trace(m_children);
67 RenderBoxModelObject::trace(visitor);
68 }
69
createAnonymous(Document * document)70 RenderInline* RenderInline::createAnonymous(Document* document)
71 {
72 RenderInline* renderer = new RenderInline(0);
73 renderer->setDocumentForAnonymous(document);
74 return renderer;
75 }
76
willBeDestroyed()77 void RenderInline::willBeDestroyed()
78 {
79 #if ENABLE(ASSERT)
80 // Make sure we do not retain "this" in the continuation outline table map of our containing blocks.
81 if (parent() && style()->visibility() == VISIBLE && style()->hasOutline()) {
82 bool containingBlockPaintsContinuationOutline = continuation() || isInlineElementContinuation();
83 if (containingBlockPaintsContinuationOutline) {
84 if (RenderBlock* cb = containingBlock()) {
85 if (RenderBlock* cbCb = cb->containingBlock())
86 ASSERT(!cbCb->paintsContinuationOutline(this));
87 }
88 }
89 }
90 #endif
91
92 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
93 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
94 children()->destroyLeftoverChildren();
95
96 // Destroy our continuation before anything other than anonymous children.
97 // The reason we don't destroy it before anonymous children is that they may
98 // have continuations of their own that are anonymous children of our continuation.
99 RenderBoxModelObject* continuation = this->continuation();
100 if (continuation) {
101 continuation->destroy();
102 setContinuation(0);
103 }
104
105 if (!documentBeingDestroyed()) {
106 if (firstLineBox()) {
107 // We can't wait for RenderBoxModelObject::destroy to clear the selection,
108 // because by then we will have nuked the line boxes.
109 // FIXME: The FrameSelection should be responsible for this when it
110 // is notified of DOM mutations.
111 if (isSelectionBorder())
112 view()->clearSelection();
113
114 // If line boxes are contained inside a root, that means we're an inline.
115 // In that case, we need to remove all the line boxes so that the parent
116 // lines aren't pointing to deleted children. If the first line box does
117 // not have a parent that means they are either already disconnected or
118 // root lines that can just be destroyed without disconnecting.
119 if (firstLineBox()->parent()) {
120 for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
121 box->remove();
122 }
123 } else if (parent())
124 parent()->dirtyLinesFromChangedChild(this);
125 }
126
127 m_lineBoxes.deleteLineBoxes();
128
129 RenderBoxModelObject::willBeDestroyed();
130 }
131
inlineElementContinuation() const132 RenderInline* RenderInline::inlineElementContinuation() const
133 {
134 RenderBoxModelObject* continuation = this->continuation();
135 if (!continuation || continuation->isInline())
136 return toRenderInline(continuation);
137 return toRenderBlock(continuation)->inlineElementContinuation();
138 }
139
updateFromStyle()140 void RenderInline::updateFromStyle()
141 {
142 RenderBoxModelObject::updateFromStyle();
143
144 // FIXME: Is this still needed. Was needed for run-ins, since run-in is considered a block display type.
145 setInline(true);
146
147 // FIXME: Support transforms and reflections on inline flows someday.
148 setHasTransform(false);
149 setHasReflection(false);
150 }
151
inFlowPositionedInlineAncestor(RenderObject * p)152 static RenderObject* inFlowPositionedInlineAncestor(RenderObject* p)
153 {
154 while (p && p->isRenderInline()) {
155 if (p->isRelPositioned())
156 return p;
157 p = p->parent();
158 }
159 return 0;
160 }
161
updateStyleOfAnonymousBlockContinuations(RenderObject * block,const RenderStyle * newStyle,const RenderStyle * oldStyle)162 static void updateStyleOfAnonymousBlockContinuations(RenderObject* block, const RenderStyle* newStyle, const RenderStyle* oldStyle)
163 {
164 for (;block && block->isAnonymousBlock(); block = block->nextSibling()) {
165 if (!toRenderBlock(block)->isAnonymousBlockContinuation())
166 continue;
167
168 RefPtr<RenderStyle> newBlockStyle;
169
170 if (!block->style()->isOutlineEquivalent(newStyle)) {
171 newBlockStyle = RenderStyle::clone(block->style());
172 newBlockStyle->setOutlineFromStyle(*newStyle);
173 }
174
175 if (block->style()->position() != newStyle->position()) {
176 // If we are no longer in-flow positioned but our descendant block(s) still have an in-flow positioned ancestor then
177 // their containing anonymous block should keep its in-flow positioning.
178 if (oldStyle->hasInFlowPosition()
179 && inFlowPositionedInlineAncestor(toRenderBlock(block)->inlineElementContinuation()))
180 continue;
181 if (!newBlockStyle)
182 newBlockStyle = RenderStyle::clone(block->style());
183 newBlockStyle->setPosition(newStyle->position());
184 }
185
186 if (newBlockStyle)
187 block->setStyle(newBlockStyle);
188 }
189 }
190
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)191 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
192 {
193 RenderBoxModelObject::styleDidChange(diff, oldStyle);
194
195 // Ensure that all of the split inlines pick up the new style. We
196 // only do this if we're an inline, since we don't want to propagate
197 // a block's style to the other inlines.
198 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
199 // and after the block share the same style, but the block doesn't
200 // need to pass its style on to anyone else.
201 RenderStyle* newStyle = style();
202 RenderInline* continuation = inlineElementContinuation();
203 for (RenderInline* currCont = continuation; currCont; currCont = currCont->inlineElementContinuation()) {
204 RenderBoxModelObject* nextCont = currCont->continuation();
205 currCont->setContinuation(0);
206 currCont->setStyle(newStyle);
207 currCont->setContinuation(nextCont);
208 }
209
210 // If an inline's outline or in-flow positioning has changed then any descendant blocks will need to change their styles accordingly.
211 // Do this by updating the styles of the descendant blocks' containing anonymous blocks - there may be more than one.
212 if (continuation && oldStyle
213 && (!newStyle->isOutlineEquivalent(oldStyle)
214 || (newStyle->position() != oldStyle->position() && (newStyle->hasInFlowPosition() || oldStyle->hasInFlowPosition())))) {
215 // If any descendant blocks exist then they will be in the next anonymous block and its siblings.
216 RenderObject* block = containingBlock()->nextSibling();
217 if (block && block->isAnonymousBlock())
218 updateStyleOfAnonymousBlockContinuations(block, newStyle, oldStyle);
219 }
220
221 if (!alwaysCreateLineBoxes()) {
222 bool alwaysCreateLineBoxesNew = hasSelfPaintingLayer() || hasBoxDecorationBackground() || newStyle->hasPadding() || newStyle->hasMargin() || newStyle->hasOutline();
223 if (oldStyle && alwaysCreateLineBoxesNew) {
224 dirtyLineBoxes(false);
225 setNeedsLayoutAndFullPaintInvalidation();
226 }
227 setAlwaysCreateLineBoxes(alwaysCreateLineBoxesNew);
228 }
229 }
230
updateAlwaysCreateLineBoxes(bool fullLayout)231 void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout)
232 {
233 // Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the
234 // background color will only cause a layout on the first rollover.
235 if (alwaysCreateLineBoxes())
236 return;
237
238 RenderStyle* parentStyle = parent()->style();
239 RenderInline* parentRenderInline = parent()->isRenderInline() ? toRenderInline(parent()) : 0;
240 bool checkFonts = document().inNoQuirksMode();
241 bool alwaysCreateLineBoxesNew = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes())
242 || (parentRenderInline && parentStyle->verticalAlign() != BASELINE)
243 || style()->verticalAlign() != BASELINE
244 || style()->textEmphasisMark() != TextEmphasisMarkNone
245 || (checkFonts && (!parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->font().fontMetrics())
246 || parentStyle->lineHeight() != style()->lineHeight()));
247
248 if (!alwaysCreateLineBoxesNew && checkFonts && document().styleEngine()->usesFirstLineRules()) {
249 // Have to check the first line style as well.
250 parentStyle = parent()->style(true);
251 RenderStyle* childStyle = style(true);
252 alwaysCreateLineBoxesNew = !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics())
253 || childStyle->verticalAlign() != BASELINE
254 || parentStyle->lineHeight() != childStyle->lineHeight();
255 }
256
257 if (alwaysCreateLineBoxesNew) {
258 if (!fullLayout)
259 dirtyLineBoxes(false);
260 setAlwaysCreateLineBoxes();
261 }
262 }
263
localCaretRect(InlineBox * inlineBox,int,LayoutUnit * extraWidthToEndOfLine)264 LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, int, LayoutUnit* extraWidthToEndOfLine)
265 {
266 if (firstChild()) {
267 // This condition is possible if the RenderInline is at an editing boundary,
268 // i.e. the VisiblePosition is:
269 // <RenderInline editingBoundary=true>|<RenderText> </RenderText></RenderInline>
270 // FIXME: need to figure out how to make this return a valid rect, note that
271 // there are no line boxes created in the above case.
272 return LayoutRect();
273 }
274
275 ASSERT_UNUSED(inlineBox, !inlineBox);
276
277 if (extraWidthToEndOfLine)
278 *extraWidthToEndOfLine = 0;
279
280 LayoutRect caretRect = localCaretRectForEmptyElement(borderAndPaddingWidth(), 0);
281
282 if (InlineBox* firstBox = firstLineBox())
283 caretRect.moveBy(roundedLayoutPoint(firstBox->topLeft()));
284
285 return caretRect;
286 }
287
addChild(RenderObject * newChild,RenderObject * beforeChild)288 void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild)
289 {
290 if (continuation())
291 return addChildToContinuation(newChild, beforeChild);
292 return addChildIgnoringContinuation(newChild, beforeChild);
293 }
294
nextContinuation(RenderObject * renderer)295 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
296 {
297 if (renderer->isInline() && !renderer->isReplaced())
298 return toRenderInline(renderer)->continuation();
299 return toRenderBlock(renderer)->inlineElementContinuation();
300 }
301
continuationBefore(RenderObject * beforeChild)302 RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild)
303 {
304 if (beforeChild && beforeChild->parent() == this)
305 return this;
306
307 RenderBoxModelObject* curr = nextContinuation(this);
308 RenderBoxModelObject* nextToLast = this;
309 RenderBoxModelObject* last = this;
310 while (curr) {
311 if (beforeChild && beforeChild->parent() == curr) {
312 if (curr->slowFirstChild() == beforeChild)
313 return last;
314 return curr;
315 }
316
317 nextToLast = last;
318 last = curr;
319 curr = nextContinuation(curr);
320 }
321
322 if (!beforeChild && !last->slowFirstChild())
323 return nextToLast;
324 return last;
325 }
326
addChildIgnoringContinuation(RenderObject * newChild,RenderObject * beforeChild)327 void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
328 {
329 // Make sure we don't append things after :after-generated content if we have it.
330 if (!beforeChild && isAfterContent(lastChild()))
331 beforeChild = lastChild();
332
333 if (!newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned()) {
334 // We are placing a block inside an inline. We have to perform a split of this
335 // inline into continuations. This involves creating an anonymous block box to hold
336 // |newChild|. We then make that block box a continuation of this inline. We take all of
337 // the children after |beforeChild| and put them in a clone of this object.
338 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
339
340 // If inside an inline affected by in-flow positioning the block needs to be affected by it too.
341 // Giving the block a layer like this allows it to collect the x/y offsets from inline parents later.
342 if (RenderObject* positionedAncestor = inFlowPositionedInlineAncestor(this))
343 newStyle->setPosition(positionedAncestor->style()->position());
344
345 // Push outline style to the block continuation.
346 if (!newStyle->isOutlineEquivalent(style()))
347 newStyle->setOutlineFromStyle(*style());
348
349 RenderBlockFlow* newBox = RenderBlockFlow::createAnonymous(&document());
350 newBox->setStyle(newStyle.release());
351 RenderBoxModelObject* oldContinuation = continuation();
352 setContinuation(newBox);
353
354 splitFlow(beforeChild, newBox, newChild, oldContinuation);
355 return;
356 }
357
358 RenderBoxModelObject::addChild(newChild, beforeChild);
359
360 newChild->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
361 }
362
clone() const363 RenderInline* RenderInline::clone() const
364 {
365 RenderInline* cloneInline = new RenderInline(node());
366 cloneInline->setStyle(style());
367 cloneInline->setFlowThreadState(flowThreadState());
368 return cloneInline;
369 }
370
splitInlines(RenderBlock * fromBlock,RenderBlock * toBlock,RenderBlock * middleBlock,RenderObject * beforeChild,RenderBoxModelObject * oldCont)371 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
372 RenderBlock* middleBlock,
373 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
374 {
375 // Create a clone of this inline.
376 RenderInline* cloneInline = clone();
377 cloneInline->setContinuation(oldCont);
378
379 // If we're splitting the inline containing the fullscreened element,
380 // |beforeChild| may be the renderer for the fullscreened element. However,
381 // that renderer is wrapped in a RenderFullScreen, so |this| is not its
382 // parent. Since the splitting logic expects |this| to be the parent, set
383 // |beforeChild| to be the RenderFullScreen.
384 if (Fullscreen* fullscreen = Fullscreen::fromIfExists(document())) {
385 const Element* fullScreenElement = fullscreen->webkitCurrentFullScreenElement();
386 if (fullScreenElement && beforeChild && beforeChild->node() == fullScreenElement)
387 beforeChild = fullscreen->fullScreenRenderer();
388 }
389
390 // Now take all of the children from beforeChild to the end and remove
391 // them from |this| and place them in the clone.
392 RenderObject* o = beforeChild;
393 while (o) {
394 RenderObject* tmp = o;
395 o = tmp->nextSibling();
396 cloneInline->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0);
397 tmp->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
398 }
399
400 // Hook |clone| up as the continuation of the middle block.
401 middleBlock->setContinuation(cloneInline);
402
403 // We have been reparented and are now under the fromBlock. We need
404 // to walk up our inline parent chain until we hit the containing block.
405 // Once we hit the containing block we're done.
406 RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
407 RenderBoxModelObject* currChild = this;
408
409 // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
410 // There will eventually be a better approach to this problem that will let us nest to a much
411 // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in
412 // incorrect rendering, but the alternative is to hang forever.
413 unsigned splitDepth = 1;
414 const unsigned cMaxSplitDepth = 200;
415 while (curr && curr != fromBlock) {
416 ASSERT(curr->isRenderInline());
417 if (splitDepth < cMaxSplitDepth) {
418 // Create a new clone.
419 RenderInline* cloneChild = cloneInline;
420 cloneInline = toRenderInline(curr)->clone();
421
422 // Insert our child clone as the first child.
423 cloneInline->addChildIgnoringContinuation(cloneChild, 0);
424
425 // Hook the clone up as a continuation of |curr|.
426 RenderInline* inlineCurr = toRenderInline(curr);
427 oldCont = inlineCurr->continuation();
428 inlineCurr->setContinuation(cloneInline);
429 cloneInline->setContinuation(oldCont);
430
431 // Now we need to take all of the children starting from the first child
432 // *after* currChild and append them all to the clone.
433 o = currChild->nextSibling();
434 while (o) {
435 RenderObject* tmp = o;
436 o = tmp->nextSibling();
437 cloneInline->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0);
438 tmp->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
439 }
440 }
441
442 // Keep walking up the chain.
443 currChild = curr;
444 curr = toRenderBoxModelObject(curr->parent());
445 splitDepth++;
446 }
447
448 // Now we are at the block level. We need to put the clone into the toBlock.
449 toBlock->children()->appendChildNode(toBlock, cloneInline);
450
451 // Now take all the children after currChild and remove them from the fromBlock
452 // and put them in the toBlock.
453 o = currChild->nextSibling();
454 while (o) {
455 RenderObject* tmp = o;
456 o = tmp->nextSibling();
457 toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp));
458 }
459 }
460
splitFlow(RenderObject * beforeChild,RenderBlock * newBlockBox,RenderObject * newChild,RenderBoxModelObject * oldCont)461 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
462 RenderObject* newChild, RenderBoxModelObject* oldCont)
463 {
464 RenderBlock* pre = 0;
465 RenderBlock* block = containingBlock();
466
467 // Delete our line boxes before we do the inline split into continuations.
468 block->deleteLineBoxTree();
469
470 bool madeNewBeforeBlock = false;
471 if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
472 // We can reuse this block and make it the preBlock of the next continuation.
473 pre = block;
474 pre->removePositionedObjects(0);
475 if (pre->isRenderBlockFlow())
476 toRenderBlockFlow(pre)->removeFloatingObjects();
477 block = block->containingBlock();
478 } else {
479 // No anonymous block available for use. Make one.
480 pre = block->createAnonymousBlock();
481 madeNewBeforeBlock = true;
482 }
483
484 RenderBlock* post = toRenderBlock(pre->createAnonymousBoxWithSameTypeAs(block));
485
486 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
487 if (madeNewBeforeBlock)
488 block->children()->insertChildNode(block, pre, boxFirst);
489 block->children()->insertChildNode(block, newBlockBox, boxFirst);
490 block->children()->insertChildNode(block, post, boxFirst);
491 block->setChildrenInline(false);
492
493 if (madeNewBeforeBlock) {
494 RenderObject* o = boxFirst;
495 while (o) {
496 RenderObject* no = o;
497 o = no->nextSibling();
498 pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, no));
499 no->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
500 }
501 }
502
503 splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
504
505 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
506 // time in makeChildrenNonInline by just setting this explicitly up front.
507 newBlockBox->setChildrenInline(false);
508
509 newBlockBox->addChild(newChild);
510
511 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
512 // get deleted properly. Because objects moves from the pre block into the post block, we want to
513 // make new line boxes instead of leaving the old line boxes around.
514 pre->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
515 block->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
516 post->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
517 }
518
addChildToContinuation(RenderObject * newChild,RenderObject * beforeChild)519 void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
520 {
521 RenderBoxModelObject* flow = continuationBefore(beforeChild);
522 ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline());
523 RenderBoxModelObject* beforeChildParent = 0;
524 if (beforeChild)
525 beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
526 else {
527 RenderBoxModelObject* cont = nextContinuation(flow);
528 if (cont)
529 beforeChildParent = cont;
530 else
531 beforeChildParent = flow;
532 }
533
534 if (newChild->isFloatingOrOutOfFlowPositioned())
535 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
536
537 // A continuation always consists of two potential candidates: an inline or an anonymous
538 // block box holding block children.
539 bool childInline = newChild->isInline();
540 bool bcpInline = beforeChildParent->isInline();
541 bool flowInline = flow->isInline();
542
543 if (flow == beforeChildParent)
544 return flow->addChildIgnoringContinuation(newChild, beforeChild);
545 else {
546 // The goal here is to match up if we can, so that we can coalesce and create the
547 // minimal # of continuations needed for the inline.
548 if (childInline == bcpInline || (beforeChild && beforeChild->isInline()))
549 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
550 if (flowInline == childInline)
551 return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append.
552 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
553 }
554 }
555
paint(PaintInfo & paintInfo,const LayoutPoint & paintOffset)556 void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
557 {
558 InlinePainter(*this).paint(paintInfo, paintOffset);
559 }
560
561 template<typename GeneratorContext>
generateLineBoxRects(GeneratorContext & yield) const562 void RenderInline::generateLineBoxRects(GeneratorContext& yield) const
563 {
564 if (!alwaysCreateLineBoxes())
565 generateCulledLineBoxRects(yield, this);
566 else if (InlineFlowBox* curr = firstLineBox()) {
567 for (; curr; curr = curr->nextLineBox())
568 yield(FloatRect(curr->topLeft(), curr->size()));
569 } else
570 yield(FloatRect());
571 }
572
573 template<typename GeneratorContext>
generateCulledLineBoxRects(GeneratorContext & yield,const RenderInline * container) const574 void RenderInline::generateCulledLineBoxRects(GeneratorContext& yield, const RenderInline* container) const
575 {
576 if (!culledInlineFirstLineBox()) {
577 yield(FloatRect());
578 return;
579 }
580
581 bool isHorizontal = style()->isHorizontalWritingMode();
582
583 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
584 if (curr->isFloatingOrOutOfFlowPositioned())
585 continue;
586
587 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
588 // direction (aligned to the root box's baseline).
589 if (curr->isBox()) {
590 RenderBox* currBox = toRenderBox(curr);
591 if (currBox->inlineBoxWrapper()) {
592 RootInlineBox& rootBox = currBox->inlineBoxWrapper()->root();
593 int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent());
594 int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height();
595 if (isHorizontal)
596 yield(FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, (currBox->width() + currBox->marginWidth()).toFloat(), logicalHeight));
597 else
598 yield(FloatRect(logicalTop, currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, (currBox->height() + currBox->marginHeight()).toFloat()));
599 }
600 } else if (curr->isRenderInline()) {
601 // If the child doesn't need line boxes either, then we can recur.
602 RenderInline* currInline = toRenderInline(curr);
603 if (!currInline->alwaysCreateLineBoxes())
604 currInline->generateCulledLineBoxRects(yield, container);
605 else {
606 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
607 RootInlineBox& rootBox = childLine->root();
608 int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent());
609 int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height();
610 if (isHorizontal)
611 yield(FloatRect(childLine->x() - childLine->marginLogicalLeft(),
612 logicalTop,
613 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
614 logicalHeight));
615 else
616 yield(FloatRect(logicalTop,
617 childLine->y() - childLine->marginLogicalLeft(),
618 logicalHeight,
619 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight()));
620 }
621 }
622 } else if (curr->isText()) {
623 RenderText* currText = toRenderText(curr);
624 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
625 RootInlineBox& rootBox = childText->root();
626 int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent());
627 int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height();
628 if (isHorizontal)
629 yield(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight));
630 else
631 yield(FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth()));
632 }
633 }
634 }
635 }
636
637 namespace {
638
639 class AbsoluteRectsGeneratorContext {
640 public:
AbsoluteRectsGeneratorContext(Vector<IntRect> & rects,const LayoutPoint & accumulatedOffset)641 AbsoluteRectsGeneratorContext(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset)
642 : m_rects(rects)
643 , m_accumulatedOffset(accumulatedOffset) { }
644
operator ()(const FloatRect & rect)645 void operator()(const FloatRect& rect)
646 {
647 IntRect intRect = enclosingIntRect(rect);
648 intRect.move(m_accumulatedOffset.x(), m_accumulatedOffset.y());
649 m_rects.append(intRect);
650 }
651 private:
652 Vector<IntRect>& m_rects;
653 const LayoutPoint& m_accumulatedOffset;
654 };
655
656 } // unnamed namespace
657
absoluteRects(Vector<IntRect> & rects,const LayoutPoint & accumulatedOffset) const658 void RenderInline::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
659 {
660 AbsoluteRectsGeneratorContext context(rects, accumulatedOffset);
661 generateLineBoxRects(context);
662
663 if (continuation()) {
664 if (continuation()->isBox()) {
665 RenderBox* box = toRenderBox(continuation());
666 continuation()->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location() + box->locationOffset()));
667 } else
668 continuation()->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location()));
669 }
670 }
671
672
673 namespace {
674
675 class AbsoluteQuadsGeneratorContext {
676 public:
AbsoluteQuadsGeneratorContext(const RenderInline * renderer,Vector<FloatQuad> & quads)677 AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector<FloatQuad>& quads)
678 : m_quads(quads)
679 , m_geometryMap()
680 {
681 m_geometryMap.pushMappingsToAncestor(renderer, 0);
682 }
683
operator ()(const FloatRect & rect)684 void operator()(const FloatRect& rect)
685 {
686 m_quads.append(m_geometryMap.absoluteRect(rect));
687 }
688 private:
689 Vector<FloatQuad>& m_quads;
690 RenderGeometryMap m_geometryMap;
691 };
692
693 } // unnamed namespace
694
absoluteQuads(Vector<FloatQuad> & quads,bool * wasFixed) const695 void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
696 {
697 AbsoluteQuadsGeneratorContext context(this, quads);
698 generateLineBoxRects(context);
699
700 if (continuation())
701 continuation()->absoluteQuads(quads, wasFixed);
702 }
703
offsetLeft() const704 LayoutUnit RenderInline::offsetLeft() const
705 {
706 LayoutPoint topLeft;
707 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
708 topLeft = flooredLayoutPoint(firstBox->topLeft());
709 return adjustedPositionRelativeToOffsetParent(topLeft).x();
710 }
711
offsetTop() const712 LayoutUnit RenderInline::offsetTop() const
713 {
714 LayoutPoint topLeft;
715 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
716 topLeft = flooredLayoutPoint(firstBox->topLeft());
717 return adjustedPositionRelativeToOffsetParent(topLeft).y();
718 }
719
computeMargin(const RenderInline * renderer,const Length & margin)720 static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin)
721 {
722 if (margin.isAuto())
723 return 0;
724 if (margin.isFixed())
725 return margin.value();
726 if (margin.isPercent())
727 return minimumValueForLength(margin, std::max<LayoutUnit>(0, renderer->containingBlock()->availableLogicalWidth()));
728 return 0;
729 }
730
marginLeft() const731 LayoutUnit RenderInline::marginLeft() const
732 {
733 return computeMargin(this, style()->marginLeft());
734 }
735
marginRight() const736 LayoutUnit RenderInline::marginRight() const
737 {
738 return computeMargin(this, style()->marginRight());
739 }
740
marginTop() const741 LayoutUnit RenderInline::marginTop() const
742 {
743 return computeMargin(this, style()->marginTop());
744 }
745
marginBottom() const746 LayoutUnit RenderInline::marginBottom() const
747 {
748 return computeMargin(this, style()->marginBottom());
749 }
750
marginStart(const RenderStyle * otherStyle) const751 LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const
752 {
753 return computeMargin(this, style()->marginStartUsing(otherStyle ? otherStyle : style()));
754 }
755
marginEnd(const RenderStyle * otherStyle) const756 LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const
757 {
758 return computeMargin(this, style()->marginEndUsing(otherStyle ? otherStyle : style()));
759 }
760
marginBefore(const RenderStyle * otherStyle) const761 LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const
762 {
763 return computeMargin(this, style()->marginBeforeUsing(otherStyle ? otherStyle : style()));
764 }
765
marginAfter(const RenderStyle * otherStyle) const766 LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const
767 {
768 return computeMargin(this, style()->marginAfterUsing(otherStyle ? otherStyle : style()));
769 }
770
renderName() const771 const char* RenderInline::renderName() const
772 {
773 if (isRelPositioned())
774 return "RenderInline (relative positioned)";
775 // FIXME: Cleanup isPseudoElement duplication with other renderName methods.
776 // crbug.com/415653
777 if (isPseudoElement()) {
778 if (style()->styleType() == BEFORE)
779 return "RenderInline (pseudo:before)";
780 if (style()->styleType() == AFTER)
781 return "RenderInline (pseudo:after)";
782 if (style()->styleType() == BACKDROP)
783 return "RenderInline (pseudo:backdrop)";
784 ASSERT_NOT_REACHED();
785 }
786 if (isAnonymous())
787 return "RenderInline (generated)";
788 return "RenderInline";
789 }
790
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction hitTestAction)791 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
792 const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
793 {
794 return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction);
795 }
796
797 namespace {
798
799 class HitTestCulledInlinesGeneratorContext {
800 public:
HitTestCulledInlinesGeneratorContext(Region & region,const HitTestLocation & location)801 HitTestCulledInlinesGeneratorContext(Region& region, const HitTestLocation& location) : m_intersected(false), m_region(region), m_location(location) { }
operator ()(const FloatRect & rect)802 void operator()(const FloatRect& rect)
803 {
804 m_intersected = m_intersected || m_location.intersects(rect);
805 m_region.unite(enclosingIntRect(rect));
806 }
intersected() const807 bool intersected() const { return m_intersected; }
808 private:
809 bool m_intersected;
810 Region& m_region;
811 const HitTestLocation& m_location;
812 };
813
814 } // unnamed namespace
815
hitTestCulledInline(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset)816 bool RenderInline::hitTestCulledInline(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset)
817 {
818 ASSERT(result.isRectBasedTest() && !alwaysCreateLineBoxes());
819 if (!visibleToHitTestRequest(request))
820 return false;
821
822 HitTestLocation tmpLocation(locationInContainer, -toLayoutSize(accumulatedOffset));
823
824 Region regionResult;
825 HitTestCulledInlinesGeneratorContext context(regionResult, tmpLocation);
826 generateCulledLineBoxRects(context, this);
827
828 if (context.intersected()) {
829 updateHitTestResult(result, tmpLocation.point());
830 // We can not use addNodeToRectBasedTestResult to determine if we fully enclose the hit-test area
831 // because it can only handle rectangular targets.
832 result.addNodeToRectBasedTestResult(node(), request, locationInContainer);
833 return regionResult.contains(tmpLocation.boundingBox());
834 }
835 return false;
836 }
837
positionForPoint(const LayoutPoint & point)838 PositionWithAffinity RenderInline::positionForPoint(const LayoutPoint& point)
839 {
840 // FIXME: Does not deal with relative positioned inlines (should it?)
841 RenderBlock* cb = containingBlock();
842 if (firstLineBox()) {
843 // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We
844 // should try to find a result by asking our containing block.
845 return cb->positionForPoint(point);
846 }
847
848 // Translate the coords from the pre-anonymous block to the post-anonymous block.
849 LayoutPoint parentBlockPoint = cb->location() + point;
850 RenderBoxModelObject* c = continuation();
851 while (c) {
852 RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c);
853 if (c->isInline() || c->slowFirstChild())
854 return c->positionForPoint(parentBlockPoint - contBlock->locationOffset());
855 c = toRenderBlock(c)->inlineElementContinuation();
856 }
857
858 return RenderBoxModelObject::positionForPoint(point);
859 }
860
861 namespace {
862
863 class LinesBoundingBoxGeneratorContext {
864 public:
LinesBoundingBoxGeneratorContext(FloatRect & rect)865 LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { }
operator ()(const FloatRect & rect)866 void operator()(const FloatRect& rect)
867 {
868 m_rect.uniteIfNonZero(rect);
869 }
870 private:
871 FloatRect& m_rect;
872 };
873
874 } // unnamed namespace
875
linesBoundingBox() const876 IntRect RenderInline::linesBoundingBox() const
877 {
878 if (!alwaysCreateLineBoxes()) {
879 ASSERT(!firstLineBox());
880 FloatRect floatResult;
881 LinesBoundingBoxGeneratorContext context(floatResult);
882 generateCulledLineBoxRects(context, this);
883 return enclosingIntRect(floatResult);
884 }
885
886 IntRect result;
887
888 // See <rdar://problem/5289721>, for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been
889 // unable to reproduce this at all (and consequently unable to figure ot why this is happening). The assert will hopefully catch the problem in debug
890 // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
891 ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
892 if (firstLineBox() && lastLineBox()) {
893 // Return the width of the minimal left side and the maximal right side.
894 float logicalLeftSide = 0;
895 float logicalRightSide = 0;
896 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
897 if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
898 logicalLeftSide = curr->logicalLeft();
899 if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
900 logicalRightSide = curr->logicalRight();
901 }
902
903 bool isHorizontal = style()->isHorizontalWritingMode();
904
905 float x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
906 float y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
907 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
908 float height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
909 result = enclosingIntRect(FloatRect(x, y, width, height));
910 }
911
912 return result;
913 }
914
culledInlineFirstLineBox() const915 InlineBox* RenderInline::culledInlineFirstLineBox() const
916 {
917 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
918 if (curr->isFloatingOrOutOfFlowPositioned())
919 continue;
920
921 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
922 // direction (aligned to the root box's baseline).
923 if (curr->isBox())
924 return toRenderBox(curr)->inlineBoxWrapper();
925 if (curr->isRenderInline()) {
926 RenderInline* currInline = toRenderInline(curr);
927 InlineBox* result = currInline->firstLineBoxIncludingCulling();
928 if (result)
929 return result;
930 } else if (curr->isText()) {
931 RenderText* currText = toRenderText(curr);
932 if (currText->firstTextBox())
933 return currText->firstTextBox();
934 }
935 }
936 return 0;
937 }
938
culledInlineLastLineBox() const939 InlineBox* RenderInline::culledInlineLastLineBox() const
940 {
941 for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) {
942 if (curr->isFloatingOrOutOfFlowPositioned())
943 continue;
944
945 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
946 // direction (aligned to the root box's baseline).
947 if (curr->isBox())
948 return toRenderBox(curr)->inlineBoxWrapper();
949 if (curr->isRenderInline()) {
950 RenderInline* currInline = toRenderInline(curr);
951 InlineBox* result = currInline->lastLineBoxIncludingCulling();
952 if (result)
953 return result;
954 } else if (curr->isText()) {
955 RenderText* currText = toRenderText(curr);
956 if (currText->lastTextBox())
957 return currText->lastTextBox();
958 }
959 }
960 return 0;
961 }
962
culledInlineVisualOverflowBoundingBox() const963 LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const
964 {
965 FloatRect floatResult;
966 LinesBoundingBoxGeneratorContext context(floatResult);
967 generateCulledLineBoxRects(context, this);
968 LayoutRect result(enclosingLayoutRect(floatResult));
969 bool isHorizontal = style()->isHorizontalWritingMode();
970 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
971 if (curr->isFloatingOrOutOfFlowPositioned())
972 continue;
973
974 // For overflow we just have to propagate by hand and recompute it all.
975 if (curr->isBox()) {
976 RenderBox* currBox = toRenderBox(curr);
977 if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) {
978 LayoutRect logicalRect = currBox->logicalVisualOverflowRectForPropagation(style());
979 if (isHorizontal) {
980 logicalRect.moveBy(currBox->location());
981 result.uniteIfNonZero(logicalRect);
982 } else {
983 logicalRect.moveBy(currBox->location());
984 result.uniteIfNonZero(logicalRect.transposedRect());
985 }
986 }
987 } else if (curr->isRenderInline()) {
988 // If the child doesn't need line boxes either, then we can recur.
989 RenderInline* currInline = toRenderInline(curr);
990 if (!currInline->alwaysCreateLineBoxes())
991 result.uniteIfNonZero(currInline->culledInlineVisualOverflowBoundingBox());
992 else if (!currInline->hasSelfPaintingLayer())
993 result.uniteIfNonZero(currInline->linesVisualOverflowBoundingBox());
994 } else if (curr->isText()) {
995 // FIXME; Overflow from text boxes is lost. We will need to cache this information in
996 // InlineTextBoxes.
997 RenderText* currText = toRenderText(curr);
998 result.uniteIfNonZero(currText->linesVisualOverflowBoundingBox());
999 }
1000 }
1001 return result;
1002 }
1003
linesVisualOverflowBoundingBox() const1004 LayoutRect RenderInline::linesVisualOverflowBoundingBox() const
1005 {
1006 if (!alwaysCreateLineBoxes())
1007 return culledInlineVisualOverflowBoundingBox();
1008
1009 if (!firstLineBox() || !lastLineBox())
1010 return LayoutRect();
1011
1012 // Return the width of the minimal left side and the maximal right side.
1013 LayoutUnit logicalLeftSide = LayoutUnit::max();
1014 LayoutUnit logicalRightSide = LayoutUnit::min();
1015 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1016 logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow());
1017 logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow());
1018 }
1019
1020 RootInlineBox& firstRootBox = firstLineBox()->root();
1021 RootInlineBox& lastRootBox = lastLineBox()->root();
1022
1023 LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop());
1024 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
1025 LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()) - logicalTop;
1026
1027 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
1028 if (!style()->isHorizontalWritingMode())
1029 rect = rect.transposedRect();
1030 return rect;
1031 }
1032
clippedOverflowRectForPaintInvalidation(const RenderLayerModelObject * paintInvalidationContainer,const PaintInvalidationState * paintInvalidationState) const1033 LayoutRect RenderInline::clippedOverflowRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) const
1034 {
1035 if ((!firstLineBoxIncludingCulling() && !continuation()) || style()->visibility() != VISIBLE)
1036 return LayoutRect();
1037
1038 LayoutRect paintInvalidationRect(linesVisualOverflowBoundingBox());
1039
1040 LayoutUnit outlineSize = style()->outlineSize();
1041 paintInvalidationRect.inflate(outlineSize);
1042
1043 mapRectToPaintInvalidationBacking(paintInvalidationContainer, paintInvalidationRect, paintInvalidationState);
1044
1045 if (outlineSize) {
1046 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1047 if (!curr->isText())
1048 paintInvalidationRect.unite(curr->rectWithOutlineForPaintInvalidation(paintInvalidationContainer, outlineSize));
1049 }
1050
1051 if (continuation() && !continuation()->isInline() && continuation()->parent())
1052 paintInvalidationRect.unite(continuation()->rectWithOutlineForPaintInvalidation(paintInvalidationContainer, outlineSize));
1053 }
1054
1055 return paintInvalidationRect;
1056 }
1057
rectWithOutlineForPaintInvalidation(const RenderLayerModelObject * paintInvalidationContainer,LayoutUnit outlineWidth,const PaintInvalidationState * paintInvalidationState) const1058 LayoutRect RenderInline::rectWithOutlineForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer, LayoutUnit outlineWidth, const PaintInvalidationState* paintInvalidationState) const
1059 {
1060 LayoutRect r(RenderBoxModelObject::rectWithOutlineForPaintInvalidation(paintInvalidationContainer, outlineWidth, paintInvalidationState));
1061 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1062 if (!curr->isText())
1063 r.unite(curr->rectWithOutlineForPaintInvalidation(paintInvalidationContainer, outlineWidth, paintInvalidationState));
1064 }
1065 return r;
1066 }
1067
mapRectToPaintInvalidationBacking(const RenderLayerModelObject * paintInvalidationContainer,LayoutRect & rect,const PaintInvalidationState * paintInvalidationState) const1068 void RenderInline::mapRectToPaintInvalidationBacking(const RenderLayerModelObject* paintInvalidationContainer, LayoutRect& rect, const PaintInvalidationState* paintInvalidationState) const
1069 {
1070 if (paintInvalidationState && paintInvalidationState->canMapToContainer(paintInvalidationContainer)) {
1071 if (style()->hasInFlowPosition() && layer())
1072 rect.move(layer()->offsetForInFlowPosition());
1073 rect.move(paintInvalidationState->paintOffset());
1074 if (paintInvalidationState->isClipped())
1075 rect.intersect(paintInvalidationState->clipRect());
1076 return;
1077 }
1078
1079 if (paintInvalidationContainer == this)
1080 return;
1081
1082 bool containerSkipped;
1083 RenderObject* o = container(paintInvalidationContainer, &containerSkipped);
1084 if (!o)
1085 return;
1086
1087 LayoutPoint topLeft = rect.location();
1088
1089 if (o->isRenderBlockFlow() && !style()->hasOutOfFlowPosition()) {
1090 RenderBlock* cb = toRenderBlock(o);
1091 if (cb->hasColumns()) {
1092 LayoutRect paintInvalidationRect(topLeft, rect.size());
1093 cb->adjustRectForColumns(paintInvalidationRect);
1094 topLeft = paintInvalidationRect.location();
1095 rect = paintInvalidationRect;
1096 }
1097 }
1098
1099 if (style()->hasInFlowPosition() && layer()) {
1100 // Apply the in-flow position offset when invalidating a rectangle. The layer
1101 // is translated, but the render box isn't, so we need to do this to get the
1102 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
1103 // flag on the RenderObject has been cleared, so use the one on the style().
1104 topLeft += layer()->offsetForInFlowPosition();
1105 }
1106
1107 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1108 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1109 rect.setLocation(topLeft);
1110 if (o->hasOverflowClip()) {
1111 RenderBox* containerBox = toRenderBox(o);
1112 containerBox->applyCachedClipAndScrollOffsetForPaintInvalidation(rect);
1113 if (rect.isEmpty())
1114 return;
1115 }
1116
1117 if (containerSkipped) {
1118 // If the paintInvalidationContainer is below o, then we need to map the rect into paintInvalidationContainer's coordinates.
1119 LayoutSize containerOffset = paintInvalidationContainer->offsetFromAncestorContainer(o);
1120 rect.move(-containerOffset);
1121 return;
1122 }
1123
1124 o->mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, paintInvalidationState);
1125 }
1126
offsetFromContainer(const RenderObject * container,const LayoutPoint & point,bool * offsetDependsOnPoint) const1127 LayoutSize RenderInline::offsetFromContainer(const RenderObject* container, const LayoutPoint& point, bool* offsetDependsOnPoint) const
1128 {
1129 ASSERT(container == this->container());
1130
1131 LayoutSize offset;
1132 if (isRelPositioned())
1133 offset += offsetForInFlowPosition();
1134
1135 offset += container->columnOffset(point);
1136
1137 if (container->hasOverflowClip())
1138 offset -= toRenderBox(container)->scrolledContentOffset();
1139
1140 if (offsetDependsOnPoint) {
1141 *offsetDependsOnPoint = container->hasColumns()
1142 || (container->isBox() && container->style()->isFlippedBlocksWritingMode())
1143 || container->isRenderFlowThread();
1144 }
1145
1146 return offset;
1147 }
1148
mapLocalToContainer(const RenderLayerModelObject * paintInvalidationContainer,TransformState & transformState,MapCoordinatesFlags mode,bool * wasFixed,const PaintInvalidationState * paintInvalidationState) const1149 void RenderInline::mapLocalToContainer(const RenderLayerModelObject* paintInvalidationContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed, const PaintInvalidationState* paintInvalidationState) const
1150 {
1151 if (paintInvalidationContainer == this)
1152 return;
1153
1154 if (paintInvalidationState && paintInvalidationState->canMapToContainer(paintInvalidationContainer)) {
1155 LayoutSize offset = paintInvalidationState->paintOffset();
1156 if (style()->hasInFlowPosition() && layer())
1157 offset += layer()->offsetForInFlowPosition();
1158 transformState.move(offset);
1159 return;
1160 }
1161
1162 bool containerSkipped;
1163 RenderObject* o = container(paintInvalidationContainer, &containerSkipped);
1164 if (!o)
1165 return;
1166
1167 if (mode & ApplyContainerFlip && o->isBox()) {
1168 if (o->style()->isFlippedBlocksWritingMode()) {
1169 IntPoint centerPoint = roundedIntPoint(transformState.mappedPoint());
1170 transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(centerPoint) - centerPoint);
1171 }
1172 mode &= ~ApplyContainerFlip;
1173 }
1174
1175 LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint()));
1176
1177 bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D());
1178 if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
1179 TransformationMatrix t;
1180 getTransformFromContainer(o, containerOffset, t);
1181 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1182 } else
1183 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1184
1185 if (containerSkipped) {
1186 // There can't be a transform between paintInvalidationContainer and o, because transforms create containers, so it should be safe
1187 // to just subtract the delta between the paintInvalidationContainer and o.
1188 LayoutSize containerOffset = paintInvalidationContainer->offsetFromAncestorContainer(o);
1189 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1190 return;
1191 }
1192
1193 o->mapLocalToContainer(paintInvalidationContainer, transformState, mode, wasFixed, paintInvalidationState);
1194 }
1195
updateDragState(bool dragOn)1196 void RenderInline::updateDragState(bool dragOn)
1197 {
1198 RenderBoxModelObject::updateDragState(dragOn);
1199 if (continuation())
1200 continuation()->updateDragState(dragOn);
1201 }
1202
childBecameNonInline(RenderObject * child)1203 void RenderInline::childBecameNonInline(RenderObject* child)
1204 {
1205 // We have to split the parent flow.
1206 RenderBlock* newBox = containingBlock()->createAnonymousBlock();
1207 RenderBoxModelObject* oldContinuation = continuation();
1208 setContinuation(newBox);
1209 RenderObject* beforeChild = child->nextSibling();
1210 children()->removeChildNode(this, child);
1211 splitFlow(beforeChild, newBox, child, oldContinuation);
1212 }
1213
updateHitTestResult(HitTestResult & result,const LayoutPoint & point)1214 void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
1215 {
1216 if (result.innerNode())
1217 return;
1218
1219 Node* n = node();
1220 LayoutPoint localPoint(point);
1221 if (n) {
1222 if (isInlineElementContinuation()) {
1223 // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space
1224 // of the principal renderer's containing block. This will end up being the innerNonSharedNode.
1225 RenderBlock* firstBlock = n->renderer()->containingBlock();
1226
1227 // Get our containing block.
1228 RenderBox* block = containingBlock();
1229 localPoint.moveBy(block->location() - firstBlock->locationOffset());
1230 }
1231
1232 result.setInnerNode(n);
1233 if (!result.innerNonSharedNode())
1234 result.setInnerNonSharedNode(n);
1235 result.setLocalPoint(localPoint);
1236 }
1237 }
1238
dirtyLineBoxes(bool fullLayout)1239 void RenderInline::dirtyLineBoxes(bool fullLayout)
1240 {
1241 if (fullLayout) {
1242 m_lineBoxes.deleteLineBoxes();
1243 return;
1244 }
1245
1246 if (!alwaysCreateLineBoxes()) {
1247 // We have to grovel into our children in order to dirty the appropriate lines.
1248 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1249 if (curr->isFloatingOrOutOfFlowPositioned())
1250 continue;
1251 if (curr->isBox() && !curr->needsLayout()) {
1252 RenderBox* currBox = toRenderBox(curr);
1253 if (currBox->inlineBoxWrapper())
1254 currBox->inlineBoxWrapper()->root().markDirty();
1255 } else if (!curr->selfNeedsLayout()) {
1256 if (curr->isRenderInline()) {
1257 RenderInline* currInline = toRenderInline(curr);
1258 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox())
1259 childLine->root().markDirty();
1260 } else if (curr->isText()) {
1261 RenderText* currText = toRenderText(curr);
1262 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox())
1263 childText->root().markDirty();
1264 }
1265 }
1266 }
1267 } else
1268 m_lineBoxes.dirtyLineBoxes();
1269 }
1270
deleteLineBoxTree()1271 void RenderInline::deleteLineBoxTree()
1272 {
1273 m_lineBoxes.deleteLineBoxTree();
1274 }
1275
createInlineFlowBox()1276 InlineFlowBox* RenderInline::createInlineFlowBox()
1277 {
1278 return new InlineFlowBox(*this);
1279 }
1280
createAndAppendInlineFlowBox()1281 InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
1282 {
1283 setAlwaysCreateLineBoxes();
1284 InlineFlowBox* flowBox = createInlineFlowBox();
1285 m_lineBoxes.appendLineBox(flowBox);
1286 return flowBox;
1287 }
1288
lineHeight(bool firstLine,LineDirectionMode,LinePositionMode) const1289 LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
1290 {
1291 if (firstLine && document().styleEngine()->usesFirstLineRules()) {
1292 RenderStyle* s = style(firstLine);
1293 if (s != style())
1294 return s->computedLineHeight();
1295 }
1296
1297 return style()->computedLineHeight();
1298 }
1299
baselinePosition(FontBaseline baselineType,bool firstLine,LineDirectionMode direction,LinePositionMode linePositionMode) const1300 int RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1301 {
1302 ASSERT(linePositionMode == PositionOnContainingLine);
1303 const FontMetrics& fontMetrics = style(firstLine)->fontMetrics();
1304 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
1305 }
1306
offsetForInFlowPositionedInline(const RenderBox & child) const1307 LayoutSize RenderInline::offsetForInFlowPositionedInline(const RenderBox& child) const
1308 {
1309 // FIXME: This function isn't right with mixed writing modes.
1310
1311 ASSERT(isRelPositioned());
1312 if (!isRelPositioned())
1313 return LayoutSize();
1314
1315 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
1316 // box from the rest of the content, but only in the cases where we know we're positioned
1317 // relative to the inline itself.
1318
1319 LayoutSize logicalOffset;
1320 LayoutUnit inlinePosition;
1321 LayoutUnit blockPosition;
1322 if (firstLineBox()) {
1323 inlinePosition = LayoutUnit::fromFloatRound(firstLineBox()->logicalLeft());
1324 blockPosition = firstLineBox()->logicalTop();
1325 } else {
1326 inlinePosition = layer()->staticInlinePosition();
1327 blockPosition = layer()->staticBlockPosition();
1328 }
1329
1330 // Per http://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-width an absolute positioned box
1331 // with a static position should locate itself as though it is a normal flow box in relation to
1332 // its containing block. If this relative-positioned inline has a negative offset we need to
1333 // compensate for it so that we align the positioned object with the edge of its containing block.
1334 if (child.style()->hasStaticInlinePosition(style()->isHorizontalWritingMode()))
1335 logicalOffset.setWidth(std::max(LayoutUnit(), -offsetForInFlowPosition().width()));
1336 else
1337 logicalOffset.setWidth(inlinePosition);
1338
1339 if (!child.style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
1340 logicalOffset.setHeight(blockPosition);
1341
1342 return style()->isHorizontalWritingMode() ? logicalOffset : logicalOffset.transposedSize();
1343 }
1344
imageChanged(WrappedImagePtr,const IntRect *)1345 void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
1346 {
1347 if (!parent())
1348 return;
1349
1350 // FIXME: We can do better.
1351 setShouldDoFullPaintInvalidation(true);
1352 }
1353
1354 namespace {
1355
1356 class AbsoluteLayoutRectsGeneratorContext {
1357 public:
AbsoluteLayoutRectsGeneratorContext(Vector<LayoutRect> & rects,const LayoutPoint & accumulatedOffset)1358 AbsoluteLayoutRectsGeneratorContext(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset)
1359 : m_rects(rects)
1360 , m_accumulatedOffset(accumulatedOffset) { }
1361
operator ()(const FloatRect & rect)1362 void operator()(const FloatRect& rect)
1363 {
1364 LayoutRect layoutRect(rect);
1365 layoutRect.move(m_accumulatedOffset.x(), m_accumulatedOffset.y());
1366 m_rects.append(layoutRect);
1367 }
1368 private:
1369 Vector<LayoutRect>& m_rects;
1370 const LayoutPoint& m_accumulatedOffset;
1371 };
1372
1373 class AbsoluteLayoutRectsIgnoringEmptyRectsGeneratorContext : public AbsoluteLayoutRectsGeneratorContext {
1374 public:
AbsoluteLayoutRectsIgnoringEmptyRectsGeneratorContext(Vector<LayoutRect> & rects,const LayoutPoint & accumulatedOffset)1375 AbsoluteLayoutRectsIgnoringEmptyRectsGeneratorContext(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset)
1376 : AbsoluteLayoutRectsGeneratorContext(rects, accumulatedOffset) { }
1377
operator ()(const FloatRect & rect)1378 void operator()(const FloatRect& rect)
1379 {
1380 if (!rect.isEmpty())
1381 AbsoluteLayoutRectsGeneratorContext::operator()(rect);
1382 }
1383 };
1384
1385 } // unnamed namespace
1386
addFocusRingRects(Vector<LayoutRect> & rects,const LayoutPoint & additionalOffset,const RenderLayerModelObject * paintContainer) const1387 void RenderInline::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) const
1388 {
1389 AbsoluteLayoutRectsIgnoringEmptyRectsGeneratorContext context(rects, additionalOffset);
1390 generateLineBoxRects(context);
1391
1392 addChildFocusRingRects(rects, additionalOffset, paintContainer);
1393
1394 if (continuation()) {
1395 // If the continuation doesn't paint into the same container, let its paint invalidation container handle it.
1396 if (paintContainer != continuation()->containerForPaintInvalidation())
1397 return;
1398 if (continuation()->isInline())
1399 continuation()->addFocusRingRects(rects, additionalOffset + (continuation()->containingBlock()->location() - containingBlock()->location()), paintContainer);
1400 else
1401 continuation()->addFocusRingRects(rects, additionalOffset + (toRenderBox(continuation())->location() - containingBlock()->location()), paintContainer);
1402 }
1403 }
1404
computeSelfHitTestRects(Vector<LayoutRect> & rects,const LayoutPoint & layerOffset) const1405 void RenderInline::computeSelfHitTestRects(Vector<LayoutRect>& rects, const LayoutPoint& layerOffset) const
1406 {
1407 AbsoluteLayoutRectsGeneratorContext context(rects, layerOffset);
1408 generateLineBoxRects(context);
1409 }
1410
addAnnotatedRegions(Vector<AnnotatedRegionValue> & regions)1411 void RenderInline::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions)
1412 {
1413 // Convert the style regions to absolute coordinates.
1414 if (style()->visibility() != VISIBLE)
1415 return;
1416
1417 if (style()->getDraggableRegionMode() == DraggableRegionNone)
1418 return;
1419
1420 AnnotatedRegionValue region;
1421 region.draggable = style()->getDraggableRegionMode() == DraggableRegionDrag;
1422 region.bounds = linesBoundingBox();
1423
1424 RenderObject* container = containingBlock();
1425 if (!container)
1426 container = this;
1427
1428 FloatPoint absPos = container->localToAbsolute();
1429 region.bounds.setX(absPos.x() + region.bounds.x());
1430 region.bounds.setY(absPos.y() + region.bounds.y());
1431
1432 regions.append(region);
1433 }
1434
1435 } // namespace blink
1436