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