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