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"
31 #include "RenderArena.h"
32 #include "RenderBlock.h"
33 #include "RenderLayer.h"
34 #include "RenderTheme.h"
35 #include "RenderView.h"
36 #include "TransformState.h"
37 #include "VisiblePosition.h"
39 #if ENABLE(DASHBOARD_SUPPORT)
47 RenderInline::RenderInline(Node* node)
48 : RenderBoxModelObject(node)
51 , m_verticalPosition(PositionUndefined)
53 setChildrenInline(true);
56 void RenderInline::destroy()
58 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
59 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
60 children()->destroyLeftoverChildren();
62 // Destroy our continuation before anything other than anonymous children.
63 // The reason we don't destroy it before anonymous children is that they may
64 // have continuations of their own that are anonymous children of our continuation.
66 m_continuation->destroy();
70 if (!documentBeingDestroyed()) {
72 // We can't wait for RenderBoxModelObject::destroy to clear the selection,
73 // because by then we will have nuked the line boxes.
74 // FIXME: The SelectionController should be responsible for this when it
75 // is notified of DOM mutations.
76 if (isSelectionBorder())
77 view()->clearSelection();
79 // If line boxes are contained inside a root, that means we're an inline.
80 // In that case, we need to remove all the line boxes so that the parent
81 // lines aren't pointing to deleted children. If the first line box does
82 // not have a parent that means they are either already disconnected or
83 // root lines that can just be destroyed without disconnecting.
84 if (firstLineBox()->parent()) {
85 for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
88 } else if (isInline() && parent())
89 parent()->dirtyLinesFromChangedChild(this);
92 m_lineBoxes.deleteLineBoxes(renderArena());
94 RenderBoxModelObject::destroy();
97 RenderInline* RenderInline::inlineElementContinuation() const
99 if (!m_continuation || m_continuation->isInline())
100 return toRenderInline(m_continuation);
101 return toRenderBlock(m_continuation)->inlineElementContinuation();
104 void RenderInline::updateBoxModelInfoFromStyle()
106 RenderBoxModelObject::updateBoxModelInfoFromStyle();
108 setInline(true); // Needed for run-ins, since run-in is considered a block display type.
110 // FIXME: Support transforms and reflections on inline flows someday.
111 setHasTransform(false);
112 setHasReflection(false);
115 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
117 RenderBoxModelObject::styleDidChange(diff, oldStyle);
119 // Ensure that all of the split inlines pick up the new style. We
120 // only do this if we're an inline, since we don't want to propagate
121 // a block's style to the other inlines.
122 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
123 // and after the block share the same style, but the block doesn't
124 // need to pass its style on to anyone else.
125 for (RenderInline* currCont = inlineElementContinuation(); currCont; currCont = currCont->inlineElementContinuation()) {
126 RenderBoxModelObject* nextCont = currCont->continuation();
127 currCont->setContinuation(0);
128 currCont->setStyle(style());
129 currCont->setContinuation(nextCont);
134 // Update pseudos for :before and :after now.
135 if (!isAnonymous() && document()->usesBeforeAfterRules()) {
136 children()->updateBeforeAfterContent(this, BEFORE);
137 children()->updateBeforeAfterContent(this, AFTER);
141 void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild)
144 return addChildToContinuation(newChild, beforeChild);
145 return addChildIgnoringContinuation(newChild, beforeChild);
148 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
150 if (renderer->isInline() && !renderer->isReplaced())
151 return toRenderInline(renderer)->continuation();
152 return toRenderBlock(renderer)->inlineElementContinuation();
155 RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild)
157 if (beforeChild && beforeChild->parent() == this)
160 RenderBoxModelObject* curr = nextContinuation(this);
161 RenderBoxModelObject* nextToLast = this;
162 RenderBoxModelObject* last = this;
164 if (beforeChild && beforeChild->parent() == curr) {
165 if (curr->firstChild() == beforeChild)
172 curr = nextContinuation(curr);
175 if (!beforeChild && !last->firstChild())
180 void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
182 // Make sure we don't append things after :after-generated content if we have it.
183 if (!beforeChild && isAfterContent(lastChild()))
184 beforeChild = lastChild();
186 if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) {
187 // We are placing a block inside an inline. We have to perform a split of this
188 // inline into continuations. This involves creating an anonymous block box to hold
189 // |newChild|. We then make that block box a continuation of this inline. We take all of
190 // the children after |beforeChild| and put them in a clone of this object.
191 RefPtr<RenderStyle> newStyle = RenderStyle::create();
192 newStyle->inheritFrom(style());
193 newStyle->setDisplay(BLOCK);
195 RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
196 newBox->setStyle(newStyle.release());
197 RenderBoxModelObject* oldContinuation = continuation();
198 setContinuation(newBox);
200 // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content
201 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after
202 // content gets properly destroyed.
203 bool isLastChild = (beforeChild == lastChild());
204 if (document()->usesBeforeAfterRules())
205 children()->updateBeforeAfterContent(this, AFTER);
206 if (isLastChild && beforeChild != lastChild())
207 beforeChild = 0; // We destroyed the last child, so now we need to update our insertion
208 // point to be 0. It's just a straight append now.
210 splitFlow(beforeChild, newBox, newChild, oldContinuation);
214 RenderBoxModelObject::addChild(newChild, beforeChild);
216 newChild->setNeedsLayoutAndPrefWidthsRecalc();
219 RenderInline* RenderInline::cloneInline(RenderInline* src)
221 RenderInline* o = new (src->renderArena()) RenderInline(src->node());
222 o->setStyle(src->style());
226 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
227 RenderBlock* middleBlock,
228 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
230 // Create a clone of this inline.
231 RenderInline* clone = cloneInline(this);
232 clone->setContinuation(oldCont);
234 // Now take all of the children from beforeChild to the end and remove
235 // them from |this| and place them in the clone.
236 RenderObject* o = beforeChild;
238 RenderObject* tmp = o;
239 o = tmp->nextSibling();
240 clone->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0);
241 tmp->setNeedsLayoutAndPrefWidthsRecalc();
244 // Hook |clone| up as the continuation of the middle block.
245 middleBlock->setContinuation(clone);
247 // We have been reparented and are now under the fromBlock. We need
248 // to walk up our inline parent chain until we hit the containing block.
249 // Once we hit the containing block we're done.
250 RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
251 RenderBoxModelObject* currChild = this;
253 // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
254 // There will eventually be a better approach to this problem that will let us nest to a much
255 // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in
256 // incorrect rendering, but the alternative is to hang forever.
257 unsigned splitDepth = 1;
258 const unsigned cMaxSplitDepth = 200;
259 while (curr && curr != fromBlock) {
260 ASSERT(curr->isRenderInline());
261 if (splitDepth < cMaxSplitDepth) {
262 // Create a new clone.
263 RenderInline* cloneChild = clone;
264 clone = cloneInline(toRenderInline(curr));
266 // Insert our child clone as the first child.
267 clone->addChildIgnoringContinuation(cloneChild, 0);
269 // Hook the clone up as a continuation of |curr|.
270 RenderInline* inlineCurr = toRenderInline(curr);
271 oldCont = inlineCurr->continuation();
272 inlineCurr->setContinuation(clone);
273 clone->setContinuation(oldCont);
275 // Someone may have indirectly caused a <q> to split. When this happens, the :after content
276 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after
277 // content gets properly destroyed.
278 if (document()->usesBeforeAfterRules())
279 inlineCurr->children()->updateBeforeAfterContent(inlineCurr, AFTER);
281 // Now we need to take all of the children starting from the first child
282 // *after* currChild and append them all to the clone.
283 o = currChild->nextSibling();
285 RenderObject* tmp = o;
286 o = tmp->nextSibling();
287 clone->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0);
288 tmp->setNeedsLayoutAndPrefWidthsRecalc();
292 // Keep walking up the chain.
294 curr = toRenderBoxModelObject(curr->parent());
298 // Now we are at the block level. We need to put the clone into the toBlock.
299 toBlock->children()->appendChildNode(toBlock, clone);
301 // Now take all the children after currChild and remove them from the fromBlock
302 // and put them in the toBlock.
303 o = currChild->nextSibling();
305 RenderObject* tmp = o;
306 o = tmp->nextSibling();
307 toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp));
311 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
312 RenderObject* newChild, RenderBoxModelObject* oldCont)
314 RenderBlock* pre = 0;
315 RenderBlock* block = containingBlock();
317 // Delete our line boxes before we do the inline split into continuations.
318 block->deleteLineBoxTree();
320 bool madeNewBeforeBlock = false;
321 if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
322 // We can reuse this block and make it the preBlock of the next continuation.
324 pre->removePositionedObjects(0);
325 block = block->containingBlock();
327 // No anonymous block available for use. Make one.
328 pre = block->createAnonymousBlock();
329 madeNewBeforeBlock = true;
332 RenderBlock* post = block->createAnonymousBlock();
334 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
335 if (madeNewBeforeBlock)
336 block->children()->insertChildNode(block, pre, boxFirst);
337 block->children()->insertChildNode(block, newBlockBox, boxFirst);
338 block->children()->insertChildNode(block, post, boxFirst);
339 block->setChildrenInline(false);
341 if (madeNewBeforeBlock) {
342 RenderObject* o = boxFirst;
344 RenderObject* no = o;
345 o = no->nextSibling();
346 pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, no));
347 no->setNeedsLayoutAndPrefWidthsRecalc();
351 splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
353 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
354 // time in makeChildrenNonInline by just setting this explicitly up front.
355 newBlockBox->setChildrenInline(false);
357 // We delayed adding the newChild until now so that the |newBlockBox| would be fully
358 // connected, thus allowing newChild access to a renderArena should it need
359 // to wrap itself in additional boxes (e.g., table construction).
360 newBlockBox->addChild(newChild);
362 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
363 // get deleted properly. Because objects moves from the pre block into the post block, we want to
364 // make new line boxes instead of leaving the old line boxes around.
365 pre->setNeedsLayoutAndPrefWidthsRecalc();
366 block->setNeedsLayoutAndPrefWidthsRecalc();
367 post->setNeedsLayoutAndPrefWidthsRecalc();
370 void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
372 RenderBoxModelObject* flow = continuationBefore(beforeChild);
373 ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline());
374 RenderBoxModelObject* beforeChildParent = 0;
376 beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
378 RenderBoxModelObject* cont = nextContinuation(flow);
380 beforeChildParent = cont;
382 beforeChildParent = flow;
385 if (newChild->isFloatingOrPositioned())
386 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
388 // A continuation always consists of two potential candidates: an inline or an anonymous
389 // block box holding block children.
390 bool childInline = newChild->isInline();
391 bool bcpInline = beforeChildParent->isInline();
392 bool flowInline = flow->isInline();
394 if (flow == beforeChildParent)
395 return flow->addChildIgnoringContinuation(newChild, beforeChild);
397 // The goal here is to match up if we can, so that we can coalesce and create the
398 // minimal # of continuations needed for the inline.
399 if (childInline == bcpInline)
400 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
401 else if (flowInline == childInline)
402 return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append.
404 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
408 void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty)
410 m_lineBoxes.paint(this, paintInfo, tx, ty);
413 void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
415 if (InlineFlowBox* curr = firstLineBox()) {
416 for (; curr; curr = curr->nextLineBox())
417 rects.append(IntRect(tx + curr->x(), ty + curr->y(), curr->logicalWidth(), curr->logicalHeight()));
419 rects.append(IntRect(tx, ty, 0, 0));
421 if (continuation()) {
422 if (continuation()->isBox()) {
423 RenderBox* box = toRenderBox(continuation());
424 continuation()->absoluteRects(rects,
425 tx - containingBlock()->x() + box->x(),
426 ty - containingBlock()->y() + box->y());
428 continuation()->absoluteRects(rects, tx - containingBlock()->x(), ty - containingBlock()->y());
432 void RenderInline::absoluteQuads(Vector<FloatQuad>& quads)
434 if (InlineFlowBox* curr = firstLineBox()) {
435 for (; curr; curr = curr->nextLineBox()) {
436 FloatRect localRect(curr->x(), curr->y(), curr->logicalWidth(), curr->logicalHeight());
437 quads.append(localToAbsoluteQuad(localRect));
440 quads.append(localToAbsoluteQuad(FloatRect()));
443 continuation()->absoluteQuads(quads);
446 int RenderInline::offsetLeft() const
448 int x = RenderBoxModelObject::offsetLeft();
450 x += firstLineBox()->x();
454 int RenderInline::offsetTop() const
456 int y = RenderBoxModelObject::offsetTop();
458 y += firstLineBox()->y();
462 static int computeMargin(const RenderInline* renderer, const Length& margin)
466 if (margin.isFixed())
467 return margin.value();
468 if (margin.isPercent())
469 return margin.calcMinValue(max(0, renderer->containingBlock()->availableLogicalWidth()));
473 int RenderInline::marginLeft() const
475 if (!style()->isHorizontalWritingMode())
477 return computeMargin(this, style()->marginLeft());
480 int RenderInline::marginRight() const
482 if (!style()->isHorizontalWritingMode())
484 return computeMargin(this, style()->marginRight());
487 int RenderInline::marginTop() const
489 if (style()->isHorizontalWritingMode())
491 return computeMargin(this, style()->marginTop());
494 int RenderInline::marginBottom() const
496 if (style()->isHorizontalWritingMode())
498 return computeMargin(this, style()->marginBottom());
501 int RenderInline::marginStart() const
503 return computeMargin(this, style()->marginStart());
506 int RenderInline::marginEnd() const
508 return computeMargin(this, style()->marginEnd());
511 const char* RenderInline::renderName() const
513 if (isRelPositioned())
514 return "RenderInline (relative positioned)";
516 return "RenderInline (generated)";
518 return "RenderInline (run-in)";
519 return "RenderInline";
522 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
523 int x, int y, int tx, int ty, HitTestAction hitTestAction)
525 return m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction);
528 VisiblePosition RenderInline::positionForPoint(const IntPoint& point)
530 // FIXME: Does not deal with relative positioned inlines (should it?)
531 RenderBlock* cb = containingBlock();
532 if (firstLineBox()) {
533 // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We
534 // should try to find a result by asking our containing block.
535 return cb->positionForPoint(point);
538 // Translate the coords from the pre-anonymous block to the post-anonymous block.
539 int parentBlockX = cb->x() + point.x();
540 int parentBlockY = cb->y() + point.y();
541 RenderBoxModelObject* c = continuation();
543 RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c);
544 if (c->isInline() || c->firstChild())
545 return c->positionForCoordinates(parentBlockX - contBlock->x(), parentBlockY - contBlock->y());
546 c = toRenderBlock(c)->inlineElementContinuation();
549 return RenderBoxModelObject::positionForPoint(point);
552 IntRect RenderInline::linesBoundingBox() const
556 // 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
557 // 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
558 // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
559 ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
560 if (firstLineBox() && lastLineBox()) {
561 // Return the width of the minimal left side and the maximal right side.
562 int logicalLeftSide = 0;
563 int logicalRightSide = 0;
564 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
565 if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
566 logicalLeftSide = curr->logicalLeft();
567 if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
568 logicalRightSide = curr->logicalRight();
571 bool isHorizontal = style()->isHorizontalWritingMode();
573 int x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
574 int y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
575 int width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
576 int height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
577 result = IntRect(x, y, width, height);
583 IntRect RenderInline::linesVisibleOverflowBoundingBox() const
585 if (!firstLineBox() || !lastLineBox())
588 // Return the width of the minimal left side and the maximal right side.
589 int logicalLeftSide = numeric_limits<int>::max();
590 int logicalRightSide = numeric_limits<int>::min();
591 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
592 logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisibleOverflow());
593 logicalRightSide = max(logicalRightSide, curr->logicalRightVisibleOverflow());
596 bool isHorizontal = style()->isHorizontalWritingMode();
598 int x = isHorizontal ? logicalLeftSide : firstLineBox()->leftVisibleOverflow();
599 int y = isHorizontal ? firstLineBox()->topVisibleOverflow() : logicalLeftSide;
600 int width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->rightVisibleOverflow() - firstLineBox()->leftVisibleOverflow();
601 int height = isHorizontal ? lastLineBox()->bottomVisibleOverflow() - firstLineBox()->topVisibleOverflow() : logicalRightSide - logicalLeftSide;
602 return IntRect(x, y, width, height);
605 IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
607 // Only run-ins are allowed in here during layout.
608 ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn());
610 if (!firstLineBox() && !continuation())
613 // Find our leftmost position.
614 IntRect boundingBox(linesVisibleOverflowBoundingBox());
615 int left = boundingBox.x();
616 int top = boundingBox.y();
618 // Now invalidate a rectangle.
619 int ow = style() ? style()->outlineSize() : 0;
621 // We need to add in the relative position offsets of any inlines (including us) up to our
623 RenderBlock* cb = containingBlock();
624 for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb;
625 inlineFlow = inlineFlow->parent()) {
626 if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer())
627 toRenderInline(inlineFlow)->layer()->relativePositionOffset(left, top);
630 IntRect r(-ow + left, -ow + top, boundingBox.width() + ow * 2, boundingBox.height() + ow * 2);
631 cb->flipForWritingMode(r);
633 if (cb->hasColumns())
634 cb->adjustRectForColumns(r);
636 if (cb->hasOverflowClip()) {
637 // cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the
638 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
639 // anyway if its size does change.
640 IntRect repaintRect(r);
641 repaintRect.move(-cb->layer()->scrolledContentOffset()); // For overflow:auto/scroll/hidden.
643 IntRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height());
644 r = intersection(repaintRect, boxRect);
647 // FIXME: need to ensure that we compute the correct repaint rect when the repaint container
649 if (repaintContainer != this)
650 cb->computeRectForRepaint(repaintContainer, r);
653 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
654 if (!curr->isText()) {
655 IntRect childRect = curr->rectWithOutlineForRepaint(repaintContainer, ow);
660 if (continuation() && !continuation()->isInline()) {
661 IntRect contRect = continuation()->rectWithOutlineForRepaint(repaintContainer, ow);
669 IntRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth)
671 IntRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
672 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
674 r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth));
679 void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
681 if (RenderView* v = view()) {
682 // LayoutState is only valid for root-relative repainting
683 if (v->layoutStateEnabled() && !repaintContainer) {
684 LayoutState* layoutState = v->layoutState();
685 if (style()->position() == RelativePosition && layer())
686 rect.move(layer()->relativePositionOffset());
687 rect.move(layoutState->m_paintOffset);
688 if (layoutState->m_clipped)
689 rect.intersect(layoutState->m_clipRect);
694 if (repaintContainer == this)
697 bool containerSkipped;
698 RenderObject* o = container(repaintContainer, &containerSkipped);
702 IntPoint topLeft = rect.location();
704 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) {
705 RenderBlock* cb = toRenderBlock(o);
706 if (cb->hasColumns()) {
707 IntRect repaintRect(topLeft, rect.size());
708 cb->adjustRectForColumns(repaintRect);
709 topLeft = repaintRect.location();
714 if (style()->position() == RelativePosition && layer()) {
715 // Apply the relative position offset when invalidating a rectangle. The layer
716 // is translated, but the render box isn't, so we need to do this to get the
717 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
718 // flag on the RenderObject has been cleared, so use the one on the style().
719 topLeft += layer()->relativePositionOffset();
722 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
723 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
724 if (o->hasOverflowClip()) {
725 RenderBox* containerBox = toRenderBox(o);
727 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
728 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
729 // anyway if its size does change.
730 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden.
732 IntRect repaintRect(topLeft, rect.size());
733 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height());
734 rect = intersection(repaintRect, boxRect);
738 rect.setLocation(topLeft);
740 if (containerSkipped) {
741 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
742 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
743 rect.move(-containerOffset);
747 o->computeRectForRepaint(repaintContainer, rect, fixed);
750 IntSize RenderInline::offsetFromContainer(RenderObject* container, const IntPoint& point) const
752 ASSERT(container == this->container());
755 if (isRelPositioned())
756 offset += relativePositionOffset();
758 container->adjustForColumns(offset, point);
760 if (container->hasOverflowClip())
761 offset -= toRenderBox(container)->layer()->scrolledContentOffset();
766 void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
768 if (repaintContainer == this)
771 if (RenderView *v = view()) {
772 if (v->layoutStateEnabled() && !repaintContainer) {
773 LayoutState* layoutState = v->layoutState();
774 IntSize offset = layoutState->m_paintOffset;
775 if (style()->position() == RelativePosition && layer())
776 offset += layer()->relativePositionOffset();
777 transformState.move(offset);
782 bool containerSkipped;
783 RenderObject* o = container(repaintContainer, &containerSkipped);
787 IntSize containerOffset = offsetFromContainer(o, roundedIntPoint(transformState.mappedPoint()));
789 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
790 if (useTransforms && shouldUseTransformFromContainer(o)) {
791 TransformationMatrix t;
792 getTransformFromContainer(o, containerOffset, t);
793 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
795 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
797 if (containerSkipped) {
798 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
799 // to just subtract the delta between the repaintContainer and o.
800 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
801 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
805 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
808 void RenderInline::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
810 // We don't expect this function to be called during layout.
811 ASSERT(!view() || !view()->layoutStateEnabled());
813 RenderObject* o = container();
817 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
819 IntSize containerOffset = offsetFromContainer(o, IntPoint());
821 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
822 if (useTransforms && shouldUseTransformFromContainer(o)) {
823 TransformationMatrix t;
824 getTransformFromContainer(o, containerOffset, t);
825 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
827 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
830 void RenderInline::updateDragState(bool dragOn)
832 RenderBoxModelObject::updateDragState(dragOn);
834 continuation()->updateDragState(dragOn);
837 void RenderInline::childBecameNonInline(RenderObject* child)
839 // We have to split the parent flow.
840 RenderBlock* newBox = containingBlock()->createAnonymousBlock();
841 RenderBoxModelObject* oldContinuation = continuation();
842 setContinuation(newBox);
843 RenderObject* beforeChild = child->nextSibling();
844 children()->removeChildNode(this, child);
845 splitFlow(beforeChild, newBox, child, oldContinuation);
848 void RenderInline::updateHitTestResult(HitTestResult& result, const IntPoint& point)
850 if (result.innerNode())
854 IntPoint localPoint(point);
856 if (isInlineElementContinuation()) {
857 // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space
858 // of the principal renderer's containing block. This will end up being the innerNonSharedNode.
859 RenderBlock* firstBlock = n->renderer()->containingBlock();
861 // Get our containing block.
862 RenderBox* block = containingBlock();
863 localPoint.move(block->x() - firstBlock->x(), block->y() - firstBlock->y());
866 result.setInnerNode(n);
867 if (!result.innerNonSharedNode())
868 result.setInnerNonSharedNode(n);
869 result.setLocalPoint(localPoint);
873 void RenderInline::dirtyLineBoxes(bool fullLayout)
876 m_lineBoxes.deleteLineBoxes(renderArena());
878 m_lineBoxes.dirtyLineBoxes();
881 InlineFlowBox* RenderInline::createInlineFlowBox()
883 return new (renderArena()) InlineFlowBox(this);
886 InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
888 InlineFlowBox* flowBox = createInlineFlowBox();
889 m_lineBoxes.appendLineBox(flowBox);
893 int RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
895 if (firstLine && document()->usesFirstLineRules()) {
896 RenderStyle* s = style(firstLine);
898 return s->computedLineHeight();
901 if (m_lineHeight == -1)
902 m_lineHeight = style()->computedLineHeight();
907 int RenderInline::baselinePosition(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
909 const Font& f = style(firstLine)->font();
910 return f.ascent() + (lineHeight(firstLine, direction, linePositionMode) - f.height()) / 2;
913 int RenderInline::verticalPositionFromCache(bool firstLine) const
915 if (firstLine) // We're only really a first-line style if the document actually uses first-line rules.
916 firstLine = document()->usesFirstLineRules();
917 int vpos = m_verticalPosition;
918 if (m_verticalPosition == PositionUndefined || firstLine) {
919 vpos = verticalPosition(firstLine);
921 m_verticalPosition = vpos;
926 IntSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const
928 ASSERT(isRelPositioned());
929 if (!isRelPositioned())
932 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
933 // box from the rest of the content, but only in the cases where we know we're positioned
934 // relative to the inline itself.
939 if (firstLineBox()) {
940 sx = firstLineBox()->x();
941 sy = firstLineBox()->y();
943 sx = layer()->staticX();
944 sy = layer()->staticY();
947 if (!child->style()->hasStaticX())
949 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
950 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
951 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
953 else if (!child->style()->isOriginalDisplayInlineType())
954 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
955 offset.setWidth(sx - (child->containingBlock()->borderLeft() + child->containingBlock()->paddingLeft()));
957 if (!child->style()->hasStaticY())
958 offset.setHeight(sy);
963 void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
968 // FIXME: We can do better.
972 void RenderInline::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
974 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
975 RootInlineBox* root = curr->root();
976 int top = max(root->lineTop(), curr->y());
977 int bottom = min(root->lineBottom(), curr->y() + curr->logicalHeight());
978 IntRect rect(tx + curr->x(), ty + top, curr->logicalWidth(), bottom - top);
983 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
984 if (!curr->isText() && !curr->isListMarker()) {
985 FloatPoint pos(tx, ty);
986 // FIXME: This doesn't work correctly with transforms.
987 if (curr->hasLayer())
988 pos = curr->localToAbsolute();
989 else if (curr->isBox())
990 pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y());
991 curr->addFocusRingRects(rects, pos.x(), pos.y());
995 if (continuation()) {
996 if (continuation()->isInline())
997 continuation()->addFocusRingRects(rects,
998 tx - containingBlock()->x() + continuation()->containingBlock()->x(),
999 ty - containingBlock()->y() + continuation()->containingBlock()->y());
1001 continuation()->addFocusRingRects(rects,
1002 tx - containingBlock()->x() + toRenderBox(continuation())->x(),
1003 ty - containingBlock()->y() + toRenderBox(continuation())->y());
1007 void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty)
1012 RenderStyle* styleToUse = style();
1013 if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) {
1014 if (!theme()->supportsFocusRing(styleToUse)) {
1015 // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
1016 paintFocusRing(graphicsContext, tx, ty, styleToUse);
1020 if (styleToUse->outlineStyleIsAuto() || styleToUse->outlineStyle() == BNONE)
1023 Vector<IntRect> rects;
1025 rects.append(IntRect());
1026 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1027 RootInlineBox* root = curr->root();
1028 int top = max(root->lineTop(), curr->y());
1029 int bottom = min(root->lineBottom(), curr->y() + curr->logicalHeight());
1030 rects.append(IntRect(curr->x(), top, curr->logicalWidth(), bottom - top));
1032 rects.append(IntRect());
1034 for (unsigned i = 1; i < rects.size() - 1; i++)
1035 paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1));
1038 void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty,
1039 const IntRect& lastline, const IntRect& thisline, const IntRect& nextline)
1041 RenderStyle* styleToUse = style();
1042 int ow = styleToUse->outlineWidth();
1043 EBorderStyle os = styleToUse->outlineStyle();
1044 Color oc = styleToUse->visitedDependentColor(CSSPropertyOutlineColor);
1046 int offset = style()->outlineOffset();
1048 int t = ty + thisline.y() - offset;
1049 int l = tx + thisline.x() - offset;
1050 int b = ty + thisline.bottom() + offset;
1051 int r = tx + thisline.right() + offset;
1054 drawLineForBoxSide(graphicsContext,
1056 t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : 0),
1058 b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : 0),
1061 (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : -ow),
1062 (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : -ow));
1065 drawLineForBoxSide(graphicsContext,
1067 t - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : 0),
1069 b + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : 0),
1072 (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : -ow),
1073 (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : -ow));
1075 if (thisline.x() < lastline.x())
1076 drawLineForBoxSide(graphicsContext,
1079 min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())),
1083 (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow);
1085 if (lastline.right() < thisline.right())
1086 drawLineForBoxSide(graphicsContext,
1087 max(lastline.isEmpty() ? -1000000 : tx + lastline.right(), l - ow),
1092 (!lastline.isEmpty() && l - ow < tx + lastline.right()) ? -ow : ow,
1096 if (thisline.x() < nextline.x())
1097 drawLineForBoxSide(graphicsContext,
1100 min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000),
1104 (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow);
1106 if (nextline.right() < thisline.right())
1107 drawLineForBoxSide(graphicsContext,
1108 max(!nextline.isEmpty() ? tx + nextline.right() : -1000000, l - ow),
1113 (!nextline.isEmpty() && l - ow < tx + nextline.right()) ? -ow : ow,
1117 #if ENABLE(DASHBOARD_SUPPORT)
1118 void RenderInline::addDashboardRegions(Vector<DashboardRegionValue>& regions)
1120 // Convert the style regions to absolute coordinates.
1121 if (style()->visibility() != VISIBLE)
1124 const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions();
1125 unsigned i, count = styleRegions.size();
1126 for (i = 0; i < count; i++) {
1127 StyleDashboardRegion styleRegion = styleRegions[i];
1129 IntRect linesBoundingBox = this->linesBoundingBox();
1130 int w = linesBoundingBox.width();
1131 int h = linesBoundingBox.height();
1133 DashboardRegionValue region;
1134 region.label = styleRegion.label;
1135 region.bounds = IntRect(linesBoundingBox.x() + styleRegion.offset.left().value(),
1136 linesBoundingBox.y() + styleRegion.offset.top().value(),
1137 w - styleRegion.offset.left().value() - styleRegion.offset.right().value(),
1138 h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value());
1139 region.type = styleRegion.type;
1141 RenderObject* container = containingBlock();
1145 region.clip = region.bounds;
1146 container->computeAbsoluteRepaintRect(region.clip);
1147 if (region.clip.height() < 0) {
1148 region.clip.setHeight(0);
1149 region.clip.setWidth(0);
1152 FloatPoint absPos = container->localToAbsolute();
1153 region.bounds.setX(absPos.x() + region.bounds.x());
1154 region.bounds.setY(absPos.y() + region.bounds.y());
1157 float pageScaleFactor = frame()->page()->chrome()->scaleFactor();
1158 if (pageScaleFactor != 1.0f) {
1159 region.bounds.scale(pageScaleFactor);
1160 region.clip.scale(pageScaleFactor);
1164 regions.append(region);
1169 } // namespace WebCore