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