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 "Page.h" 31 #include "RenderArena.h" 32 #include "RenderBlock.h" 33 #include "RenderView.h" 34 #include "TransformState.h" 35 #include "VisiblePosition.h" 36 37 #if ENABLE(DASHBOARD_SUPPORT) 38 #include "Frame.h" 39 #endif 40 41 using namespace std; 42 43 namespace WebCore { 44 45 RenderInline::RenderInline(Node* node) 46 : RenderBoxModelObject(node) 47 , m_continuation(0) 48 , m_lineHeight(-1) 49 , m_verticalPosition(PositionUndefined) 50 { 51 setChildrenInline(true); 52 } 53 54 void RenderInline::destroy() 55 { 56 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will 57 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. 58 children()->destroyLeftoverChildren(); 59 60 // Destroy our continuation before anything other than anonymous children. 61 // The reason we don't destroy it before anonymous children is that they may 62 // have continuations of their own that are anonymous children of our continuation. 63 if (m_continuation) { 64 m_continuation->destroy(); 65 m_continuation = 0; 66 } 67 68 if (!documentBeingDestroyed()) { 69 if (firstLineBox()) { 70 // We can't wait for RenderBoxModelObject::destroy to clear the selection, 71 // because by then we will have nuked the line boxes. 72 // FIXME: The SelectionController should be responsible for this when it 73 // is notified of DOM mutations. 74 if (isSelectionBorder()) 75 view()->clearSelection(); 76 77 // If line boxes are contained inside a root, that means we're an inline. 78 // In that case, we need to remove all the line boxes so that the parent 79 // lines aren't pointing to deleted children. If the first line box does 80 // not have a parent that means they are either already disconnected or 81 // root lines that can just be destroyed without disconnecting. 82 if (firstLineBox()->parent()) { 83 for (InlineRunBox* box = firstLineBox(); box; box = box->nextLineBox()) 84 box->remove(); 85 } 86 } else if (isInline() && parent()) 87 parent()->dirtyLinesFromChangedChild(this); 88 } 89 90 m_lineBoxes.deleteLineBoxes(renderArena()); 91 92 RenderBoxModelObject::destroy(); 93 } 94 95 RenderInline* RenderInline::inlineContinuation() const 96 { 97 if (!m_continuation || m_continuation->isInline()) 98 return toRenderInline(m_continuation); 99 return toRenderBlock(m_continuation)->inlineContinuation(); 100 } 101 102 void RenderInline::updateBoxModelInfoFromStyle() 103 { 104 RenderBoxModelObject::updateBoxModelInfoFromStyle(); 105 106 setInline(true); // Needed for run-ins, since run-in is considered a block display type. 107 108 // FIXME: Support transforms and reflections on inline flows someday. 109 setHasTransform(false); 110 setHasReflection(false); 111 } 112 113 void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 114 { 115 RenderBoxModelObject::styleDidChange(diff, oldStyle); 116 117 // Ensure that all of the split inlines pick up the new style. We 118 // only do this if we're an inline, since we don't want to propagate 119 // a block's style to the other inlines. 120 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before 121 // and after the block share the same style, but the block doesn't 122 // need to pass its style on to anyone else. 123 for (RenderInline* currCont = inlineContinuation(); currCont; currCont = currCont->inlineContinuation()) { 124 RenderBoxModelObject* nextCont = currCont->continuation(); 125 currCont->setContinuation(0); 126 currCont->setStyle(style()); 127 currCont->setContinuation(nextCont); 128 } 129 130 m_lineHeight = -1; 131 132 // Update pseudos for :before and :after now. 133 if (!isAnonymous() && document()->usesBeforeAfterRules()) { 134 children()->updateBeforeAfterContent(this, BEFORE); 135 children()->updateBeforeAfterContent(this, AFTER); 136 } 137 } 138 139 void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild) 140 { 141 if (continuation()) 142 return addChildToContinuation(newChild, beforeChild); 143 return addChildIgnoringContinuation(newChild, beforeChild); 144 } 145 146 static RenderBoxModelObject* nextContinuation(RenderObject* renderer) 147 { 148 if (renderer->isInline() && !renderer->isReplaced()) 149 return toRenderInline(renderer)->continuation(); 150 return toRenderBlock(renderer)->inlineContinuation(); 151 } 152 153 RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild) 154 { 155 if (beforeChild && beforeChild->parent() == this) 156 return this; 157 158 RenderBoxModelObject* curr = nextContinuation(this); 159 RenderBoxModelObject* nextToLast = this; 160 RenderBoxModelObject* last = this; 161 while (curr) { 162 if (beforeChild && beforeChild->parent() == curr) { 163 if (curr->firstChild() == beforeChild) 164 return last; 165 return curr; 166 } 167 168 nextToLast = last; 169 last = curr; 170 curr = nextContinuation(curr); 171 } 172 173 if (!beforeChild && !last->firstChild()) 174 return nextToLast; 175 return last; 176 } 177 178 void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) 179 { 180 // Make sure we don't append things after :after-generated content if we have it. 181 if (!beforeChild && isAfterContent(lastChild())) 182 beforeChild = lastChild(); 183 184 if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) { 185 // We are placing a block inside an inline. We have to perform a split of this 186 // inline into continuations. This involves creating an anonymous block box to hold 187 // |newChild|. We then make that block box a continuation of this inline. We take all of 188 // the children after |beforeChild| and put them in a clone of this object. 189 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 190 newStyle->inheritFrom(style()); 191 newStyle->setDisplay(BLOCK); 192 193 RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); 194 newBox->setStyle(newStyle.release()); 195 RenderBoxModelObject* oldContinuation = continuation(); 196 setContinuation(newBox); 197 198 // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content 199 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after 200 // content gets properly destroyed. 201 bool isLastChild = (beforeChild == lastChild()); 202 if (document()->usesBeforeAfterRules()) 203 children()->updateBeforeAfterContent(this, AFTER); 204 if (isLastChild && beforeChild != lastChild()) 205 beforeChild = 0; // We destroyed the last child, so now we need to update our insertion 206 // point to be 0. It's just a straight append now. 207 208 splitFlow(beforeChild, newBox, newChild, oldContinuation); 209 return; 210 } 211 212 RenderBoxModelObject::addChild(newChild, beforeChild); 213 214 newChild->setNeedsLayoutAndPrefWidthsRecalc(); 215 } 216 217 RenderInline* RenderInline::cloneInline(RenderInline* src) 218 { 219 RenderInline* o = new (src->renderArena()) RenderInline(src->node()); 220 o->setStyle(src->style()); 221 return o; 222 } 223 224 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, 225 RenderBlock* middleBlock, 226 RenderObject* beforeChild, RenderBoxModelObject* oldCont) 227 { 228 // Create a clone of this inline. 229 RenderInline* clone = cloneInline(this); 230 clone->setContinuation(oldCont); 231 232 // Now take all of the children from beforeChild to the end and remove 233 // them from |this| and place them in the clone. 234 RenderObject* o = beforeChild; 235 while (o) { 236 RenderObject* tmp = o; 237 o = tmp->nextSibling(); 238 clone->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0); 239 tmp->setNeedsLayoutAndPrefWidthsRecalc(); 240 } 241 242 // Hook |clone| up as the continuation of the middle block. 243 middleBlock->setInlineContinuation(clone); 244 245 // We have been reparented and are now under the fromBlock. We need 246 // to walk up our inline parent chain until we hit the containing block. 247 // Once we hit the containing block we're done. 248 RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); 249 RenderBoxModelObject* currChild = this; 250 251 // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone. 252 // There will eventually be a better approach to this problem that will let us nest to a much 253 // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in 254 // incorrect rendering, but the alternative is to hang forever. 255 unsigned splitDepth = 1; 256 const unsigned cMaxSplitDepth = 200; 257 while (curr && curr != fromBlock) { 258 ASSERT(curr->isRenderInline()); 259 if (splitDepth < cMaxSplitDepth) { 260 // Create a new clone. 261 RenderInline* cloneChild = clone; 262 clone = cloneInline(toRenderInline(curr)); 263 264 // Insert our child clone as the first child. 265 clone->addChildIgnoringContinuation(cloneChild, 0); 266 267 // Hook the clone up as a continuation of |curr|. 268 RenderInline* inlineCurr = toRenderInline(curr); 269 oldCont = inlineCurr->continuation(); 270 inlineCurr->setContinuation(clone); 271 clone->setContinuation(oldCont); 272 273 // Someone may have indirectly caused a <q> to split. When this happens, the :after content 274 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after 275 // content gets properly destroyed. 276 if (document()->usesBeforeAfterRules()) 277 inlineCurr->children()->updateBeforeAfterContent(this, AFTER); 278 279 // Now we need to take all of the children starting from the first child 280 // *after* currChild and append them all to the clone. 281 o = currChild->nextSibling(); 282 while (o) { 283 RenderObject* tmp = o; 284 o = tmp->nextSibling(); 285 clone->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0); 286 tmp->setNeedsLayoutAndPrefWidthsRecalc(); 287 } 288 } 289 290 // Keep walking up the chain. 291 currChild = curr; 292 curr = toRenderBoxModelObject(curr->parent()); 293 splitDepth++; 294 } 295 296 // Now we are at the block level. We need to put the clone into the toBlock. 297 toBlock->children()->appendChildNode(toBlock, clone); 298 299 // Now take all the children after currChild and remove them from the fromBlock 300 // and put them in the toBlock. 301 o = currChild->nextSibling(); 302 while (o) { 303 RenderObject* tmp = o; 304 o = tmp->nextSibling(); 305 toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp)); 306 } 307 } 308 309 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, 310 RenderObject* newChild, RenderBoxModelObject* oldCont) 311 { 312 RenderBlock* pre = 0; 313 RenderBlock* block = containingBlock(); 314 315 // Delete our line boxes before we do the inline split into continuations. 316 block->deleteLineBoxTree(); 317 318 bool madeNewBeforeBlock = false; 319 if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) { 320 // We can reuse this block and make it the preBlock of the next continuation. 321 pre = block; 322 pre->removePositionedObjects(0); 323 block = block->containingBlock(); 324 } else { 325 // No anonymous block available for use. Make one. 326 pre = block->createAnonymousBlock(); 327 madeNewBeforeBlock = true; 328 } 329 330 RenderBlock* post = block->createAnonymousBlock(); 331 332 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); 333 if (madeNewBeforeBlock) 334 block->children()->insertChildNode(block, pre, boxFirst); 335 block->children()->insertChildNode(block, newBlockBox, boxFirst); 336 block->children()->insertChildNode(block, post, boxFirst); 337 block->setChildrenInline(false); 338 339 if (madeNewBeforeBlock) { 340 RenderObject* o = boxFirst; 341 while (o) { 342 RenderObject* no = o; 343 o = no->nextSibling(); 344 pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, no)); 345 no->setNeedsLayoutAndPrefWidthsRecalc(); 346 } 347 } 348 349 splitInlines(pre, post, newBlockBox, beforeChild, oldCont); 350 351 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting 352 // time in makeChildrenNonInline by just setting this explicitly up front. 353 newBlockBox->setChildrenInline(false); 354 355 // We delayed adding the newChild until now so that the |newBlockBox| would be fully 356 // connected, thus allowing newChild access to a renderArena should it need 357 // to wrap itself in additional boxes (e.g., table construction). 358 newBlockBox->addChild(newChild); 359 360 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) 361 // get deleted properly. Because objects moves from the pre block into the post block, we want to 362 // make new line boxes instead of leaving the old line boxes around. 363 pre->setNeedsLayoutAndPrefWidthsRecalc(); 364 block->setNeedsLayoutAndPrefWidthsRecalc(); 365 post->setNeedsLayoutAndPrefWidthsRecalc(); 366 } 367 368 void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) 369 { 370 RenderBoxModelObject* flow = continuationBefore(beforeChild); 371 ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline()); 372 RenderBoxModelObject* beforeChildParent = 0; 373 if (beforeChild) 374 beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); 375 else { 376 RenderBoxModelObject* cont = nextContinuation(flow); 377 if (cont) 378 beforeChildParent = cont; 379 else 380 beforeChildParent = flow; 381 } 382 383 if (newChild->isFloatingOrPositioned()) 384 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 385 386 // A continuation always consists of two potential candidates: an inline or an anonymous 387 // block box holding block children. 388 bool childInline = newChild->isInline(); 389 bool bcpInline = beforeChildParent->isInline(); 390 bool flowInline = flow->isInline(); 391 392 if (flow == beforeChildParent) 393 return flow->addChildIgnoringContinuation(newChild, beforeChild); 394 else { 395 // The goal here is to match up if we can, so that we can coalesce and create the 396 // minimal # of continuations needed for the inline. 397 if (childInline == bcpInline) 398 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 399 else if (flowInline == childInline) 400 return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. 401 else 402 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 403 } 404 } 405 406 void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty) 407 { 408 m_lineBoxes.paint(this, paintInfo, tx, ty); 409 } 410 411 void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty) 412 { 413 if (InlineRunBox* curr = firstLineBox()) { 414 for (; curr; curr = curr->nextLineBox()) 415 rects.append(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())); 416 } else 417 rects.append(IntRect(tx, ty, 0, 0)); 418 419 if (continuation()) { 420 if (continuation()->isBox()) { 421 RenderBox* box = toRenderBox(continuation()); 422 continuation()->absoluteRects(rects, 423 tx - containingBlock()->x() + box->x(), 424 ty - containingBlock()->y() + box->y()); 425 } else 426 continuation()->absoluteRects(rects, tx - containingBlock()->x(), ty - containingBlock()->y()); 427 } 428 } 429 430 void RenderInline::absoluteQuads(Vector<FloatQuad>& quads) 431 { 432 if (InlineRunBox* curr = firstLineBox()) { 433 for (; curr; curr = curr->nextLineBox()) { 434 FloatRect localRect(curr->x(), curr->y(), curr->width(), curr->height()); 435 quads.append(localToAbsoluteQuad(localRect)); 436 } 437 } else 438 quads.append(localToAbsoluteQuad(FloatRect())); 439 440 if (continuation()) 441 continuation()->absoluteQuads(quads); 442 } 443 444 int RenderInline::offsetLeft() const 445 { 446 int x = RenderBoxModelObject::offsetLeft(); 447 if (firstLineBox()) 448 x += firstLineBox()->x(); 449 return x; 450 } 451 452 int RenderInline::offsetTop() const 453 { 454 int y = RenderBoxModelObject::offsetTop(); 455 if (firstLineBox()) 456 y += firstLineBox()->y(); 457 return y; 458 } 459 460 int RenderInline::marginLeft() const 461 { 462 Length margin = style()->marginLeft(); 463 if (margin.isAuto()) 464 return 0; 465 if (margin.isFixed()) 466 return margin.value(); 467 if (margin.isPercent()) 468 return margin.calcMinValue(max(0, containingBlock()->availableWidth())); 469 return 0; 470 } 471 472 int RenderInline::marginRight() const 473 { 474 Length margin = style()->marginRight(); 475 if (margin.isAuto()) 476 return 0; 477 if (margin.isFixed()) 478 return margin.value(); 479 if (margin.isPercent()) 480 return margin.calcMinValue(max(0, containingBlock()->availableWidth())); 481 return 0; 482 } 483 484 const char* RenderInline::renderName() const 485 { 486 if (isRelPositioned()) 487 return "RenderInline (relative positioned)"; 488 if (isAnonymous()) 489 return "RenderInline (generated)"; 490 if (isRunIn()) 491 return "RenderInline (run-in)"; 492 return "RenderInline"; 493 } 494 495 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, 496 int x, int y, int tx, int ty, HitTestAction hitTestAction) 497 { 498 return m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction); 499 } 500 501 VisiblePosition RenderInline::positionForPoint(const IntPoint& point) 502 { 503 // FIXME: Does not deal with relative positioned inlines (should it?) 504 RenderBlock* cb = containingBlock(); 505 if (firstLineBox()) { 506 // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We 507 // should try to find a result by asking our containing block. 508 return cb->positionForPoint(point); 509 } 510 511 // Translate the coords from the pre-anonymous block to the post-anonymous block. 512 int parentBlockX = cb->x() + point.x(); 513 int parentBlockY = cb->y() + point.y(); 514 RenderBoxModelObject* c = continuation(); 515 while (c) { 516 RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c); 517 if (c->isInline() || c->firstChild()) 518 return c->positionForCoordinates(parentBlockX - contBlock->x(), parentBlockY - contBlock->y()); 519 c = toRenderBlock(c)->inlineContinuation(); 520 } 521 522 return RenderBoxModelObject::positionForPoint(point); 523 } 524 525 IntRect RenderInline::linesBoundingBox() const 526 { 527 IntRect result; 528 529 // 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 530 // 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 531 // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now. 532 ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist. 533 if (firstLineBox() && lastLineBox()) { 534 // Return the width of the minimal left side and the maximal right side. 535 int leftSide = 0; 536 int rightSide = 0; 537 for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { 538 if (curr == firstLineBox() || curr->x() < leftSide) 539 leftSide = curr->x(); 540 if (curr == firstLineBox() || curr->x() + curr->width() > rightSide) 541 rightSide = curr->x() + curr->width(); 542 } 543 result.setWidth(rightSide - leftSide); 544 result.setX(leftSide); 545 result.setHeight(lastLineBox()->y() + lastLineBox()->height() - firstLineBox()->y()); 546 result.setY(firstLineBox()->y()); 547 } 548 549 return result; 550 } 551 552 IntRect RenderInline::linesVisibleOverflowBoundingBox() const 553 { 554 if (!firstLineBox() || !lastLineBox()) 555 return IntRect(); 556 557 // Return the width of the minimal left side and the maximal right side. 558 int leftSide = numeric_limits<int>::max(); 559 int rightSide = numeric_limits<int>::min(); 560 for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) { 561 leftSide = min(leftSide, curr->leftVisibleOverflow()); 562 rightSide = max(rightSide, curr->rightVisibleOverflow()); 563 } 564 565 return IntRect(leftSide, firstLineBox()->topVisibleOverflow(), rightSide - leftSide, 566 lastLineBox()->bottomVisibleOverflow() - firstLineBox()->topVisibleOverflow()); 567 } 568 569 IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) 570 { 571 // Only run-ins are allowed in here during layout. 572 ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn()); 573 574 if (!firstLineBox() && !continuation()) 575 return IntRect(); 576 577 // Find our leftmost position. 578 IntRect boundingBox(linesVisibleOverflowBoundingBox()); 579 int left = boundingBox.x(); 580 int top = boundingBox.y(); 581 582 // Now invalidate a rectangle. 583 int ow = style() ? style()->outlineSize() : 0; 584 585 // We need to add in the relative position offsets of any inlines (including us) up to our 586 // containing block. 587 RenderBlock* cb = containingBlock(); 588 for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb; 589 inlineFlow = inlineFlow->parent()) { 590 if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer()) 591 toRenderInline(inlineFlow)->layer()->relativePositionOffset(left, top); 592 } 593 594 IntRect r(-ow + left, -ow + top, boundingBox.width() + ow * 2, boundingBox.height() + ow * 2); 595 if (cb->hasColumns()) 596 cb->adjustRectForColumns(r); 597 598 if (cb->hasOverflowClip()) { 599 // cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the 600 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint 601 // anyway if its size does change. 602 int x = r.x(); 603 int y = r.y(); 604 IntRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height()); 605 cb->layer()->subtractScrolledContentOffset(x, y); // For overflow:auto/scroll/hidden. 606 IntRect repaintRect(x, y, r.width(), r.height()); 607 r = intersection(repaintRect, boxRect); 608 } 609 610 // FIXME: need to ensure that we compute the correct repaint rect when the repaint container 611 // is an inline. 612 if (repaintContainer != this) 613 cb->computeRectForRepaint(repaintContainer, r); 614 615 if (ow) { 616 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { 617 if (!curr->isText()) { 618 IntRect childRect = curr->rectWithOutlineForRepaint(repaintContainer, ow); 619 r.unite(childRect); 620 } 621 } 622 623 if (continuation() && !continuation()->isInline()) { 624 IntRect contRect = continuation()->rectWithOutlineForRepaint(repaintContainer, ow); 625 r.unite(contRect); 626 } 627 } 628 629 return r; 630 } 631 632 IntRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) 633 { 634 IntRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); 635 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { 636 if (!curr->isText()) 637 r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth)); 638 } 639 return r; 640 } 641 642 void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) 643 { 644 if (RenderView* v = view()) { 645 // LayoutState is only valid for root-relative repainting 646 if (v->layoutStateEnabled() && !repaintContainer) { 647 LayoutState* layoutState = v->layoutState(); 648 if (style()->position() == RelativePosition && layer()) 649 rect.move(layer()->relativePositionOffset()); 650 rect.move(layoutState->m_offset); 651 if (layoutState->m_clipped) 652 rect.intersect(layoutState->m_clipRect); 653 return; 654 } 655 } 656 657 if (repaintContainer == this) 658 return; 659 660 bool containerSkipped; 661 RenderObject* o = container(repaintContainer, &containerSkipped); 662 if (!o) 663 return; 664 665 IntPoint topLeft = rect.location(); 666 667 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) { 668 RenderBlock* cb = toRenderBlock(o); 669 if (cb->hasColumns()) { 670 IntRect repaintRect(topLeft, rect.size()); 671 cb->adjustRectForColumns(repaintRect); 672 topLeft = repaintRect.location(); 673 rect = repaintRect; 674 } 675 } 676 677 if (style()->position() == RelativePosition && layer()) { 678 // Apply the relative position offset when invalidating a rectangle. The layer 679 // is translated, but the render box isn't, so we need to do this to get the 680 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position 681 // flag on the RenderObject has been cleared, so use the one on the style(). 682 topLeft += layer()->relativePositionOffset(); 683 } 684 685 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, 686 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. 687 if (o->hasOverflowClip()) { 688 RenderBox* containerBox = toRenderBox(o); 689 690 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the 691 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint 692 // anyway if its size does change. 693 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden. 694 695 IntRect repaintRect(topLeft, rect.size()); 696 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height()); 697 rect = intersection(repaintRect, boxRect); 698 if (rect.isEmpty()) 699 return; 700 } else 701 rect.setLocation(topLeft); 702 703 if (containerSkipped) { 704 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. 705 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 706 rect.move(-containerOffset); 707 return; 708 } 709 710 o->computeRectForRepaint(repaintContainer, rect, fixed); 711 } 712 713 IntSize RenderInline::offsetFromContainer(RenderObject* container) const 714 { 715 ASSERT(container == this->container()); 716 717 IntSize offset; 718 if (isRelPositioned()) 719 offset += relativePositionOffset(); 720 721 if (!isInline() || isReplaced()) { 722 RenderBlock* cb; 723 if (container->isBlockFlow() && (cb = toRenderBlock(container))->hasColumns()) { 724 IntRect rect(0, 0, 1, 1); 725 cb->adjustRectForColumns(rect); 726 } 727 } 728 729 if (container->hasOverflowClip()) 730 offset -= toRenderBox(container)->layer()->scrolledContentOffset(); 731 732 return offset; 733 } 734 735 void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const 736 { 737 if (repaintContainer == this) 738 return; 739 740 if (RenderView *v = view()) { 741 if (v->layoutStateEnabled() && !repaintContainer) { 742 LayoutState* layoutState = v->layoutState(); 743 IntSize offset = layoutState->m_offset; 744 if (style()->position() == RelativePosition && layer()) 745 offset += layer()->relativePositionOffset(); 746 transformState.move(offset); 747 return; 748 } 749 } 750 751 bool containerSkipped; 752 RenderObject* o = container(repaintContainer, &containerSkipped); 753 if (!o) 754 return; 755 756 IntSize containerOffset = offsetFromContainer(o); 757 758 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); 759 if (useTransforms && shouldUseTransformFromContainer(o)) { 760 TransformationMatrix t; 761 getTransformFromContainer(o, containerOffset, t); 762 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 763 } else 764 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 765 766 if (containerSkipped) { 767 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe 768 // to just subtract the delta between the repaintContainer and o. 769 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 770 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 771 return; 772 } 773 774 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); 775 } 776 777 void RenderInline::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const 778 { 779 // We don't expect this function to be called during layout. 780 ASSERT(!view() || !view()->layoutStateEnabled()); 781 782 RenderObject* o = container(); 783 if (!o) 784 return; 785 786 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); 787 788 IntSize containerOffset = offsetFromContainer(o); 789 790 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); 791 if (useTransforms && shouldUseTransformFromContainer(o)) { 792 TransformationMatrix t; 793 getTransformFromContainer(o, containerOffset, t); 794 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 795 } else 796 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 797 } 798 799 void RenderInline::updateDragState(bool dragOn) 800 { 801 RenderBoxModelObject::updateDragState(dragOn); 802 if (continuation()) 803 continuation()->updateDragState(dragOn); 804 } 805 806 void RenderInline::childBecameNonInline(RenderObject* child) 807 { 808 // We have to split the parent flow. 809 RenderBlock* newBox = containingBlock()->createAnonymousBlock(); 810 RenderBoxModelObject* oldContinuation = continuation(); 811 setContinuation(newBox); 812 RenderObject* beforeChild = child->nextSibling(); 813 children()->removeChildNode(this, child); 814 splitFlow(beforeChild, newBox, child, oldContinuation); 815 } 816 817 void RenderInline::updateHitTestResult(HitTestResult& result, const IntPoint& point) 818 { 819 if (result.innerNode()) 820 return; 821 822 Node* n = node(); 823 IntPoint localPoint(point); 824 if (n) { 825 if (isInlineContinuation()) { 826 // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space 827 // of the principal renderer's containing block. This will end up being the innerNonSharedNode. 828 RenderBlock* firstBlock = n->renderer()->containingBlock(); 829 830 // Get our containing block. 831 RenderBox* block = containingBlock(); 832 localPoint.move(block->x() - firstBlock->x(), block->y() - firstBlock->y()); 833 } 834 835 result.setInnerNode(n); 836 if (!result.innerNonSharedNode()) 837 result.setInnerNonSharedNode(n); 838 result.setLocalPoint(localPoint); 839 } 840 } 841 842 void RenderInline::dirtyLineBoxes(bool fullLayout) 843 { 844 if (fullLayout) 845 m_lineBoxes.deleteLineBoxes(renderArena()); 846 else 847 m_lineBoxes.dirtyLineBoxes(); 848 } 849 850 InlineFlowBox* RenderInline::createInlineFlowBox() 851 { 852 return new (renderArena()) InlineFlowBox(this); 853 } 854 855 InlineFlowBox* RenderInline::createAndAppendInlineFlowBox() 856 { 857 InlineFlowBox* flowBox = createInlineFlowBox(); 858 m_lineBoxes.appendLineBox(flowBox); 859 return flowBox; 860 } 861 862 int RenderInline::lineHeight(bool firstLine, bool /*isRootLineBox*/) const 863 { 864 if (firstLine && document()->usesFirstLineRules()) { 865 RenderStyle* s = style(firstLine); 866 if (s != style()) 867 return s->computedLineHeight(); 868 } 869 870 if (m_lineHeight == -1) 871 m_lineHeight = style()->computedLineHeight(); 872 873 return m_lineHeight; 874 } 875 876 int RenderInline::verticalPositionFromCache(bool firstLine) const 877 { 878 if (firstLine) // We're only really a first-line style if the document actually uses first-line rules. 879 firstLine = document()->usesFirstLineRules(); 880 int vpos = m_verticalPosition; 881 if (m_verticalPosition == PositionUndefined || firstLine) { 882 vpos = verticalPosition(firstLine); 883 if (!firstLine) 884 m_verticalPosition = vpos; 885 } 886 return vpos; 887 } 888 889 IntSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const 890 { 891 ASSERT(isRelPositioned()); 892 if (!isRelPositioned()) 893 return IntSize(); 894 895 // When we have an enclosing relpositioned inline, we need to add in the offset of the first line 896 // box from the rest of the content, but only in the cases where we know we're positioned 897 // relative to the inline itself. 898 899 IntSize offset; 900 int sx; 901 int sy; 902 if (firstLineBox()) { 903 sx = firstLineBox()->x(); 904 sy = firstLineBox()->y(); 905 } else { 906 sx = layer()->staticX(); 907 sy = layer()->staticY(); 908 } 909 910 if (!child->style()->hasStaticX()) 911 offset.setWidth(sx); 912 // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside 913 // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct 914 // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers 915 // do. 916 else if (!child->style()->isOriginalDisplayInlineType()) 917 // Avoid adding in the left border/padding of the containing block twice. Subtract it out. 918 offset.setWidth(sx - (child->containingBlock()->borderLeft() + child->containingBlock()->paddingLeft())); 919 920 if (!child->style()->hasStaticY()) 921 offset.setHeight(sy); 922 923 return offset; 924 } 925 926 void RenderInline::imageChanged(WrappedImagePtr, const IntRect*) 927 { 928 if (!parent()) 929 return; 930 931 // FIXME: We can do better. 932 repaint(); 933 } 934 935 void RenderInline::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) 936 { 937 for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { 938 RootInlineBox* root = curr->root(); 939 int top = max(root->lineTop(), curr->y()); 940 int bottom = min(root->lineBottom(), curr->y() + curr->height()); 941 IntRect rect(tx + curr->x(), ty + top, curr->width(), bottom - top); 942 if (!rect.isEmpty()) 943 rects.append(rect); 944 } 945 946 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { 947 if (!curr->isText() && !curr->isListMarker()) { 948 FloatPoint pos(tx, ty); 949 // FIXME: This doesn't work correctly with transforms. 950 if (curr->hasLayer()) 951 pos = curr->localToAbsolute(); 952 else if (curr->isBox()) 953 pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y()); 954 curr->addFocusRingRects(rects, pos.x(), pos.y()); 955 } 956 } 957 958 if (continuation()) { 959 if (continuation()->isInline()) 960 continuation()->addFocusRingRects(rects, 961 tx - containingBlock()->x() + continuation()->containingBlock()->x(), 962 ty - containingBlock()->y() + continuation()->containingBlock()->y()); 963 else 964 continuation()->addFocusRingRects(rects, 965 tx - containingBlock()->x() + toRenderBox(continuation())->x(), 966 ty - containingBlock()->y() + toRenderBox(continuation())->y()); 967 } 968 } 969 970 void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty) 971 { 972 if (!hasOutline()) 973 return; 974 975 if (style()->outlineStyleIsAuto() || hasOutlineAnnotation()) { 976 int ow = style()->outlineWidth(); 977 Color oc = style()->outlineColor(); 978 if (!oc.isValid()) 979 oc = style()->color(); 980 981 Vector<IntRect> focusRingRects; 982 addFocusRingRects(focusRingRects, tx, ty); 983 if (style()->outlineStyleIsAuto()) 984 graphicsContext->drawFocusRing(focusRingRects, ow, style()->outlineOffset(), oc); 985 else 986 addPDFURLRect(graphicsContext, unionRect(focusRingRects)); 987 } 988 989 if (style()->outlineStyleIsAuto() || style()->outlineStyle() == BNONE) 990 return; 991 992 Vector<IntRect> rects; 993 994 rects.append(IntRect()); 995 for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { 996 RootInlineBox* root = curr->root(); 997 int top = max(root->lineTop(), curr->y()); 998 int bottom = min(root->lineBottom(), curr->y() + curr->height()); 999 rects.append(IntRect(curr->x(), top, curr->width(), bottom - top)); 1000 } 1001 rects.append(IntRect()); 1002 1003 for (unsigned i = 1; i < rects.size() - 1; i++) 1004 paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1)); 1005 } 1006 1007 void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty, 1008 const IntRect& lastline, const IntRect& thisline, const IntRect& nextline) 1009 { 1010 int ow = style()->outlineWidth(); 1011 EBorderStyle os = style()->outlineStyle(); 1012 Color oc = style()->outlineColor(); 1013 if (!oc.isValid()) 1014 oc = style()->color(); 1015 1016 int offset = style()->outlineOffset(); 1017 1018 int t = ty + thisline.y() - offset; 1019 int l = tx + thisline.x() - offset; 1020 int b = ty + thisline.bottom() + offset; 1021 int r = tx + thisline.right() + offset; 1022 1023 // left edge 1024 drawLineForBoxSide(graphicsContext, 1025 l - ow, 1026 t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : 0), 1027 l, 1028 b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : 0), 1029 BSLeft, 1030 oc, style()->color(), os, 1031 (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : -ow), 1032 (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : -ow)); 1033 1034 // right edge 1035 drawLineForBoxSide(graphicsContext, 1036 r, 1037 t - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : 0), 1038 r + ow, 1039 b + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : 0), 1040 BSRight, 1041 oc, style()->color(), os, 1042 (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : -ow), 1043 (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : -ow)); 1044 // upper edge 1045 if (thisline.x() < lastline.x()) 1046 drawLineForBoxSide(graphicsContext, 1047 l - ow, 1048 t - ow, 1049 min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())), 1050 t , 1051 BSTop, oc, style()->color(), os, 1052 ow, 1053 (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow); 1054 1055 if (lastline.right() < thisline.right()) 1056 drawLineForBoxSide(graphicsContext, 1057 max(lastline.isEmpty() ? -1000000 : tx + lastline.right(), l - ow), 1058 t - ow, 1059 r + ow, 1060 t , 1061 BSTop, oc, style()->color(), os, 1062 (!lastline.isEmpty() && l - ow < tx + lastline.right()) ? -ow : ow, 1063 ow); 1064 1065 // lower edge 1066 if (thisline.x() < nextline.x()) 1067 drawLineForBoxSide(graphicsContext, 1068 l - ow, 1069 b, 1070 min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000), 1071 b + ow, 1072 BSBottom, oc, style()->color(), os, 1073 ow, 1074 (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow); 1075 1076 if (nextline.right() < thisline.right()) 1077 drawLineForBoxSide(graphicsContext, 1078 max(!nextline.isEmpty() ? tx + nextline.right() : -1000000, l - ow), 1079 b, 1080 r + ow, 1081 b + ow, 1082 BSBottom, oc, style()->color(), os, 1083 (!nextline.isEmpty() && l - ow < tx + nextline.right()) ? -ow : ow, 1084 ow); 1085 } 1086 1087 #if ENABLE(DASHBOARD_SUPPORT) 1088 void RenderInline::addDashboardRegions(Vector<DashboardRegionValue>& regions) 1089 { 1090 // Convert the style regions to absolute coordinates. 1091 if (style()->visibility() != VISIBLE) 1092 return; 1093 1094 const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions(); 1095 unsigned i, count = styleRegions.size(); 1096 for (i = 0; i < count; i++) { 1097 StyleDashboardRegion styleRegion = styleRegions[i]; 1098 1099 IntRect linesBoundingBox = this->linesBoundingBox(); 1100 int w = linesBoundingBox.width(); 1101 int h = linesBoundingBox.height(); 1102 1103 DashboardRegionValue region; 1104 region.label = styleRegion.label; 1105 region.bounds = IntRect(linesBoundingBox.x() + styleRegion.offset.left().value(), 1106 linesBoundingBox.y() + styleRegion.offset.top().value(), 1107 w - styleRegion.offset.left().value() - styleRegion.offset.right().value(), 1108 h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); 1109 region.type = styleRegion.type; 1110 1111 RenderObject* container = containingBlock(); 1112 if (!container) 1113 container = this; 1114 1115 region.clip = region.bounds; 1116 container->computeAbsoluteRepaintRect(region.clip); 1117 if (region.clip.height() < 0) { 1118 region.clip.setHeight(0); 1119 region.clip.setWidth(0); 1120 } 1121 1122 FloatPoint absPos = container->localToAbsolute(); 1123 region.bounds.setX(absPos.x() + region.bounds.x()); 1124 region.bounds.setY(absPos.y() + region.bounds.y()); 1125 1126 if (document()->frame()) { 1127 float pageScaleFactor = document()->frame()->page()->chrome()->scaleFactor(); 1128 if (pageScaleFactor != 1.0f) { 1129 region.bounds.scale(pageScaleFactor); 1130 region.clip.scale(pageScaleFactor); 1131 } 1132 } 1133 1134 regions.append(region); 1135 } 1136 } 1137 #endif 1138 1139 } // namespace WebCore 1140