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 "RenderGeometryMap.h"
36 #include "RenderLayer.h"
37 #include "RenderTheme.h"
38 #include "RenderView.h"
39 #include "TransformState.h"
40 #include "VisiblePosition.h"
42 #if ENABLE(DASHBOARD_SUPPORT)
50 RenderInline::RenderInline(Node* node)
51 : RenderBoxModelObject(node)
52 , m_alwaysCreateLineBoxes(false)
54 setChildrenInline(true);
57 void RenderInline::willBeDestroyed()
60 // Make sure we do not retain "this" in the continuation outline table map of our containing blocks.
61 if (parent() && style()->visibility() == VISIBLE && hasOutline()) {
62 bool containingBlockPaintsContinuationOutline = continuation() || isInlineElementContinuation();
63 if (containingBlockPaintsContinuationOutline) {
64 if (RenderBlock* cb = containingBlock()) {
65 if (RenderBlock* cbCb = cb->containingBlock())
66 ASSERT(!cbCb->paintsContinuationOutline(this));
72 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
73 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
74 children()->destroyLeftoverChildren();
76 // Destroy our continuation before anything other than anonymous children.
77 // The reason we don't destroy it before anonymous children is that they may
78 // have continuations of their own that are anonymous children of our continuation.
79 RenderBoxModelObject* continuation = this->continuation();
81 continuation->destroy();
85 if (!documentBeingDestroyed()) {
87 // We can't wait for RenderBoxModelObject::destroy to clear the selection,
88 // because by then we will have nuked the line boxes.
89 // FIXME: The FrameSelection should be responsible for this when it
90 // is notified of DOM mutations.
91 if (isSelectionBorder())
92 view()->clearSelection();
94 // If line boxes are contained inside a root, that means we're an inline.
95 // In that case, we need to remove all the line boxes so that the parent
96 // lines aren't pointing to deleted children. If the first line box does
97 // not have a parent that means they are either already disconnected or
98 // root lines that can just be destroyed without disconnecting.
99 if (firstLineBox()->parent()) {
100 for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
104 parent()->dirtyLinesFromChangedChild(this);
107 m_lineBoxes.deleteLineBoxes(renderArena());
109 RenderBoxModelObject::willBeDestroyed();
112 RenderInline* RenderInline::inlineElementContinuation() const
114 RenderBoxModelObject* continuation = this->continuation();
115 if (!continuation || continuation->isInline())
116 return toRenderInline(continuation);
117 return toRenderBlock(continuation)->inlineElementContinuation();
120 void RenderInline::updateBoxModelInfoFromStyle()
122 RenderBoxModelObject::updateBoxModelInfoFromStyle();
124 setInline(true); // Needed for run-ins, since run-in is considered a block display type.
126 // FIXME: Support transforms and reflections on inline flows someday.
127 setHasTransform(false);
128 setHasReflection(false);
131 static bool hasRelPositionedInlineAncestor(RenderObject* p)
133 while (p && p->isRenderInline()) {
134 if (p->isRelPositioned())
141 static void updateStyleOfAnonymousBlockContinuations(RenderObject* block, const RenderStyle* newStyle, const RenderStyle* oldStyle)
143 for (;block && block->isAnonymousBlock(); block = block->nextSibling()) {
144 if (!toRenderBlock(block)->isAnonymousBlockContinuation() || block->style()->position() == newStyle->position())
146 // If we are no longer relatively positioned but our descendant block(s) still have a relatively positioned ancestor then
147 // their containing anonymous block should keep its relative positioning.
148 RenderInline* cont = toRenderBlock(block)->inlineElementContinuation();
149 if (oldStyle->position() == RelativePosition && hasRelPositionedInlineAncestor(cont))
151 RefPtr<RenderStyle> blockStyle = RenderStyle::createAnonymousStyleWithDisplay(block->style(), BLOCK);
152 blockStyle->setPosition(newStyle->position());
153 block->setStyle(blockStyle);
157 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
159 RenderBoxModelObject::styleDidChange(diff, oldStyle);
161 // Ensure that all of the split inlines pick up the new style. We
162 // only do this if we're an inline, since we don't want to propagate
163 // a block's style to the other inlines.
164 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
165 // and after the block share the same style, but the block doesn't
166 // need to pass its style on to anyone else.
167 RenderStyle* newStyle = style();
168 RenderInline* continuation = inlineElementContinuation();
169 for (RenderInline* currCont = continuation; currCont; currCont = currCont->inlineElementContinuation()) {
170 RenderBoxModelObject* nextCont = currCont->continuation();
171 currCont->setContinuation(0);
172 currCont->setStyle(newStyle);
173 currCont->setContinuation(nextCont);
176 // If an inline's relative positioning has changed then any descendant blocks will need to change their relative positioning accordingly.
177 // Do this by updating the position of the descendant blocks' containing anonymous blocks - there may be more than one.
178 if (continuation && oldStyle && newStyle->position() != oldStyle->position()
179 && (newStyle->position() == RelativePosition || (oldStyle->position() == RelativePosition))) {
180 // If any descendant blocks exist then they will be in the next anonymous block and its siblings.
181 RenderObject* block = containingBlock()->nextSibling();
182 ASSERT(block && block->isAnonymousBlock());
183 updateStyleOfAnonymousBlockContinuations(block, newStyle, oldStyle);
186 if (!m_alwaysCreateLineBoxes) {
187 bool alwaysCreateLineBoxes = hasSelfPaintingLayer() || hasBoxDecorations() || newStyle->hasPadding() || newStyle->hasMargin() || hasOutline();
188 if (oldStyle && alwaysCreateLineBoxes) {
189 dirtyLineBoxes(false);
190 setNeedsLayout(true);
192 m_alwaysCreateLineBoxes = alwaysCreateLineBoxes;
195 // Update pseudos for :before and :after now.
196 if (!isAnonymous() && document()->usesBeforeAfterRules()) {
197 children()->updateBeforeAfterContent(this, BEFORE);
198 children()->updateBeforeAfterContent(this, AFTER);
202 void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout)
204 // Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the
205 // background color will only cause a layout on the first rollover.
206 if (m_alwaysCreateLineBoxes)
209 RenderStyle* parentStyle = parent()->style();
210 RenderInline* parentRenderInline = parent()->isRenderInline() ? toRenderInline(parent()) : 0;
211 bool checkFonts = document()->inNoQuirksMode();
212 bool alwaysCreateLineBoxes = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes())
213 || (parentRenderInline && parentStyle->verticalAlign() != BASELINE)
214 || style()->verticalAlign() != BASELINE
215 || style()->textEmphasisMark() != TextEmphasisMarkNone
216 || (checkFonts && (!parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->font().fontMetrics())
217 || parentStyle->lineHeight() != style()->lineHeight()))
218 || (inRenderFlowThread() && enclosingRenderFlowThread()->hasRegionsWithStyling());
220 if (!alwaysCreateLineBoxes && checkFonts && document()->usesFirstLineRules()) {
221 // Have to check the first line style as well.
222 parentStyle = parent()->style(true);
223 RenderStyle* childStyle = style(true);
224 alwaysCreateLineBoxes = !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics())
225 || childStyle->verticalAlign() != BASELINE
226 || parentStyle->lineHeight() != childStyle->lineHeight();
229 if (alwaysCreateLineBoxes) {
231 dirtyLineBoxes(false);
232 m_alwaysCreateLineBoxes = true;
236 LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, int, LayoutUnit* extraWidthToEndOfLine)
239 // This condition is possible if the RenderInline is at an editing boundary,
240 // i.e. the VisiblePosition is:
241 // <RenderInline editingBoundary=true>|<RenderText> </RenderText></RenderInline>
242 // FIXME: need to figure out how to make this return a valid rect, note that
243 // there are no line boxes created in the above case.
247 ASSERT_UNUSED(inlineBox, !inlineBox);
249 if (extraWidthToEndOfLine)
250 *extraWidthToEndOfLine = 0;
252 LayoutRect caretRect = localCaretRectForEmptyElement(borderAndPaddingWidth(), 0);
254 if (InlineBox* firstBox = firstLineBox())
255 caretRect.moveBy(roundedLayoutPoint(firstBox->topLeft()));
260 void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild)
263 return addChildToContinuation(newChild, beforeChild);
264 return addChildIgnoringContinuation(newChild, beforeChild);
267 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
269 if (renderer->isInline() && !renderer->isReplaced())
270 return toRenderInline(renderer)->continuation();
271 return toRenderBlock(renderer)->inlineElementContinuation();
274 RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild)
276 if (beforeChild && beforeChild->parent() == this)
279 RenderBoxModelObject* curr = nextContinuation(this);
280 RenderBoxModelObject* nextToLast = this;
281 RenderBoxModelObject* last = this;
283 if (beforeChild && beforeChild->parent() == curr) {
284 if (curr->firstChild() == beforeChild)
291 curr = nextContinuation(curr);
294 if (!beforeChild && !last->firstChild())
299 void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
301 // Make sure we don't append things after :after-generated content if we have it.
302 if (!beforeChild && isAfterContent(lastChild()))
303 beforeChild = lastChild();
305 if (!newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned()) {
306 // We are placing a block inside an inline. We have to perform a split of this
307 // inline into continuations. This involves creating an anonymous block box to hold
308 // |newChild|. We then make that block box a continuation of this inline. We take all of
309 // the children after |beforeChild| and put them in a clone of this object.
310 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
312 // If inside an inline affected by relative positioning the block needs to be affected by it too.
313 // Giving the block a layer like this allows it to collect the x/y offsets from inline parents later.
314 if (hasRelPositionedInlineAncestor(this))
315 newStyle->setPosition(RelativePosition);
317 RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
318 newBox->setStyle(newStyle.release());
319 RenderBoxModelObject* oldContinuation = continuation();
320 setContinuation(newBox);
322 // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content
323 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after
324 // content gets properly destroyed.
325 bool isLastChild = (beforeChild == lastChild());
326 if (document()->usesBeforeAfterRules())
327 children()->updateBeforeAfterContent(this, AFTER);
328 if (isLastChild && beforeChild != lastChild())
329 beforeChild = 0; // We destroyed the last child, so now we need to update our insertion
330 // point to be 0. It's just a straight append now.
332 splitFlow(beforeChild, newBox, newChild, oldContinuation);
336 RenderBoxModelObject::addChild(newChild, beforeChild);
338 newChild->setNeedsLayoutAndPrefWidthsRecalc();
341 RenderInline* RenderInline::cloneInline(RenderInline* src)
343 RenderInline* o = new (src->renderArena()) RenderInline(src->node());
344 o->setStyle(src->style());
348 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
349 RenderBlock* middleBlock,
350 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
352 // Create a clone of this inline.
353 RenderInline* clone = cloneInline(this);
354 clone->setContinuation(oldCont);
356 // Now take all of the children from beforeChild to the end and remove
357 // them from |this| and place them in the clone.
358 RenderObject* o = beforeChild;
360 RenderObject* tmp = o;
361 o = tmp->nextSibling();
362 clone->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0);
363 tmp->setNeedsLayoutAndPrefWidthsRecalc();
366 // Hook |clone| up as the continuation of the middle block.
367 middleBlock->setContinuation(clone);
369 // We have been reparented and are now under the fromBlock. We need
370 // to walk up our inline parent chain until we hit the containing block.
371 // Once we hit the containing block we're done.
372 RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
373 RenderBoxModelObject* currChild = this;
375 // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
376 // There will eventually be a better approach to this problem that will let us nest to a much
377 // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in
378 // incorrect rendering, but the alternative is to hang forever.
379 unsigned splitDepth = 1;
380 const unsigned cMaxSplitDepth = 200;
381 while (curr && curr != fromBlock) {
382 ASSERT(curr->isRenderInline());
383 if (splitDepth < cMaxSplitDepth) {
384 // Create a new clone.
385 RenderInline* cloneChild = clone;
386 clone = cloneInline(toRenderInline(curr));
388 // Insert our child clone as the first child.
389 clone->addChildIgnoringContinuation(cloneChild, 0);
391 // Hook the clone up as a continuation of |curr|.
392 RenderInline* inlineCurr = toRenderInline(curr);
393 oldCont = inlineCurr->continuation();
394 inlineCurr->setContinuation(clone);
395 clone->setContinuation(oldCont);
397 // Someone may have indirectly caused a <q> to split. When this happens, the :after content
398 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after
399 // content gets properly destroyed.
400 if (document()->usesBeforeAfterRules())
401 inlineCurr->children()->updateBeforeAfterContent(inlineCurr, AFTER);
403 // Now we need to take all of the children starting from the first child
404 // *after* currChild and append them all to the clone.
405 o = currChild->nextSibling();
407 RenderObject* tmp = o;
408 o = tmp->nextSibling();
409 clone->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0);
410 tmp->setNeedsLayoutAndPrefWidthsRecalc();
414 // Keep walking up the chain.
416 curr = toRenderBoxModelObject(curr->parent());
420 // Now we are at the block level. We need to put the clone into the toBlock.
421 toBlock->children()->appendChildNode(toBlock, clone);
423 // Now take all the children after currChild and remove them from the fromBlock
424 // and put them in the toBlock.
425 o = currChild->nextSibling();
427 RenderObject* tmp = o;
428 o = tmp->nextSibling();
429 toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp));
433 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
434 RenderObject* newChild, RenderBoxModelObject* oldCont)
436 RenderBlock* pre = 0;
437 RenderBlock* block = containingBlock();
439 // Delete our line boxes before we do the inline split into continuations.
440 block->deleteLineBoxTree();
442 bool madeNewBeforeBlock = false;
443 if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
444 // We can reuse this block and make it the preBlock of the next continuation.
446 pre->removePositionedObjects(0);
447 block = block->containingBlock();
449 // No anonymous block available for use. Make one.
450 pre = block->createAnonymousBlock();
451 madeNewBeforeBlock = true;
454 RenderBlock* post = toRenderBlock(pre->createAnonymousBoxWithSameTypeAs(block));
456 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
457 if (madeNewBeforeBlock)
458 block->children()->insertChildNode(block, pre, boxFirst);
459 block->children()->insertChildNode(block, newBlockBox, boxFirst);
460 block->children()->insertChildNode(block, post, boxFirst);
461 block->setChildrenInline(false);
463 if (madeNewBeforeBlock) {
464 RenderObject* o = boxFirst;
466 RenderObject* no = o;
467 o = no->nextSibling();
468 pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, no));
469 no->setNeedsLayoutAndPrefWidthsRecalc();
473 splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
475 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
476 // time in makeChildrenNonInline by just setting this explicitly up front.
477 newBlockBox->setChildrenInline(false);
479 // We delayed adding the newChild until now so that the |newBlockBox| would be fully
480 // connected, thus allowing newChild access to a renderArena should it need
481 // to wrap itself in additional boxes (e.g., table construction).
482 newBlockBox->addChild(newChild);
484 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
485 // get deleted properly. Because objects moves from the pre block into the post block, we want to
486 // make new line boxes instead of leaving the old line boxes around.
487 pre->setNeedsLayoutAndPrefWidthsRecalc();
488 block->setNeedsLayoutAndPrefWidthsRecalc();
489 post->setNeedsLayoutAndPrefWidthsRecalc();
492 void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
494 RenderBoxModelObject* flow = continuationBefore(beforeChild);
495 ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline());
496 RenderBoxModelObject* beforeChildParent = 0;
498 beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
500 RenderBoxModelObject* cont = nextContinuation(flow);
502 beforeChildParent = cont;
504 beforeChildParent = flow;
507 if (newChild->isFloatingOrOutOfFlowPositioned())
508 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
510 // A continuation always consists of two potential candidates: an inline or an anonymous
511 // block box holding block children.
512 bool childInline = newChild->isInline();
513 bool bcpInline = beforeChildParent->isInline();
514 bool flowInline = flow->isInline();
516 if (flow == beforeChildParent)
517 return flow->addChildIgnoringContinuation(newChild, beforeChild);
519 // The goal here is to match up if we can, so that we can coalesce and create the
520 // minimal # of continuations needed for the inline.
521 if (childInline == bcpInline)
522 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
523 else if (flowInline == childInline)
524 return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append.
526 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
530 void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
532 m_lineBoxes.paint(this, paintInfo, paintOffset);
535 template<typename GeneratorContext>
536 void RenderInline::generateLineBoxRects(GeneratorContext& yield) const
538 if (!alwaysCreateLineBoxes())
539 generateCulledLineBoxRects(yield, this);
540 else if (InlineFlowBox* curr = firstLineBox()) {
541 for (; curr; curr = curr->nextLineBox())
542 yield(FloatRect(curr->topLeft(), curr->size()));
547 template<typename GeneratorContext>
548 void RenderInline::generateCulledLineBoxRects(GeneratorContext& yield, const RenderInline* container) const
550 if (!culledInlineFirstLineBox()) {
555 bool isHorizontal = style()->isHorizontalWritingMode();
557 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
558 if (curr->isFloatingOrOutOfFlowPositioned())
561 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
562 // direction (aligned to the root box's baseline).
564 RenderBox* currBox = toRenderBox(curr);
565 if (currBox->inlineBoxWrapper()) {
566 RootInlineBox* rootBox = currBox->inlineBoxWrapper()->root();
567 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
568 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
570 yield(FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, currBox->width() + currBox->marginWidth(), logicalHeight));
572 yield(FloatRect(logicalTop, currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, currBox->height() + currBox->marginHeight()));
574 } else if (curr->isRenderInline()) {
575 // If the child doesn't need line boxes either, then we can recur.
576 RenderInline* currInline = toRenderInline(curr);
577 if (!currInline->alwaysCreateLineBoxes())
578 currInline->generateCulledLineBoxRects(yield, container);
580 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
581 RootInlineBox* rootBox = childLine->root();
582 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
583 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
585 yield(FloatRect(childLine->x() - childLine->marginLogicalLeft(),
587 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
590 yield(FloatRect(logicalTop,
591 childLine->y() - childLine->marginLogicalLeft(),
593 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight()));
596 } else if (curr->isText()) {
597 RenderText* currText = toRenderText(curr);
598 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
599 RootInlineBox* rootBox = childText->root();
600 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
601 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
603 yield(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight));
605 yield(FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth()));
613 class AbsoluteRectsGeneratorContext {
615 AbsoluteRectsGeneratorContext(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset)
617 , m_accumulatedOffset(accumulatedOffset) { }
619 void operator()(const FloatRect& rect)
621 IntRect intRect = enclosingIntRect(rect);
622 intRect.move(m_accumulatedOffset.x(), m_accumulatedOffset.y());
623 m_rects.append(intRect);
626 Vector<IntRect>& m_rects;
627 const LayoutPoint& m_accumulatedOffset;
630 } // unnamed namespace
632 void RenderInline::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
634 AbsoluteRectsGeneratorContext context(rects, accumulatedOffset);
635 generateLineBoxRects(context);
637 if (continuation()) {
638 if (continuation()->isBox()) {
639 RenderBox* box = toRenderBox(continuation());
640 continuation()->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location() + box->locationOffset()));
642 continuation()->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location()));
649 class AbsoluteQuadsGeneratorContext {
651 AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector<FloatQuad>& quads)
655 m_geometryMap.pushMappingsToAncestor(renderer, 0);
658 void operator()(const FloatRect& rect)
660 m_quads.append(m_geometryMap.absoluteRect(rect));
663 Vector<FloatQuad>& m_quads;
664 RenderGeometryMap m_geometryMap;
667 } // unnamed namespace
669 void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
671 AbsoluteQuadsGeneratorContext context(this, quads);
672 generateLineBoxRects(context);
675 continuation()->absoluteQuads(quads, wasFixed);
678 LayoutUnit RenderInline::offsetLeft() const
681 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
682 topLeft = flooredLayoutPoint(firstBox->topLeft());
683 return adjustedPositionRelativeToOffsetParent(topLeft).x();
686 LayoutUnit RenderInline::offsetTop() const
689 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
690 topLeft = flooredLayoutPoint(firstBox->topLeft());
691 return adjustedPositionRelativeToOffsetParent(topLeft).y();
694 static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin)
698 if (margin.isFixed())
699 return margin.value();
700 if (margin.isPercent())
701 return minimumValueForLength(margin, max<LayoutUnit>(0, renderer->containingBlock()->availableLogicalWidth()));
702 if (margin.isViewportPercentage())
703 return valueForLength(margin, 0, renderer->view());
707 LayoutUnit RenderInline::marginLeft() const
709 return computeMargin(this, style()->marginLeft());
712 LayoutUnit RenderInline::marginRight() const
714 return computeMargin(this, style()->marginRight());
717 LayoutUnit RenderInline::marginTop() const
719 return computeMargin(this, style()->marginTop());
722 LayoutUnit RenderInline::marginBottom() const
724 return computeMargin(this, style()->marginBottom());
727 LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const
729 return computeMargin(this, style()->marginStartUsing(otherStyle ? otherStyle : style()));
732 LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const
734 return computeMargin(this, style()->marginEndUsing(otherStyle ? otherStyle : style()));
737 LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const
739 return computeMargin(this, style()->marginBeforeUsing(otherStyle ? otherStyle : style()));
742 LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const
744 return computeMargin(this, style()->marginAfterUsing(otherStyle ? otherStyle : style()));
747 const char* RenderInline::renderName() const
749 if (isRelPositioned())
750 return "RenderInline (relative positioned)";
752 return "RenderInline (generated)";
754 return "RenderInline (run-in)";
755 return "RenderInline";
758 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
759 const HitTestPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
761 return m_lineBoxes.hitTest(this, request, result, pointInContainer, accumulatedOffset, hitTestAction);
764 VisiblePosition RenderInline::positionForPoint(const LayoutPoint& point)
766 // FIXME: Does not deal with relative positioned inlines (should it?)
767 RenderBlock* cb = containingBlock();
768 if (firstLineBox()) {
769 // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We
770 // should try to find a result by asking our containing block.
771 return cb->positionForPoint(point);
774 // Translate the coords from the pre-anonymous block to the post-anonymous block.
775 LayoutPoint parentBlockPoint = cb->location() + point;
776 RenderBoxModelObject* c = continuation();
778 RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c);
779 if (c->isInline() || c->firstChild())
780 return c->positionForPoint(parentBlockPoint - contBlock->locationOffset());
781 c = toRenderBlock(c)->inlineElementContinuation();
784 return RenderBoxModelObject::positionForPoint(point);
789 class LinesBoundingBoxGeneratorContext {
791 LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { }
792 void operator()(const FloatRect& rect)
794 m_rect.uniteIfNonZero(rect);
800 } // unnamed namespace
802 IntRect RenderInline::linesBoundingBox() const
804 if (!alwaysCreateLineBoxes()) {
805 ASSERT(!firstLineBox());
806 FloatRect floatResult;
807 LinesBoundingBoxGeneratorContext context(floatResult);
808 generateCulledLineBoxRects(context, this);
809 return enclosingIntRect(floatResult);
814 // 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
815 // 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
816 // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
817 ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
818 if (firstLineBox() && lastLineBox()) {
819 // Return the width of the minimal left side and the maximal right side.
820 float logicalLeftSide = 0;
821 float logicalRightSide = 0;
822 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
823 if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
824 logicalLeftSide = curr->logicalLeft();
825 if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
826 logicalRightSide = curr->logicalRight();
829 bool isHorizontal = style()->isHorizontalWritingMode();
831 float x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
832 float y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
833 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
834 float height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
835 result = enclosingIntRect(FloatRect(x, y, width, height));
841 InlineBox* RenderInline::culledInlineFirstLineBox() const
843 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
844 if (curr->isFloatingOrOutOfFlowPositioned())
847 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
848 // direction (aligned to the root box's baseline).
850 return toRenderBox(curr)->inlineBoxWrapper();
851 if (curr->isRenderInline()) {
852 RenderInline* currInline = toRenderInline(curr);
853 InlineBox* result = currInline->firstLineBoxIncludingCulling();
856 } else if (curr->isText()) {
857 RenderText* currText = toRenderText(curr);
858 if (currText->firstTextBox())
859 return currText->firstTextBox();
865 InlineBox* RenderInline::culledInlineLastLineBox() const
867 for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) {
868 if (curr->isFloatingOrOutOfFlowPositioned())
871 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
872 // direction (aligned to the root box's baseline).
874 return toRenderBox(curr)->inlineBoxWrapper();
875 if (curr->isRenderInline()) {
876 RenderInline* currInline = toRenderInline(curr);
877 InlineBox* result = currInline->lastLineBoxIncludingCulling();
880 } else if (curr->isText()) {
881 RenderText* currText = toRenderText(curr);
882 if (currText->lastTextBox())
883 return currText->lastTextBox();
889 LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const
891 FloatRect floatResult;
892 LinesBoundingBoxGeneratorContext context(floatResult);
893 generateCulledLineBoxRects(context, this);
894 LayoutRect result(enclosingLayoutRect(floatResult));
895 bool isHorizontal = style()->isHorizontalWritingMode();
896 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
897 if (curr->isFloatingOrOutOfFlowPositioned())
900 // For overflow we just have to propagate by hand and recompute it all.
902 RenderBox* currBox = toRenderBox(curr);
903 if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) {
904 LayoutRect logicalRect = currBox->logicalVisualOverflowRectForPropagation(style());
906 logicalRect.moveBy(currBox->location());
907 result.uniteIfNonZero(logicalRect);
909 logicalRect.moveBy(currBox->location());
910 result.uniteIfNonZero(logicalRect.transposedRect());
913 } else if (curr->isRenderInline()) {
914 // If the child doesn't need line boxes either, then we can recur.
915 RenderInline* currInline = toRenderInline(curr);
916 if (!currInline->alwaysCreateLineBoxes())
917 result.uniteIfNonZero(currInline->culledInlineVisualOverflowBoundingBox());
918 else if (!currInline->hasSelfPaintingLayer())
919 result.uniteIfNonZero(currInline->linesVisualOverflowBoundingBox());
920 } else if (curr->isText()) {
921 // FIXME; Overflow from text boxes is lost. We will need to cache this information in
923 RenderText* currText = toRenderText(curr);
924 result.uniteIfNonZero(currText->linesVisualOverflowBoundingBox());
930 LayoutRect RenderInline::linesVisualOverflowBoundingBox() const
932 if (!alwaysCreateLineBoxes())
933 return culledInlineVisualOverflowBoundingBox();
935 if (!firstLineBox() || !lastLineBox())
938 // Return the width of the minimal left side and the maximal right side.
939 LayoutUnit logicalLeftSide = MAX_LAYOUT_UNIT;
940 LayoutUnit logicalRightSide = MIN_LAYOUT_UNIT;
941 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
942 logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow());
943 logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow());
946 RootInlineBox* firstRootBox = firstLineBox()->root();
947 RootInlineBox* lastRootBox = lastLineBox()->root();
949 LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox->lineTop());
950 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
951 LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox->lineBottom()) - logicalTop;
953 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
954 if (!style()->isHorizontalWritingMode())
955 rect = rect.transposedRect();
959 LayoutRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) const
961 // Only run-ins are allowed in here during layout.
962 ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn());
964 if (!firstLineBoxIncludingCulling() && !continuation())
967 LayoutRect repaintRect(linesVisualOverflowBoundingBox());
968 bool hitRepaintContainer = false;
970 // We need to add in the relative position offsets of any inlines (including us) up to our
972 RenderBlock* cb = containingBlock();
973 for (const RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb;
974 inlineFlow = inlineFlow->parent()) {
975 if (inlineFlow == repaintContainer) {
976 hitRepaintContainer = true;
979 if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer())
980 repaintRect.move(toRenderInline(inlineFlow)->layer()->relativePositionOffset());
983 LayoutUnit outlineSize = style()->outlineSize();
984 repaintRect.inflate(outlineSize);
986 if (hitRepaintContainer || !cb)
989 if (cb->hasColumns())
990 cb->adjustRectForColumns(repaintRect);
992 if (cb->hasOverflowClip()) {
993 // cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the
994 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
995 // anyway if its size does change.
996 repaintRect.move(-cb->scrolledContentOffset()); // For overflow:auto/scroll/hidden.
998 LayoutRect boxRect(LayoutPoint(), cb->cachedSizeForOverflowClip());
999 repaintRect.intersect(boxRect);
1002 cb->computeRectForRepaint(repaintContainer, repaintRect);
1005 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1006 if (!curr->isText())
1007 repaintRect.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineSize));
1010 if (continuation() && !continuation()->isInline() && continuation()->parent())
1011 repaintRect.unite(continuation()->rectWithOutlineForRepaint(repaintContainer, outlineSize));
1017 LayoutRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, LayoutUnit outlineWidth) const
1019 LayoutRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
1020 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1021 if (!curr->isText())
1022 r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth));
1027 void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, LayoutRect& rect, bool fixed) const
1029 if (RenderView* v = view()) {
1030 // LayoutState is only valid for root-relative repainting
1031 if (v->layoutStateEnabled() && !repaintContainer) {
1032 LayoutState* layoutState = v->layoutState();
1033 if (style()->position() == RelativePosition && layer())
1034 rect.move(layer()->relativePositionOffset());
1035 rect.move(layoutState->m_paintOffset);
1036 if (layoutState->m_clipped)
1037 rect.intersect(layoutState->m_clipRect);
1042 if (repaintContainer == this)
1045 bool containerSkipped;
1046 RenderObject* o = container(repaintContainer, &containerSkipped);
1050 LayoutPoint topLeft = rect.location();
1052 if (o->isBlockFlow() && !style()->isOutOfFlowPositioned()) {
1053 RenderBlock* cb = toRenderBlock(o);
1054 if (cb->hasColumns()) {
1055 LayoutRect repaintRect(topLeft, rect.size());
1056 cb->adjustRectForColumns(repaintRect);
1057 topLeft = repaintRect.location();
1062 if (style()->position() == RelativePosition && layer()) {
1063 // Apply the relative position offset when invalidating a rectangle. The layer
1064 // is translated, but the render box isn't, so we need to do this to get the
1065 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
1066 // flag on the RenderObject has been cleared, so use the one on the style().
1067 topLeft += layer()->relativePositionOffset();
1070 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1071 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1072 if (o->hasOverflowClip()) {
1073 RenderBox* containerBox = toRenderBox(o);
1075 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
1076 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
1077 // anyway if its size does change.
1078 topLeft -= containerBox->scrolledContentOffset(); // For overflow:auto/scroll/hidden.
1080 LayoutRect repaintRect(topLeft, rect.size());
1081 LayoutRect boxRect(LayoutPoint(), containerBox->cachedSizeForOverflowClip());
1082 rect = intersection(repaintRect, boxRect);
1086 rect.setLocation(topLeft);
1088 if (containerSkipped) {
1089 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
1090 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1091 rect.move(-containerOffset);
1095 o->computeRectForRepaint(repaintContainer, rect, fixed);
1098 LayoutSize RenderInline::offsetFromContainer(RenderObject* container, const LayoutPoint& point, bool* offsetDependsOnPoint) const
1100 ASSERT(container == this->container());
1103 if (isRelPositioned())
1104 offset += relativePositionOffset();
1106 container->adjustForColumns(offset, point);
1108 if (container->hasOverflowClip())
1109 offset -= toRenderBox(container)->scrolledContentOffset();
1111 if (offsetDependsOnPoint)
1112 *offsetDependsOnPoint = container->hasColumns() || (container->isBox() && container->style()->isFlippedBlocksWritingMode());
1117 void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState, ApplyContainerFlipOrNot applyContainerFlip, bool* wasFixed) const
1119 if (repaintContainer == this)
1122 if (RenderView *v = view()) {
1123 if (v->layoutStateEnabled() && !repaintContainer) {
1124 LayoutState* layoutState = v->layoutState();
1125 LayoutSize offset = layoutState->m_paintOffset;
1126 if (style()->position() == RelativePosition && layer())
1127 offset += layer()->relativePositionOffset();
1128 transformState.move(offset);
1133 bool containerSkipped;
1134 RenderObject* o = container(repaintContainer, &containerSkipped);
1138 if (applyContainerFlip && o->isBox()) {
1139 if (o->style()->isFlippedBlocksWritingMode()) {
1140 IntPoint centerPoint = roundedIntPoint(transformState.mappedPoint());
1141 transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(centerPoint) - centerPoint);
1143 applyContainerFlip = DoNotApplyContainerFlip;
1146 LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint()));
1148 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
1149 if (useTransforms && shouldUseTransformFromContainer(o)) {
1150 TransformationMatrix t;
1151 getTransformFromContainer(o, containerOffset, t);
1152 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1154 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1156 if (containerSkipped) {
1157 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1158 // to just subtract the delta between the repaintContainer and o.
1159 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1160 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1164 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState, applyContainerFlip, wasFixed);
1167 const RenderObject* RenderInline::pushMappingToContainer(const RenderBoxModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
1169 ASSERT(ancestorToStopAt != this);
1171 bool ancestorSkipped;
1172 RenderObject* container = this->container(ancestorToStopAt, &ancestorSkipped);
1176 LayoutSize adjustmentForSkippedAncestor;
1177 if (ancestorSkipped) {
1178 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1179 // to just subtract the delta between the ancestor and o.
1180 adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(container);
1183 bool offsetDependsOnPoint = false;
1184 LayoutSize containerOffset = offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint);
1186 bool preserve3D = container->style()->preserves3D() || style()->preserves3D();
1187 if (shouldUseTransformFromContainer(container)) {
1188 TransformationMatrix t;
1189 getTransformFromContainer(container, containerOffset, t);
1190 t.translateRight(adjustmentForSkippedAncestor.width(), adjustmentForSkippedAncestor.height()); // FIXME: right?
1191 geometryMap.push(this, t, preserve3D, offsetDependsOnPoint);
1193 containerOffset += adjustmentForSkippedAncestor;
1194 geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint);
1197 return ancestorSkipped ? ancestorToStopAt : container;
1200 void RenderInline::updateDragState(bool dragOn)
1202 RenderBoxModelObject::updateDragState(dragOn);
1204 continuation()->updateDragState(dragOn);
1207 void RenderInline::childBecameNonInline(RenderObject* child)
1209 // We have to split the parent flow.
1210 RenderBlock* newBox = containingBlock()->createAnonymousBlock();
1211 RenderBoxModelObject* oldContinuation = continuation();
1212 setContinuation(newBox);
1213 RenderObject* beforeChild = child->nextSibling();
1214 children()->removeChildNode(this, child);
1215 splitFlow(beforeChild, newBox, child, oldContinuation);
1218 void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
1220 if (result.innerNode())
1224 LayoutPoint localPoint(point);
1226 if (isInlineElementContinuation()) {
1227 // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space
1228 // of the principal renderer's containing block. This will end up being the innerNonSharedNode.
1229 RenderBlock* firstBlock = n->renderer()->containingBlock();
1231 // Get our containing block.
1232 RenderBox* block = containingBlock();
1233 localPoint.moveBy(block->location() - firstBlock->locationOffset());
1236 result.setInnerNode(n);
1237 if (!result.innerNonSharedNode())
1238 result.setInnerNonSharedNode(n);
1239 result.setLocalPoint(localPoint);
1243 void RenderInline::dirtyLineBoxes(bool fullLayout)
1246 m_lineBoxes.deleteLineBoxes(renderArena());
1250 if (!alwaysCreateLineBoxes()) {
1251 // We have to grovel into our children in order to dirty the appropriate lines.
1252 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1253 if (curr->isFloatingOrOutOfFlowPositioned())
1255 if (curr->isBox() && !curr->needsLayout()) {
1256 RenderBox* currBox = toRenderBox(curr);
1257 if (currBox->inlineBoxWrapper())
1258 currBox->inlineBoxWrapper()->root()->markDirty();
1259 } else if (!curr->selfNeedsLayout()) {
1260 if (curr->isRenderInline()) {
1261 RenderInline* currInline = toRenderInline(curr);
1262 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox())
1263 childLine->root()->markDirty();
1264 } else if (curr->isText()) {
1265 RenderText* currText = toRenderText(curr);
1266 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox())
1267 childText->root()->markDirty();
1272 m_lineBoxes.dirtyLineBoxes();
1275 void RenderInline::deleteLineBoxTree()
1277 m_lineBoxes.deleteLineBoxTree(renderArena());
1280 InlineFlowBox* RenderInline::createInlineFlowBox()
1282 return new (renderArena()) InlineFlowBox(this);
1285 InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
1287 setAlwaysCreateLineBoxes();
1288 InlineFlowBox* flowBox = createInlineFlowBox();
1289 m_lineBoxes.appendLineBox(flowBox);
1293 LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
1295 if (firstLine && document()->usesFirstLineRules()) {
1296 RenderStyle* s = style(firstLine);
1298 return s->computedLineHeight(view());
1301 return style()->computedLineHeight(view());
1304 LayoutUnit RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1306 const FontMetrics& fontMetrics = style(firstLine)->fontMetrics();
1307 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
1310 LayoutSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const
1312 // FIXME: This function isn't right with mixed writing modes.
1314 ASSERT(isRelPositioned());
1315 if (!isRelPositioned())
1316 return LayoutSize();
1318 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
1319 // box from the rest of the content, but only in the cases where we know we're positioned
1320 // relative to the inline itself.
1322 LayoutSize logicalOffset;
1323 LayoutUnit inlinePosition;
1324 LayoutUnit blockPosition;
1325 if (firstLineBox()) {
1326 inlinePosition = roundedLayoutUnit(firstLineBox()->logicalLeft());
1327 blockPosition = firstLineBox()->logicalTop();
1329 inlinePosition = layer()->staticInlinePosition();
1330 blockPosition = layer()->staticBlockPosition();
1333 if (!child->style()->hasStaticInlinePosition(style()->isHorizontalWritingMode()))
1334 logicalOffset.setWidth(inlinePosition);
1336 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
1337 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
1338 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
1340 else if (!child->style()->isOriginalDisplayInlineType())
1341 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
1342 logicalOffset.setWidth(inlinePosition - child->containingBlock()->borderAndPaddingLogicalLeft());
1344 if (!child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
1345 logicalOffset.setHeight(blockPosition);
1347 return style()->isHorizontalWritingMode() ? logicalOffset : logicalOffset.transposedSize();
1350 void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
1355 // FIXME: We can do better.
1359 void RenderInline::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset)
1361 AbsoluteRectsGeneratorContext context(rects, additionalOffset);
1362 generateLineBoxRects(context);
1364 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1365 if (!curr->isText() && !curr->isListMarker()) {
1366 FloatPoint pos(additionalOffset);
1367 // FIXME: This doesn't work correctly with transforms.
1368 if (curr->hasLayer())
1369 pos = curr->localToAbsolute();
1370 else if (curr->isBox())
1371 pos.move(toRenderBox(curr)->locationOffset());
1372 curr->addFocusRingRects(rects, flooredIntPoint(pos));
1376 if (continuation()) {
1377 if (continuation()->isInline())
1378 continuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + continuation()->containingBlock()->location() - containingBlock()->location()));
1380 continuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + toRenderBox(continuation())->location() - containingBlock()->location()));
1384 void RenderInline::paintOutline(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset)
1389 RenderStyle* styleToUse = style();
1390 if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) {
1391 if (!theme()->supportsFocusRing(styleToUse)) {
1392 // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
1393 paintFocusRing(graphicsContext, paintOffset, styleToUse);
1397 if (graphicsContext->paintingDisabled())
1400 if (styleToUse->outlineStyleIsAuto() || styleToUse->outlineStyle() == BNONE)
1403 Vector<LayoutRect> rects;
1405 rects.append(LayoutRect());
1406 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1407 RootInlineBox* root = curr->root();
1408 LayoutUnit top = max<LayoutUnit>(root->lineTop(), curr->logicalTop());
1409 LayoutUnit bottom = min<LayoutUnit>(root->lineBottom(), curr->logicalBottom());
1410 rects.append(LayoutRect(curr->x(), top, curr->logicalWidth(), bottom - top));
1412 rects.append(LayoutRect());
1414 Color outlineColor = styleToUse->visitedDependentColor(CSSPropertyOutlineColor);
1415 bool useTransparencyLayer = outlineColor.hasAlpha();
1416 if (useTransparencyLayer) {
1417 graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255);
1418 outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
1421 for (unsigned i = 1; i < rects.size() - 1; i++)
1422 paintOutlineForLine(graphicsContext, paintOffset, rects.at(i - 1), rects.at(i), rects.at(i + 1), outlineColor);
1424 if (useTransparencyLayer)
1425 graphicsContext->endTransparencyLayer();
1428 void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset,
1429 const LayoutRect& lastline, const LayoutRect& thisline, const LayoutRect& nextline,
1430 const Color outlineColor)
1432 RenderStyle* styleToUse = style();
1433 int outlineWidth = styleToUse->outlineWidth();
1434 EBorderStyle outlineStyle = styleToUse->outlineStyle();
1436 bool antialias = shouldAntialiasLines(graphicsContext);
1438 int offset = style()->outlineOffset();
1440 LayoutRect box(LayoutPoint(paintOffset.x() + thisline.x() - offset, paintOffset.y() + thisline.y() - offset),
1441 LayoutSize(thisline.width() + offset, thisline.height() + offset));
1443 IntRect pixelSnappedBox = pixelSnappedIntRect(box);
1444 IntRect pixelSnappedLastLine = pixelSnappedIntRect(paintOffset.x() + lastline.x(), 0, lastline.width(), 0);
1445 IntRect pixelSnappedNextLine = pixelSnappedIntRect(paintOffset.x() + nextline.x(), 0, nextline.width(), 0);
1448 drawLineForBoxSide(graphicsContext,
1449 pixelSnappedBox.x() - outlineWidth,
1450 pixelSnappedBox.y() - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : 0),
1451 pixelSnappedBox.x(),
1452 pixelSnappedBox.maxY() + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : 0),
1454 outlineColor, outlineStyle,
1455 (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth),
1456 (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth),
1460 drawLineForBoxSide(graphicsContext,
1461 pixelSnappedBox.maxX(),
1462 pixelSnappedBox.y() - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : 0),
1463 pixelSnappedBox.maxX() + outlineWidth,
1464 pixelSnappedBox.maxY() + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : 0),
1466 outlineColor, outlineStyle,
1467 (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : -outlineWidth),
1468 (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : -outlineWidth),
1471 if (thisline.x() < lastline.x())
1472 drawLineForBoxSide(graphicsContext,
1473 pixelSnappedBox.x() - outlineWidth,
1474 pixelSnappedBox.y() - outlineWidth,
1475 min(pixelSnappedBox.maxX() + outlineWidth, (lastline.isEmpty() ? 1000000 : pixelSnappedLastLine.x())),
1476 pixelSnappedBox.y(),
1477 BSTop, outlineColor, outlineStyle,
1479 (!lastline.isEmpty() && paintOffset.x() + lastline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth,
1482 if (lastline.maxX() < thisline.maxX())
1483 drawLineForBoxSide(graphicsContext,
1484 max(lastline.isEmpty() ? -1000000 : pixelSnappedLastLine.maxX(), pixelSnappedBox.x() - outlineWidth),
1485 pixelSnappedBox.y() - outlineWidth,
1486 pixelSnappedBox.maxX() + outlineWidth,
1487 pixelSnappedBox.y(),
1488 BSTop, outlineColor, outlineStyle,
1489 (!lastline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + lastline.maxX()) ? -outlineWidth : outlineWidth,
1490 outlineWidth, antialias);
1492 if (thisline.x() == thisline.maxX())
1493 drawLineForBoxSide(graphicsContext,
1494 pixelSnappedBox.x() - outlineWidth,
1495 pixelSnappedBox.y() - outlineWidth,
1496 pixelSnappedBox.maxX() + outlineWidth,
1497 pixelSnappedBox.y(),
1498 BSTop, outlineColor, outlineStyle,
1504 if (thisline.x() < nextline.x())
1505 drawLineForBoxSide(graphicsContext,
1506 pixelSnappedBox.x() - outlineWidth,
1507 pixelSnappedBox.maxY(),
1508 min(pixelSnappedBox.maxX() + outlineWidth, !nextline.isEmpty() ? pixelSnappedNextLine.x() + 1 : 1000000),
1509 pixelSnappedBox.maxY() + outlineWidth,
1510 BSBottom, outlineColor, outlineStyle,
1512 (!nextline.isEmpty() && paintOffset.x() + nextline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth,
1515 if (nextline.maxX() < thisline.maxX())
1516 drawLineForBoxSide(graphicsContext,
1517 max(!nextline.isEmpty() ? pixelSnappedNextLine.maxX() : -1000000, pixelSnappedBox.x() - outlineWidth),
1518 pixelSnappedBox.maxY(),
1519 pixelSnappedBox.maxX() + outlineWidth,
1520 pixelSnappedBox.maxY() + outlineWidth,
1521 BSBottom, outlineColor, outlineStyle,
1522 (!nextline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + nextline.maxX()) ? -outlineWidth : outlineWidth,
1523 outlineWidth, antialias);
1525 if (thisline.x() == thisline.maxX())
1526 drawLineForBoxSide(graphicsContext,
1527 pixelSnappedBox.x() - outlineWidth,
1528 pixelSnappedBox.maxY(),
1529 pixelSnappedBox.maxX() + outlineWidth,
1530 pixelSnappedBox.maxY() + outlineWidth,
1531 BSBottom, outlineColor, outlineStyle,
1537 #if ENABLE(DASHBOARD_SUPPORT)
1538 void RenderInline::addDashboardRegions(Vector<DashboardRegionValue>& regions)
1540 // Convert the style regions to absolute coordinates.
1541 if (style()->visibility() != VISIBLE)
1544 const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions();
1545 unsigned i, count = styleRegions.size();
1546 for (i = 0; i < count; i++) {
1547 StyleDashboardRegion styleRegion = styleRegions[i];
1549 LayoutRect linesBoundingBox = this->linesBoundingBox();
1550 LayoutUnit w = linesBoundingBox.width();
1551 LayoutUnit h = linesBoundingBox.height();
1553 DashboardRegionValue region;
1554 region.label = styleRegion.label;
1555 region.bounds = LayoutRect(linesBoundingBox.x() + styleRegion.offset.left().value(),
1556 linesBoundingBox.y() + styleRegion.offset.top().value(),
1557 w - styleRegion.offset.left().value() - styleRegion.offset.right().value(),
1558 h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value());
1559 region.type = styleRegion.type;
1561 RenderObject* container = containingBlock();
1565 region.clip = region.bounds;
1566 container->computeAbsoluteRepaintRect(region.clip);
1567 if (region.clip.height() < 0) {
1568 region.clip.setHeight(0);
1569 region.clip.setWidth(0);
1572 FloatPoint absPos = container->localToAbsolute();
1573 region.bounds.setX(absPos.x() + region.bounds.x());
1574 region.bounds.setY(absPos.y() + region.bounds.y());
1576 regions.append(region);
1581 } // namespace WebCore