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 "InlineTextBox.h"
31 #include "Page.h"
32 #include "RenderArena.h"
33 #include "RenderBlock.h"
34 #include "RenderLayer.h"
35 #include "RenderTheme.h"
36 #include "RenderView.h"
37 #include "TransformState.h"
38 #include "VisiblePosition.h"
39
40 #if ENABLE(DASHBOARD_SUPPORT)
41 #include "Frame.h"
42 #endif
43
44 using namespace std;
45
46 namespace WebCore {
47
RenderInline(Node * node)48 RenderInline::RenderInline(Node* node)
49 : RenderBoxModelObject(node)
50 , m_lineHeight(-1)
51 , m_alwaysCreateLineBoxes(false)
52 {
53 setChildrenInline(true);
54 }
55
destroy()56 void RenderInline::destroy()
57 {
58 #ifndef NDEBUG
59 // Make sure we do not retain "this" in the continuation outline table map of our containing blocks.
60 if (parent() && style()->visibility() == VISIBLE && hasOutline()) {
61 bool containingBlockPaintsContinuationOutline = continuation() || isInlineElementContinuation();
62 if (containingBlockPaintsContinuationOutline) {
63 if (RenderBlock* cb = containingBlock()) {
64 if (RenderBlock* cbCb = cb->containingBlock())
65 ASSERT(!cbCb->paintsContinuationOutline(this));
66 }
67 }
68 }
69 #endif
70
71 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
72 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
73 children()->destroyLeftoverChildren();
74
75 // Destroy our continuation before anything other than anonymous children.
76 // The reason we don't destroy it before anonymous children is that they may
77 // have continuations of their own that are anonymous children of our continuation.
78 RenderBoxModelObject* continuation = this->continuation();
79 if (continuation) {
80 continuation->destroy();
81 setContinuation(0);
82 }
83
84 if (!documentBeingDestroyed()) {
85 if (firstLineBox()) {
86 // We can't wait for RenderBoxModelObject::destroy to clear the selection,
87 // because by then we will have nuked the line boxes.
88 // FIXME: The SelectionController should be responsible for this when it
89 // is notified of DOM mutations.
90 if (isSelectionBorder())
91 view()->clearSelection();
92
93 // If line boxes are contained inside a root, that means we're an inline.
94 // In that case, we need to remove all the line boxes so that the parent
95 // lines aren't pointing to deleted children. If the first line box does
96 // not have a parent that means they are either already disconnected or
97 // root lines that can just be destroyed without disconnecting.
98 if (firstLineBox()->parent()) {
99 for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
100 box->remove();
101 }
102 } else if (isInline() && parent())
103 parent()->dirtyLinesFromChangedChild(this);
104 }
105
106 m_lineBoxes.deleteLineBoxes(renderArena());
107
108 RenderBoxModelObject::destroy();
109 }
110
inlineElementContinuation() const111 RenderInline* RenderInline::inlineElementContinuation() const
112 {
113 RenderBoxModelObject* continuation = this->continuation();
114 if (!continuation || continuation->isInline())
115 return toRenderInline(continuation);
116 return toRenderBlock(continuation)->inlineElementContinuation();
117 }
118
updateBoxModelInfoFromStyle()119 void RenderInline::updateBoxModelInfoFromStyle()
120 {
121 RenderBoxModelObject::updateBoxModelInfoFromStyle();
122
123 setInline(true); // Needed for run-ins, since run-in is considered a block display type.
124
125 // FIXME: Support transforms and reflections on inline flows someday.
126 setHasTransform(false);
127 setHasReflection(false);
128 }
129
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)130 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
131 {
132 RenderBoxModelObject::styleDidChange(diff, oldStyle);
133
134 // Ensure that all of the split inlines pick up the new style. We
135 // only do this if we're an inline, since we don't want to propagate
136 // a block's style to the other inlines.
137 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
138 // and after the block share the same style, but the block doesn't
139 // need to pass its style on to anyone else.
140 for (RenderInline* currCont = inlineElementContinuation(); currCont; currCont = currCont->inlineElementContinuation()) {
141 RenderBoxModelObject* nextCont = currCont->continuation();
142 currCont->setContinuation(0);
143 currCont->setStyle(style());
144 currCont->setContinuation(nextCont);
145 }
146
147 m_lineHeight = -1;
148
149 if (!m_alwaysCreateLineBoxes) {
150 bool alwaysCreateLineBoxes = hasSelfPaintingLayer() || hasBoxDecorations() || style()->hasPadding() || style()->hasMargin() || style()->hasOutline();
151 if (oldStyle && alwaysCreateLineBoxes) {
152 dirtyLineBoxes(false);
153 setNeedsLayout(true);
154 }
155 m_alwaysCreateLineBoxes = alwaysCreateLineBoxes;
156 }
157
158 // Update pseudos for :before and :after now.
159 if (!isAnonymous() && document()->usesBeforeAfterRules()) {
160 children()->updateBeforeAfterContent(this, BEFORE);
161 children()->updateBeforeAfterContent(this, AFTER);
162 }
163 }
164
updateAlwaysCreateLineBoxes()165 void RenderInline::updateAlwaysCreateLineBoxes()
166 {
167 // Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the
168 // background color will only cause a layout on the first rollover.
169 if (m_alwaysCreateLineBoxes)
170 return;
171
172 RenderStyle* parentStyle = parent()->style();
173 RenderInline* parentRenderInline = parent()->isRenderInline() ? toRenderInline(parent()) : 0;
174 bool checkFonts = document()->inNoQuirksMode();
175 bool alwaysCreateLineBoxes = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes())
176 || (parentRenderInline && parentStyle->verticalAlign() != BASELINE)
177 || style()->verticalAlign() != BASELINE
178 || style()->textEmphasisMark() != TextEmphasisMarkNone
179 || (checkFonts && (!parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->font().fontMetrics())
180 || parentStyle->lineHeight() != style()->lineHeight()));
181
182 if (!alwaysCreateLineBoxes && checkFonts && document()->usesFirstLineRules()) {
183 // Have to check the first line style as well.
184 parentStyle = parent()->style(true);
185 RenderStyle* childStyle = style(true);
186 alwaysCreateLineBoxes = !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics())
187 || childStyle->verticalAlign() != BASELINE
188 || parentStyle->lineHeight() != childStyle->lineHeight();
189 }
190
191 if (alwaysCreateLineBoxes) {
192 dirtyLineBoxes(false);
193 m_alwaysCreateLineBoxes = true;
194 }
195 }
196
addChild(RenderObject * newChild,RenderObject * beforeChild)197 void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild)
198 {
199 if (continuation())
200 return addChildToContinuation(newChild, beforeChild);
201 return addChildIgnoringContinuation(newChild, beforeChild);
202 }
203
nextContinuation(RenderObject * renderer)204 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
205 {
206 if (renderer->isInline() && !renderer->isReplaced())
207 return toRenderInline(renderer)->continuation();
208 return toRenderBlock(renderer)->inlineElementContinuation();
209 }
210
continuationBefore(RenderObject * beforeChild)211 RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild)
212 {
213 if (beforeChild && beforeChild->parent() == this)
214 return this;
215
216 RenderBoxModelObject* curr = nextContinuation(this);
217 RenderBoxModelObject* nextToLast = this;
218 RenderBoxModelObject* last = this;
219 while (curr) {
220 if (beforeChild && beforeChild->parent() == curr) {
221 if (curr->firstChild() == beforeChild)
222 return last;
223 return curr;
224 }
225
226 nextToLast = last;
227 last = curr;
228 curr = nextContinuation(curr);
229 }
230
231 if (!beforeChild && !last->firstChild())
232 return nextToLast;
233 return last;
234 }
235
addChildIgnoringContinuation(RenderObject * newChild,RenderObject * beforeChild)236 void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
237 {
238 // Make sure we don't append things after :after-generated content if we have it.
239 if (!beforeChild && isAfterContent(lastChild()))
240 beforeChild = lastChild();
241
242 if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) {
243 // We are placing a block inside an inline. We have to perform a split of this
244 // inline into continuations. This involves creating an anonymous block box to hold
245 // |newChild|. We then make that block box a continuation of this inline. We take all of
246 // the children after |beforeChild| and put them in a clone of this object.
247 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style());
248 newStyle->setDisplay(BLOCK);
249
250 RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
251 newBox->setStyle(newStyle.release());
252 RenderBoxModelObject* oldContinuation = continuation();
253 setContinuation(newBox);
254
255 // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content
256 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after
257 // content gets properly destroyed.
258 bool isLastChild = (beforeChild == lastChild());
259 if (document()->usesBeforeAfterRules())
260 children()->updateBeforeAfterContent(this, AFTER);
261 if (isLastChild && beforeChild != lastChild())
262 beforeChild = 0; // We destroyed the last child, so now we need to update our insertion
263 // point to be 0. It's just a straight append now.
264
265 splitFlow(beforeChild, newBox, newChild, oldContinuation);
266 return;
267 }
268
269 RenderBoxModelObject::addChild(newChild, beforeChild);
270
271 newChild->setNeedsLayoutAndPrefWidthsRecalc();
272 }
273
cloneInline(RenderInline * src)274 RenderInline* RenderInline::cloneInline(RenderInline* src)
275 {
276 RenderInline* o = new (src->renderArena()) RenderInline(src->node());
277 o->setStyle(src->style());
278 return o;
279 }
280
splitInlines(RenderBlock * fromBlock,RenderBlock * toBlock,RenderBlock * middleBlock,RenderObject * beforeChild,RenderBoxModelObject * oldCont)281 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
282 RenderBlock* middleBlock,
283 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
284 {
285 // Create a clone of this inline.
286 RenderInline* clone = cloneInline(this);
287 clone->setContinuation(oldCont);
288
289 // Now take all of the children from beforeChild to the end and remove
290 // them from |this| and place them in the clone.
291 RenderObject* o = beforeChild;
292 while (o) {
293 RenderObject* tmp = o;
294 o = tmp->nextSibling();
295 clone->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0);
296 tmp->setNeedsLayoutAndPrefWidthsRecalc();
297 }
298
299 // Hook |clone| up as the continuation of the middle block.
300 middleBlock->setContinuation(clone);
301
302 // We have been reparented and are now under the fromBlock. We need
303 // to walk up our inline parent chain until we hit the containing block.
304 // Once we hit the containing block we're done.
305 RenderBoxModelObject* curr = toRenderBoxModelObject(parent());
306 RenderBoxModelObject* currChild = this;
307
308 // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
309 // There will eventually be a better approach to this problem that will let us nest to a much
310 // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in
311 // incorrect rendering, but the alternative is to hang forever.
312 unsigned splitDepth = 1;
313 const unsigned cMaxSplitDepth = 200;
314 while (curr && curr != fromBlock) {
315 ASSERT(curr->isRenderInline());
316 if (splitDepth < cMaxSplitDepth) {
317 // Create a new clone.
318 RenderInline* cloneChild = clone;
319 clone = cloneInline(toRenderInline(curr));
320
321 // Insert our child clone as the first child.
322 clone->addChildIgnoringContinuation(cloneChild, 0);
323
324 // Hook the clone up as a continuation of |curr|.
325 RenderInline* inlineCurr = toRenderInline(curr);
326 oldCont = inlineCurr->continuation();
327 inlineCurr->setContinuation(clone);
328 clone->setContinuation(oldCont);
329
330 // Someone may have indirectly caused a <q> to split. When this happens, the :after content
331 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after
332 // content gets properly destroyed.
333 if (document()->usesBeforeAfterRules())
334 inlineCurr->children()->updateBeforeAfterContent(inlineCurr, AFTER);
335
336 // Now we need to take all of the children starting from the first child
337 // *after* currChild and append them all to the clone.
338 o = currChild->nextSibling();
339 while (o) {
340 RenderObject* tmp = o;
341 o = tmp->nextSibling();
342 clone->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0);
343 tmp->setNeedsLayoutAndPrefWidthsRecalc();
344 }
345 }
346
347 // Keep walking up the chain.
348 currChild = curr;
349 curr = toRenderBoxModelObject(curr->parent());
350 splitDepth++;
351 }
352
353 // Now we are at the block level. We need to put the clone into the toBlock.
354 toBlock->children()->appendChildNode(toBlock, clone);
355
356 // Now take all the children after currChild and remove them from the fromBlock
357 // and put them in the toBlock.
358 o = currChild->nextSibling();
359 while (o) {
360 RenderObject* tmp = o;
361 o = tmp->nextSibling();
362 toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp));
363 }
364 }
365
splitFlow(RenderObject * beforeChild,RenderBlock * newBlockBox,RenderObject * newChild,RenderBoxModelObject * oldCont)366 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
367 RenderObject* newChild, RenderBoxModelObject* oldCont)
368 {
369 RenderBlock* pre = 0;
370 RenderBlock* block = containingBlock();
371
372 // Delete our line boxes before we do the inline split into continuations.
373 block->deleteLineBoxTree();
374
375 bool madeNewBeforeBlock = false;
376 if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
377 // We can reuse this block and make it the preBlock of the next continuation.
378 pre = block;
379 pre->removePositionedObjects(0);
380 block = block->containingBlock();
381 } else {
382 // No anonymous block available for use. Make one.
383 pre = block->createAnonymousBlock();
384 madeNewBeforeBlock = true;
385 }
386
387 RenderBlock* post = block->createAnonymousBlock();
388
389 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
390 if (madeNewBeforeBlock)
391 block->children()->insertChildNode(block, pre, boxFirst);
392 block->children()->insertChildNode(block, newBlockBox, boxFirst);
393 block->children()->insertChildNode(block, post, boxFirst);
394 block->setChildrenInline(false);
395
396 if (madeNewBeforeBlock) {
397 RenderObject* o = boxFirst;
398 while (o) {
399 RenderObject* no = o;
400 o = no->nextSibling();
401 pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, no));
402 no->setNeedsLayoutAndPrefWidthsRecalc();
403 }
404 }
405
406 splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
407
408 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
409 // time in makeChildrenNonInline by just setting this explicitly up front.
410 newBlockBox->setChildrenInline(false);
411
412 // We delayed adding the newChild until now so that the |newBlockBox| would be fully
413 // connected, thus allowing newChild access to a renderArena should it need
414 // to wrap itself in additional boxes (e.g., table construction).
415 newBlockBox->addChild(newChild);
416
417 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
418 // get deleted properly. Because objects moves from the pre block into the post block, we want to
419 // make new line boxes instead of leaving the old line boxes around.
420 pre->setNeedsLayoutAndPrefWidthsRecalc();
421 block->setNeedsLayoutAndPrefWidthsRecalc();
422 post->setNeedsLayoutAndPrefWidthsRecalc();
423 }
424
addChildToContinuation(RenderObject * newChild,RenderObject * beforeChild)425 void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
426 {
427 RenderBoxModelObject* flow = continuationBefore(beforeChild);
428 ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline());
429 RenderBoxModelObject* beforeChildParent = 0;
430 if (beforeChild)
431 beforeChildParent = toRenderBoxModelObject(beforeChild->parent());
432 else {
433 RenderBoxModelObject* cont = nextContinuation(flow);
434 if (cont)
435 beforeChildParent = cont;
436 else
437 beforeChildParent = flow;
438 }
439
440 if (newChild->isFloatingOrPositioned())
441 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
442
443 // A continuation always consists of two potential candidates: an inline or an anonymous
444 // block box holding block children.
445 bool childInline = newChild->isInline();
446 bool bcpInline = beforeChildParent->isInline();
447 bool flowInline = flow->isInline();
448
449 if (flow == beforeChildParent)
450 return flow->addChildIgnoringContinuation(newChild, beforeChild);
451 else {
452 // The goal here is to match up if we can, so that we can coalesce and create the
453 // minimal # of continuations needed for the inline.
454 if (childInline == bcpInline)
455 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
456 else if (flowInline == childInline)
457 return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append.
458 else
459 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
460 }
461 }
462
paint(PaintInfo & paintInfo,int tx,int ty)463 void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty)
464 {
465 m_lineBoxes.paint(this, paintInfo, tx, ty);
466 }
467
absoluteRects(Vector<IntRect> & rects,int tx,int ty)468 void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
469 {
470 if (!alwaysCreateLineBoxes())
471 culledInlineAbsoluteRects(this, rects, IntSize(tx, ty));
472 else if (InlineFlowBox* curr = firstLineBox()) {
473 for (; curr; curr = curr->nextLineBox())
474 rects.append(enclosingIntRect(FloatRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())));
475 } else
476 rects.append(IntRect(tx, ty, 0, 0));
477
478 if (continuation()) {
479 if (continuation()->isBox()) {
480 RenderBox* box = toRenderBox(continuation());
481 continuation()->absoluteRects(rects,
482 tx - containingBlock()->x() + box->x(),
483 ty - containingBlock()->y() + box->y());
484 } else
485 continuation()->absoluteRects(rects, tx - containingBlock()->x(), ty - containingBlock()->y());
486 }
487 }
488
culledInlineAbsoluteRects(const RenderInline * container,Vector<IntRect> & rects,const IntSize & offset)489 void RenderInline::culledInlineAbsoluteRects(const RenderInline* container, Vector<IntRect>& rects, const IntSize& offset)
490 {
491 bool isHorizontal = style()->isHorizontalWritingMode();
492 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
493 if (curr->isFloatingOrPositioned())
494 continue;
495
496 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
497 // direction (aligned to the root box's baseline).
498 if (curr->isBox()) {
499 RenderBox* currBox = toRenderBox(curr);
500 if (currBox->inlineBoxWrapper()) {
501 RootInlineBox* rootBox = currBox->inlineBoxWrapper()->root();
502 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
503 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
504 FloatRect result;
505 if (isHorizontal)
506 result = FloatRect(offset.width() + currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), offset.height() + logicalTop, currBox->width() + currBox->marginLeft() + currBox->marginRight(), logicalHeight);
507 else
508 result = FloatRect(offset.width() + logicalTop, offset.height() + currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, currBox->height() + currBox->marginTop() + currBox->marginBottom());
509 rects.append(enclosingIntRect(result));
510 }
511 } else if (curr->isRenderInline()) {
512 // If the child doesn't need line boxes either, then we can recur.
513 RenderInline* currInline = toRenderInline(curr);
514 if (!currInline->alwaysCreateLineBoxes())
515 currInline->culledInlineAbsoluteRects(container, rects, offset);
516 else {
517 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
518 RootInlineBox* rootBox = childLine->root();
519 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
520 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
521 FloatRect result;
522 if (isHorizontal)
523 result = FloatRect(offset.width() + childLine->x() - childLine->marginLogicalLeft(),
524 offset.height() + logicalTop,
525 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
526 logicalHeight);
527 else
528 result = FloatRect(offset.width() + logicalTop,
529 offset.height() + childLine->y() - childLine->marginLogicalLeft(),
530 logicalHeight,
531 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight());
532 rects.append(enclosingIntRect(result));
533 }
534 }
535 } else if (curr->isText()) {
536 RenderText* currText = toRenderText(curr);
537 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
538 RootInlineBox* rootBox = childText->root();
539 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
540 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
541 FloatRect result;
542 if (isHorizontal)
543 result = FloatRect(offset.width() + childText->x(), offset.height() + logicalTop, childText->logicalWidth(), logicalHeight);
544 else
545 result = FloatRect(offset.width() + logicalTop, offset.height() + childText->y(), logicalHeight, childText->logicalWidth());
546 rects.append(enclosingIntRect(result));
547 }
548 }
549 }
550 }
551
absoluteQuads(Vector<FloatQuad> & quads)552 void RenderInline::absoluteQuads(Vector<FloatQuad>& quads)
553 {
554 if (!alwaysCreateLineBoxes())
555 culledInlineAbsoluteQuads(this, quads);
556 else if (InlineFlowBox* curr = firstLineBox()) {
557 for (; curr; curr = curr->nextLineBox()) {
558 FloatRect localRect(curr->x(), curr->y(), curr->width(), curr->height());
559 quads.append(localToAbsoluteQuad(localRect));
560 }
561 } else
562 quads.append(localToAbsoluteQuad(FloatRect()));
563
564 if (continuation())
565 continuation()->absoluteQuads(quads);
566 }
567
culledInlineAbsoluteQuads(const RenderInline * container,Vector<FloatQuad> & quads)568 void RenderInline::culledInlineAbsoluteQuads(const RenderInline* container, Vector<FloatQuad>& quads)
569 {
570 if (!culledInlineFirstLineBox()) {
571 quads.append(localToAbsoluteQuad(FloatRect()));
572 return;
573 }
574
575 bool isHorizontal = style()->isHorizontalWritingMode();
576 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
577 if (curr->isFloatingOrPositioned())
578 continue;
579
580 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
581 // direction (aligned to the root box's baseline).
582 if (curr->isBox()) {
583 RenderBox* currBox = toRenderBox(curr);
584 if (currBox->inlineBoxWrapper()) {
585 RootInlineBox* rootBox = currBox->inlineBoxWrapper()->root();
586 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
587 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
588 FloatRect result;
589 if (isHorizontal)
590 result = FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, currBox->width() + currBox->marginLeft() + currBox->marginRight(), logicalHeight);
591 else
592 result = FloatRect(logicalTop, currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, currBox->height() + currBox->marginTop() + currBox->marginBottom());
593 quads.append(localToAbsoluteQuad(result));
594 }
595 } else if (curr->isRenderInline()) {
596 // If the child doesn't need line boxes either, then we can recur.
597 RenderInline* currInline = toRenderInline(curr);
598 if (!currInline->alwaysCreateLineBoxes())
599 currInline->culledInlineAbsoluteQuads(container, quads);
600 else {
601 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
602 RootInlineBox* rootBox = childLine->root();
603 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
604 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
605 FloatRect result;
606 if (isHorizontal)
607 result = FloatRect(childLine->x() - childLine->marginLogicalLeft(),
608 logicalTop,
609 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
610 logicalHeight);
611 else
612 result = FloatRect(logicalTop,
613 childLine->y() - childLine->marginLogicalLeft(),
614 logicalHeight,
615 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight());
616 quads.append(localToAbsoluteQuad(result));
617 }
618 }
619 } else if (curr->isText()) {
620 RenderText* currText = toRenderText(curr);
621 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
622 RootInlineBox* rootBox = childText->root();
623 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
624 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
625 FloatRect result;
626 if (isHorizontal)
627 result = FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight);
628 else
629 result = FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth());
630 quads.append(localToAbsoluteQuad(result));
631 }
632 }
633 }
634 }
635
offsetLeft() const636 int RenderInline::offsetLeft() const
637 {
638 int x = RenderBoxModelObject::offsetLeft();
639 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
640 x += firstBox->x();
641 return x;
642 }
643
offsetTop() const644 int RenderInline::offsetTop() const
645 {
646 int y = RenderBoxModelObject::offsetTop();
647 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
648 y += firstBox->y();
649 return y;
650 }
651
computeMargin(const RenderInline * renderer,const Length & margin)652 static int computeMargin(const RenderInline* renderer, const Length& margin)
653 {
654 if (margin.isAuto())
655 return 0;
656 if (margin.isFixed())
657 return margin.value();
658 if (margin.isPercent())
659 return margin.calcMinValue(max(0, renderer->containingBlock()->availableLogicalWidth()));
660 return 0;
661 }
662
marginLeft() const663 int RenderInline::marginLeft() const
664 {
665 return computeMargin(this, style()->marginLeft());
666 }
667
marginRight() const668 int RenderInline::marginRight() const
669 {
670 return computeMargin(this, style()->marginRight());
671 }
672
marginTop() const673 int RenderInline::marginTop() const
674 {
675 return computeMargin(this, style()->marginTop());
676 }
677
marginBottom() const678 int RenderInline::marginBottom() const
679 {
680 return computeMargin(this, style()->marginBottom());
681 }
682
marginStart() const683 int RenderInline::marginStart() const
684 {
685 return computeMargin(this, style()->marginStart());
686 }
687
marginEnd() const688 int RenderInline::marginEnd() const
689 {
690 return computeMargin(this, style()->marginEnd());
691 }
692
marginBefore() const693 int RenderInline::marginBefore() const
694 {
695 return computeMargin(this, style()->marginBefore());
696 }
697
marginAfter() const698 int RenderInline::marginAfter() const
699 {
700 return computeMargin(this, style()->marginAfter());
701 }
702
renderName() const703 const char* RenderInline::renderName() const
704 {
705 if (isRelPositioned())
706 return "RenderInline (relative positioned)";
707 if (isAnonymous())
708 return "RenderInline (generated)";
709 if (isRunIn())
710 return "RenderInline (run-in)";
711 return "RenderInline";
712 }
713
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,HitTestAction hitTestAction)714 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
715 int x, int y, int tx, int ty, HitTestAction hitTestAction)
716 {
717 return m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction);
718 }
719
positionForPoint(const IntPoint & point)720 VisiblePosition RenderInline::positionForPoint(const IntPoint& point)
721 {
722 // FIXME: Does not deal with relative positioned inlines (should it?)
723 RenderBlock* cb = containingBlock();
724 if (firstLineBox()) {
725 // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We
726 // should try to find a result by asking our containing block.
727 return cb->positionForPoint(point);
728 }
729
730 // Translate the coords from the pre-anonymous block to the post-anonymous block.
731 int parentBlockX = cb->x() + point.x();
732 int parentBlockY = cb->y() + point.y();
733 RenderBoxModelObject* c = continuation();
734 while (c) {
735 RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c);
736 if (c->isInline() || c->firstChild())
737 return c->positionForCoordinates(parentBlockX - contBlock->x(), parentBlockY - contBlock->y());
738 c = toRenderBlock(c)->inlineElementContinuation();
739 }
740
741 return RenderBoxModelObject::positionForPoint(point);
742 }
743
linesBoundingBox() const744 IntRect RenderInline::linesBoundingBox() const
745 {
746 if (!alwaysCreateLineBoxes()) {
747 ASSERT(!firstLineBox());
748 return enclosingIntRect(culledInlineBoundingBox(this));
749 }
750
751 IntRect result;
752
753 // 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
754 // 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
755 // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
756 ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
757 if (firstLineBox() && lastLineBox()) {
758 // Return the width of the minimal left side and the maximal right side.
759 float logicalLeftSide = 0;
760 float logicalRightSide = 0;
761 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
762 if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
763 logicalLeftSide = curr->logicalLeft();
764 if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
765 logicalRightSide = curr->logicalRight();
766 }
767
768 bool isHorizontal = style()->isHorizontalWritingMode();
769
770 float x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
771 float y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
772 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
773 float height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
774 result = enclosingIntRect(FloatRect(x, y, width, height));
775 }
776
777 return result;
778 }
779
culledInlineBoundingBox(const RenderInline * container) const780 FloatRect RenderInline::culledInlineBoundingBox(const RenderInline* container) const
781 {
782 FloatRect result;
783 bool isHorizontal = style()->isHorizontalWritingMode();
784 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
785 if (curr->isFloatingOrPositioned())
786 continue;
787
788 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
789 // direction (aligned to the root box's baseline).
790 if (curr->isBox()) {
791 RenderBox* currBox = toRenderBox(curr);
792 if (currBox->inlineBoxWrapper()) {
793 RootInlineBox* rootBox = currBox->inlineBoxWrapper()->root();
794 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
795 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
796 if (isHorizontal)
797 result.uniteIfNonZero(FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, currBox->width() + currBox->marginLeft() + currBox->marginRight(), logicalHeight));
798 else
799 result.uniteIfNonZero(FloatRect(logicalTop, currBox->inlineBoxWrapper()->y() - currBox->marginTop(), logicalHeight, currBox->height() + currBox->marginTop() + currBox->marginBottom()));
800 }
801 } else if (curr->isRenderInline()) {
802 // If the child doesn't need line boxes either, then we can recur.
803 RenderInline* currInline = toRenderInline(curr);
804 if (!currInline->alwaysCreateLineBoxes())
805 result.uniteIfNonZero(currInline->culledInlineBoundingBox(container));
806 else {
807 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
808 RootInlineBox* rootBox = childLine->root();
809 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
810 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
811 if (isHorizontal)
812 result.uniteIfNonZero(FloatRect(childLine->x() - childLine->marginLogicalLeft(),
813 logicalTop,
814 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
815 logicalHeight));
816 else
817 result.uniteIfNonZero(FloatRect(logicalTop,
818 childLine->y() - childLine->marginLogicalLeft(),
819 logicalHeight,
820 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight()));
821
822 }
823 }
824 } else if (curr->isText()) {
825 RenderText* currText = toRenderText(curr);
826 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) {
827 RootInlineBox* rootBox = childText->root();
828 int logicalTop = rootBox->logicalTop() + (rootBox->renderer()->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox->isFirstLineStyle())->font().fontMetrics().ascent());
829 int logicalHeight = container->style(rootBox->isFirstLineStyle())->font().fontMetrics().height();
830 if (isHorizontal)
831 result.uniteIfNonZero(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight));
832 else
833 result.uniteIfNonZero(FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth()));
834 }
835 }
836 }
837 return enclosingIntRect(result);
838 }
839
culledInlineFirstLineBox() const840 InlineBox* RenderInline::culledInlineFirstLineBox() const
841 {
842 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
843 if (curr->isFloatingOrPositioned())
844 continue;
845
846 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
847 // direction (aligned to the root box's baseline).
848 if (curr->isBox()) {
849 RenderBox* currBox = toRenderBox(curr);
850 if (currBox->inlineBoxWrapper())
851 return currBox->inlineBoxWrapper();
852 } else if (curr->isRenderInline()) {
853 RenderInline* currInline = toRenderInline(curr);
854 InlineBox* result = currInline->firstLineBoxIncludingCulling();
855 if (result)
856 return result;
857 } else if (curr->isText()) {
858 RenderText* currText = toRenderText(curr);
859 if (currText->firstTextBox())
860 return currText->firstTextBox();
861 }
862 }
863 return 0;
864 }
865
culledInlineLastLineBox() const866 InlineBox* RenderInline::culledInlineLastLineBox() const
867 {
868 for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) {
869 if (curr->isFloatingOrPositioned())
870 continue;
871
872 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
873 // direction (aligned to the root box's baseline).
874 if (curr->isBox()) {
875 RenderBox* currBox = toRenderBox(curr);
876 if (currBox->inlineBoxWrapper())
877 return currBox->inlineBoxWrapper();
878 } else if (curr->isRenderInline()) {
879 RenderInline* currInline = toRenderInline(curr);
880 InlineBox* result = currInline->lastLineBoxIncludingCulling();
881 if (result)
882 return result;
883 } else if (curr->isText()) {
884 RenderText* currText = toRenderText(curr);
885 if (currText->lastTextBox())
886 return currText->lastTextBox();
887 }
888 }
889 return 0;
890 }
891
culledInlineVisualOverflowBoundingBox() const892 IntRect RenderInline::culledInlineVisualOverflowBoundingBox() const
893 {
894 IntRect result(culledInlineBoundingBox(this));
895 bool isHorizontal = style()->isHorizontalWritingMode();
896 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
897 if (curr->isFloatingOrPositioned())
898 continue;
899
900 // For overflow we just have to propagate by hand and recompute it all.
901 if (curr->isBox()) {
902 RenderBox* currBox = toRenderBox(curr);
903 if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) {
904 IntRect logicalRect = currBox->logicalVisualOverflowRectForPropagation(style());
905 if (isHorizontal) {
906 logicalRect.move(currBox->x(), currBox->y());
907 result.uniteIfNonZero(logicalRect);
908 } else {
909 logicalRect.move(currBox->y(), currBox->x());
910 result.uniteIfNonZero(logicalRect.transposedRect());
911 }
912 }
913 } else if (curr->isRenderInline()) {
914 // If the child doesn't need line boxes either, then we can recur.
915 RenderInline* currInline = toRenderInline(curr);
916 if (!currInline->alwaysCreateLineBoxes())
917 result.uniteIfNonZero(currInline->culledInlineVisualOverflowBoundingBox());
918 else if (!currInline->hasSelfPaintingLayer())
919 result.uniteIfNonZero(currInline->linesVisualOverflowBoundingBox());
920 } else if (curr->isText()) {
921 // FIXME; Overflow from text boxes is lost. We will need to cache this information in
922 // InlineTextBoxes.
923 RenderText* currText = toRenderText(curr);
924 result.uniteIfNonZero(currText->linesVisualOverflowBoundingBox());
925 }
926 }
927 return result;
928 }
929
linesVisualOverflowBoundingBox() const930 IntRect RenderInline::linesVisualOverflowBoundingBox() const
931 {
932 if (!alwaysCreateLineBoxes())
933 return culledInlineVisualOverflowBoundingBox();
934
935 if (!firstLineBox() || !lastLineBox())
936 return IntRect();
937
938 // Return the width of the minimal left side and the maximal right side.
939 int logicalLeftSide = numeric_limits<int>::max();
940 int logicalRightSide = numeric_limits<int>::min();
941 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
942 logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow());
943 logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow());
944 }
945
946 RootInlineBox* firstRootBox = firstLineBox()->root();
947 RootInlineBox* lastRootBox = lastLineBox()->root();
948
949 int logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox->lineTop());
950 int logicalWidth = logicalRightSide - logicalLeftSide;
951 int logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox->lineBottom()) - logicalTop;
952
953 IntRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
954 if (!style()->isHorizontalWritingMode())
955 rect = rect.transposedRect();
956 return rect;
957 }
958
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)959 IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
960 {
961 // Only run-ins are allowed in here during layout.
962 ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn());
963
964 if (!firstLineBoxIncludingCulling() && !continuation())
965 return IntRect();
966
967 // Find our leftmost position.
968 IntRect boundingBox(linesVisualOverflowBoundingBox());
969 int left = boundingBox.x();
970 int top = boundingBox.y();
971
972 // Now invalidate a rectangle.
973 int ow = style() ? style()->outlineSize() : 0;
974
975 // We need to add in the relative position offsets of any inlines (including us) up to our
976 // containing block.
977 RenderBlock* cb = containingBlock();
978 for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb;
979 inlineFlow = inlineFlow->parent()) {
980 if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer())
981 toRenderInline(inlineFlow)->layer()->relativePositionOffset(left, top);
982 }
983
984 IntRect r(-ow + left, -ow + top, boundingBox.width() + ow * 2, boundingBox.height() + ow * 2);
985
986 if (cb->hasColumns())
987 cb->adjustRectForColumns(r);
988
989 if (cb->hasOverflowClip()) {
990 // cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the
991 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
992 // anyway if its size does change.
993 IntRect repaintRect(r);
994 repaintRect.move(-cb->layer()->scrolledContentOffset()); // For overflow:auto/scroll/hidden.
995
996 IntRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height());
997 r = intersection(repaintRect, boxRect);
998 }
999
1000 // FIXME: need to ensure that we compute the correct repaint rect when the repaint container
1001 // is an inline.
1002 if (repaintContainer != this)
1003 cb->computeRectForRepaint(repaintContainer, r);
1004
1005 if (ow) {
1006 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1007 if (!curr->isText()) {
1008 IntRect childRect = curr->rectWithOutlineForRepaint(repaintContainer, ow);
1009 r.unite(childRect);
1010 }
1011 }
1012
1013 if (continuation() && !continuation()->isInline()) {
1014 IntRect contRect = continuation()->rectWithOutlineForRepaint(repaintContainer, ow);
1015 r.unite(contRect);
1016 }
1017 }
1018
1019 return r;
1020 }
1021
rectWithOutlineForRepaint(RenderBoxModelObject * repaintContainer,int outlineWidth)1022 IntRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth)
1023 {
1024 IntRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
1025 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1026 if (!curr->isText())
1027 r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth));
1028 }
1029 return r;
1030 }
1031
computeRectForRepaint(RenderBoxModelObject * repaintContainer,IntRect & rect,bool fixed)1032 void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
1033 {
1034 if (RenderView* v = view()) {
1035 // LayoutState is only valid for root-relative repainting
1036 if (v->layoutStateEnabled() && !repaintContainer) {
1037 LayoutState* layoutState = v->layoutState();
1038 if (style()->position() == RelativePosition && layer())
1039 rect.move(layer()->relativePositionOffset());
1040 rect.move(layoutState->m_paintOffset);
1041 if (layoutState->m_clipped)
1042 rect.intersect(layoutState->m_clipRect);
1043 return;
1044 }
1045 }
1046
1047 if (repaintContainer == this)
1048 return;
1049
1050 bool containerSkipped;
1051 RenderObject* o = container(repaintContainer, &containerSkipped);
1052 if (!o)
1053 return;
1054
1055 IntPoint topLeft = rect.location();
1056
1057 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) {
1058 RenderBlock* cb = toRenderBlock(o);
1059 if (cb->hasColumns()) {
1060 IntRect repaintRect(topLeft, rect.size());
1061 cb->adjustRectForColumns(repaintRect);
1062 topLeft = repaintRect.location();
1063 rect = repaintRect;
1064 }
1065 }
1066
1067 if (style()->position() == RelativePosition && layer()) {
1068 // Apply the relative position offset when invalidating a rectangle. The layer
1069 // is translated, but the render box isn't, so we need to do this to get the
1070 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
1071 // flag on the RenderObject has been cleared, so use the one on the style().
1072 topLeft += layer()->relativePositionOffset();
1073 }
1074
1075 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1076 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1077 if (o->hasOverflowClip()) {
1078 RenderBox* containerBox = toRenderBox(o);
1079
1080 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
1081 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
1082 // anyway if its size does change.
1083 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden.
1084
1085 IntRect repaintRect(topLeft, rect.size());
1086 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height());
1087 rect = intersection(repaintRect, boxRect);
1088 if (rect.isEmpty())
1089 return;
1090 } else
1091 rect.setLocation(topLeft);
1092
1093 if (containerSkipped) {
1094 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
1095 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1096 rect.move(-containerOffset);
1097 return;
1098 }
1099
1100 o->computeRectForRepaint(repaintContainer, rect, fixed);
1101 }
1102
offsetFromContainer(RenderObject * container,const IntPoint & point) const1103 IntSize RenderInline::offsetFromContainer(RenderObject* container, const IntPoint& point) const
1104 {
1105 ASSERT(container == this->container());
1106
1107 IntSize offset;
1108 if (isRelPositioned())
1109 offset += relativePositionOffset();
1110
1111 container->adjustForColumns(offset, point);
1112
1113 if (container->hasOverflowClip())
1114 offset -= toRenderBox(container)->layer()->scrolledContentOffset();
1115
1116 return offset;
1117 }
1118
mapLocalToContainer(RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState) const1119 void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
1120 {
1121 if (repaintContainer == this)
1122 return;
1123
1124 if (RenderView *v = view()) {
1125 if (v->layoutStateEnabled() && !repaintContainer) {
1126 LayoutState* layoutState = v->layoutState();
1127 IntSize offset = layoutState->m_paintOffset;
1128 if (style()->position() == RelativePosition && layer())
1129 offset += layer()->relativePositionOffset();
1130 transformState.move(offset);
1131 return;
1132 }
1133 }
1134
1135 bool containerSkipped;
1136 RenderObject* o = container(repaintContainer, &containerSkipped);
1137 if (!o)
1138 return;
1139
1140 IntPoint centerPoint = roundedIntPoint(transformState.mappedPoint());
1141 if (o->isBox() && o->style()->isFlippedBlocksWritingMode())
1142 transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(roundedIntPoint(transformState.mappedPoint())) - centerPoint);
1143
1144 IntSize containerOffset = offsetFromContainer(o, roundedIntPoint(transformState.mappedPoint()));
1145
1146 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
1147 if (useTransforms && shouldUseTransformFromContainer(o)) {
1148 TransformationMatrix t;
1149 getTransformFromContainer(o, containerOffset, t);
1150 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1151 } else
1152 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1153
1154 if (containerSkipped) {
1155 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1156 // to just subtract the delta between the repaintContainer and o.
1157 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1158 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1159 return;
1160 }
1161
1162 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
1163 }
1164
mapAbsoluteToLocalPoint(bool fixed,bool useTransforms,TransformState & transformState) const1165 void RenderInline::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
1166 {
1167 // We don't expect this function to be called during layout.
1168 ASSERT(!view() || !view()->layoutStateEnabled());
1169
1170 RenderObject* o = container();
1171 if (!o)
1172 return;
1173
1174 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
1175
1176 IntSize containerOffset = offsetFromContainer(o, IntPoint());
1177
1178 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
1179 if (useTransforms && shouldUseTransformFromContainer(o)) {
1180 TransformationMatrix t;
1181 getTransformFromContainer(o, containerOffset, t);
1182 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1183 } else
1184 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1185 }
1186
updateDragState(bool dragOn)1187 void RenderInline::updateDragState(bool dragOn)
1188 {
1189 RenderBoxModelObject::updateDragState(dragOn);
1190 if (continuation())
1191 continuation()->updateDragState(dragOn);
1192 }
1193
childBecameNonInline(RenderObject * child)1194 void RenderInline::childBecameNonInline(RenderObject* child)
1195 {
1196 // We have to split the parent flow.
1197 RenderBlock* newBox = containingBlock()->createAnonymousBlock();
1198 RenderBoxModelObject* oldContinuation = continuation();
1199 setContinuation(newBox);
1200 RenderObject* beforeChild = child->nextSibling();
1201 children()->removeChildNode(this, child);
1202 splitFlow(beforeChild, newBox, child, oldContinuation);
1203 }
1204
updateHitTestResult(HitTestResult & result,const IntPoint & point)1205 void RenderInline::updateHitTestResult(HitTestResult& result, const IntPoint& point)
1206 {
1207 if (result.innerNode())
1208 return;
1209
1210 Node* n = node();
1211 IntPoint localPoint(point);
1212 if (n) {
1213 if (isInlineElementContinuation()) {
1214 // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space
1215 // of the principal renderer's containing block. This will end up being the innerNonSharedNode.
1216 RenderBlock* firstBlock = n->renderer()->containingBlock();
1217
1218 // Get our containing block.
1219 RenderBox* block = containingBlock();
1220 localPoint.move(block->x() - firstBlock->x(), block->y() - firstBlock->y());
1221 }
1222
1223 result.setInnerNode(n);
1224 if (!result.innerNonSharedNode())
1225 result.setInnerNonSharedNode(n);
1226 result.setLocalPoint(localPoint);
1227 }
1228 }
1229
dirtyLineBoxes(bool fullLayout)1230 void RenderInline::dirtyLineBoxes(bool fullLayout)
1231 {
1232 if (fullLayout) {
1233 m_lineBoxes.deleteLineBoxes(renderArena());
1234 return;
1235 }
1236
1237 if (!alwaysCreateLineBoxes()) {
1238 // We have to grovel into our children in order to dirty the appropriate lines.
1239 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1240 if (curr->isFloatingOrPositioned())
1241 continue;
1242 if (curr->isBox() && !curr->needsLayout()) {
1243 RenderBox* currBox = toRenderBox(curr);
1244 if (currBox->inlineBoxWrapper())
1245 currBox->inlineBoxWrapper()->root()->markDirty();
1246 } else if (!curr->selfNeedsLayout()) {
1247 if (curr->isRenderInline()) {
1248 RenderInline* currInline = toRenderInline(curr);
1249 for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox())
1250 childLine->root()->markDirty();
1251 } else if (curr->isText()) {
1252 RenderText* currText = toRenderText(curr);
1253 for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox())
1254 childText->root()->markDirty();
1255 }
1256 }
1257 }
1258 } else
1259 m_lineBoxes.dirtyLineBoxes();
1260 }
1261
createInlineFlowBox()1262 InlineFlowBox* RenderInline::createInlineFlowBox()
1263 {
1264 return new (renderArena()) InlineFlowBox(this);
1265 }
1266
createAndAppendInlineFlowBox()1267 InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
1268 {
1269 setAlwaysCreateLineBoxes();
1270 InlineFlowBox* flowBox = createInlineFlowBox();
1271 m_lineBoxes.appendLineBox(flowBox);
1272 return flowBox;
1273 }
1274
lineHeight(bool firstLine,LineDirectionMode,LinePositionMode) const1275 int RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
1276 {
1277 if (firstLine && document()->usesFirstLineRules()) {
1278 RenderStyle* s = style(firstLine);
1279 if (s != style())
1280 return s->computedLineHeight();
1281 }
1282
1283 if (m_lineHeight == -1)
1284 m_lineHeight = style()->computedLineHeight();
1285
1286 return m_lineHeight;
1287 }
1288
baselinePosition(FontBaseline baselineType,bool firstLine,LineDirectionMode direction,LinePositionMode linePositionMode) const1289 int RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1290 {
1291 const FontMetrics& fontMetrics = style(firstLine)->fontMetrics();
1292 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
1293 }
1294
relativePositionedInlineOffset(const RenderBox * child) const1295 IntSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const
1296 {
1297 // FIXME: This function isn't right with mixed writing modes.
1298
1299 ASSERT(isRelPositioned());
1300 if (!isRelPositioned())
1301 return IntSize();
1302
1303 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
1304 // box from the rest of the content, but only in the cases where we know we're positioned
1305 // relative to the inline itself.
1306
1307 IntSize logicalOffset;
1308 int inlinePosition;
1309 int blockPosition;
1310 if (firstLineBox()) {
1311 inlinePosition = lroundf(firstLineBox()->logicalLeft());
1312 blockPosition = firstLineBox()->logicalTop();
1313 } else {
1314 inlinePosition = layer()->staticInlinePosition();
1315 blockPosition = layer()->staticBlockPosition();
1316 }
1317
1318 if (!child->style()->hasStaticInlinePosition(style()->isHorizontalWritingMode()))
1319 logicalOffset.setWidth(inlinePosition);
1320
1321 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
1322 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
1323 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
1324 // do.
1325 else if (!child->style()->isOriginalDisplayInlineType())
1326 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
1327 logicalOffset.setWidth(inlinePosition - child->containingBlock()->borderAndPaddingLogicalLeft());
1328
1329 if (!child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
1330 logicalOffset.setHeight(blockPosition);
1331
1332 return style()->isHorizontalWritingMode() ? logicalOffset : logicalOffset.transposedSize();
1333 }
1334
imageChanged(WrappedImagePtr,const IntRect *)1335 void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
1336 {
1337 if (!parent())
1338 return;
1339
1340 // FIXME: We can do better.
1341 repaint();
1342 }
1343
addFocusRingRects(Vector<IntRect> & rects,int tx,int ty)1344 void RenderInline::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
1345 {
1346 if (!alwaysCreateLineBoxes())
1347 culledInlineAbsoluteRects(this, rects, IntSize(tx, ty));
1348 else {
1349 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
1350 rects.append(enclosingIntRect(FloatRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())));
1351 }
1352
1353 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
1354 if (!curr->isText() && !curr->isListMarker()) {
1355 FloatPoint pos(tx, ty);
1356 // FIXME: This doesn't work correctly with transforms.
1357 if (curr->hasLayer())
1358 pos = curr->localToAbsolute();
1359 else if (curr->isBox())
1360 pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y());
1361 curr->addFocusRingRects(rects, pos.x(), pos.y());
1362 }
1363 }
1364
1365 if (continuation()) {
1366 if (continuation()->isInline())
1367 continuation()->addFocusRingRects(rects,
1368 tx - containingBlock()->x() + continuation()->containingBlock()->x(),
1369 ty - containingBlock()->y() + continuation()->containingBlock()->y());
1370 else
1371 continuation()->addFocusRingRects(rects,
1372 tx - containingBlock()->x() + toRenderBox(continuation())->x(),
1373 ty - containingBlock()->y() + toRenderBox(continuation())->y());
1374 }
1375 }
1376
paintOutline(GraphicsContext * graphicsContext,int tx,int ty)1377 void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty)
1378 {
1379 if (!hasOutline())
1380 return;
1381
1382 RenderStyle* styleToUse = style();
1383 if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) {
1384 if (!theme()->supportsFocusRing(styleToUse)) {
1385 // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
1386 paintFocusRing(graphicsContext, tx, ty, styleToUse);
1387 }
1388 }
1389
1390 if (styleToUse->outlineStyleIsAuto() || styleToUse->outlineStyle() == BNONE)
1391 return;
1392
1393 Vector<IntRect> rects;
1394
1395 rects.append(IntRect());
1396 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1397 RootInlineBox* root = curr->root();
1398 int top = max(root->lineTop(), curr->logicalTop());
1399 int bottom = min(root->lineBottom(), curr->logicalBottom());
1400 rects.append(IntRect(curr->x(), top, curr->logicalWidth(), bottom - top));
1401 }
1402 rects.append(IntRect());
1403
1404 for (unsigned i = 1; i < rects.size() - 1; i++)
1405 paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1));
1406 }
1407
paintOutlineForLine(GraphicsContext * graphicsContext,int tx,int ty,const IntRect & lastline,const IntRect & thisline,const IntRect & nextline)1408 void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty,
1409 const IntRect& lastline, const IntRect& thisline, const IntRect& nextline)
1410 {
1411 RenderStyle* styleToUse = style();
1412 int ow = styleToUse->outlineWidth();
1413 EBorderStyle os = styleToUse->outlineStyle();
1414 Color oc = styleToUse->visitedDependentColor(CSSPropertyOutlineColor);
1415
1416 int offset = style()->outlineOffset();
1417
1418 int t = ty + thisline.y() - offset;
1419 int l = tx + thisline.x() - offset;
1420 int b = ty + thisline.maxY() + offset;
1421 int r = tx + thisline.maxX() + offset;
1422
1423 // left edge
1424 drawLineForBoxSide(graphicsContext,
1425 l - ow,
1426 t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? ow : 0),
1427 l,
1428 b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? ow : 0),
1429 BSLeft,
1430 oc, os,
1431 (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? ow : -ow),
1432 (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? ow : -ow));
1433
1434 // right edge
1435 drawLineForBoxSide(graphicsContext,
1436 r,
1437 t - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? ow : 0),
1438 r + ow,
1439 b + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? ow : 0),
1440 BSRight,
1441 oc, os,
1442 (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? ow : -ow),
1443 (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? ow : -ow));
1444 // upper edge
1445 if (thisline.x() < lastline.x())
1446 drawLineForBoxSide(graphicsContext,
1447 l - ow,
1448 t - ow,
1449 min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())),
1450 t ,
1451 BSTop, oc, os,
1452 ow,
1453 (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow);
1454
1455 if (lastline.maxX() < thisline.maxX())
1456 drawLineForBoxSide(graphicsContext,
1457 max(lastline.isEmpty() ? -1000000 : tx + lastline.maxX(), l - ow),
1458 t - ow,
1459 r + ow,
1460 t ,
1461 BSTop, oc, os,
1462 (!lastline.isEmpty() && l - ow < tx + lastline.maxX()) ? -ow : ow,
1463 ow);
1464
1465 // lower edge
1466 if (thisline.x() < nextline.x())
1467 drawLineForBoxSide(graphicsContext,
1468 l - ow,
1469 b,
1470 min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000),
1471 b + ow,
1472 BSBottom, oc, os,
1473 ow,
1474 (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow);
1475
1476 if (nextline.maxX() < thisline.maxX())
1477 drawLineForBoxSide(graphicsContext,
1478 max(!nextline.isEmpty() ? tx + nextline.maxX() : -1000000, l - ow),
1479 b,
1480 r + ow,
1481 b + ow,
1482 BSBottom, oc, os,
1483 (!nextline.isEmpty() && l - ow < tx + nextline.maxX()) ? -ow : ow,
1484 ow);
1485 }
1486
1487 #if ENABLE(DASHBOARD_SUPPORT)
addDashboardRegions(Vector<DashboardRegionValue> & regions)1488 void RenderInline::addDashboardRegions(Vector<DashboardRegionValue>& regions)
1489 {
1490 // Convert the style regions to absolute coordinates.
1491 if (style()->visibility() != VISIBLE)
1492 return;
1493
1494 const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions();
1495 unsigned i, count = styleRegions.size();
1496 for (i = 0; i < count; i++) {
1497 StyleDashboardRegion styleRegion = styleRegions[i];
1498
1499 IntRect linesBoundingBox = this->linesBoundingBox();
1500 int w = linesBoundingBox.width();
1501 int h = linesBoundingBox.height();
1502
1503 DashboardRegionValue region;
1504 region.label = styleRegion.label;
1505 region.bounds = IntRect(linesBoundingBox.x() + styleRegion.offset.left().value(),
1506 linesBoundingBox.y() + styleRegion.offset.top().value(),
1507 w - styleRegion.offset.left().value() - styleRegion.offset.right().value(),
1508 h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value());
1509 region.type = styleRegion.type;
1510
1511 RenderObject* container = containingBlock();
1512 if (!container)
1513 container = this;
1514
1515 region.clip = region.bounds;
1516 container->computeAbsoluteRepaintRect(region.clip);
1517 if (region.clip.height() < 0) {
1518 region.clip.setHeight(0);
1519 region.clip.setWidth(0);
1520 }
1521
1522 FloatPoint absPos = container->localToAbsolute();
1523 region.bounds.setX(absPos.x() + region.bounds.x());
1524 region.bounds.setY(absPos.y() + region.bounds.y());
1525
1526 if (frame()) {
1527 float pageScaleFactor = frame()->page()->chrome()->scaleFactor();
1528 if (pageScaleFactor != 1.0f) {
1529 region.bounds.scale(pageScaleFactor);
1530 region.clip.scale(pageScaleFactor);
1531 }
1532 }
1533
1534 regions.append(region);
1535 }
1536 }
1537 #endif
1538
1539 } // namespace WebCore
1540