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