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