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