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