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