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