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.
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.
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.
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.
24 #include "RenderInline.h"
27 #include "FloatQuad.h"
28 #include "FrameSelection.h"
29 #include "GraphicsContext.h"
30 #include "HitTestResult.h"
31 #include "InlineElementBox.h"
32 #include "InlineTextBox.h"
34 #include "RenderBlock.h"
35 #include "RenderChildIterator.h"
36 #include "RenderFullScreen.h"
37 #include "RenderGeometryMap.h"
38 #include "RenderIterator.h"
39 #include "RenderLayer.h"
40 #include "RenderLineBreak.h"
41 #include "RenderListMarker.h"
42 #include "RenderNamedFlowThread.h"
43 #include "RenderTheme.h"
44 #include "RenderView.h"
46 #include "StyleInheritedData.h"
47 #include "TransformState.h"
48 #include "VisiblePosition.h"
50 #if ENABLE(DASHBOARD_SUPPORT)
56 RenderInline::RenderInline(Element& element, RenderStyle&& style)
57 : RenderBoxModelObject(element, WTFMove(style), RenderInlineFlag)
59 setChildrenInline(true);
62 RenderInline::RenderInline(Document& document, RenderStyle&& style)
63 : RenderBoxModelObject(document, WTFMove(style), RenderInlineFlag)
65 setChildrenInline(true);
68 void RenderInline::willBeDestroyed()
71 // Make sure we do not retain "this" in the continuation outline table map of our containing blocks.
72 if (parent() && style().visibility() == VISIBLE && hasOutline()) {
73 bool containingBlockPaintsContinuationOutline = continuation() || isInlineElementContinuation();
74 if (containingBlockPaintsContinuationOutline) {
75 if (RenderBlock* cb = containingBlock()) {
76 if (RenderBlock* cbCb = cb->containingBlock())
77 ASSERT(!cbCb->paintsContinuationOutline(this));
83 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
84 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
85 destroyLeftoverChildren();
87 if (!documentBeingDestroyed()) {
89 // We can't wait for RenderBoxModelObject::destroy to clear the selection,
90 // because by then we will have nuked the line boxes.
91 if (isSelectionBorder())
92 frame().selection().setNeedsSelectionUpdate();
94 // If line boxes are contained inside a root, that means we're an inline.
95 // In that case, we need to remove all the line boxes so that the parent
96 // lines aren't pointing to deleted children. If the first line box does
97 // not have a parent that means they are either already disconnected or
98 // root lines that can just be destroyed without disconnecting.
99 if (firstLineBox()->parent()) {
100 for (auto* box = firstLineBox(); box; box = box->nextLineBox())
101 box->removeFromParent();
104 parent()->dirtyLinesFromChangedChild(*this);
107 m_lineBoxes.deleteLineBoxes();
109 RenderBoxModelObject::willBeDestroyed();
112 RenderInline* RenderInline::inlineElementContinuation() const
114 RenderBoxModelObject* continuation = this->continuation();
118 if (is<RenderInline>(*continuation))
119 return downcast<RenderInline>(continuation);
121 return is<RenderBlock>(*continuation) ? downcast<RenderBlock>(*continuation).inlineElementContinuation() : nullptr;
124 void RenderInline::updateFromStyle()
126 RenderBoxModelObject::updateFromStyle();
128 // FIXME: Support transforms and reflections on inline flows someday.
129 setHasTransformRelatedProperty(false);
130 setHasReflection(false);
133 static RenderElement* inFlowPositionedInlineAncestor(RenderElement* p)
135 while (p && p->isRenderInline()) {
136 if (p->isInFlowPositioned())
143 static void updateStyleOfAnonymousBlockContinuations(const RenderBlock& block, const RenderStyle* newStyle, const RenderStyle* oldStyle)
145 // If any descendant blocks exist then they will be in the next anonymous block and its siblings.
146 for (RenderBox* box = block.nextSiblingBox(); box && box->isAnonymousBlock(); box = box->nextSiblingBox()) {
147 if (box->style().position() == newStyle->position())
150 if (!is<RenderBlock>(*box))
153 RenderBlock& block = downcast<RenderBlock>(*box);
154 if (!block.isAnonymousBlockContinuation())
157 // If we are no longer in-flow positioned but our descendant block(s) still have an in-flow positioned ancestor then
158 // their containing anonymous block should keep its in-flow positioning.
159 RenderInline* continuation = block.inlineElementContinuation();
160 if (oldStyle->hasInFlowPosition() && inFlowPositionedInlineAncestor(continuation))
162 auto blockStyle = RenderStyle::createAnonymousStyleWithDisplay(block.style(), BLOCK);
163 blockStyle.setPosition(newStyle->position());
164 block.setStyle(WTFMove(blockStyle));
168 void RenderInline::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
170 RenderBoxModelObject::styleWillChange(diff, newStyle);
172 // Check if this inline can hold absolute positioned elmements even after the style change.
173 if (canContainAbsolutelyPositionedObjects() && newStyle.position() == StaticPosition) {
174 // RenderInlines forward their absolute positioned descendants to their (non-anonymous) containing block.
175 auto* container = containingBlockForAbsolutePosition();
176 if (container && !container->canContainAbsolutelyPositionedObjects())
177 container->removePositionedObjects(nullptr, NewContainingBlock);
181 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
183 RenderBoxModelObject::styleDidChange(diff, oldStyle);
185 // Ensure that all of the split inlines pick up the new style. We
186 // only do this if we're an inline, since we don't want to propagate
187 // a block's style to the other inlines.
188 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
189 // and after the block share the same style, but the block doesn't
190 // need to pass its style on to anyone else.
191 auto& newStyle = style();
192 RenderInline* continuation = inlineElementContinuation();
194 for (RenderInline* currCont = continuation; currCont; currCont = currCont->inlineElementContinuation()) {
195 RenderBoxModelObject* nextCont = currCont->continuation();
196 currCont->setContinuation(nullptr);
197 currCont->setStyle(RenderStyle::clone(newStyle));
198 currCont->setContinuation(nextCont);
200 // If an inline's in-flow positioning has changed and it is part of an active continuation as a descendant of an anonymous containing block,
201 // then any descendant blocks will need to change their in-flow positioning accordingly.
202 // Do this by updating the position of the descendant blocks' containing anonymous blocks - there may be more than one.
203 if (containingBlock()->isAnonymousBlock() && oldStyle && newStyle.position() != oldStyle->position() && (newStyle.hasInFlowPosition() || oldStyle->hasInFlowPosition()))
204 updateStyleOfAnonymousBlockContinuations(*containingBlock(), &newStyle, oldStyle);
207 if (!alwaysCreateLineBoxes()) {
208 bool alwaysCreateLineBoxes = hasSelfPaintingLayer() || hasVisibleBoxDecorations() || newStyle.hasBorder() || newStyle.hasPadding() || newStyle.hasMargin() || hasOutline();
209 if (oldStyle && alwaysCreateLineBoxes) {
210 dirtyLineBoxes(false);
213 setRenderInlineAlwaysCreatesLineBoxes(alwaysCreateLineBoxes);
217 void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout)
219 // Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the
220 // background color will only cause a layout on the first rollover.
221 if (alwaysCreateLineBoxes())
224 auto* parentStyle = &parent()->style();
225 RenderInline* parentRenderInline = is<RenderInline>(*parent()) ? downcast<RenderInline>(parent()) : nullptr;
226 bool checkFonts = document().inNoQuirksMode();
227 RenderFlowThread* flowThread = flowThreadContainingBlock();
228 bool alwaysCreateLineBoxes = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes())
229 || (parentRenderInline && parentStyle->verticalAlign() != BASELINE)
230 || style().verticalAlign() != BASELINE
231 || style().textEmphasisMark() != TextEmphasisMarkNone
232 || (checkFonts && (!parentStyle->fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(style().fontCascade().fontMetrics())
233 || parentStyle->lineHeight() != style().lineHeight()))
234 || (flowThread && flowThread->isRenderNamedFlowThread()); // FIXME: Enable the optimization once we make overflow computation for culled inlines in regions.
236 if (!alwaysCreateLineBoxes && checkFonts && view().usesFirstLineRules()) {
237 // Have to check the first line style as well.
238 parentStyle = &parent()->firstLineStyle();
239 auto& childStyle = firstLineStyle();
240 alwaysCreateLineBoxes = !parentStyle->fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle.fontCascade().fontMetrics())
241 || childStyle.verticalAlign() != BASELINE
242 || parentStyle->lineHeight() != childStyle.lineHeight();
245 if (alwaysCreateLineBoxes) {
247 dirtyLineBoxes(false);
248 setAlwaysCreateLineBoxes();
252 LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, int, LayoutUnit* extraWidthToEndOfLine)
255 // This condition is possible if the RenderInline is at an editing boundary,
256 // i.e. the VisiblePosition is:
257 // <RenderInline editingBoundary=true>|<RenderText> </RenderText></RenderInline>
258 // FIXME: need to figure out how to make this return a valid rect, note that
259 // there are no line boxes created in the above case.
263 ASSERT_UNUSED(inlineBox, !inlineBox);
265 if (extraWidthToEndOfLine)
266 *extraWidthToEndOfLine = 0;
268 LayoutRect caretRect = localCaretRectForEmptyElement(horizontalBorderAndPaddingExtent(), 0);
270 if (InlineBox* firstBox = firstLineBox())
271 caretRect.moveBy(LayoutPoint(firstBox->topLeft()));
276 void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild)
279 return addChildToContinuation(newChild, beforeChild);
280 return addChildIgnoringContinuation(newChild, beforeChild);
283 static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
285 if (is<RenderInline>(*renderer) && !renderer->isReplaced())
286 return downcast<RenderInline>(*renderer).continuation();
287 return downcast<RenderBlock>(*renderer).inlineElementContinuation();
290 RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild)
292 if (beforeChild && beforeChild->parent() == this)
295 RenderBoxModelObject* curr = nextContinuation(this);
296 RenderBoxModelObject* nextToLast = this;
297 RenderBoxModelObject* last = this;
299 if (beforeChild && beforeChild->parent() == curr) {
300 if (curr->firstChild() == beforeChild)
307 curr = nextContinuation(curr);
310 if (!beforeChild && !last->firstChild())
315 static bool newChildIsInline(const RenderObject& newChild, const RenderInline& parent)
317 // inline parent generates inline-table.
318 return newChild.isInline() | (parent.childRequiresTable(newChild) && parent.style().display() == INLINE);
321 void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
323 // Make sure we don't append things after :after-generated content if we have it.
324 if (!beforeChild && isAfterContent(lastChild()))
325 beforeChild = lastChild();
327 bool useNewBlockInsideInlineModel = document().settings()->newBlockInsideInlineModelEnabled();
328 bool childInline = newChildIsInline(*newChild, *this);
329 // This code is for the old block-inside-inline model that uses continuations.
330 if (!useNewBlockInsideInlineModel && !childInline && !newChild->isFloatingOrOutOfFlowPositioned()) {
331 // We are placing a block inside an inline. We have to perform a split of this
332 // inline into continuations. This involves creating an anonymous block box to hold
333 // |newChild|. We then make that block box a continuation of this inline. We take all of
334 // the children after |beforeChild| and put them in a clone of this object.
335 auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
337 // If inside an inline affected by in-flow positioning the block needs to be affected by it too.
338 // Giving the block a layer like this allows it to collect the x/y offsets from inline parents later.
339 if (auto positionedAncestor = inFlowPositionedInlineAncestor(this))
340 newStyle.setPosition(positionedAncestor->style().position());
342 RenderBlock* newBox = new RenderBlockFlow(document(), WTFMove(newStyle));
343 newBox->initializeStyle();
344 RenderBoxModelObject* oldContinuation = continuation();
345 setContinuation(newBox);
347 splitFlow(beforeChild, newBox, newChild, oldContinuation);
351 if (!useNewBlockInsideInlineModel) {
352 RenderBoxModelObject::addChild(newChild, beforeChild);
353 newChild->setNeedsLayoutAndPrefWidthsRecalc();
357 // This code is for the new block-inside-inline model that uses anonymous inline blocks.
358 // If the requested beforeChild is not one of our children, then this is most likely because
359 // there is an anonymous inline-block box within this object that contains the beforeChild.
360 // Insert the child into the anonymous inline-block box instead of here.
361 // A second possibility is that the beforeChild is an anonymous block inside the anonymous inline block.
362 // This can happen if inlines are inserted in between two of the anonymous inline block's block-level
363 // children after it has been created.
364 if (beforeChild && beforeChild->parent() != this) {
365 ASSERT(beforeChild->parent());
366 ASSERT(beforeChild->parent()->isAnonymousInlineBlock() || beforeChild->parent()->isAnonymousBlock());
367 if (beforeChild->parent()->isAnonymousInlineBlock()) {
368 if (!childInline || (childInline && beforeChild->parent()->firstChild() != beforeChild))
369 beforeChild->parent()->addChild(newChild, beforeChild);
371 addChild(newChild, beforeChild->parent());
372 } else if (beforeChild->parent()->isAnonymousBlock()) {
373 ASSERT(!beforeChild->parent()->parent() || beforeChild->parent()->parent()->isAnonymousInlineBlock());
375 if (childInline || (!childInline && beforeChild->parent()->firstChild() != beforeChild))
376 beforeChild->parent()->addChild(newChild, beforeChild);
378 addChild(newChild, beforeChild->parent());
384 // We are placing a block inside an inline. We have to place the block inside an anonymous inline-block.
385 // This inline-block can house a sequence of contiguous block-level children, and they will all sit on the
386 // same "line" together. We try to reuse an existing inline-block if possible.
388 if (beforeChild->previousSibling() && beforeChild->previousSibling()->isAnonymousInlineBlock()) {
389 downcast<RenderBlockFlow>(beforeChild->previousSibling())->addChild(newChild);
393 if (lastChild() && lastChild()->isAnonymousInlineBlock()) {
394 downcast<RenderBlockFlow>(lastChild())->addChild(newChild);
399 if (!newChild->isFloatingOrOutOfFlowPositioned()) {
400 // There was no suitable existing anonymous inline-block. Create a new one.
401 RenderBlockFlow* anonymousInlineBlock = new RenderBlockFlow(document(), RenderStyle::createAnonymousStyleWithDisplay(style(), INLINE_BLOCK));
402 anonymousInlineBlock->initializeStyle();
404 RenderBoxModelObject::addChild(anonymousInlineBlock, beforeChild);
405 anonymousInlineBlock->addChild(newChild);
410 RenderBoxModelObject::addChild(newChild, beforeChild);
412 newChild->setNeedsLayoutAndPrefWidthsRecalc();
415 RenderPtr<RenderInline> RenderInline::clone() const
417 RenderPtr<RenderInline> cloneInline = createRenderer<RenderInline>(*element(), RenderStyle::clone(style()));
418 cloneInline->initializeStyle();
419 cloneInline->setFlowThreadState(flowThreadState());
420 cloneInline->setHasOutlineAutoAncestor(hasOutlineAutoAncestor());
424 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
425 RenderBlock* middleBlock,
426 RenderObject* beforeChild, RenderBoxModelObject* oldCont)
428 // Create a clone of this inline.
429 RenderPtr<RenderInline> cloneInline = clone();
430 #if ENABLE(FULLSCREEN_API)
431 // If we're splitting the inline containing the fullscreened element,
432 // |beforeChild| may be the renderer for the fullscreened element. However,
433 // that renderer is wrapped in a RenderFullScreen, so |this| is not its
434 // parent. Since the splitting logic expects |this| to be the parent, set
435 // |beforeChild| to be the RenderFullScreen.
436 const Element* fullScreenElement = document().webkitCurrentFullScreenElement();
437 if (fullScreenElement && beforeChild && beforeChild->node() == fullScreenElement)
438 beforeChild = document().fullScreenRenderer();
440 // Now take all of the children from beforeChild to the end and remove
441 // them from |this| and place them in the clone.
442 for (RenderObject* rendererToMove = beforeChild; rendererToMove;) {
443 RenderObject* nextSibling = rendererToMove->nextSibling();
444 // When anonymous wrapper is present, we might need to move the whole subtree instead.
445 if (rendererToMove->parent() != this) {
446 auto* anonymousParent = rendererToMove->parent();
447 while (anonymousParent && anonymousParent->parent() != this) {
448 ASSERT(anonymousParent->isAnonymous());
449 anonymousParent = anonymousParent->parent();
451 if (!anonymousParent) {
452 ASSERT_NOT_REACHED();
455 // If beforeChild is the first child in the subtree, we could just move the whole subtree.
456 if (!rendererToMove->previousSibling()) {
457 // Reparent the whole anonymous wrapper tree.
458 rendererToMove = anonymousParent;
459 // Skip to the next sibling that is not in this subtree.
460 nextSibling = anonymousParent->nextSibling();
461 } else if (!rendererToMove->nextSibling()) {
462 // This is the last renderer in the subtree. We need to jump out of the wrapper subtree, so that
463 // the siblings are getting reparented too.
464 nextSibling = anonymousParent->nextSibling();
466 // Otherwise just move the renderer to the inline clone. Should the renderer need an anon
467 // wrapper, the addChild() will generate one for it.
468 // FIXME: When the anonymous wrapper has multiple children, we end up traversing up to the topmost wrapper
469 // every time, which is a bit wasteful.
471 rendererToMove->parent()->removeChildInternal(*rendererToMove, NotifyChildren);
472 cloneInline->addChildIgnoringContinuation(rendererToMove);
473 rendererToMove->setNeedsLayoutAndPrefWidthsRecalc();
474 rendererToMove = nextSibling;
476 cloneInline->setContinuation(oldCont);
477 // Hook |clone| up as the continuation of the middle block.
478 middleBlock->setContinuation(cloneInline.get());
480 // We have been reparented and are now under the fromBlock. We need
481 // to walk up our inline parent chain until we hit the containing block.
482 // Once we hit the containing block we're done.
483 RenderBoxModelObject* current = downcast<RenderBoxModelObject>(parent());
484 RenderBoxModelObject* currentChild = this;
486 // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
487 // There will eventually be a better approach to this problem that will let us nest to a much
488 // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in
489 // incorrect rendering, but the alternative is to hang forever.
490 unsigned splitDepth = 1;
491 const unsigned cMaxSplitDepth = 200;
492 while (current && current != fromBlock) {
493 if (splitDepth < cMaxSplitDepth) {
494 // Create a new clone.
495 RenderPtr<RenderInline> cloneChild = WTFMove(cloneInline);
496 cloneInline = downcast<RenderInline>(*current).clone();
498 // Insert our child clone as the first child.
499 cloneInline->addChildIgnoringContinuation(cloneChild.leakPtr());
501 // Hook the clone up as a continuation of |curr|.
502 RenderInline& currentInline = downcast<RenderInline>(*current);
503 oldCont = currentInline.continuation();
504 currentInline.setContinuation(cloneInline.get());
505 cloneInline->setContinuation(oldCont);
507 // Now we need to take all of the children starting from the first child
508 // *after* currentChild and append them all to the clone.
509 for (auto* current = currentChild->nextSibling(); current;) {
510 auto* next = current->nextSibling();
511 currentInline.removeChildInternal(*current, NotifyChildren);
512 cloneInline->addChildIgnoringContinuation(current);
513 current->setNeedsLayoutAndPrefWidthsRecalc();
518 // Keep walking up the chain.
519 currentChild = current;
520 current = downcast<RenderBoxModelObject>(current->parent());
524 // Clear the flow thread containing blocks cached during the detached state insertions.
525 cloneInline->invalidateFlowThreadContainingBlockIncludingDescendants();
527 // Now we are at the block level. We need to put the clone into the toBlock.
528 toBlock->insertChildInternal(cloneInline.leakPtr(), nullptr, NotifyChildren);
530 // Now take all the children after currentChild and remove them from the fromBlock
531 // and put them in the toBlock.
532 for (auto* current = currentChild->nextSibling(); current;) {
533 auto* next = current->nextSibling();
534 fromBlock->removeChildInternal(*current, NotifyChildren);
535 toBlock->insertChildInternal(current, nullptr, NotifyChildren);
540 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
541 RenderObject* newChild, RenderBoxModelObject* oldCont)
543 RenderBlock* pre = nullptr;
544 RenderBlock* block = containingBlock();
546 // Delete our line boxes before we do the inline split into continuations.
547 block->deleteLines();
549 bool madeNewBeforeBlock = false;
550 if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
551 // We can reuse this block and make it the preBlock of the next continuation.
553 pre->removePositionedObjects(nullptr);
554 // FIXME-BLOCKFLOW: The enclosing method should likely be switched over
555 // to only work on RenderBlockFlow, in which case this conversion can be
557 if (is<RenderBlockFlow>(*pre))
558 downcast<RenderBlockFlow>(*pre).removeFloatingObjects();
559 block = block->containingBlock();
561 // No anonymous block available for use. Make one.
562 pre = block->createAnonymousBlock();
563 madeNewBeforeBlock = true;
566 RenderBlock& post = downcast<RenderBlock>(*pre->createAnonymousBoxWithSameTypeAs(block));
568 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
569 if (madeNewBeforeBlock)
570 block->insertChildInternal(pre, boxFirst, NotifyChildren);
571 block->insertChildInternal(newBlockBox, boxFirst, NotifyChildren);
572 block->insertChildInternal(&post, boxFirst, NotifyChildren);
573 block->setChildrenInline(false);
575 if (madeNewBeforeBlock) {
576 RenderObject* o = boxFirst;
578 RenderObject* no = o;
579 o = no->nextSibling();
580 block->removeChildInternal(*no, NotifyChildren);
581 pre->insertChildInternal(no, nullptr, NotifyChildren);
582 no->setNeedsLayoutAndPrefWidthsRecalc();
586 splitInlines(pre, &post, newBlockBox, beforeChild, oldCont);
588 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
589 // time in makeChildrenNonInline by just setting this explicitly up front.
590 newBlockBox->setChildrenInline(false);
592 // We delayed adding the newChild until now so that the |newBlockBox| would be fully
593 // connected, thus allowing newChild access to a renderArena should it need
594 // to wrap itself in additional boxes (e.g., table construction).
595 newBlockBox->addChild(newChild);
597 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
598 // get deleted properly. Because objects moves from the pre block into the post block, we want to
599 // make new line boxes instead of leaving the old line boxes around.
600 pre->setNeedsLayoutAndPrefWidthsRecalc();
601 block->setNeedsLayoutAndPrefWidthsRecalc();
602 post.setNeedsLayoutAndPrefWidthsRecalc();
605 void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild)
607 RenderBoxModelObject* flow = continuationBefore(beforeChild);
608 // It may or may not be the direct parent of the beforeChild.
609 RenderBoxModelObject* beforeChildAncestor = nullptr;
610 // In case of anonymous wrappers, the parent of the beforeChild is mostly irrelevant. What we need is
611 // the topmost wrapper.
612 if (beforeChild && !is<RenderBlock>(beforeChild->parent()) && beforeChild->parent()->isAnonymous()) {
613 RenderElement* anonymousParent = beforeChild->parent();
614 while (anonymousParent && anonymousParent->parent() && anonymousParent->parent()->isAnonymous())
615 anonymousParent = anonymousParent->parent();
616 ASSERT(anonymousParent && anonymousParent->parent());
617 beforeChildAncestor = downcast<RenderBoxModelObject>(anonymousParent->parent());
619 ASSERT(!beforeChild || is<RenderBlock>(*beforeChild->parent()) || is<RenderInline>(*beforeChild->parent()));
621 beforeChildAncestor = downcast<RenderBoxModelObject>(beforeChild->parent());
623 if (RenderBoxModelObject* continuation = nextContinuation(flow))
624 beforeChildAncestor = continuation;
626 beforeChildAncestor = flow;
630 if (newChild->isFloatingOrOutOfFlowPositioned())
631 return beforeChildAncestor->addChildIgnoringContinuation(newChild, beforeChild);
633 if (flow == beforeChildAncestor)
634 return flow->addChildIgnoringContinuation(newChild, beforeChild);
635 // A continuation always consists of two potential candidates: an inline or an anonymous
636 // block box holding block children.
637 bool childInline = newChildIsInline(*newChild, *this);
638 // The goal here is to match up if we can, so that we can coalesce and create the
639 // minimal # of continuations needed for the inline.
640 if (childInline == beforeChildAncestor->isInline())
641 return beforeChildAncestor->addChildIgnoringContinuation(newChild, beforeChild);
642 if (flow->isInline() == childInline)
643 return flow->addChildIgnoringContinuation(newChild); // Just treat like an append.
644 return beforeChildAncestor->addChildIgnoringContinuation(newChild, beforeChild);
647 void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
649 m_lineBoxes.paint(this, paintInfo, paintOffset);
652 template<typename GeneratorContext>
653 void RenderInline::generateLineBoxRects(GeneratorContext& context) const
655 if (!alwaysCreateLineBoxes())
656 generateCulledLineBoxRects(context, this);
657 else if (InlineFlowBox* curr = firstLineBox()) {
658 for (; curr; curr = curr->nextLineBox())
659 context.addRect(FloatRect(curr->topLeft(), curr->size()));
661 context.addRect(FloatRect());
664 template<typename GeneratorContext>
665 void RenderInline::generateCulledLineBoxRects(GeneratorContext& context, const RenderInline* container) const
667 if (!culledInlineFirstLineBox()) {
668 context.addRect(FloatRect());
672 bool isHorizontal = style().isHorizontalWritingMode();
674 for (auto& current : childrenOfType<RenderObject>(*this)) {
675 if (current.isFloatingOrOutOfFlowPositioned())
678 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
679 // direction (aligned to the root box's baseline).
680 if (is<RenderBox>(current)) {
681 auto& renderBox = downcast<RenderBox>(current);
682 if (renderBox.inlineBoxWrapper()) {
683 const RootInlineBox& rootBox = renderBox.inlineBoxWrapper()->root();
684 const RenderStyle& containerStyle = rootBox.isFirstLine() ? container->firstLineStyle() : container->style();
685 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().fontCascade().fontMetrics().ascent() - containerStyle.fontCascade().fontMetrics().ascent());
686 int logicalHeight = containerStyle.fontCascade().fontMetrics().height();
688 context.addRect(FloatRect(renderBox.inlineBoxWrapper()->x() - renderBox.marginLeft(), logicalTop, renderBox.width() + renderBox.horizontalMarginExtent(), logicalHeight));
690 context.addRect(FloatRect(logicalTop, renderBox.inlineBoxWrapper()->y() - renderBox.marginTop(), logicalHeight, renderBox.height() + renderBox.verticalMarginExtent()));
692 } else if (is<RenderInline>(current)) {
693 // If the child doesn't need line boxes either, then we can recur.
694 auto& renderInline = downcast<RenderInline>(current);
695 if (!renderInline.alwaysCreateLineBoxes())
696 renderInline.generateCulledLineBoxRects(context, container);
698 for (InlineFlowBox* childLine = renderInline.firstLineBox(); childLine; childLine = childLine->nextLineBox()) {
699 const RootInlineBox& rootBox = childLine->root();
700 const RenderStyle& containerStyle = rootBox.isFirstLine() ? container->firstLineStyle() : container->style();
701 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().fontCascade().fontMetrics().ascent() - containerStyle.fontCascade().fontMetrics().ascent());
702 int logicalHeight = containerStyle.fontMetrics().height();
704 context.addRect(FloatRect(childLine->x() - childLine->marginLogicalLeft(),
706 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(),
709 context.addRect(FloatRect(logicalTop,
710 childLine->y() - childLine->marginLogicalLeft(),
712 childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight()));
716 } else if (is<RenderText>(current)) {
717 auto& currText = downcast<RenderText>(current);
718 for (InlineTextBox* childText = currText.firstTextBox(); childText; childText = childText->nextTextBox()) {
719 const RootInlineBox& rootBox = childText->root();
720 const RenderStyle& containerStyle = rootBox.isFirstLine() ? container->firstLineStyle() : container->style();
721 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().fontCascade().fontMetrics().ascent() - containerStyle.fontCascade().fontMetrics().ascent());
722 int logicalHeight = containerStyle.fontCascade().fontMetrics().height();
724 context.addRect(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight));
726 context.addRect(FloatRect(logicalTop, childText->y(), logicalHeight, childText->logicalWidth()));
728 } else if (is<RenderLineBreak>(current)) {
729 if (auto* inlineBox = downcast<RenderLineBreak>(current).inlineBoxWrapper()) {
730 // FIXME: This could use a helper to share these with text path.
731 const RootInlineBox& rootBox = inlineBox->root();
732 const RenderStyle& containerStyle = rootBox.isFirstLine() ? container->firstLineStyle() : container->style();
733 int logicalTop = rootBox.logicalTop() + (rootBox.lineStyle().fontCascade().fontMetrics().ascent() - containerStyle.fontCascade().fontMetrics().ascent());
734 int logicalHeight = containerStyle.fontMetrics().height();
736 context.addRect(FloatRect(inlineBox->x(), logicalTop, inlineBox->logicalWidth(), logicalHeight));
738 context.addRect(FloatRect(logicalTop, inlineBox->y(), logicalHeight, inlineBox->logicalWidth()));
746 class AbsoluteRectsGeneratorContext {
748 AbsoluteRectsGeneratorContext(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset)
750 , m_accumulatedOffset(accumulatedOffset) { }
752 void addRect(const FloatRect& rect)
754 LayoutRect adjustedRect = LayoutRect(rect);
755 adjustedRect.moveBy(m_accumulatedOffset);
756 m_rects.append(adjustedRect);
759 Vector<LayoutRect>& m_rects;
760 const LayoutPoint& m_accumulatedOffset;
763 } // unnamed namespace
765 void RenderInline::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
767 Vector<LayoutRect> lineboxRects;
768 AbsoluteRectsGeneratorContext context(lineboxRects, accumulatedOffset);
769 generateLineBoxRects(context);
770 for (const auto& rect : lineboxRects)
771 rects.append(snappedIntRect(rect));
773 if (RenderBoxModelObject* continuation = this->continuation()) {
774 if (is<RenderBox>(*continuation)) {
775 auto& box = downcast<RenderBox>(*continuation);
776 continuation->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location() + box.locationOffset()));
778 continuation->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location()));
785 class AbsoluteQuadsGeneratorContext {
787 AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector<FloatQuad>& quads)
791 m_geometryMap.pushMappingsToAncestor(renderer, nullptr);
794 void addRect(const FloatRect& rect)
796 m_quads.append(m_geometryMap.absoluteRect(rect));
799 Vector<FloatQuad>& m_quads;
800 RenderGeometryMap m_geometryMap;
803 } // unnamed namespace
805 void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
807 AbsoluteQuadsGeneratorContext context(this, quads);
808 generateLineBoxRects(context);
810 if (RenderBoxModelObject* continuation = this->continuation())
811 continuation->absoluteQuads(quads, wasFixed);
815 void RenderInline::absoluteQuadsForSelection(Vector<FloatQuad>& quads) const
817 AbsoluteQuadsGeneratorContext context(this, quads);
818 generateLineBoxRects(context);
822 LayoutUnit RenderInline::offsetLeft() const
825 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
826 topLeft = flooredLayoutPoint(firstBox->topLeft());
827 return adjustedPositionRelativeToOffsetParent(topLeft).x();
830 LayoutUnit RenderInline::offsetTop() const
833 if (InlineBox* firstBox = firstLineBoxIncludingCulling())
834 topLeft = flooredLayoutPoint(firstBox->topLeft());
835 return adjustedPositionRelativeToOffsetParent(topLeft).y();
838 static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin)
842 if (margin.isFixed())
843 return margin.value();
844 if (margin.isPercentOrCalculated())
845 return minimumValueForLength(margin, std::max<LayoutUnit>(0, renderer->containingBlock()->availableLogicalWidth()));
849 LayoutUnit RenderInline::marginLeft() const
851 return computeMargin(this, style().marginLeft());
854 LayoutUnit RenderInline::marginRight() const
856 return computeMargin(this, style().marginRight());
859 LayoutUnit RenderInline::marginTop() const
861 return computeMargin(this, style().marginTop());
864 LayoutUnit RenderInline::marginBottom() const
866 return computeMargin(this, style().marginBottom());
869 LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const
871 return computeMargin(this, style().marginStartUsing(otherStyle ? otherStyle : &style()));
874 LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const
876 return computeMargin(this, style().marginEndUsing(otherStyle ? otherStyle : &style()));
879 LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const
881 return computeMargin(this, style().marginBeforeUsing(otherStyle ? otherStyle : &style()));
884 LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const
886 return computeMargin(this, style().marginAfterUsing(otherStyle ? otherStyle : &style()));
889 const char* RenderInline::renderName() const
891 if (isRelPositioned())
892 return "RenderInline (relative positioned)";
893 if (isStickyPositioned())
894 return "RenderInline (sticky positioned)";
895 // FIXME: Temporary hack while the new generated content system is being implemented.
896 if (isPseudoElement())
897 return "RenderInline (generated)";
899 return "RenderInline (generated)";
900 return "RenderInline";
903 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
904 const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
906 return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction);
911 class HitTestCulledInlinesGeneratorContext {
913 HitTestCulledInlinesGeneratorContext(Region& region, const HitTestLocation& location)
914 : m_intersected(false)
916 , m_location(location)
919 void addRect(const FloatRect& rect)
921 m_intersected = m_intersected || m_location.intersects(rect);
922 m_region.unite(enclosingIntRect(rect));
925 bool intersected() const { return m_intersected; }
930 const HitTestLocation& m_location;
933 } // unnamed namespace
935 bool RenderInline::hitTestCulledInline(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset)
937 ASSERT(result.isRectBasedTest() && !alwaysCreateLineBoxes());
938 if (!visibleToHitTesting())
941 HitTestLocation tmpLocation(locationInContainer, -toLayoutSize(accumulatedOffset));
944 HitTestCulledInlinesGeneratorContext context(regionResult, tmpLocation);
945 generateCulledLineBoxRects(context, this);
947 if (context.intersected()) {
948 updateHitTestResult(result, tmpLocation.point());
949 // We can not use addNodeToRectBasedTestResult to determine if we fully enclose the hit-test area
950 // because it can only handle rectangular targets.
951 result.addNodeToRectBasedTestResult(element(), request, locationInContainer);
952 return regionResult.contains(tmpLocation.boundingBox());
957 VisiblePosition RenderInline::positionForPoint(const LayoutPoint& point, const RenderRegion* region)
959 // FIXME: Does not deal with relative or sticky positioned inlines (should it?)
960 RenderBlock& containingBlock = *this->containingBlock();
961 if (firstLineBox()) {
962 // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We
963 // should try to find a result by asking our containing block.
964 return containingBlock.positionForPoint(point, region);
967 // Translate the coords from the pre-anonymous block to the post-anonymous block.
968 LayoutPoint parentBlockPoint = containingBlock.location() + point;
969 RenderBoxModelObject* continuation = this->continuation();
970 while (continuation) {
971 RenderBlock* currentBlock = continuation->isInline() ? continuation->containingBlock() : downcast<RenderBlock>(continuation);
972 if (continuation->isInline() || continuation->firstChild())
973 return continuation->positionForPoint(parentBlockPoint - currentBlock->locationOffset(), region);
974 continuation = downcast<RenderBlock>(*continuation).inlineElementContinuation();
977 return RenderBoxModelObject::positionForPoint(point, region);
982 class LinesBoundingBoxGeneratorContext {
984 LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { }
986 void addRect(const FloatRect& rect)
988 m_rect.uniteIfNonZero(rect);
994 } // unnamed namespace
996 IntRect RenderInline::linesBoundingBox() const
998 if (!alwaysCreateLineBoxes()) {
999 ASSERT(!firstLineBox());
1000 FloatRect floatResult;
1001 LinesBoundingBoxGeneratorContext context(floatResult);
1002 generateCulledLineBoxRects(context, this);
1003 return enclosingIntRect(floatResult);
1008 // 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
1009 // 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
1010 // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
1011 ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
1012 if (firstLineBox() && lastLineBox()) {
1013 // Return the width of the minimal left side and the maximal right side.
1014 float logicalLeftSide = 0;
1015 float logicalRightSide = 0;
1016 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1017 if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
1018 logicalLeftSide = curr->logicalLeft();
1019 if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
1020 logicalRightSide = curr->logicalRight();
1023 bool isHorizontal = style().isHorizontalWritingMode();
1025 float x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
1026 float y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
1027 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
1028 float height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
1029 result = enclosingIntRect(FloatRect(x, y, width, height));
1035 InlineBox* RenderInline::culledInlineFirstLineBox() const
1037 for (auto& current : childrenOfType<RenderObject>(*this)) {
1038 if (current.isFloatingOrOutOfFlowPositioned())
1041 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
1042 // direction (aligned to the root box's baseline).
1043 if (is<RenderBox>(current)) {
1044 auto& renderBox = downcast<RenderBox>(current);
1045 if (renderBox.inlineBoxWrapper())
1046 return renderBox.inlineBoxWrapper();
1047 } else if (is<RenderLineBreak>(current)) {
1048 auto& renderBR = downcast<RenderLineBreak>(current);
1049 if (renderBR.inlineBoxWrapper())
1050 return renderBR.inlineBoxWrapper();
1051 } else if (is<RenderInline>(current)) {
1052 auto& renderInline = downcast<RenderInline>(current);
1053 if (InlineBox* result = renderInline.firstLineBoxIncludingCulling())
1055 } else if (is<RenderText>(current)) {
1056 auto& renderText = downcast<RenderText>(current);
1057 if (renderText.firstTextBox())
1058 return renderText.firstTextBox();
1064 InlineBox* RenderInline::culledInlineLastLineBox() const
1066 for (RenderObject* current = lastChild(); current; current = current->previousSibling()) {
1067 if (current->isFloatingOrOutOfFlowPositioned())
1070 // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block
1071 // direction (aligned to the root box's baseline).
1072 if (is<RenderBox>(*current)) {
1073 const auto& renderBox = downcast<RenderBox>(*current);
1074 if (renderBox.inlineBoxWrapper())
1075 return renderBox.inlineBoxWrapper();
1076 } else if (is<RenderLineBreak>(*current)) {
1077 RenderLineBreak& renderBR = downcast<RenderLineBreak>(*current);
1078 if (renderBR.inlineBoxWrapper())
1079 return renderBR.inlineBoxWrapper();
1080 } else if (is<RenderInline>(*current)) {
1081 RenderInline& renderInline = downcast<RenderInline>(*current);
1082 if (InlineBox* result = renderInline.lastLineBoxIncludingCulling())
1084 } else if (is<RenderText>(*current)) {
1085 RenderText& renderText = downcast<RenderText>(*current);
1086 if (renderText.lastTextBox())
1087 return renderText.lastTextBox();
1093 LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const
1095 FloatRect floatResult;
1096 LinesBoundingBoxGeneratorContext context(floatResult);
1097 generateCulledLineBoxRects(context, this);
1098 LayoutRect result(enclosingLayoutRect(floatResult));
1099 bool isHorizontal = style().isHorizontalWritingMode();
1100 for (auto& current : childrenOfType<RenderObject>(*this)) {
1101 if (current.isFloatingOrOutOfFlowPositioned())
1104 // For overflow we just have to propagate by hand and recompute it all.
1105 if (is<RenderBox>(current)) {
1106 auto& renderBox = downcast<RenderBox>(current);
1107 if (!renderBox.hasSelfPaintingLayer() && renderBox.inlineBoxWrapper()) {
1108 LayoutRect logicalRect = renderBox.logicalVisualOverflowRectForPropagation(&style());
1110 logicalRect.moveBy(renderBox.location());
1111 result.uniteIfNonZero(logicalRect);
1113 logicalRect.moveBy(renderBox.location());
1114 result.uniteIfNonZero(logicalRect.transposedRect());
1117 } else if (is<RenderInline>(current)) {
1118 // If the child doesn't need line boxes either, then we can recur.
1119 auto& renderInline = downcast<RenderInline>(current);
1120 if (!renderInline.alwaysCreateLineBoxes())
1121 result.uniteIfNonZero(renderInline.culledInlineVisualOverflowBoundingBox());
1122 else if (!renderInline.hasSelfPaintingLayer())
1123 result.uniteIfNonZero(renderInline.linesVisualOverflowBoundingBox());
1124 } else if (is<RenderText>(current)) {
1125 // FIXME; Overflow from text boxes is lost. We will need to cache this information in
1127 auto& renderText = downcast<RenderText>(current);
1128 result.uniteIfNonZero(renderText.linesVisualOverflowBoundingBox());
1134 LayoutRect RenderInline::linesVisualOverflowBoundingBox() const
1136 if (!alwaysCreateLineBoxes())
1137 return culledInlineVisualOverflowBoundingBox();
1139 if (!firstLineBox() || !lastLineBox())
1140 return LayoutRect();
1142 // Return the width of the minimal left side and the maximal right side.
1143 LayoutUnit logicalLeftSide = LayoutUnit::max();
1144 LayoutUnit logicalRightSide = LayoutUnit::min();
1145 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1146 logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow());
1147 logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow());
1150 const RootInlineBox& firstRootBox = firstLineBox()->root();
1151 const RootInlineBox& lastRootBox = lastLineBox()->root();
1153 LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop());
1154 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
1155 LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()) - logicalTop;
1157 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
1158 if (!style().isHorizontalWritingMode())
1159 rect = rect.transposedRect();
1163 LayoutRect RenderInline::linesVisualOverflowBoundingBoxInRegion(const RenderRegion* region) const
1165 ASSERT(alwaysCreateLineBoxes());
1168 if (!firstLineBox() || !lastLineBox())
1169 return LayoutRect();
1171 // Return the width of the minimal left side and the maximal right side.
1172 LayoutUnit logicalLeftSide = LayoutUnit::max();
1173 LayoutUnit logicalRightSide = LayoutUnit::min();
1174 LayoutUnit logicalTop;
1175 LayoutUnit logicalHeight;
1176 InlineFlowBox* lastInlineInRegion = 0;
1177 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1178 const RootInlineBox& root = curr->root();
1179 if (root.containingRegion() != region) {
1180 if (lastInlineInRegion)
1185 if (!lastInlineInRegion)
1186 logicalTop = curr->logicalTopVisualOverflow(root.lineTop());
1188 lastInlineInRegion = curr;
1190 logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow());
1191 logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow());
1194 if (!lastInlineInRegion)
1195 return LayoutRect();
1197 logicalHeight = lastInlineInRegion->logicalBottomVisualOverflow(lastInlineInRegion->root().lineBottom()) - logicalTop;
1199 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
1201 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
1202 if (!style().isHorizontalWritingMode())
1203 rect = rect.transposedRect();
1207 LayoutRect RenderInline::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
1209 // Only first-letter renderers are allowed in here during layout. They mutate the tree triggering repaints.
1210 ASSERT(!view().layoutStateEnabled() || style().styleType() == FIRST_LETTER);
1212 if (!firstLineBoxIncludingCulling() && !continuation())
1213 return LayoutRect();
1215 LayoutRect repaintRect(linesVisualOverflowBoundingBox());
1216 bool hitRepaintContainer = false;
1218 // We need to add in the in-flow position offsets of any inlines (including us) up to our
1219 // containing block.
1220 RenderBlock* containingBlock = this->containingBlock();
1221 for (const RenderElement* inlineFlow = this; is<RenderInline>(inlineFlow) && inlineFlow != containingBlock;
1222 inlineFlow = inlineFlow->parent()) {
1223 if (inlineFlow == repaintContainer) {
1224 hitRepaintContainer = true;
1227 if (inlineFlow->style().hasInFlowPosition() && inlineFlow->hasLayer())
1228 repaintRect.move(downcast<RenderInline>(*inlineFlow).layer()->offsetForInFlowPosition());
1231 LayoutUnit outlineSize = style().outlineSize();
1232 repaintRect.inflate(outlineSize);
1234 if (hitRepaintContainer || !containingBlock)
1237 if (containingBlock->hasOverflowClip())
1238 containingBlock->applyCachedClipAndScrollOffsetForRepaint(repaintRect);
1240 repaintRect = containingBlock->computeRectForRepaint(repaintRect, repaintContainer);
1243 for (auto& child : childrenOfType<RenderElement>(*this))
1244 repaintRect.unite(child.rectWithOutlineForRepaint(repaintContainer, outlineSize));
1246 if (RenderBoxModelObject* continuation = this->continuation()) {
1247 if (!continuation->isInline() && continuation->parent())
1248 repaintRect.unite(continuation->rectWithOutlineForRepaint(repaintContainer, outlineSize));
1255 LayoutRect RenderInline::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
1257 LayoutRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
1258 for (auto& child : childrenOfType<RenderElement>(*this))
1259 r.unite(child.rectWithOutlineForRepaint(repaintContainer, outlineWidth));
1263 LayoutRect RenderInline::computeRectForRepaint(const LayoutRect& rect, const RenderLayerModelObject* repaintContainer, bool fixed) const
1265 // LayoutState is only valid for root-relative repainting
1266 LayoutRect adjustedRect = rect;
1267 if (view().layoutStateEnabled() && !repaintContainer) {
1268 LayoutState* layoutState = view().layoutState();
1269 if (style().hasInFlowPosition() && layer())
1270 adjustedRect.move(layer()->offsetForInFlowPosition());
1271 adjustedRect.move(layoutState->m_paintOffset);
1272 if (layoutState->m_clipped)
1273 adjustedRect.intersect(layoutState->m_clipRect);
1274 return adjustedRect;
1277 if (repaintContainer == this)
1278 return adjustedRect;
1280 bool containerSkipped;
1281 RenderElement* container = this->container(repaintContainer, containerSkipped);
1283 return adjustedRect;
1285 LayoutPoint topLeft = adjustedRect.location();
1287 if (style().hasInFlowPosition() && layer()) {
1288 // Apply the in-flow position offset when invalidating a rectangle. The layer
1289 // is translated, but the render box isn't, so we need to do this to get the
1290 // right dirty rect. Since this is called from RenderObject::setStyle, the relative or sticky position
1291 // flag on the RenderObject has been cleared, so use the one on the style().
1292 topLeft += layer()->offsetForInFlowPosition();
1295 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1296 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1297 adjustedRect.setLocation(topLeft);
1298 if (container->hasOverflowClip()) {
1299 downcast<RenderBox>(*container).applyCachedClipAndScrollOffsetForRepaint(adjustedRect);
1300 if (adjustedRect.isEmpty())
1301 return adjustedRect;
1304 if (containerSkipped) {
1305 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
1306 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(*container);
1307 adjustedRect.move(-containerOffset);
1308 return adjustedRect;
1310 return container->computeRectForRepaint(adjustedRect, repaintContainer, fixed);
1313 LayoutSize RenderInline::offsetFromContainer(RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const
1315 ASSERT(&container == this->container());
1318 if (isInFlowPositioned())
1319 offset += offsetForInFlowPosition();
1321 if (is<RenderBox>(container))
1322 offset -= toLayoutSize(downcast<RenderBox>(container).scrollPosition());
1324 if (offsetDependsOnPoint)
1325 *offsetDependsOnPoint = (is<RenderBox>(container) && container.style().isFlippedBlocksWritingMode()) || is<RenderFlowThread>(container);
1330 void RenderInline::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const
1332 if (repaintContainer == this)
1335 if (view().layoutStateEnabled() && !repaintContainer) {
1336 LayoutState* layoutState = view().layoutState();
1337 LayoutSize offset = layoutState->m_paintOffset;
1338 if (style().hasInFlowPosition() && layer())
1339 offset += layer()->offsetForInFlowPosition();
1340 transformState.move(offset);
1344 bool containerSkipped;
1345 RenderElement* container = this->container(repaintContainer, containerSkipped);
1349 if (mode & ApplyContainerFlip && is<RenderBox>(*container)) {
1350 if (container->style().isFlippedBlocksWritingMode()) {
1351 LayoutPoint centerPoint(transformState.mappedPoint());
1352 transformState.move(downcast<RenderBox>(*container).flipForWritingMode(centerPoint) - centerPoint);
1354 mode &= ~ApplyContainerFlip;
1357 LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint(transformState.mappedPoint()));
1359 bool preserve3D = mode & UseTransforms && (container->style().preserves3D() || style().preserves3D());
1360 if (mode & UseTransforms && shouldUseTransformFromContainer(container)) {
1361 TransformationMatrix t;
1362 getTransformFromContainer(container, containerOffset, t);
1363 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1365 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1367 if (containerSkipped) {
1368 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1369 // to just subtract the delta between the repaintContainer and o.
1370 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(*container);
1371 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1375 container->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
1378 const RenderObject* RenderInline::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
1380 ASSERT(ancestorToStopAt != this);
1382 bool ancestorSkipped;
1383 RenderElement* container = this->container(ancestorToStopAt, ancestorSkipped);
1387 LayoutSize adjustmentForSkippedAncestor;
1388 if (ancestorSkipped) {
1389 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1390 // to just subtract the delta between the ancestor and o.
1391 adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(*container);
1394 bool offsetDependsOnPoint = false;
1395 LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint(), &offsetDependsOnPoint);
1397 bool preserve3D = container->style().preserves3D() || style().preserves3D();
1398 if (shouldUseTransformFromContainer(container)) {
1399 TransformationMatrix t;
1400 getTransformFromContainer(container, containerOffset, t);
1401 t.translateRight(adjustmentForSkippedAncestor.width(), adjustmentForSkippedAncestor.height()); // FIXME: right?
1402 geometryMap.push(this, t, preserve3D, offsetDependsOnPoint);
1404 containerOffset += adjustmentForSkippedAncestor;
1405 geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint);
1408 return ancestorSkipped ? ancestorToStopAt : container;
1411 void RenderInline::updateDragState(bool dragOn)
1413 RenderBoxModelObject::updateDragState(dragOn);
1414 if (RenderBoxModelObject* continuation = this->continuation())
1415 continuation->updateDragState(dragOn);
1418 void RenderInline::childBecameNonInline(RenderElement& child)
1420 // We have to split the parent flow.
1421 RenderBlock* newBox = containingBlock()->createAnonymousBlock();
1422 RenderBoxModelObject* oldContinuation = continuation();
1423 setContinuation(newBox);
1424 RenderObject* beforeChild = child.nextSibling();
1425 removeChildInternal(child, NotifyChildren);
1426 splitFlow(beforeChild, newBox, &child, oldContinuation);
1429 void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
1431 if (result.innerNode())
1434 LayoutPoint localPoint(point);
1435 if (Element* element = this->element()) {
1436 if (isInlineElementContinuation()) {
1437 // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space
1438 // of the principal renderer's containing block. This will end up being the innerNonSharedNode.
1439 RenderBlock* firstBlock = element->renderer()->containingBlock();
1441 // Get our containing block.
1442 RenderBox* block = containingBlock();
1443 localPoint.moveBy(block->location() - firstBlock->locationOffset());
1446 result.setInnerNode(element);
1447 if (!result.innerNonSharedNode())
1448 result.setInnerNonSharedNode(element);
1449 result.setLocalPoint(localPoint);
1453 void RenderInline::dirtyLineBoxes(bool fullLayout)
1456 m_lineBoxes.deleteLineBoxes();
1460 if (!alwaysCreateLineBoxes()) {
1461 // We have to grovel into our children in order to dirty the appropriate lines.
1462 for (auto& current : childrenOfType<RenderObject>(*this)) {
1463 if (current.isFloatingOrOutOfFlowPositioned())
1465 if (is<RenderBox>(current) && !current.needsLayout()) {
1466 auto& renderBox = downcast<RenderBox>(current);
1467 if (renderBox.inlineBoxWrapper())
1468 renderBox.inlineBoxWrapper()->root().markDirty();
1469 } else if (!current.selfNeedsLayout()) {
1470 if (is<RenderInline>(current)) {
1471 auto& renderInline = downcast<RenderInline>(current);
1472 for (InlineFlowBox* childLine = renderInline.firstLineBox(); childLine; childLine = childLine->nextLineBox())
1473 childLine->root().markDirty();
1474 } else if (is<RenderText>(current)) {
1475 auto& renderText = downcast<RenderText>(current);
1476 for (InlineTextBox* childText = renderText.firstTextBox(); childText; childText = childText->nextTextBox())
1477 childText->root().markDirty();
1478 } else if (is<RenderLineBreak>(current)) {
1479 auto& renderBR = downcast<RenderLineBreak>(current);
1480 if (renderBR.inlineBoxWrapper())
1481 renderBR.inlineBoxWrapper()->root().markDirty();
1486 m_lineBoxes.dirtyLineBoxes();
1489 void RenderInline::deleteLines()
1491 m_lineBoxes.deleteLineBoxTree();
1494 std::unique_ptr<InlineFlowBox> RenderInline::createInlineFlowBox()
1496 return std::make_unique<InlineFlowBox>(*this);
1499 InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
1501 setAlwaysCreateLineBoxes();
1502 auto newFlowBox = createInlineFlowBox();
1503 auto flowBox = newFlowBox.get();
1504 m_lineBoxes.appendLineBox(WTFMove(newFlowBox));
1508 LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
1510 if (firstLine && view().usesFirstLineRules()) {
1511 const RenderStyle& firstLineStyle = this->firstLineStyle();
1512 if (&firstLineStyle != &style())
1513 return firstLineStyle.computedLineHeight();
1516 return style().computedLineHeight();
1519 int RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1521 const RenderStyle& style = firstLine ? firstLineStyle() : this->style();
1522 const FontMetrics& fontMetrics = style.fontMetrics();
1523 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
1526 LayoutSize RenderInline::offsetForInFlowPositionedInline(const RenderBox* child) const
1528 // FIXME: This function isn't right with mixed writing modes.
1530 ASSERT(isInFlowPositioned());
1531 if (!isInFlowPositioned())
1532 return LayoutSize();
1534 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line
1535 // box from the rest of the content, but only in the cases where we know we're positioned
1536 // relative to the inline itself.
1538 LayoutSize logicalOffset;
1539 LayoutUnit inlinePosition;
1540 LayoutUnit blockPosition;
1541 if (firstLineBox()) {
1542 inlinePosition = LayoutUnit::fromFloatRound(firstLineBox()->logicalLeft());
1543 blockPosition = firstLineBox()->logicalTop();
1545 inlinePosition = layer()->staticInlinePosition();
1546 blockPosition = layer()->staticBlockPosition();
1549 if (!child->style().hasStaticInlinePosition(style().isHorizontalWritingMode()))
1550 logicalOffset.setWidth(inlinePosition);
1552 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside
1553 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct
1554 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers
1556 else if (!child->style().isOriginalDisplayInlineType())
1557 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
1558 logicalOffset.setWidth(inlinePosition - child->containingBlock()->borderAndPaddingLogicalLeft());
1560 if (!child->style().hasStaticBlockPosition(style().isHorizontalWritingMode()))
1561 logicalOffset.setHeight(blockPosition);
1563 return style().isHorizontalWritingMode() ? logicalOffset : logicalOffset.transposedSize();
1566 void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
1571 // FIXME: We can do better.
1575 void RenderInline::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
1577 AbsoluteRectsGeneratorContext context(rects, additionalOffset);
1578 generateLineBoxRects(context);
1580 for (auto& child : childrenOfType<RenderElement>(*this)) {
1581 if (is<RenderListMarker>(child))
1583 FloatPoint pos(additionalOffset);
1584 // FIXME: This doesn't work correctly with transforms.
1585 if (child.hasLayer())
1586 pos = child.localToContainerPoint(FloatPoint(), paintContainer);
1587 else if (is<RenderBox>(child))
1588 pos.move(downcast<RenderBox>(child).locationOffset());
1589 child.addFocusRingRects(rects, flooredIntPoint(pos), paintContainer);
1592 if (RenderBoxModelObject* continuation = this->continuation()) {
1593 if (continuation->isInline())
1594 continuation->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + continuation->containingBlock()->location() - containingBlock()->location())), paintContainer);
1596 continuation->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + downcast<RenderBox>(*continuation).location() - containingBlock()->location())), paintContainer);
1600 void RenderInline::paintOutline(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1605 auto& styleToUse = style();
1606 // Only paint the focus ring by hand if the theme isn't able to draw it.
1607 if (styleToUse.outlineStyleIsAuto() && !theme().supportsFocusRing(styleToUse)) {
1608 Vector<LayoutRect> focusRingRects;
1609 addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer);
1610 paintFocusRing(paintInfo, styleToUse, focusRingRects);
1613 if (hasOutlineAnnotation() && !styleToUse.outlineStyleIsAuto() && !theme().supportsFocusRing(styleToUse))
1614 addPDFURLRect(paintInfo, paintOffset);
1616 GraphicsContext& graphicsContext = paintInfo.context();
1617 if (graphicsContext.paintingDisabled())
1620 if (styleToUse.outlineStyleIsAuto() || !styleToUse.hasOutline())
1623 Vector<LayoutRect> rects;
1624 rects.append(LayoutRect());
1625 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
1626 const RootInlineBox& rootBox = curr->root();
1627 LayoutUnit top = std::max<LayoutUnit>(rootBox.lineTop(), curr->logicalTop());
1628 LayoutUnit bottom = std::min<LayoutUnit>(rootBox.lineBottom(), curr->logicalBottom());
1629 rects.append(LayoutRect(curr->x(), top, curr->logicalWidth(), bottom - top));
1631 rects.append(LayoutRect());
1633 Color outlineColor = styleToUse.visitedDependentColor(CSSPropertyOutlineColor);
1634 bool useTransparencyLayer = outlineColor.hasAlpha();
1635 if (useTransparencyLayer) {
1636 graphicsContext.beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255);
1637 outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
1640 for (unsigned i = 1; i < rects.size() - 1; i++)
1641 paintOutlineForLine(graphicsContext, paintOffset, rects.at(i - 1), rects.at(i), rects.at(i + 1), outlineColor);
1643 if (useTransparencyLayer)
1644 graphicsContext.endTransparencyLayer();
1647 void RenderInline::paintOutlineForLine(GraphicsContext& graphicsContext, const LayoutPoint& paintOffset,
1648 const LayoutRect& previousLine, const LayoutRect& thisLine, const LayoutRect& nextLine, const Color outlineColor)
1650 const auto& styleToUse = style();
1651 float outlineOffset = styleToUse.outlineOffset();
1652 LayoutRect outlineBoxRect = thisLine;
1653 outlineBoxRect.inflate(outlineOffset);
1654 outlineBoxRect.moveBy(paintOffset);
1655 if (outlineBoxRect.isEmpty())
1658 float outlineWidth = styleToUse.outlineWidth();
1659 EBorderStyle outlineStyle = styleToUse.outlineStyle();
1660 bool antialias = shouldAntialiasLines(graphicsContext);
1662 auto adjustedPreviousLine = previousLine;
1663 adjustedPreviousLine.moveBy(paintOffset);
1664 auto adjustedNextLine = nextLine;
1665 adjustedNextLine.moveBy(paintOffset);
1667 float adjacentWidth1 = 0;
1668 float adjacentWidth2 = 0;
1670 auto topLeft = outlineBoxRect.minXMinYCorner();
1671 if (previousLine.isEmpty() || thisLine.x() < previousLine.x() || (previousLine.maxX()) <= thisLine.x()) {
1672 topLeft.move(-outlineWidth, -outlineWidth);
1673 adjacentWidth1 = outlineWidth;
1675 topLeft.move(-outlineWidth, 2 * outlineOffset);
1676 adjacentWidth1 = -outlineWidth;
1678 auto bottomRight = outlineBoxRect.minXMaxYCorner();
1679 if (nextLine.isEmpty() || thisLine.x() <= nextLine.x() || (nextLine.maxX()) <= thisLine.x()) {
1680 bottomRight.move(0, outlineWidth);
1681 adjacentWidth2 = outlineWidth;
1683 bottomRight.move(0, -2 * outlineOffset);
1684 adjacentWidth2 = -outlineWidth;
1686 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSLeft, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1689 topLeft = outlineBoxRect.maxXMinYCorner();
1690 if (previousLine.isEmpty() || previousLine.maxX() < thisLine.maxX() || thisLine.maxX() <= previousLine.x()) {
1691 topLeft.move(0, -outlineWidth);
1692 adjacentWidth1 = outlineWidth;
1694 topLeft.move(0, 2 * outlineOffset);
1695 adjacentWidth1 = -outlineWidth;
1697 bottomRight = outlineBoxRect.maxXMaxYCorner();
1698 if (nextLine.isEmpty() || nextLine.maxX() <= thisLine.maxX() || thisLine.maxX() <= nextLine.x()) {
1699 bottomRight.move(outlineWidth, outlineWidth);
1700 adjacentWidth2 = outlineWidth;
1702 bottomRight.move(outlineWidth, -2 * outlineOffset);
1703 adjacentWidth2 = -outlineWidth;
1705 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSRight, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1708 if (thisLine.x() < previousLine.x()) {
1709 topLeft = outlineBoxRect.minXMinYCorner();
1710 topLeft.move(-outlineWidth, -outlineWidth);
1711 adjacentWidth1 = outlineWidth;
1712 bottomRight = outlineBoxRect.maxXMinYCorner();
1713 bottomRight.move(outlineWidth, 0);
1714 if (!previousLine.isEmpty() && adjustedPreviousLine.x() < bottomRight.x()) {
1715 bottomRight.setX(adjustedPreviousLine.x() - outlineOffset);
1716 adjacentWidth2 = -outlineWidth;
1718 adjacentWidth2 = outlineWidth;
1719 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1722 if (previousLine.maxX() < thisLine.maxX()) {
1723 topLeft = outlineBoxRect.minXMinYCorner();
1724 topLeft.move(-outlineWidth, -outlineWidth);
1725 if (!previousLine.isEmpty() && adjustedPreviousLine.maxX() > topLeft.x()) {
1726 topLeft.setX(adjustedPreviousLine.maxX() + outlineOffset);
1727 adjacentWidth1 = -outlineWidth;
1729 adjacentWidth1 = outlineWidth;
1730 bottomRight = outlineBoxRect.maxXMinYCorner();
1731 bottomRight.move(outlineWidth, 0);
1732 adjacentWidth2 = outlineWidth;
1733 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1736 if (thisLine.x() == thisLine.maxX()) {
1737 topLeft = outlineBoxRect.minXMinYCorner();
1738 topLeft.move(-outlineWidth, -outlineWidth);
1739 adjacentWidth1 = outlineWidth;
1740 bottomRight = outlineBoxRect.maxXMinYCorner();
1741 bottomRight.move(outlineWidth, 0);
1742 adjacentWidth2 = outlineWidth;
1743 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1747 if (thisLine.x() < nextLine.x()) {
1748 topLeft = outlineBoxRect.minXMaxYCorner();
1749 topLeft.move(-outlineWidth, 0);
1750 adjacentWidth1 = outlineWidth;
1751 bottomRight = outlineBoxRect.maxXMaxYCorner();
1752 bottomRight.move(outlineWidth, outlineWidth);
1753 if (!nextLine.isEmpty() && (adjustedNextLine.x() < bottomRight.x())) {
1754 bottomRight.setX(adjustedNextLine.x() - outlineOffset);
1755 adjacentWidth2 = -outlineWidth;
1757 adjacentWidth2 = outlineWidth;
1758 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1761 if (nextLine.maxX() < thisLine.maxX()) {
1762 topLeft = outlineBoxRect.minXMaxYCorner();
1763 topLeft.move(-outlineWidth, 0);
1764 if (!nextLine.isEmpty() && adjustedNextLine.maxX() > topLeft.x()) {
1765 topLeft.setX(adjustedNextLine.maxX() + outlineOffset);
1766 adjacentWidth1 = -outlineWidth;
1768 adjacentWidth1 = outlineWidth;
1769 bottomRight = outlineBoxRect.maxXMaxYCorner();
1770 bottomRight.move(outlineWidth, outlineWidth);
1771 adjacentWidth2 = outlineWidth;
1772 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1775 if (thisLine.x() == thisLine.maxX()) {
1776 topLeft = outlineBoxRect.minXMaxYCorner();
1777 topLeft.move(-outlineWidth, 0);
1778 adjacentWidth1 = outlineWidth;
1779 bottomRight = outlineBoxRect.maxXMaxYCorner();
1780 bottomRight.move(outlineWidth, outlineWidth);
1781 adjacentWidth2 = outlineWidth;
1782 drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
1786 #if ENABLE(DASHBOARD_SUPPORT)
1787 void RenderInline::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions)
1789 // Convert the style regions to absolute coordinates.
1790 if (style().visibility() != VISIBLE)
1793 const Vector<StyleDashboardRegion>& styleRegions = style().dashboardRegions();
1794 unsigned i, count = styleRegions.size();
1795 for (i = 0; i < count; i++) {
1796 StyleDashboardRegion styleRegion = styleRegions[i];
1798 LayoutRect linesBoundingBox = this->linesBoundingBox();
1799 LayoutUnit w = linesBoundingBox.width();
1800 LayoutUnit h = linesBoundingBox.height();
1802 AnnotatedRegionValue region;
1803 region.label = styleRegion.label;
1804 region.bounds = LayoutRect(linesBoundingBox.x() + styleRegion.offset.left().value(),
1805 linesBoundingBox.y() + styleRegion.offset.top().value(),
1806 w - styleRegion.offset.left().value() - styleRegion.offset.right().value(),
1807 h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value());
1808 region.type = styleRegion.type;
1810 RenderObject* container = containingBlock();
1814 region.clip = region.bounds;
1815 container->computeAbsoluteRepaintRect(region.clip);
1816 if (region.clip.height() < 0) {
1817 region.clip.setHeight(0);
1818 region.clip.setWidth(0);
1821 FloatPoint absPos = container->localToAbsolute();
1822 region.bounds.setX(absPos.x() + region.bounds.x());
1823 region.bounds.setY(absPos.y() + region.bounds.y());
1825 regions.append(region);
1830 } // namespace WebCore