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.
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.
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.
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.
24 #include "RenderInline.h"
27 #include "FloatQuad.h"
28 #include "GraphicsContext.h"
29 #include "HitTestResult.h"
30 #include "InlineTextBox.h"
32 #include "RenderArena.h"
33 #include "RenderBlock.h"
34 #include "RenderFlowThread.h"
35 #include "RenderFullScreen.h"
36 #include "RenderGeometryMap.h"
37 #include "RenderLayer.h"
38 #include "RenderLineBreak.h"
39 #include "RenderTheme.h"
40 #include "RenderView.h"
41 #include "StyleInheritedData.h"
42 #include "TransformState.h"
43 #include "VisiblePosition.h"
45 #if ENABLE(DASHBOARD_SUPPORT) || ENABLE(DRAGGABLE_REGION)
53 RenderInline::RenderInline(Element* element)
54 : RenderBoxModelObject(element, RenderInlineFlag)
55 , m_alwaysCreateLineBoxes(false)
57 setChildrenInline(true);
60 RenderInline* RenderInline::createAnonymous(Document& document)
62 RenderInline* renderer = new (*document.renderArena()) RenderInline(0);
63 renderer->setDocumentForAnonymous(document);
67 void RenderInline::willBeDestroyed()
70 // Make sure we do not retain "this" in the continuation outline table map of our containing blocks.
71 if (parent() && style()->visibility() == VISIBLE && hasOutline()) {
72 bool containingBlockPaintsContinuationOutline = continuation() || isInlineElementContinuation();
73 if (containingBlockPaintsContinuationOutline) {
74 if (RenderBlock* cb = containingBlock()) {
75 if (RenderBlock* cbCb = cb->containingBlock())
76 ASSERT(!cbCb->paintsContinuationOutline(this));
82 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
83 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
84 destroyLeftoverChildren();
86 // Destroy our continuation before anything other than anonymous children.
87 // The reason we don't destroy it before anonymous children is that they may
88 // have continuations of their own that are anonymous children of our continuation.
89 RenderBoxModelObject* continuation = this->continuation();
91 continuation->destroy();
95 if (!documentBeingDestroyed()) {
97 // We can't wait for RenderBoxModelObject::destroy to clear the selection,
98 // because by then we will have nuked the line boxes.
99 // FIXME: The FrameSelection should be responsible for this when it
100 // is notified of DOM mutations.
101 if (isSelectionBorder())
102 view().clearSelection();
104 // If line boxes are contained inside a root, that means we're an inline.
105 // In that case, we need to remove all the line boxes so that the parent
106 // lines aren't pointing to deleted children. If the first line box does
107 // not have a parent that means they are either already disconnected or
108 // root lines that can just be destroyed without disconnecting.
109 if (firstLineBox()->parent()) {
110 for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
114 parent()->dirtyLinesFromChangedChild(this);
117 m_lineBoxes.deleteLineBoxes(renderArena());
119 RenderBoxModelObject::willBeDestroyed();
122 RenderInline* RenderInline::inlineElementContinuation() const
124 RenderBoxModelObject* continuation = this->continuation();
125 if (!continuation || continuation->isInline())
126 return toRenderInline(continuation);
127 return toRenderBlock(continuation)->inlineElementContinuation();
130 void RenderInline::updateFromStyle()
132 RenderBoxModelObject::updateFromStyle();
134 setInline(true); // Needed for run-ins, since run-in is considered a block display type.
136 // FIXME: Support transforms and reflections on inline flows someday.
137 setHasTransform(false);
138 setHasReflection(false);
141 static RenderObject* inFlowPositionedInlineAncestor(RenderObject* p)
143 while (p && p->isRenderInline()) {
144 if (p->isInFlowPositioned())
151 static void updateStyleOfAnonymousBlockContinuations(RenderBlock* block, const RenderStyle* newStyle, const RenderStyle* oldStyle)
153 for (;block && block->isAnonymousBlock(); block = toRenderBlock(block->nextSibling())) {
154 if (!block->isAnonymousBlockContinuation() || block->style()->position() == newStyle->position())
156 // If we are no longer in-flow positioned but our descendant block(s) still have an in-flow positioned ancestor then
157 // their containing anonymous block should keep its in-flow positioning.
158 RenderInline* cont = block->inlineElementContinuation();
159 if (oldStyle->hasInFlowPosition() && inFlowPositionedInlineAncestor(cont))
161 RefPtr<RenderStyle> blockStyle = RenderStyle::createAnonymousStyleWithDisplay(block->style(), BLOCK);
162 blockStyle->setPosition(newStyle->position());
163 block->setStyle(blockStyle);
167 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
169 RenderBoxModelObject::styleDidChange(diff, oldStyle);
171 // Ensure that all of the split inlines pick up the new style. We
172 // only do this if we're an inline, since we don't want to propagate
173 // a block's style to the other inlines.
174 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
175 // and after the block share the same style, but the block doesn't
176 // need to pass its style on to anyone else.
177 RenderStyle* newStyle = style();
178 RenderInline* continuation = inlineElementContinuation();
179 for (RenderInline* currCont = continuation; currCont; currCont = currCont->inlineElementContinuation()) {
180 RenderBoxModelObject* nextCont = currCont->continuation();
181 currCont->setContinuation(0);
182 currCont->setStyle(newStyle);
183 currCont->setContinuation(nextCont);
186 // If an inline's in-flow positioning has changed then any descendant blocks will need to change their in-flow positioning accordingly.
187 // Do this by updating the position of the descendant blocks' containing anonymous blocks - there may be more than one.
188 if (continuation && oldStyle && newStyle->position() != oldStyle->position()
189 && (newStyle->hasInFlowPosition() || oldStyle->hasInFlowPosition())) {
190 // If any descendant blocks exist then they will be in the next anonymous block and its siblings.
191 RenderObject* block = containingBlock()->nextSibling();
192 ASSERT(block && block->isAnonymousBlock());
193 updateStyleOfAnonymousBlockContinuations(toRenderBlock(block), newStyle, oldStyle);
196 if (!m_alwaysCreateLineBoxes) {
197 bool alwaysCreateLineBoxes = hasSelfPaintingLayer() || hasBoxDecorations() || newStyle->hasPadding() || newStyle->hasMargin() || hasOutline();
198 if (oldStyle && alwaysCreateLineBoxes) {
199 dirtyLineBoxes(false);
202 m_alwaysCreateLineBoxes = alwaysCreateLineBoxes;
206 void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout)
208 // Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the
209 // background color will only cause a layout on the first rollover.
210 if (m_alwaysCreateLineBoxes)
213 RenderStyle* parentStyle = parent()->style();
214 RenderInline* parentRenderInline = parent()->isRenderInline() ? toRenderInline(parent()) : 0;
215 bool checkFonts = document().inNoQuirksMode();
216 RenderFlowThread* flowThread = flowThreadContainingBlock();
217 bool alwaysCreateLineBoxes = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes())
218 || (parentRenderInline && parentStyle->verticalAlign() != BASELINE)
219 || style()->verticalAlign() != BASELINE
220 || style()->textEmphasisMark() != TextEmphasisMarkNone
221 || (checkFonts && (!parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->font().fontMetrics())
222 || parentStyle->lineHeight() != style()->lineHeight()))
223 || (flowThread && flowThread->hasRegionsWithStyling());
225 if (!alwaysCreateLineBoxes && checkFonts && document().styleSheetCollection().usesFirstLineRules()) {
226 // Have to check the first line style as well.
227 parentStyle = parent()->firstLineStyle();
228 RenderStyle* childStyle = firstLineStyle();
229 alwaysCreateLineBoxes = !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics())
230 || childStyle->verticalAlign() != BASELINE
231 || parentStyle->lineHeight() != childStyle->lineHeight();
234 if (alwaysCreateLineBoxes) {
236 dirtyLineBoxes(false);
237 m_alwaysCreateLineBoxes = true;
241 LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, int, LayoutUnit* extraWidthToEndOfLine)
244 // This condition is possible if the RenderInline is at an editing boundary,
245 // i.e. the VisiblePosition is:
246 // <RenderInline editingBoundary=true>|<RenderText> </RenderText></RenderInline>
247 // FIXME: need to figure out how to make this return a valid rect, note that
248 // there are no line boxes created in the above case.
252 ASSERT_UNUSED(inlineBox, !inlineBox);
254 if (extraWidthToEndOfLine)
255 *extraWidthToEndOfLine = 0;
257 LayoutRect caretRect = localCaretRectForEmptyElement(borderAndPaddingWidth(), 0);
259 if (InlineBox* firstBox = firstLineBox())
260 caretRect.moveBy(roundedLayoutPoint(firstBox->topLeft()));
265 void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild)
268 return addChildToContinuation(newChild, beforeChild);
269 return addChildIgnoringContinuation(newChild, beforeChild);
272 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
274 if (renderer->isInline() && !renderer->isReplaced())
275 return toRenderInline(renderer)->continuation();
276 return toRenderBlock(renderer)->inlineElementContinuation();
279 RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild)
281 if (beforeChild && beforeChild->parent() == this)
284 RenderBoxModelObject* curr = nextContinuation(this);
285 RenderBoxModelObject* nextToLast = this;
286 RenderBoxModelObject* last = this;
288 if (beforeChild && beforeChild->parent() == curr) {
289 if (curr->firstChild() == beforeChild)
296 curr = nextContinuation(curr);
299 if (!beforeChild && !last->firstChild())
304 void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
306 // Make sure we don't append things after :after-generated content if we have it.
307 if (!beforeChild && isAfterContent(lastChild()))
308 beforeChild = lastChild();
310 if (!newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned()) {
311 // We are placing a block inside an inline. We have to perform a split of this
312 // inline into continuations. This involves creating an anonymous block box to hold
313 // |newChild|. We then make that block box a continuation of this inline. We take all of
314 // the children after |beforeChild| and put them in a clone of this object.
315 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
317 // If inside an inline affected by in-flow positioning the block needs to be affected by it too.
318 // Giving the block a layer like this allows it to collect the x/y offsets from inline parents later.
319 if (RenderObject* positionedAncestor = inFlowPositionedInlineAncestor(this))
320 newStyle->setPosition(positionedAncestor->style()->position());
322 RenderBlock* newBox = RenderBlock::createAnonymous(document());
323 newBox->setStyle(newStyle.release());
324 RenderBoxModelObject* oldContinuation = continuation();
325 setContinuation(newBox);
327 splitFlow(beforeChild, newBox, newChild, oldContinuation);
331 RenderBoxModelObject::addChild(newChild, beforeChild);
333 newChild->setNeedsLayoutAndPrefWidthsRecalc();
336 RenderInline* RenderInline::clone() const
338 RenderInline* cloneInline = new (renderArena()) RenderInline(element());
339 cloneInline->setStyle(style());
340 cloneInline->setFlowThreadState(flowThreadState());
344 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
345 RenderBlock* middleBlock,
346 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
348 // Create a clone of this inline.
349 RenderInline* cloneInline = clone();
350 cloneInline->setContinuation(oldCont);
352 #if ENABLE(FULLSCREEN_API)
353 // If we're splitting the inline containing the fullscreened element,
354 // |beforeChild| may be the renderer for the fullscreened element. However,
355 // that renderer is wrapped in a RenderFullScreen, so |this| is not its
356 // parent. Since the splitting logic expects |this| to be the parent, set
357 // |beforeChild| to be the RenderFullScreen.
358 const Element* fullScreenElement = document().webkitCurrentFullScreenElement();
359 if (fullScreenElement && beforeChild && beforeChild->node() == fullScreenElement)
360 beforeChild = document().fullScreenRenderer();
363 // Now take all of the children from beforeChild to the end and remove
364 // them from |this| and place them in the clone.
365 RenderObject* o = beforeChild;
367 RenderObject* tmp = o;
368 o = tmp->nextSibling();
369 removeChildInternal(tmp, NotifyChildren);
370 cloneInline->addChildIgnoringContinuation(tmp, 0);
371 tmp->setNeedsLayoutAndPrefWidthsRecalc();
374 // Hook |clone| up as the continuation of the middle block.
375 middleBlock->setContinuation(cloneInline);
377 // We have been reparented and are now under the fromBlock. We need
378 // to walk up our inline parent chain until we hit the containing block.
379 // Once we hit the containing block we're done.
380 RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
381 RenderBoxModelObject* currChild = this;
383 // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
384 // There will eventually be a better approach to this problem that will let us nest to a much
385 // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in
386 // incorrect rendering, but the alternative is to hang forever.
387 unsigned splitDepth = 1;
388 const unsigned cMaxSplitDepth = 200;
389 while (curr && curr != fromBlock) {
390 ASSERT(curr->isRenderInline());
391 if (splitDepth < cMaxSplitDepth) {
392 // Create a new clone.
393 RenderInline* cloneChild = cloneInline;
394 cloneInline = toRenderInline(curr)->clone();
396 // Insert our child clone as the first child.
397 cloneInline->addChildIgnoringContinuation(cloneChild, 0);
399 // Hook the clone up as a continuation of |curr|.
400 RenderInline* inlineCurr = toRenderInline(curr);
401 oldCont = inlineCurr->continuation();
402 inlineCurr->setContinuation(cloneInline);
403 cloneInline->setContinuation(oldCont);
405 // Now we need to take all of the children starting from the first child
406 // *after* currChild and append them all to the clone.
407 o = currChild->nextSibling();
409 RenderObject* tmp = o;
410 o = tmp->nextSibling();
411 inlineCurr->removeChildInternal(tmp, NotifyChildren);
412 cloneInline->addChildIgnoringContinuation(tmp, 0);
413 tmp->setNeedsLayoutAndPrefWidthsRecalc();
417 // Keep walking up the chain.
419 curr = toRenderBoxModelObject(curr->parent());
423 // Now we are at the block level. We need to put the clone into the toBlock.
424 toBlock->insertChildInternal(cloneInline, nullptr, NotifyChildren);
426 // Now take all the children after currChild and remove them from the fromBlock
427 // and put them in the toBlock.
428 o = currChild->nextSibling();
430 RenderObject* tmp = o;
431 o = tmp->nextSibling();
432 fromBlock->removeChildInternal(tmp, NotifyChildren);
433 toBlock->insertChildInternal(tmp, nullptr, NotifyChildren);
437 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
438 RenderObject* newChild, RenderBoxModelObject* oldCont)
440 RenderBlock* pre = 0;
441 RenderBlock* block = containingBlock();
443 // Delete our line boxes before we do the inline split into continuations.
444 block->deleteLineBoxTree();
446 bool madeNewBeforeBlock = false;
447 if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
448 // We can reuse this block and make it the preBlock of the next continuation.
450 pre->removePositionedObjects(0);
451 // FIXME-BLOCKFLOW: The enclosing method should likely be switched over
452 // to only work on RenderBlockFlow, in which case this conversion can be
454 if (pre->isRenderBlockFlow())
455 toRenderBlockFlow(pre)->removeFloatingObjects();
456 block = block->containingBlock();
458 // No anonymous block available for use. Make one.
459 pre = block->createAnonymousBlock();
460 madeNewBeforeBlock = true;
463 RenderBlock* post = toRenderBlock(pre->createAnonymousBoxWithSameTypeAs(block));
465 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
466 if (madeNewBeforeBlock)
467 block->insertChildInternal(pre, boxFirst, NotifyChildren);
468 block->insertChildInternal(newBlockBox, boxFirst, NotifyChildren);
469 block->insertChildInternal(post, boxFirst, NotifyChildren);
470 block->setChildrenInline(false);
472 if (madeNewBeforeBlock) {
473 RenderObject* o = boxFirst;
475 RenderObject* no = o;
476 o = no->nextSibling();
477 block->removeChildInternal(no, NotifyChildren);
478 pre->insertChildInternal(no, nullptr, NotifyChildren);
479 no->setNeedsLayoutAndPrefWidthsRecalc();
483 splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
485 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
486 // time in makeChildrenNonInline by just setting this explicitly up front.
487 newBlockBox->setChildrenInline(false);
489 // We delayed adding the newChild until now so that the |newBlockBox| would be fully
490 // connected, thus allowing newChild access to a renderArena should it need
491 // to wrap itself in additional boxes (e.g., table construction).
492 newBlockBox->addChild(newChild);
494 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
495 // get deleted properly. Because objects moves from the pre block into the post block, we want to
496 // make new line boxes instead of leaving the old line boxes around.
497 pre->setNeedsLayoutAndPrefWidthsRecalc();
498 block->setNeedsLayoutAndPrefWidthsRecalc();
499 post->setNeedsLayoutAndPrefWidthsRecalc();
502 void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
504 RenderBoxModelObject* flow = continuationBefore(beforeChild);
505 ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline());
506 RenderBoxModelObject* beforeChildParent = 0;
508 beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
510 RenderBoxModelObject* cont = nextContinuation(flow);
512 beforeChildParent = cont;
514 beforeChildParent = flow;
517 if (newChild->isFloatingOrOutOfFlowPositioned())
518 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
520 // A continuation always consists of two potential candidates: an inline or an anonymous
521 // block box holding block children.
522 bool childInline = newChild->isInline();
523 bool bcpInline = beforeChildParent->isInline();
524 bool flowInline = flow->isInline();
526 if (flow == beforeChildParent)
527 return flow->addChildIgnoringContinuation(newChild, beforeChild);
529 // The goal here is to match up if we can, so that we can coalesce and create the
530 // minimal # of continuations needed for the inline.
531 if (childInline == bcpInline)
532 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
533 else if (flowInline == childInline)
534 return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append.
536 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
540 void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
542 m_lineBoxes.paint(this, paintInfo, paintOffset);
545 template<typename GeneratorContext>
546 void RenderInline::generateLineBoxRects(GeneratorContext& yield) const
548 if (!alwaysCreateLineBoxes())
549 generateCulledLineBoxRects(yield, this);
550 else if (InlineFlowBox* curr = firstLineBox()) {
551 for (; curr; curr = curr->nextLineBox())
552 yield(FloatRect(curr->topLeft(), curr->size()));
557 template<typename GeneratorContext>
558 void RenderInline::generateCulledLineBoxRects(GeneratorContext& yield, const RenderInline* container) const
560 if (!culledInlineFirstLineBox()) {
565 bool isHorizontal = style()->isHorizontalWritingMode();
567 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
568 if (curr->isFloatingOrOutOfFlowPositioned())
571 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
572 // direction (aligned to the root box's baseline).
574 RenderBox* currBox = toRenderBox(curr);
575 if (currBox->inlineBoxWrapper()) {
576 const RootInlineBox& rootBox = currBox->inlineBoxWrapper()->root();
577 const RenderStyle& containerStyle = rootBox.isFirstLine() ? *container->firstLineStyle() : *container->style();
578 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().font().fontMetrics().ascent() - containerStyle.font().fontMetrics().ascent());
579 int logicalHeight = containerStyle.font().fontMetrics().height();
581 yield(FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, currBox->width() + currBox->marginWidth(), logicalHeight));
583 yield(FloatRect(logicalTop, currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, currBox->height() + currBox->marginHeight()));
585 } else if (curr->isRenderInline()) {
586 // If the child doesn't need line boxes either, then we can recur.
587 RenderInline* currInline = toRenderInline(curr);
588 if (!currInline->alwaysCreateLineBoxes())
589 currInline->generateCulledLineBoxRects(yield, container);
591 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
592 const RootInlineBox& rootBox = childLine->root();
593 const RenderStyle& containerStyle = rootBox.isFirstLine() ? *container->firstLineStyle() : *container->style();
594 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().font().fontMetrics().ascent() - containerStyle.font().fontMetrics().ascent());
595 int logicalHeight = containerStyle.fontMetrics().height();
597 yield(FloatRect(childLine->x() - childLine->marginLogicalLeft(),
599 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
602 yield(FloatRect(logicalTop,
603 childLine->y() - childLine->marginLogicalLeft(),
605 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight()));
608 } else if (curr->isText()) {
609 RenderText* currText = toRenderText(curr);
610 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
611 const RootInlineBox& rootBox = childText->root();
612 const RenderStyle& containerStyle = rootBox.isFirstLine() ? *container->firstLineStyle() : *container->style();
613 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().font().fontMetrics().ascent() - containerStyle.font().fontMetrics().ascent());
614 int logicalHeight = containerStyle.font().fontMetrics().height();
616 yield(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight));
618 yield(FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth()));
620 } else if (curr->isLineBreak()) {
621 if (InlineBox* inlineBox = toRenderLineBreak(curr)->inlineBoxWrapper()) {
622 // FIXME: This could use a helper to share these with text path.
623 const RootInlineBox& rootBox = inlineBox->root();
624 const RenderStyle& containerStyle = rootBox.isFirstLine() ? *container->firstLineStyle() : *container->style();
625 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().font().fontMetrics().ascent() - containerStyle.font().fontMetrics().ascent());
626 int logicalHeight = containerStyle.fontMetrics().height();
628 yield(FloatRect(inlineBox->x(), logicalTop, inlineBox->logicalWidth(), logicalHeight));
630 yield(FloatRect(logicalTop, inlineBox->y(), logicalHeight, inlineBox->logicalWidth()));
638 class AbsoluteRectsGeneratorContext {
640 AbsoluteRectsGeneratorContext(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset)
642 , m_accumulatedOffset(accumulatedOffset) { }
644 void operator()(const FloatRect& rect)
646 IntRect intRect = enclosingIntRect(rect);
647 intRect.move(m_accumulatedOffset.x(), m_accumulatedOffset.y());
648 m_rects.append(intRect);
651 Vector<IntRect>& m_rects;
652 const LayoutPoint& m_accumulatedOffset;
655 } // unnamed namespace
657 void RenderInline::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
659 AbsoluteRectsGeneratorContext context(rects, accumulatedOffset);
660 generateLineBoxRects(context);
662 if (RenderBoxModelObject* continuation = this->continuation()) {
663 if (continuation->isBox()) {
664 RenderBox* box = toRenderBox(continuation);
665 continuation->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location() + box->locationOffset()));
667 continuation->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location()));
674 class AbsoluteQuadsGeneratorContext {
676 AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector<FloatQuad>& quads)
680 m_geometryMap.pushMappingsToAncestor(renderer, 0);
683 void operator()(const FloatRect& rect)
685 m_quads.append(m_geometryMap.absoluteRect(rect));
688 Vector<FloatQuad>& m_quads;
689 RenderGeometryMap m_geometryMap;
692 } // unnamed namespace
694 void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
696 AbsoluteQuadsGeneratorContext context(this, quads);
697 generateLineBoxRects(context);
699 if (RenderBoxModelObject* continuation = this->continuation())
700 continuation->absoluteQuads(quads, wasFixed);
703 LayoutUnit RenderInline::offsetLeft() const
706 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
707 topLeft = flooredLayoutPoint(firstBox->topLeft());
708 return adjustedPositionRelativeToOffsetParent(topLeft).x();
711 LayoutUnit RenderInline::offsetTop() const
714 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
715 topLeft = flooredLayoutPoint(firstBox->topLeft());
716 return adjustedPositionRelativeToOffsetParent(topLeft).y();
719 static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin)
723 if (margin.isFixed())
724 return margin.value();
725 if (margin.isPercent())
726 return minimumValueForLength(margin, max<LayoutUnit>(0, renderer->containingBlock()->availableLogicalWidth()));
727 if (margin.isViewportPercentage())
728 return valueForLength(margin, 0, &renderer->view());
732 LayoutUnit RenderInline::marginLeft() const
734 return computeMargin(this, style()->marginLeft());
737 LayoutUnit RenderInline::marginRight() const
739 return computeMargin(this, style()->marginRight());
742 LayoutUnit RenderInline::marginTop() const
744 return computeMargin(this, style()->marginTop());
747 LayoutUnit RenderInline::marginBottom() const
749 return computeMargin(this, style()->marginBottom());
752 LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const
754 return computeMargin(this, style()->marginStartUsing(otherStyle ? otherStyle : style()));
757 LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const
759 return computeMargin(this, style()->marginEndUsing(otherStyle ? otherStyle : style()));
762 LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const
764 return computeMargin(this, style()->marginBeforeUsing(otherStyle ? otherStyle : style()));
767 LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const
769 return computeMargin(this, style()->marginAfterUsing(otherStyle ? otherStyle : style()));
772 const char* RenderInline::renderName() const
774 if (isRelPositioned())
775 return "RenderInline (relative positioned)";
776 if (isStickyPositioned())
777 return "RenderInline (sticky positioned)";
778 // FIXME: Temporary hack while the new generated content system is being implemented.
779 if (isPseudoElement())
780 return "RenderInline (generated)";
782 return "RenderInline (generated)";
783 if (style() && isRunIn())
784 return "RenderInline (run-in)";
785 return "RenderInline";
788 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
789 const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
791 return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction);
796 class HitTestCulledInlinesGeneratorContext {
798 HitTestCulledInlinesGeneratorContext(Region& region, const HitTestLocation& location) : m_intersected(false), m_region(region), m_location(location) { }
799 void operator()(const FloatRect& rect)
801 m_intersected = m_intersected || m_location.intersects(rect);
802 m_region.unite(enclosingIntRect(rect));
804 bool intersected() const { return m_intersected; }
808 const HitTestLocation& m_location;
811 } // unnamed namespace
813 bool RenderInline::hitTestCulledInline(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset)
815 ASSERT(result.isRectBasedTest() && !alwaysCreateLineBoxes());
816 if (!visibleToHitTesting())
819 HitTestLocation tmpLocation(locationInContainer, -toLayoutSize(accumulatedOffset));
822 HitTestCulledInlinesGeneratorContext context(regionResult, tmpLocation);
823 generateCulledLineBoxRects(context, this);
825 if (context.intersected()) {
826 updateHitTestResult(result, tmpLocation.point());
827 // We can not use addNodeToRectBasedTestResult to determine if we fully enclose the hit-test area
828 // because it can only handle rectangular targets.
829 result.addNodeToRectBasedTestResult(element(), request, locationInContainer);
830 return regionResult.contains(tmpLocation.boundingBox());
835 VisiblePosition RenderInline::positionForPoint(const LayoutPoint& point)
837 // FIXME: Does not deal with relative or sticky positioned inlines (should it?)
838 RenderBlock* cb = containingBlock();
839 if (firstLineBox()) {
840 // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We
841 // should try to find a result by asking our containing block.
842 return cb->positionForPoint(point);
845 // Translate the coords from the pre-anonymous block to the post-anonymous block.
846 LayoutPoint parentBlockPoint = cb->location() + point;
847 RenderBoxModelObject* c = continuation();
849 RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c);
850 if (c->isInline() || c->firstChild())
851 return c->positionForPoint(parentBlockPoint - contBlock->locationOffset());
852 c = toRenderBlock(c)->inlineElementContinuation();
855 return RenderBoxModelObject::positionForPoint(point);
860 class LinesBoundingBoxGeneratorContext {
862 LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { }
863 void operator()(const FloatRect& rect)
865 m_rect.uniteIfNonZero(rect);
871 } // unnamed namespace
873 IntRect RenderInline::linesBoundingBox() const
875 if (!alwaysCreateLineBoxes()) {
876 ASSERT(!firstLineBox());
877 FloatRect floatResult;
878 LinesBoundingBoxGeneratorContext context(floatResult);
879 generateCulledLineBoxRects(context, this);
880 return enclosingIntRect(floatResult);
885 // 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
886 // 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
887 // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
888 ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
889 if (firstLineBox() && lastLineBox()) {
890 // Return the width of the minimal left side and the maximal right side.
891 float logicalLeftSide = 0;
892 float logicalRightSide = 0;
893 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
894 if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
895 logicalLeftSide = curr->logicalLeft();
896 if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
897 logicalRightSide = curr->logicalRight();
900 bool isHorizontal = style()->isHorizontalWritingMode();
902 float x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
903 float y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
904 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
905 float height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
906 result = enclosingIntRect(FloatRect(x, y, width, height));
912 InlineBox* RenderInline::culledInlineFirstLineBox() const
914 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
915 if (curr->isFloatingOrOutOfFlowPositioned())
918 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
919 // direction (aligned to the root box's baseline).
921 return toRenderBox(curr)->inlineBoxWrapper();
922 if (curr->isLineBreak()) {
923 RenderLineBreak* renderBR = toRenderLineBreak(curr);
924 if (renderBR->inlineBoxWrapper())
925 return renderBR->inlineBoxWrapper();
926 } else if (curr->isRenderInline()) {
927 RenderInline* currInline = toRenderInline(curr);
928 InlineBox* result = currInline->firstLineBoxIncludingCulling();
931 } else if (curr->isText()) {
932 RenderText* currText = toRenderText(curr);
933 if (currText->firstTextBox())
934 return currText->firstTextBox();
940 InlineBox* RenderInline::culledInlineLastLineBox() const
942 for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) {
943 if (curr->isFloatingOrOutOfFlowPositioned())
946 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
947 // direction (aligned to the root box's baseline).
949 return toRenderBox(curr)->inlineBoxWrapper();
950 if (curr->isLineBreak()) {
951 RenderLineBreak* renderBR = toRenderLineBreak(curr);
952 if (renderBR->inlineBoxWrapper())
953 return renderBR->inlineBoxWrapper();
954 } else if (curr->isRenderInline()) {
955 RenderInline* currInline = toRenderInline(curr);
956 InlineBox* result = currInline->lastLineBoxIncludingCulling();
959 } else if (curr->isText()) {
960 RenderText* currText = toRenderText(curr);
961 if (currText->lastTextBox())
962 return currText->lastTextBox();
968 LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const
970 FloatRect floatResult;
971 LinesBoundingBoxGeneratorContext context(floatResult);
972 generateCulledLineBoxRects(context, this);
973 LayoutRect result(enclosingLayoutRect(floatResult));
974 bool isHorizontal = style()->isHorizontalWritingMode();
975 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
976 if (curr->isFloatingOrOutOfFlowPositioned())
979 // For overflow we just have to propagate by hand and recompute it all.
981 RenderBox* currBox = toRenderBox(curr);
982 if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) {
983 LayoutRect logicalRect = currBox->logicalVisualOverflowRectForPropagation(style());
985 logicalRect.moveBy(currBox->location());
986 result.uniteIfNonZero(logicalRect);
988 logicalRect.moveBy(currBox->location());
989 result.uniteIfNonZero(logicalRect.transposedRect());
992 } else if (curr->isRenderInline()) {
993 // If the child doesn't need line boxes either, then we can recur.
994 RenderInline* currInline = toRenderInline(curr);
995 if (!currInline->alwaysCreateLineBoxes())
996 result.uniteIfNonZero(currInline->culledInlineVisualOverflowBoundingBox());
997 else if (!currInline->hasSelfPaintingLayer())
998 result.uniteIfNonZero(currInline->linesVisualOverflowBoundingBox());
999 } else if (curr->isText()) {
1000 // FIXME; Overflow from text boxes is lost. We will need to cache this information in
1002 RenderText* currText = toRenderText(curr);
1003 result.uniteIfNonZero(currText->linesVisualOverflowBoundingBox());
1009 LayoutRect RenderInline::linesVisualOverflowBoundingBox() const
1011 if (!alwaysCreateLineBoxes())
1012 return culledInlineVisualOverflowBoundingBox();
1014 if (!firstLineBox() || !lastLineBox())
1015 return LayoutRect();
1017 // Return the width of the minimal left side and the maximal right side.
1018 LayoutUnit logicalLeftSide = LayoutUnit::max();
1019 LayoutUnit logicalRightSide = LayoutUnit::min();
1020 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1021 logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow());
1022 logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow());
1025 const RootInlineBox& firstRootBox = firstLineBox()->root();
1026 const RootInlineBox& lastRootBox = lastLineBox()->root();
1028 LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop());
1029 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
1030 LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()) - logicalTop;
1032 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
1033 if (!style()->isHorizontalWritingMode())
1034 rect = rect.transposedRect();
1038 LayoutRect RenderInline::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
1040 // Only run-ins and first-letter renderers are allowed in here during layout. They mutate the tree triggering repaints.
1041 ASSERT(!view().layoutStateEnabled() || isRunIn() || style()->styleType() == FIRST_LETTER);
1043 if (!firstLineBoxIncludingCulling() && !continuation())
1044 return LayoutRect();
1046 LayoutRect repaintRect(linesVisualOverflowBoundingBox());
1047 bool hitRepaintContainer = false;
1049 // We need to add in the in-flow position offsets of any inlines (including us) up to our
1050 // containing block.
1051 RenderBlock* cb = containingBlock();
1052 for (const RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb;
1053 inlineFlow = inlineFlow->parent()) {
1054 if (inlineFlow == repaintContainer) {
1055 hitRepaintContainer = true;
1058 if (inlineFlow->style()->hasInFlowPosition() && inlineFlow->hasLayer())
1059 repaintRect.move(toRenderInline(inlineFlow)->layer()->offsetForInFlowPosition());
1062 LayoutUnit outlineSize = style()->outlineSize();
1063 repaintRect.inflate(outlineSize);
1065 if (hitRepaintContainer || !cb)
1068 if (cb->hasColumns())
1069 cb->adjustRectForColumns(repaintRect);
1071 if (cb->hasOverflowClip())
1072 cb->applyCachedClipAndScrollOffsetForRepaint(repaintRect);
1074 cb->computeRectForRepaint(repaintContainer, repaintRect);
1077 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1078 if (!curr->isText())
1079 repaintRect.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineSize));
1082 if (RenderBoxModelObject* continuation = this->continuation()) {
1083 if (!continuation->isInline() && continuation->parent())
1084 repaintRect.unite(continuation->rectWithOutlineForRepaint(repaintContainer, outlineSize));
1091 LayoutRect RenderInline::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
1093 LayoutRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
1094 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1095 if (!curr->isText())
1096 r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth));
1101 void RenderInline::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const
1103 // LayoutState is only valid for root-relative repainting
1104 if (view().layoutStateEnabled() && !repaintContainer) {
1105 LayoutState* layoutState = view().layoutState();
1106 if (style()->hasInFlowPosition() && layer())
1107 rect.move(layer()->offsetForInFlowPosition());
1108 rect.move(layoutState->m_paintOffset);
1109 if (layoutState->m_clipped)
1110 rect.intersect(layoutState->m_clipRect);
1114 if (repaintContainer == this)
1117 bool containerSkipped;
1118 RenderElement* o = container(repaintContainer, &containerSkipped);
1122 LayoutPoint topLeft = rect.location();
1124 if (o->isRenderBlockFlow() && !style()->hasOutOfFlowPosition()) {
1125 RenderBlock* cb = toRenderBlock(o);
1126 if (cb->hasColumns()) {
1127 LayoutRect repaintRect(topLeft, rect.size());
1128 cb->adjustRectForColumns(repaintRect);
1129 topLeft = repaintRect.location();
1134 if (style()->hasInFlowPosition() && layer()) {
1135 // Apply the in-flow position offset when invalidating a rectangle. The layer
1136 // is translated, but the render box isn't, so we need to do this to get the
1137 // right dirty rect. Since this is called from RenderObject::setStyle, the relative or sticky position
1138 // flag on the RenderObject has been cleared, so use the one on the style().
1139 topLeft += layer()->offsetForInFlowPosition();
1142 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1143 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1144 rect.setLocation(topLeft);
1145 if (o->hasOverflowClip()) {
1146 RenderBox* containerBox = toRenderBox(o);
1147 containerBox->applyCachedClipAndScrollOffsetForRepaint(rect);
1152 if (containerSkipped) {
1153 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
1154 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1155 rect.move(-containerOffset);
1159 o->computeRectForRepaint(repaintContainer, rect, fixed);
1162 LayoutSize RenderInline::offsetFromContainer(RenderObject* container, const LayoutPoint& point, bool* offsetDependsOnPoint) const
1164 ASSERT(container == this->container());
1167 if (isInFlowPositioned())
1168 offset += offsetForInFlowPosition();
1170 container->adjustForColumns(offset, point);
1172 if (container->hasOverflowClip())
1173 offset -= toRenderBox(container)->scrolledContentOffset();
1175 if (offsetDependsOnPoint)
1176 *offsetDependsOnPoint = container->hasColumns()
1177 || (container->isBox() && container->style()->isFlippedBlocksWritingMode())
1178 || container->isRenderFlowThread();
1183 void RenderInline::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const
1185 if (repaintContainer == this)
1188 if (view().layoutStateEnabled() && !repaintContainer) {
1189 LayoutState* layoutState = view().layoutState();
1190 LayoutSize offset = layoutState->m_paintOffset;
1191 if (style()->hasInFlowPosition() && layer())
1192 offset += layer()->offsetForInFlowPosition();
1193 transformState.move(offset);
1197 bool containerSkipped;
1198 RenderElement* o = container(repaintContainer, &containerSkipped);
1202 if (mode & ApplyContainerFlip && o->isBox()) {
1203 if (o->style()->isFlippedBlocksWritingMode()) {
1204 IntPoint centerPoint = roundedIntPoint(transformState.mappedPoint());
1205 transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(centerPoint) - centerPoint);
1207 mode &= ~ApplyContainerFlip;
1210 LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint()));
1212 bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D());
1213 if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
1214 TransformationMatrix t;
1215 getTransformFromContainer(o, containerOffset, t);
1216 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1218 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1220 if (containerSkipped) {
1221 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1222 // to just subtract the delta between the repaintContainer and o.
1223 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1224 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1228 o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
1231 const RenderObject* RenderInline::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
1233 ASSERT(ancestorToStopAt != this);
1235 bool ancestorSkipped;
1236 RenderElement* container = this->container(ancestorToStopAt, &ancestorSkipped);
1240 LayoutSize adjustmentForSkippedAncestor;
1241 if (ancestorSkipped) {
1242 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1243 // to just subtract the delta between the ancestor and o.
1244 adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(container);
1247 bool offsetDependsOnPoint = false;
1248 LayoutSize containerOffset = offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint);
1250 bool preserve3D = container->style()->preserves3D() || style()->preserves3D();
1251 if (shouldUseTransformFromContainer(container)) {
1252 TransformationMatrix t;
1253 getTransformFromContainer(container, containerOffset, t);
1254 t.translateRight(adjustmentForSkippedAncestor.width(), adjustmentForSkippedAncestor.height()); // FIXME: right?
1255 geometryMap.push(this, t, preserve3D, offsetDependsOnPoint);
1257 containerOffset += adjustmentForSkippedAncestor;
1258 geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint);
1261 return ancestorSkipped ? ancestorToStopAt : container;
1264 void RenderInline::updateDragState(bool dragOn)
1266 RenderBoxModelObject::updateDragState(dragOn);
1267 if (RenderBoxModelObject* continuation = this->continuation())
1268 continuation->updateDragState(dragOn);
1271 void RenderInline::childBecameNonInline(RenderObject* child)
1273 // We have to split the parent flow.
1274 RenderBlock* newBox = containingBlock()->createAnonymousBlock();
1275 RenderBoxModelObject* oldContinuation = continuation();
1276 setContinuation(newBox);
1277 RenderObject* beforeChild = child->nextSibling();
1278 removeChildInternal(child, NotifyChildren);
1279 splitFlow(beforeChild, newBox, child, oldContinuation);
1282 void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
1284 if (result.innerNode())
1287 LayoutPoint localPoint(point);
1288 if (Element* element = this->element()) {
1289 if (isInlineElementContinuation()) {
1290 // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space
1291 // of the principal renderer's containing block. This will end up being the innerNonSharedNode.
1292 RenderBlock* firstBlock = element->renderer()->containingBlock();
1294 // Get our containing block.
1295 RenderBox* block = containingBlock();
1296 localPoint.moveBy(block->location() - firstBlock->locationOffset());
1299 result.setInnerNode(element);
1300 if (!result.innerNonSharedNode())
1301 result.setInnerNonSharedNode(element);
1302 result.setLocalPoint(localPoint);
1306 void RenderInline::dirtyLineBoxes(bool fullLayout)
1309 m_lineBoxes.deleteLineBoxes(renderArena());
1313 if (!alwaysCreateLineBoxes()) {
1314 // We have to grovel into our children in order to dirty the appropriate lines.
1315 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1316 if (curr->isFloatingOrOutOfFlowPositioned())
1318 if (curr->isBox() && !curr->needsLayout()) {
1319 RenderBox* currBox = toRenderBox(curr);
1320 if (currBox->inlineBoxWrapper())
1321 currBox->inlineBoxWrapper()->root().markDirty();
1322 } else if (!curr->selfNeedsLayout()) {
1323 if (curr->isRenderInline()) {
1324 RenderInline* currInline = toRenderInline(curr);
1325 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox())
1326 childLine->root().markDirty();
1327 } else if (curr->isText()) {
1328 RenderText* currText = toRenderText(curr);
1329 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox())
1330 childText->root().markDirty();
1331 } else if (curr->isLineBreak()) {
1332 RenderLineBreak* currBR = toRenderLineBreak(curr);
1333 if (currBR->inlineBoxWrapper())
1334 currBR->inlineBoxWrapper()->root().markDirty();
1339 m_lineBoxes.dirtyLineBoxes();
1342 void RenderInline::deleteLineBoxTree()
1344 m_lineBoxes.deleteLineBoxTree(renderArena());
1347 InlineFlowBox* RenderInline::createInlineFlowBox()
1349 return new (renderArena()) InlineFlowBox(*this);
1352 InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
1354 setAlwaysCreateLineBoxes();
1355 InlineFlowBox* flowBox = createInlineFlowBox();
1356 m_lineBoxes.appendLineBox(flowBox);
1360 LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
1362 if (firstLine && document().styleSheetCollection().usesFirstLineRules()) {
1363 const RenderStyle& firstLineStyle = *this->firstLineStyle();
1364 if (&firstLineStyle != style())
1365 return firstLineStyle.computedLineHeight(&view());
1368 return style()->computedLineHeight(&view());
1371 int RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1373 const RenderStyle& style = firstLine ? *firstLineStyle() : *this->style();
1374 const FontMetrics& fontMetrics = style.fontMetrics();
1375 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
1378 LayoutSize RenderInline::offsetForInFlowPositionedInline(const RenderBox* child) const
1380 // FIXME: This function isn't right with mixed writing modes.
1382 ASSERT(isInFlowPositioned());
1383 if (!isInFlowPositioned())
1384 return LayoutSize();
1386 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
1387 // box from the rest of the content, but only in the cases where we know we're positioned
1388 // relative to the inline itself.
1390 LayoutSize logicalOffset;
1391 LayoutUnit inlinePosition;
1392 LayoutUnit blockPosition;
1393 if (firstLineBox()) {
1394 inlinePosition = roundedLayoutUnit(firstLineBox()->logicalLeft());
1395 blockPosition = firstLineBox()->logicalTop();
1397 inlinePosition = layer()->staticInlinePosition();
1398 blockPosition = layer()->staticBlockPosition();
1401 if (!child->style()->hasStaticInlinePosition(style()->isHorizontalWritingMode()))
1402 logicalOffset.setWidth(inlinePosition);
1404 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
1405 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
1406 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
1408 else if (!child->style()->isOriginalDisplayInlineType())
1409 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
1410 logicalOffset.setWidth(inlinePosition - child->containingBlock()->borderAndPaddingLogicalLeft());
1412 if (!child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
1413 logicalOffset.setHeight(blockPosition);
1415 return style()->isHorizontalWritingMode() ? logicalOffset : logicalOffset.transposedSize();
1418 void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
1423 // FIXME: We can do better.
1427 void RenderInline::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
1429 AbsoluteRectsGeneratorContext context(rects, additionalOffset);
1430 generateLineBoxRects(context);
1432 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1433 if (!curr->isText() && !curr->isListMarker()) {
1434 FloatPoint pos(additionalOffset);
1435 // FIXME: This doesn't work correctly with transforms.
1436 if (curr->hasLayer())
1437 pos = curr->localToContainerPoint(FloatPoint(), paintContainer);
1438 else if (curr->isBox())
1439 pos.move(toRenderBox(curr)->locationOffset());
1440 curr->addFocusRingRects(rects, flooredIntPoint(pos), paintContainer);
1444 if (RenderBoxModelObject* continuation = this->continuation()) {
1445 if (continuation->isInline())
1446 continuation->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + continuation->containingBlock()->location() - containingBlock()->location()), paintContainer);
1448 continuation->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + toRenderBox(continuation)->location() - containingBlock()->location()), paintContainer);
1452 void RenderInline::paintOutline(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1457 RenderStyle* styleToUse = style();
1458 if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) {
1459 if (!theme()->supportsFocusRing(styleToUse)) {
1460 // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
1461 paintFocusRing(paintInfo, paintOffset, styleToUse);
1465 GraphicsContext* graphicsContext = paintInfo.context;
1466 if (graphicsContext->paintingDisabled())
1469 if (styleToUse->outlineStyleIsAuto() || styleToUse->outlineStyle() == BNONE)
1472 Vector<LayoutRect> rects;
1474 rects.append(LayoutRect());
1475 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1476 const RootInlineBox& rootBox = curr->root();
1477 LayoutUnit top = max<LayoutUnit>(rootBox.lineTop(), curr->logicalTop());
1478 LayoutUnit bottom = min<LayoutUnit>(rootBox.lineBottom(), curr->logicalBottom());
1479 rects.append(LayoutRect(curr->x(), top, curr->logicalWidth(), bottom - top));
1481 rects.append(LayoutRect());
1483 Color outlineColor = styleToUse->visitedDependentColor(CSSPropertyOutlineColor);
1484 bool useTransparencyLayer = outlineColor.hasAlpha();
1485 if (useTransparencyLayer) {
1486 graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255);
1487 outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
1490 for (unsigned i = 1; i < rects.size() - 1; i++)
1491 paintOutlineForLine(graphicsContext, paintOffset, rects.at(i - 1), rects.at(i), rects.at(i + 1), outlineColor);
1493 if (useTransparencyLayer)
1494 graphicsContext->endTransparencyLayer();
1497 void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset,
1498 const LayoutRect& lastline, const LayoutRect& thisline, const LayoutRect& nextline,
1499 const Color outlineColor)
1501 RenderStyle* styleToUse = style();
1502 int outlineWidth = styleToUse->outlineWidth();
1503 EBorderStyle outlineStyle = styleToUse->outlineStyle();
1505 bool antialias = shouldAntialiasLines(graphicsContext);
1507 int offset = style()->outlineOffset();
1509 LayoutRect box(LayoutPoint(paintOffset.x() + thisline.x() - offset, paintOffset.y() + thisline.y() - offset),
1510 LayoutSize(thisline.width() + offset, thisline.height() + offset));
1512 IntRect pixelSnappedBox = pixelSnappedIntRect(box);
1513 if (pixelSnappedBox.isEmpty())
1515 IntRect pixelSnappedLastLine = pixelSnappedIntRect(paintOffset.x() + lastline.x(), 0, lastline.width(), 0);
1516 IntRect pixelSnappedNextLine = pixelSnappedIntRect(paintOffset.x() + nextline.x(), 0, nextline.width(), 0);
1519 drawLineForBoxSide(graphicsContext,
1520 pixelSnappedBox.x() - outlineWidth,
1521 pixelSnappedBox.y() - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : 0),
1522 pixelSnappedBox.x(),
1523 pixelSnappedBox.maxY() + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : 0),
1525 outlineColor, outlineStyle,
1526 (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth),
1527 (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth),
1531 drawLineForBoxSide(graphicsContext,
1532 pixelSnappedBox.maxX(),
1533 pixelSnappedBox.y() - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : 0),
1534 pixelSnappedBox.maxX() + outlineWidth,
1535 pixelSnappedBox.maxY() + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : 0),
1537 outlineColor, outlineStyle,
1538 (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : -outlineWidth),
1539 (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : -outlineWidth),
1542 if (thisline.x() < lastline.x())
1543 drawLineForBoxSide(graphicsContext,
1544 pixelSnappedBox.x() - outlineWidth,
1545 pixelSnappedBox.y() - outlineWidth,
1546 min(pixelSnappedBox.maxX() + outlineWidth, (lastline.isEmpty() ? 1000000 : pixelSnappedLastLine.x())),
1547 pixelSnappedBox.y(),
1548 BSTop, outlineColor, outlineStyle,
1550 (!lastline.isEmpty() && paintOffset.x() + lastline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth,
1553 if (lastline.maxX() < thisline.maxX())
1554 drawLineForBoxSide(graphicsContext,
1555 max(lastline.isEmpty() ? -1000000 : pixelSnappedLastLine.maxX(), pixelSnappedBox.x() - outlineWidth),
1556 pixelSnappedBox.y() - outlineWidth,
1557 pixelSnappedBox.maxX() + outlineWidth,
1558 pixelSnappedBox.y(),
1559 BSTop, outlineColor, outlineStyle,
1560 (!lastline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + lastline.maxX()) ? -outlineWidth : outlineWidth,
1561 outlineWidth, antialias);
1563 if (thisline.x() == thisline.maxX())
1564 drawLineForBoxSide(graphicsContext,
1565 pixelSnappedBox.x() - outlineWidth,
1566 pixelSnappedBox.y() - outlineWidth,
1567 pixelSnappedBox.maxX() + outlineWidth,
1568 pixelSnappedBox.y(),
1569 BSTop, outlineColor, outlineStyle,
1575 if (thisline.x() < nextline.x())
1576 drawLineForBoxSide(graphicsContext,
1577 pixelSnappedBox.x() - outlineWidth,
1578 pixelSnappedBox.maxY(),
1579 min(pixelSnappedBox.maxX() + outlineWidth, !nextline.isEmpty() ? pixelSnappedNextLine.x() + 1 : 1000000),
1580 pixelSnappedBox.maxY() + outlineWidth,
1581 BSBottom, outlineColor, outlineStyle,
1583 (!nextline.isEmpty() && paintOffset.x() + nextline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth,
1586 if (nextline.maxX() < thisline.maxX())
1587 drawLineForBoxSide(graphicsContext,
1588 max(!nextline.isEmpty() ? pixelSnappedNextLine.maxX() : -1000000, pixelSnappedBox.x() - outlineWidth),
1589 pixelSnappedBox.maxY(),
1590 pixelSnappedBox.maxX() + outlineWidth,
1591 pixelSnappedBox.maxY() + outlineWidth,
1592 BSBottom, outlineColor, outlineStyle,
1593 (!nextline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + nextline.maxX()) ? -outlineWidth : outlineWidth,
1594 outlineWidth, antialias);
1596 if (thisline.x() == thisline.maxX())
1597 drawLineForBoxSide(graphicsContext,
1598 pixelSnappedBox.x() - outlineWidth,
1599 pixelSnappedBox.maxY(),
1600 pixelSnappedBox.maxX() + outlineWidth,
1601 pixelSnappedBox.maxY() + outlineWidth,
1602 BSBottom, outlineColor, outlineStyle,
1608 #if ENABLE(DASHBOARD_SUPPORT) || ENABLE(DRAGGABLE_REGION)
1609 void RenderInline::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions)
1611 // Convert the style regions to absolute coordinates.
1612 if (style()->visibility() != VISIBLE)
1615 #if ENABLE(DASHBOARD_SUPPORT)
1616 const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions();
1617 unsigned i, count = styleRegions.size();
1618 for (i = 0; i < count; i++) {
1619 StyleDashboardRegion styleRegion = styleRegions[i];
1621 LayoutRect linesBoundingBox = this->linesBoundingBox();
1622 LayoutUnit w = linesBoundingBox.width();
1623 LayoutUnit h = linesBoundingBox.height();
1625 AnnotatedRegionValue region;
1626 region.label = styleRegion.label;
1627 region.bounds = LayoutRect(linesBoundingBox.x() + styleRegion.offset.left().value(),
1628 linesBoundingBox.y() + styleRegion.offset.top().value(),
1629 w - styleRegion.offset.left().value() - styleRegion.offset.right().value(),
1630 h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value());
1631 region.type = styleRegion.type;
1633 RenderObject* container = containingBlock();
1637 region.clip = region.bounds;
1638 container->computeAbsoluteRepaintRect(region.clip);
1639 if (region.clip.height() < 0) {
1640 region.clip.setHeight(0);
1641 region.clip.setWidth(0);
1644 FloatPoint absPos = container->localToAbsolute();
1645 region.bounds.setX(absPos.x() + region.bounds.x());
1646 region.bounds.setY(absPos.y() + region.bounds.y());
1648 regions.append(region);
1650 #else // ENABLE(DRAGGABLE_REGION)
1651 if (style()->getDraggableRegionMode() == DraggableRegionNone)
1654 AnnotatedRegionValue region;
1655 region.draggable = style()->getDraggableRegionMode() == DraggableRegionDrag;
1656 region.bounds = linesBoundingBox();
1658 RenderObject* container = containingBlock();
1662 FloatPoint absPos = container->localToAbsolute();
1663 region.bounds.setX(absPos.x() + region.bounds.x());
1664 region.bounds.setY(absPos.y() + region.bounds.y());
1666 regions.append(region);
1671 } // namespace WebCore