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