• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "config.h"
24 #include "RenderInline.h"
25 
26 #include "Chrome.h"
27 #include "FloatQuad.h"
28 #include "GraphicsContext.h"
29 #include "HitTestResult.h"
30 #include "Page.h"
31 #include "RenderArena.h"
32 #include "RenderBlock.h"
33 #include "RenderView.h"
34 #include "TransformState.h"
35 #include "VisiblePosition.h"
36 
37 #if ENABLE(DASHBOARD_SUPPORT)
38 #include "Frame.h"
39 #endif
40 
41 using namespace std;
42 
43 namespace WebCore {
44 
RenderInline(Node * node)45 RenderInline::RenderInline(Node* node)
46     : RenderBoxModelObject(node)
47     , m_continuation(0)
48     , m_lineHeight(-1)
49     , m_verticalPosition(PositionUndefined)
50 {
51     setChildrenInline(true);
52 }
53 
destroy()54 void RenderInline::destroy()
55 {
56     // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
57     // properly dirty line boxes that they are removed from.  Effects that do :before/:after only on hover could crash otherwise.
58     children()->destroyLeftoverChildren();
59 
60     // Destroy our continuation before anything other than anonymous children.
61     // The reason we don't destroy it before anonymous children is that they may
62     // have continuations of their own that are anonymous children of our continuation.
63     if (m_continuation) {
64         m_continuation->destroy();
65         m_continuation = 0;
66     }
67 
68     if (!documentBeingDestroyed()) {
69         if (firstLineBox()) {
70             // We can't wait for RenderBoxModelObject::destroy to clear the selection,
71             // because by then we will have nuked the line boxes.
72             // FIXME: The SelectionController should be responsible for this when it
73             // is notified of DOM mutations.
74             if (isSelectionBorder())
75                 view()->clearSelection();
76 
77             // If line boxes are contained inside a root, that means we're an inline.
78             // In that case, we need to remove all the line boxes so that the parent
79             // lines aren't pointing to deleted children. If the first line box does
80             // not have a parent that means they are either already disconnected or
81             // root lines that can just be destroyed without disconnecting.
82             if (firstLineBox()->parent()) {
83                 for (InlineRunBox* box = firstLineBox(); box; box = box->nextLineBox())
84                     box->remove();
85             }
86         } else if (isInline() && parent())
87             parent()->dirtyLinesFromChangedChild(this);
88     }
89 
90     m_lineBoxes.deleteLineBoxes(renderArena());
91 
92     RenderBoxModelObject::destroy();
93 }
94 
inlineContinuation() const95 RenderInline* RenderInline::inlineContinuation() const
96 {
97     if (!m_continuation || m_continuation->isInline())
98         return toRenderInline(m_continuation);
99     return toRenderBlock(m_continuation)->inlineContinuation();
100 }
101 
updateBoxModelInfoFromStyle()102 void RenderInline::updateBoxModelInfoFromStyle()
103 {
104     RenderBoxModelObject::updateBoxModelInfoFromStyle();
105 
106     setInline(true); // Needed for run-ins, since run-in is considered a block display type.
107 
108     // FIXME: Support transforms and reflections on inline flows someday.
109     setHasTransform(false);
110     setHasReflection(false);
111 }
112 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)113 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
114 {
115     RenderBoxModelObject::styleDidChange(diff, oldStyle);
116 
117     // Ensure that all of the split inlines pick up the new style. We
118     // only do this if we're an inline, since we don't want to propagate
119     // a block's style to the other inlines.
120     // e.g., <font>foo <h4>goo</h4> moo</font>.  The <font> inlines before
121     // and after the block share the same style, but the block doesn't
122     // need to pass its style on to anyone else.
123     for (RenderInline* currCont = inlineContinuation(); currCont; currCont = currCont->inlineContinuation()) {
124         RenderBoxModelObject* nextCont = currCont->continuation();
125         currCont->setContinuation(0);
126         currCont->setStyle(style());
127         currCont->setContinuation(nextCont);
128     }
129 
130     m_lineHeight = -1;
131 
132     // Update pseudos for :before and :after now.
133     if (!isAnonymous() && document()->usesBeforeAfterRules()) {
134         children()->updateBeforeAfterContent(this, BEFORE);
135         children()->updateBeforeAfterContent(this, AFTER);
136     }
137 }
138 
addChild(RenderObject * newChild,RenderObject * beforeChild)139 void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild)
140 {
141     if (continuation())
142         return addChildToContinuation(newChild, beforeChild);
143     return addChildIgnoringContinuation(newChild, beforeChild);
144 }
145 
nextContinuation(RenderObject * renderer)146 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
147 {
148     if (renderer->isInline() && !renderer->isReplaced())
149         return toRenderInline(renderer)->continuation();
150     return toRenderBlock(renderer)->inlineContinuation();
151 }
152 
continuationBefore(RenderObject * beforeChild)153 RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild)
154 {
155     if (beforeChild && beforeChild->parent() == this)
156         return this;
157 
158     RenderBoxModelObject* curr = nextContinuation(this);
159     RenderBoxModelObject* nextToLast = this;
160     RenderBoxModelObject* last = this;
161     while (curr) {
162         if (beforeChild && beforeChild->parent() == curr) {
163             if (curr->firstChild() == beforeChild)
164                 return last;
165             return curr;
166         }
167 
168         nextToLast = last;
169         last = curr;
170         curr = nextContinuation(curr);
171     }
172 
173     if (!beforeChild && !last->firstChild())
174         return nextToLast;
175     return last;
176 }
177 
addChildIgnoringContinuation(RenderObject * newChild,RenderObject * beforeChild)178 void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
179 {
180     // Make sure we don't append things after :after-generated content if we have it.
181     if (!beforeChild && isAfterContent(lastChild()))
182         beforeChild = lastChild();
183 
184     if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) {
185         // We are placing a block inside an inline. We have to perform a split of this
186         // inline into continuations.  This involves creating an anonymous block box to hold
187         // |newChild|.  We then make that block box a continuation of this inline.  We take all of
188         // the children after |beforeChild| and put them in a clone of this object.
189         RefPtr<RenderStyle> newStyle = RenderStyle::create();
190         newStyle->inheritFrom(style());
191         newStyle->setDisplay(BLOCK);
192 
193         RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
194         newBox->setStyle(newStyle.release());
195         RenderBoxModelObject* oldContinuation = continuation();
196         setContinuation(newBox);
197 
198         // Someone may have put a <p> inside a <q>, causing a split.  When this happens, the :after content
199         // has to move into the inline continuation.  Call updateBeforeAfterContent to ensure that our :after
200         // content gets properly destroyed.
201         bool isLastChild = (beforeChild == lastChild());
202         if (document()->usesBeforeAfterRules())
203             children()->updateBeforeAfterContent(this, AFTER);
204         if (isLastChild && beforeChild != lastChild())
205             beforeChild = 0; // We destroyed the last child, so now we need to update our insertion
206                              // point to be 0.  It's just a straight append now.
207 
208         splitFlow(beforeChild, newBox, newChild, oldContinuation);
209         return;
210     }
211 
212     RenderBoxModelObject::addChild(newChild, beforeChild);
213 
214     newChild->setNeedsLayoutAndPrefWidthsRecalc();
215 }
216 
cloneInline(RenderInline * src)217 RenderInline* RenderInline::cloneInline(RenderInline* src)
218 {
219     RenderInline* o = new (src->renderArena()) RenderInline(src->node());
220     o->setStyle(src->style());
221     return o;
222 }
223 
splitInlines(RenderBlock * fromBlock,RenderBlock * toBlock,RenderBlock * middleBlock,RenderObject * beforeChild,RenderBoxModelObject * oldCont)224 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
225                                 RenderBlock* middleBlock,
226                                 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
227 {
228     // Create a clone of this inline.
229     RenderInline* clone = cloneInline(this);
230     clone->setContinuation(oldCont);
231 
232     // Now take all of the children from beforeChild to the end and remove
233     // them from |this| and place them in the clone.
234     RenderObject* o = beforeChild;
235     while (o) {
236         RenderObject* tmp = o;
237         o = tmp->nextSibling();
238         clone->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0);
239         tmp->setNeedsLayoutAndPrefWidthsRecalc();
240     }
241 
242     // Hook |clone| up as the continuation of the middle block.
243     middleBlock->setInlineContinuation(clone);
244 
245     // We have been reparented and are now under the fromBlock.  We need
246     // to walk up our inline parent chain until we hit the containing block.
247     // Once we hit the containing block we're done.
248     RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
249     RenderBoxModelObject* currChild = this;
250 
251     // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
252     // There will eventually be a better approach to this problem that will let us nest to a much
253     // greater depth (see bugzilla bug 13430) but for now we have a limit.  This *will* result in
254     // incorrect rendering, but the alternative is to hang forever.
255     unsigned splitDepth = 1;
256     const unsigned cMaxSplitDepth = 200;
257     while (curr && curr != fromBlock) {
258         ASSERT(curr->isRenderInline());
259         if (splitDepth < cMaxSplitDepth) {
260             // Create a new clone.
261             RenderInline* cloneChild = clone;
262             clone = cloneInline(toRenderInline(curr));
263 
264             // Insert our child clone as the first child.
265             clone->addChildIgnoringContinuation(cloneChild, 0);
266 
267             // Hook the clone up as a continuation of |curr|.
268             RenderInline* inlineCurr = toRenderInline(curr);
269             oldCont = inlineCurr->continuation();
270             inlineCurr->setContinuation(clone);
271             clone->setContinuation(oldCont);
272 
273             // Someone may have indirectly caused a <q> to split.  When this happens, the :after content
274             // has to move into the inline continuation.  Call updateBeforeAfterContent to ensure that the inline's :after
275             // content gets properly destroyed.
276             if (document()->usesBeforeAfterRules())
277                 inlineCurr->children()->updateBeforeAfterContent(this, AFTER);
278 
279             // Now we need to take all of the children starting from the first child
280             // *after* currChild and append them all to the clone.
281             o = currChild->nextSibling();
282             while (o) {
283                 RenderObject* tmp = o;
284                 o = tmp->nextSibling();
285                 clone->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0);
286                 tmp->setNeedsLayoutAndPrefWidthsRecalc();
287             }
288         }
289 
290         // Keep walking up the chain.
291         currChild = curr;
292         curr = toRenderBoxModelObject(curr->parent());
293         splitDepth++;
294     }
295 
296     // Now we are at the block level. We need to put the clone into the toBlock.
297     toBlock->children()->appendChildNode(toBlock, clone);
298 
299     // Now take all the children after currChild and remove them from the fromBlock
300     // and put them in the toBlock.
301     o = currChild->nextSibling();
302     while (o) {
303         RenderObject* tmp = o;
304         o = tmp->nextSibling();
305         toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp));
306     }
307 }
308 
splitFlow(RenderObject * beforeChild,RenderBlock * newBlockBox,RenderObject * newChild,RenderBoxModelObject * oldCont)309 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
310                              RenderObject* newChild, RenderBoxModelObject* oldCont)
311 {
312     RenderBlock* pre = 0;
313     RenderBlock* block = containingBlock();
314 
315     // Delete our line boxes before we do the inline split into continuations.
316     block->deleteLineBoxTree();
317 
318     bool madeNewBeforeBlock = false;
319     if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
320         // We can reuse this block and make it the preBlock of the next continuation.
321         pre = block;
322         pre->removePositionedObjects(0);
323         block = block->containingBlock();
324     } else {
325         // No anonymous block available for use.  Make one.
326         pre = block->createAnonymousBlock();
327         madeNewBeforeBlock = true;
328     }
329 
330     RenderBlock* post = block->createAnonymousBlock();
331 
332     RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
333     if (madeNewBeforeBlock)
334         block->children()->insertChildNode(block, pre, boxFirst);
335     block->children()->insertChildNode(block, newBlockBox, boxFirst);
336     block->children()->insertChildNode(block, post, boxFirst);
337     block->setChildrenInline(false);
338 
339     if (madeNewBeforeBlock) {
340         RenderObject* o = boxFirst;
341         while (o) {
342             RenderObject* no = o;
343             o = no->nextSibling();
344             pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, no));
345             no->setNeedsLayoutAndPrefWidthsRecalc();
346         }
347     }
348 
349     splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
350 
351     // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
352     // time in makeChildrenNonInline by just setting this explicitly up front.
353     newBlockBox->setChildrenInline(false);
354 
355     // We delayed adding the newChild until now so that the |newBlockBox| would be fully
356     // connected, thus allowing newChild access to a renderArena should it need
357     // to wrap itself in additional boxes (e.g., table construction).
358     newBlockBox->addChild(newChild);
359 
360     // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
361     // get deleted properly.  Because objects moves from the pre block into the post block, we want to
362     // make new line boxes instead of leaving the old line boxes around.
363     pre->setNeedsLayoutAndPrefWidthsRecalc();
364     block->setNeedsLayoutAndPrefWidthsRecalc();
365     post->setNeedsLayoutAndPrefWidthsRecalc();
366 }
367 
addChildToContinuation(RenderObject * newChild,RenderObject * beforeChild)368 void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
369 {
370     RenderBoxModelObject* flow = continuationBefore(beforeChild);
371     ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline());
372     RenderBoxModelObject* beforeChildParent = 0;
373     if (beforeChild)
374         beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
375     else {
376         RenderBoxModelObject* cont = nextContinuation(flow);
377         if (cont)
378             beforeChildParent = cont;
379         else
380             beforeChildParent = flow;
381     }
382 
383     if (newChild->isFloatingOrPositioned())
384         return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
385 
386     // A continuation always consists of two potential candidates: an inline or an anonymous
387     // block box holding block children.
388     bool childInline = newChild->isInline();
389     bool bcpInline = beforeChildParent->isInline();
390     bool flowInline = flow->isInline();
391 
392     if (flow == beforeChildParent)
393         return flow->addChildIgnoringContinuation(newChild, beforeChild);
394     else {
395         // The goal here is to match up if we can, so that we can coalesce and create the
396         // minimal # of continuations needed for the inline.
397         if (childInline == bcpInline)
398             return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
399         else if (flowInline == childInline)
400             return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append.
401         else
402             return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
403     }
404 }
405 
paint(PaintInfo & paintInfo,int tx,int ty)406 void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty)
407 {
408     m_lineBoxes.paint(this, paintInfo, tx, ty);
409 }
410 
absoluteRects(Vector<IntRect> & rects,int tx,int ty)411 void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
412 {
413     if (InlineRunBox* curr = firstLineBox()) {
414         for (; curr; curr = curr->nextLineBox())
415             rects.append(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height()));
416     } else
417         rects.append(IntRect(tx, ty, 0, 0));
418 
419     if (continuation()) {
420         if (continuation()->isBox()) {
421             RenderBox* box = toRenderBox(continuation());
422             continuation()->absoluteRects(rects,
423                                           tx - containingBlock()->x() + box->x(),
424                                           ty - containingBlock()->y() + box->y());
425         } else
426             continuation()->absoluteRects(rects, tx - containingBlock()->x(), ty - containingBlock()->y());
427     }
428 }
429 
absoluteQuads(Vector<FloatQuad> & quads)430 void RenderInline::absoluteQuads(Vector<FloatQuad>& quads)
431 {
432     if (InlineRunBox* curr = firstLineBox()) {
433         for (; curr; curr = curr->nextLineBox()) {
434             FloatRect localRect(curr->x(), curr->y(), curr->width(), curr->height());
435             quads.append(localToAbsoluteQuad(localRect));
436         }
437     } else
438         quads.append(localToAbsoluteQuad(FloatRect()));
439 
440     if (continuation())
441         continuation()->absoluteQuads(quads);
442 }
443 
offsetLeft() const444 int RenderInline::offsetLeft() const
445 {
446     int x = RenderBoxModelObject::offsetLeft();
447     if (firstLineBox())
448         x += firstLineBox()->x();
449     return x;
450 }
451 
offsetTop() const452 int RenderInline::offsetTop() const
453 {
454     int y = RenderBoxModelObject::offsetTop();
455     if (firstLineBox())
456         y += firstLineBox()->y();
457     return y;
458 }
459 
marginLeft() const460 int RenderInline::marginLeft() const
461 {
462     Length margin = style()->marginLeft();
463     if (margin.isAuto())
464         return 0;
465     if (margin.isFixed())
466         return margin.value();
467     if (margin.isPercent())
468         return margin.calcMinValue(max(0, containingBlock()->availableWidth()));
469     return 0;
470 }
471 
marginRight() const472 int RenderInline::marginRight() const
473 {
474     Length margin = style()->marginRight();
475     if (margin.isAuto())
476         return 0;
477     if (margin.isFixed())
478         return margin.value();
479     if (margin.isPercent())
480         return margin.calcMinValue(max(0, containingBlock()->availableWidth()));
481     return 0;
482 }
483 
renderName() const484 const char* RenderInline::renderName() const
485 {
486     if (isRelPositioned())
487         return "RenderInline (relative positioned)";
488     if (isAnonymous())
489         return "RenderInline (generated)";
490     if (isRunIn())
491         return "RenderInline (run-in)";
492     return "RenderInline";
493 }
494 
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,HitTestAction hitTestAction)495 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
496                                 int x, int y, int tx, int ty, HitTestAction hitTestAction)
497 {
498     return m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction);
499 }
500 
positionForPoint(const IntPoint & point)501 VisiblePosition RenderInline::positionForPoint(const IntPoint& point)
502 {
503     // FIXME: Does not deal with relative positioned inlines (should it?)
504     RenderBlock* cb = containingBlock();
505     if (firstLineBox()) {
506         // This inline actually has a line box.  We must have clicked in the border/padding of one of these boxes.  We
507         // should try to find a result by asking our containing block.
508         return cb->positionForPoint(point);
509     }
510 
511     // Translate the coords from the pre-anonymous block to the post-anonymous block.
512     int parentBlockX = cb->x() + point.x();
513     int parentBlockY = cb->y() + point.y();
514     RenderBoxModelObject* c = continuation();
515     while (c) {
516         RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c);
517         if (c->isInline() || c->firstChild())
518             return c->positionForCoordinates(parentBlockX - contBlock->x(), parentBlockY - contBlock->y());
519         c = toRenderBlock(c)->inlineContinuation();
520     }
521 
522     return RenderBoxModelObject::positionForPoint(point);
523 }
524 
linesBoundingBox() const525 IntRect RenderInline::linesBoundingBox() const
526 {
527     IntRect result;
528 
529     // 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
530     // 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
531     // builds and help us someday figure out why.  We also put in a redundant check of lastLineBox() to avoid the crash for now.
532     ASSERT(!firstLineBox() == !lastLineBox());  // Either both are null or both exist.
533     if (firstLineBox() && lastLineBox()) {
534         // Return the width of the minimal left side and the maximal right side.
535         int leftSide = 0;
536         int rightSide = 0;
537         for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
538             if (curr == firstLineBox() || curr->x() < leftSide)
539                 leftSide = curr->x();
540             if (curr == firstLineBox() || curr->x() + curr->width() > rightSide)
541                 rightSide = curr->x() + curr->width();
542         }
543         result.setWidth(rightSide - leftSide);
544         result.setX(leftSide);
545         result.setHeight(lastLineBox()->y() + lastLineBox()->height() - firstLineBox()->y());
546         result.setY(firstLineBox()->y());
547     }
548 
549     return result;
550 }
551 
linesVisibleOverflowBoundingBox() const552 IntRect RenderInline::linesVisibleOverflowBoundingBox() const
553 {
554     if (!firstLineBox() || !lastLineBox())
555         return IntRect();
556 
557     // Return the width of the minimal left side and the maximal right side.
558     int leftSide = numeric_limits<int>::max();
559     int rightSide = numeric_limits<int>::min();
560     for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) {
561         leftSide = min(leftSide, curr->leftVisibleOverflow());
562         rightSide = max(rightSide, curr->rightVisibleOverflow());
563     }
564 
565     return IntRect(leftSide, firstLineBox()->topVisibleOverflow(), rightSide - leftSide,
566         lastLineBox()->bottomVisibleOverflow() - firstLineBox()->topVisibleOverflow());
567 }
568 
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)569 IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
570 {
571     // Only run-ins are allowed in here during layout.
572     ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn());
573 
574     if (!firstLineBox() && !continuation())
575         return IntRect();
576 
577     // Find our leftmost position.
578     IntRect boundingBox(linesVisibleOverflowBoundingBox());
579     int left = boundingBox.x();
580     int top = boundingBox.y();
581 
582     // Now invalidate a rectangle.
583     int ow = style() ? style()->outlineSize() : 0;
584 
585     // We need to add in the relative position offsets of any inlines (including us) up to our
586     // containing block.
587     RenderBlock* cb = containingBlock();
588     for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb;
589          inlineFlow = inlineFlow->parent()) {
590          if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer())
591             toRenderInline(inlineFlow)->layer()->relativePositionOffset(left, top);
592     }
593 
594     IntRect r(-ow + left, -ow + top, boundingBox.width() + ow * 2, boundingBox.height() + ow * 2);
595     if (cb->hasColumns())
596         cb->adjustRectForColumns(r);
597 
598     if (cb->hasOverflowClip()) {
599         // cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the
600         // layer's size instead.  Even if the layer's size is wrong, the layer itself will repaint
601         // anyway if its size does change.
602         int x = r.x();
603         int y = r.y();
604         IntRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height());
605         cb->layer()->subtractScrolledContentOffset(x, y); // For overflow:auto/scroll/hidden.
606         IntRect repaintRect(x, y, r.width(), r.height());
607         r = intersection(repaintRect, boxRect);
608     }
609 
610     // FIXME: need to ensure that we compute the correct repaint rect when the repaint container
611     // is an inline.
612     if (repaintContainer != this)
613         cb->computeRectForRepaint(repaintContainer, r);
614 
615     if (ow) {
616         for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
617             if (!curr->isText()) {
618                 IntRect childRect = curr->rectWithOutlineForRepaint(repaintContainer, ow);
619                 r.unite(childRect);
620             }
621         }
622 
623         if (continuation() && !continuation()->isInline()) {
624             IntRect contRect = continuation()->rectWithOutlineForRepaint(repaintContainer, ow);
625             r.unite(contRect);
626         }
627     }
628 
629     return r;
630 }
631 
rectWithOutlineForRepaint(RenderBoxModelObject * repaintContainer,int outlineWidth)632 IntRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth)
633 {
634     IntRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
635     for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
636         if (!curr->isText())
637             r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth));
638     }
639     return r;
640 }
641 
computeRectForRepaint(RenderBoxModelObject * repaintContainer,IntRect & rect,bool fixed)642 void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
643 {
644     if (RenderView* v = view()) {
645         // LayoutState is only valid for root-relative repainting
646         if (v->layoutStateEnabled() && !repaintContainer) {
647             LayoutState* layoutState = v->layoutState();
648             if (style()->position() == RelativePosition && layer())
649                 rect.move(layer()->relativePositionOffset());
650             rect.move(layoutState->m_offset);
651             if (layoutState->m_clipped)
652                 rect.intersect(layoutState->m_clipRect);
653             return;
654         }
655     }
656 
657     if (repaintContainer == this)
658         return;
659 
660     bool containerSkipped;
661     RenderObject* o = container(repaintContainer, &containerSkipped);
662     if (!o)
663         return;
664 
665     IntPoint topLeft = rect.location();
666 
667     if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) {
668         RenderBlock* cb = toRenderBlock(o);
669         if (cb->hasColumns()) {
670             IntRect repaintRect(topLeft, rect.size());
671             cb->adjustRectForColumns(repaintRect);
672             topLeft = repaintRect.location();
673             rect = repaintRect;
674         }
675     }
676 
677     if (style()->position() == RelativePosition && layer()) {
678         // Apply the relative position offset when invalidating a rectangle.  The layer
679         // is translated, but the render box isn't, so we need to do this to get the
680         // right dirty rect.  Since this is called from RenderObject::setStyle, the relative position
681         // flag on the RenderObject has been cleared, so use the one on the style().
682         topLeft += layer()->relativePositionOffset();
683     }
684 
685     // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
686     // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
687     if (o->hasOverflowClip()) {
688         RenderBox* containerBox = toRenderBox(o);
689 
690         // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
691         // layer's size instead.  Even if the layer's size is wrong, the layer itself will repaint
692         // anyway if its size does change.
693         topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden.
694 
695         IntRect repaintRect(topLeft, rect.size());
696         IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height());
697         rect = intersection(repaintRect, boxRect);
698         if (rect.isEmpty())
699             return;
700     } else
701         rect.setLocation(topLeft);
702 
703     if (containerSkipped) {
704         // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
705         IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
706         rect.move(-containerOffset);
707         return;
708     }
709 
710     o->computeRectForRepaint(repaintContainer, rect, fixed);
711 }
712 
offsetFromContainer(RenderObject * container) const713 IntSize RenderInline::offsetFromContainer(RenderObject* container) const
714 {
715     ASSERT(container == this->container());
716 
717     IntSize offset;
718     if (isRelPositioned())
719         offset += relativePositionOffset();
720 
721     if (!isInline() || isReplaced()) {
722         RenderBlock* cb;
723         if (container->isBlockFlow() && (cb = toRenderBlock(container))->hasColumns()) {
724             IntRect rect(0, 0, 1, 1);
725             cb->adjustRectForColumns(rect);
726         }
727     }
728 
729     if (container->hasOverflowClip())
730         offset -= toRenderBox(container)->layer()->scrolledContentOffset();
731 
732     return offset;
733 }
734 
mapLocalToContainer(RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState) const735 void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
736 {
737     if (repaintContainer == this)
738         return;
739 
740     if (RenderView *v = view()) {
741         if (v->layoutStateEnabled() && !repaintContainer) {
742             LayoutState* layoutState = v->layoutState();
743             IntSize offset = layoutState->m_offset;
744             if (style()->position() == RelativePosition && layer())
745                 offset += layer()->relativePositionOffset();
746             transformState.move(offset);
747             return;
748         }
749     }
750 
751     bool containerSkipped;
752     RenderObject* o = container(repaintContainer, &containerSkipped);
753     if (!o)
754         return;
755 
756     IntSize containerOffset = offsetFromContainer(o);
757 
758     bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
759     if (useTransforms && shouldUseTransformFromContainer(o)) {
760         TransformationMatrix t;
761         getTransformFromContainer(o, containerOffset, t);
762         transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
763     } else
764         transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
765 
766     if (containerSkipped) {
767         // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
768         // to just subtract the delta between the repaintContainer and o.
769         IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
770         transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
771         return;
772     }
773 
774     o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
775 }
776 
mapAbsoluteToLocalPoint(bool fixed,bool useTransforms,TransformState & transformState) const777 void RenderInline::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
778 {
779     // We don't expect this function to be called during layout.
780     ASSERT(!view() || !view()->layoutStateEnabled());
781 
782     RenderObject* o = container();
783     if (!o)
784         return;
785 
786     o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
787 
788     IntSize containerOffset = offsetFromContainer(o);
789 
790     bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
791     if (useTransforms && shouldUseTransformFromContainer(o)) {
792         TransformationMatrix t;
793         getTransformFromContainer(o, containerOffset, t);
794         transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
795     } else
796         transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
797 }
798 
updateDragState(bool dragOn)799 void RenderInline::updateDragState(bool dragOn)
800 {
801     RenderBoxModelObject::updateDragState(dragOn);
802     if (continuation())
803         continuation()->updateDragState(dragOn);
804 }
805 
childBecameNonInline(RenderObject * child)806 void RenderInline::childBecameNonInline(RenderObject* child)
807 {
808     // We have to split the parent flow.
809     RenderBlock* newBox = containingBlock()->createAnonymousBlock();
810     RenderBoxModelObject* oldContinuation = continuation();
811     setContinuation(newBox);
812     RenderObject* beforeChild = child->nextSibling();
813     children()->removeChildNode(this, child);
814     splitFlow(beforeChild, newBox, child, oldContinuation);
815 }
816 
updateHitTestResult(HitTestResult & result,const IntPoint & point)817 void RenderInline::updateHitTestResult(HitTestResult& result, const IntPoint& point)
818 {
819     if (result.innerNode())
820         return;
821 
822     Node* n = node();
823     IntPoint localPoint(point);
824     if (n) {
825         if (isInlineContinuation()) {
826             // We're in the continuation of a split inline.  Adjust our local point to be in the coordinate space
827             // of the principal renderer's containing block.  This will end up being the innerNonSharedNode.
828             RenderBlock* firstBlock = n->renderer()->containingBlock();
829 
830             // Get our containing block.
831             RenderBox* block = containingBlock();
832             localPoint.move(block->x() - firstBlock->x(), block->y() - firstBlock->y());
833         }
834 
835         result.setInnerNode(n);
836         if (!result.innerNonSharedNode())
837             result.setInnerNonSharedNode(n);
838         result.setLocalPoint(localPoint);
839     }
840 }
841 
dirtyLineBoxes(bool fullLayout)842 void RenderInline::dirtyLineBoxes(bool fullLayout)
843 {
844     if (fullLayout)
845         m_lineBoxes.deleteLineBoxes(renderArena());
846     else
847         m_lineBoxes.dirtyLineBoxes();
848 }
849 
createInlineFlowBox()850 InlineFlowBox* RenderInline::createInlineFlowBox()
851 {
852     return new (renderArena()) InlineFlowBox(this);
853 }
854 
createAndAppendInlineFlowBox()855 InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
856 {
857     InlineFlowBox* flowBox = createInlineFlowBox();
858     m_lineBoxes.appendLineBox(flowBox);
859     return flowBox;
860 }
861 
lineHeight(bool firstLine,bool) const862 int RenderInline::lineHeight(bool firstLine, bool /*isRootLineBox*/) const
863 {
864     if (firstLine && document()->usesFirstLineRules()) {
865         RenderStyle* s = style(firstLine);
866         if (s != style())
867             return s->computedLineHeight();
868     }
869 
870     if (m_lineHeight == -1)
871         m_lineHeight = style()->computedLineHeight();
872 
873     return m_lineHeight;
874 }
875 
verticalPositionFromCache(bool firstLine) const876 int RenderInline::verticalPositionFromCache(bool firstLine) const
877 {
878     if (firstLine) // We're only really a first-line style if the document actually uses first-line rules.
879         firstLine = document()->usesFirstLineRules();
880     int vpos = m_verticalPosition;
881     if (m_verticalPosition == PositionUndefined || firstLine) {
882         vpos = verticalPosition(firstLine);
883         if (!firstLine)
884             m_verticalPosition = vpos;
885     }
886     return vpos;
887 }
888 
relativePositionedInlineOffset(const RenderBox * child) const889 IntSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const
890 {
891     ASSERT(isRelPositioned());
892     if (!isRelPositioned())
893         return IntSize();
894 
895     // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
896     // box from the rest of the content, but only in the cases where we know we're positioned
897     // relative to the inline itself.
898 
899     IntSize offset;
900     int sx;
901     int sy;
902     if (firstLineBox()) {
903         sx = firstLineBox()->x();
904         sy = firstLineBox()->y();
905     } else {
906         sx = layer()->staticX();
907         sy = layer()->staticY();
908     }
909 
910     if (!child->style()->hasStaticX())
911         offset.setWidth(sx);
912     // This is not terribly intuitive, but we have to match other browsers.  Despite being a block display type inside
913     // an inline, we still keep our x locked to the left of the relative positioned inline.  Arguably the correct
914     // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
915     // do.
916     else if (!child->style()->isOriginalDisplayInlineType())
917         // Avoid adding in the left border/padding of the containing block twice.  Subtract it out.
918         offset.setWidth(sx - (child->containingBlock()->borderLeft() + child->containingBlock()->paddingLeft()));
919 
920     if (!child->style()->hasStaticY())
921         offset.setHeight(sy);
922 
923     return offset;
924 }
925 
imageChanged(WrappedImagePtr,const IntRect *)926 void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
927 {
928     if (!parent())
929         return;
930 
931     // FIXME: We can do better.
932     repaint();
933 }
934 
addFocusRingRects(Vector<IntRect> & rects,int tx,int ty)935 void RenderInline::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
936 {
937     for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
938         RootInlineBox* root = curr->root();
939         int top = max(root->lineTop(), curr->y());
940         int bottom = min(root->lineBottom(), curr->y() + curr->height());
941         IntRect rect(tx + curr->x(), ty + top, curr->width(), bottom - top);
942         if (!rect.isEmpty())
943             rects.append(rect);
944     }
945 
946     for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
947         if (!curr->isText() && !curr->isListMarker()) {
948             FloatPoint pos(tx, ty);
949             // FIXME: This doesn't work correctly with transforms.
950             if (curr->hasLayer())
951                 pos = curr->localToAbsolute();
952             else if (curr->isBox())
953                 pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y());
954            curr->addFocusRingRects(rects, pos.x(), pos.y());
955         }
956     }
957 
958     if (continuation()) {
959         if (continuation()->isInline())
960             continuation()->addFocusRingRects(rects,
961                                               tx - containingBlock()->x() + continuation()->containingBlock()->x(),
962                                               ty - containingBlock()->y() + continuation()->containingBlock()->y());
963         else
964             continuation()->addFocusRingRects(rects,
965                                               tx - containingBlock()->x() + toRenderBox(continuation())->x(),
966                                               ty - containingBlock()->y() + toRenderBox(continuation())->y());
967     }
968 }
969 
paintOutline(GraphicsContext * graphicsContext,int tx,int ty)970 void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty)
971 {
972     if (!hasOutline())
973         return;
974 
975     if (style()->outlineStyleIsAuto() || hasOutlineAnnotation()) {
976         int ow = style()->outlineWidth();
977         Color oc = style()->outlineColor();
978         if (!oc.isValid())
979             oc = style()->color();
980 
981         Vector<IntRect> focusRingRects;
982         addFocusRingRects(focusRingRects, tx, ty);
983         if (style()->outlineStyleIsAuto())
984             graphicsContext->drawFocusRing(focusRingRects, ow, style()->outlineOffset(), oc);
985         else
986             addPDFURLRect(graphicsContext, unionRect(focusRingRects));
987     }
988 
989     if (style()->outlineStyleIsAuto() || style()->outlineStyle() == BNONE)
990         return;
991 
992     Vector<IntRect> rects;
993 
994     rects.append(IntRect());
995     for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
996         RootInlineBox* root = curr->root();
997         int top = max(root->lineTop(), curr->y());
998         int bottom = min(root->lineBottom(), curr->y() + curr->height());
999         rects.append(IntRect(curr->x(), top, curr->width(), bottom - top));
1000     }
1001     rects.append(IntRect());
1002 
1003     for (unsigned i = 1; i < rects.size() - 1; i++)
1004         paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1));
1005 }
1006 
paintOutlineForLine(GraphicsContext * graphicsContext,int tx,int ty,const IntRect & lastline,const IntRect & thisline,const IntRect & nextline)1007 void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty,
1008                                        const IntRect& lastline, const IntRect& thisline, const IntRect& nextline)
1009 {
1010     int ow = style()->outlineWidth();
1011     EBorderStyle os = style()->outlineStyle();
1012     Color oc = style()->outlineColor();
1013     if (!oc.isValid())
1014         oc = style()->color();
1015 
1016     int offset = style()->outlineOffset();
1017 
1018     int t = ty + thisline.y() - offset;
1019     int l = tx + thisline.x() - offset;
1020     int b = ty + thisline.bottom() + offset;
1021     int r = tx + thisline.right() + offset;
1022 
1023     // left edge
1024     drawLineForBoxSide(graphicsContext,
1025                l - ow,
1026                t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : 0),
1027                l,
1028                b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : 0),
1029                BSLeft,
1030                oc, style()->color(), os,
1031                (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : -ow),
1032                (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : -ow));
1033 
1034     // right edge
1035     drawLineForBoxSide(graphicsContext,
1036                r,
1037                t - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : 0),
1038                r + ow,
1039                b + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : 0),
1040                BSRight,
1041                oc, style()->color(), os,
1042                (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : -ow),
1043                (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : -ow));
1044     // upper edge
1045     if (thisline.x() < lastline.x())
1046         drawLineForBoxSide(graphicsContext,
1047                    l - ow,
1048                    t - ow,
1049                    min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())),
1050                    t ,
1051                    BSTop, oc, style()->color(), os,
1052                    ow,
1053                    (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow);
1054 
1055     if (lastline.right() < thisline.right())
1056         drawLineForBoxSide(graphicsContext,
1057                    max(lastline.isEmpty() ? -1000000 : tx + lastline.right(), l - ow),
1058                    t - ow,
1059                    r + ow,
1060                    t ,
1061                    BSTop, oc, style()->color(), os,
1062                    (!lastline.isEmpty() && l - ow < tx + lastline.right()) ? -ow : ow,
1063                    ow);
1064 
1065     // lower edge
1066     if (thisline.x() < nextline.x())
1067         drawLineForBoxSide(graphicsContext,
1068                    l - ow,
1069                    b,
1070                    min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000),
1071                    b + ow,
1072                    BSBottom, oc, style()->color(), os,
1073                    ow,
1074                    (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow);
1075 
1076     if (nextline.right() < thisline.right())
1077         drawLineForBoxSide(graphicsContext,
1078                    max(!nextline.isEmpty() ? tx + nextline.right() : -1000000, l - ow),
1079                    b,
1080                    r + ow,
1081                    b + ow,
1082                    BSBottom, oc, style()->color(), os,
1083                    (!nextline.isEmpty() && l - ow < tx + nextline.right()) ? -ow : ow,
1084                    ow);
1085 }
1086 
1087 #if ENABLE(DASHBOARD_SUPPORT)
addDashboardRegions(Vector<DashboardRegionValue> & regions)1088 void RenderInline::addDashboardRegions(Vector<DashboardRegionValue>& regions)
1089 {
1090     // Convert the style regions to absolute coordinates.
1091     if (style()->visibility() != VISIBLE)
1092         return;
1093 
1094     const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions();
1095     unsigned i, count = styleRegions.size();
1096     for (i = 0; i < count; i++) {
1097         StyleDashboardRegion styleRegion = styleRegions[i];
1098 
1099         IntRect linesBoundingBox = this->linesBoundingBox();
1100         int w = linesBoundingBox.width();
1101         int h = linesBoundingBox.height();
1102 
1103         DashboardRegionValue region;
1104         region.label = styleRegion.label;
1105         region.bounds = IntRect(linesBoundingBox.x() + styleRegion.offset.left().value(),
1106                                 linesBoundingBox.y() + styleRegion.offset.top().value(),
1107                                 w - styleRegion.offset.left().value() - styleRegion.offset.right().value(),
1108                                 h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value());
1109         region.type = styleRegion.type;
1110 
1111         RenderObject* container = containingBlock();
1112         if (!container)
1113             container = this;
1114 
1115         region.clip = region.bounds;
1116         container->computeAbsoluteRepaintRect(region.clip);
1117         if (region.clip.height() < 0) {
1118             region.clip.setHeight(0);
1119             region.clip.setWidth(0);
1120         }
1121 
1122         FloatPoint absPos = container->localToAbsolute();
1123         region.bounds.setX(absPos.x() + region.bounds.x());
1124         region.bounds.setY(absPos.y() + region.bounds.y());
1125 
1126         if (document()->frame()) {
1127             float pageScaleFactor = document()->frame()->page()->chrome()->scaleFactor();
1128             if (pageScaleFactor != 1.0f) {
1129                 region.bounds.scale(pageScaleFactor);
1130                 region.clip.scale(pageScaleFactor);
1131             }
1132         }
1133 
1134         regions.append(region);
1135     }
1136 }
1137 #endif
1138 
1139 } // namespace WebCore
1140