1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "config.h" 6 #include "core/paint/BlockPainter.h" 7 8 #include "core/editing/Caret.h" 9 #include "core/editing/FrameSelection.h" 10 #include "core/frame/LocalFrame.h" 11 #include "core/frame/Settings.h" 12 #include "core/page/Page.h" 13 #include "core/paint/BoxPainter.h" 14 #include "core/paint/InlinePainter.h" 15 #include "core/paint/LineBoxListPainter.h" 16 #include "core/rendering/GraphicsContextAnnotator.h" 17 #include "core/rendering/PaintInfo.h" 18 #include "core/rendering/RenderBlock.h" 19 #include "core/rendering/RenderFlexibleBox.h" 20 #include "core/rendering/RenderInline.h" 21 #include "core/rendering/RenderLayer.h" 22 #include "platform/geometry/LayoutPoint.h" 23 #include "platform/geometry/LayoutRect.h" 24 #include "platform/graphics/GraphicsContextCullSaver.h" 25 #include "platform/graphics/GraphicsContextStateSaver.h" 26 27 namespace blink { 28 29 void BlockPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 30 { 31 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, &m_renderBlock); 32 33 LayoutPoint adjustedPaintOffset = paintOffset + m_renderBlock.location(); 34 35 PaintPhase phase = paintInfo.phase; 36 37 LayoutRect overflowBox; 38 // Check if we need to do anything at all. 39 // FIXME: Could eliminate the isDocumentElement() check if we fix background painting so that the RenderView 40 // paints the root's background. 41 if (!m_renderBlock.isDocumentElement()) { 42 overflowBox = overflowRectForPaintRejection(); 43 m_renderBlock.flipForWritingMode(overflowBox); 44 overflowBox.moveBy(adjustedPaintOffset); 45 if (!overflowBox.intersects(paintInfo.rect)) 46 return; 47 } 48 49 // There are some cases where not all clipped visual overflow is accounted for. 50 // FIXME: reduce the number of such cases. 51 ContentsClipBehavior contentsClipBehavior = ForceContentsClip; 52 if (m_renderBlock.hasOverflowClip() && !m_renderBlock.hasControlClip() && !(m_renderBlock.shouldPaintSelectionGaps() && phase == PaintPhaseForeground) && !hasCaret()) 53 contentsClipBehavior = SkipContentsClipIfPossible; 54 55 bool pushedClip = m_renderBlock.pushContentsClip(paintInfo, adjustedPaintOffset, contentsClipBehavior); 56 { 57 GraphicsContextCullSaver cullSaver(*paintInfo.context); 58 // Cull if we have more than one child and we didn't already clip. 59 bool shouldCull = m_renderBlock.document().settings()->containerCullingEnabled() && !pushedClip && !m_renderBlock.isDocumentElement() 60 && m_renderBlock.firstChild() && m_renderBlock.lastChild() && m_renderBlock.firstChild() != m_renderBlock.lastChild(); 61 if (shouldCull) 62 cullSaver.cull(overflowBox); 63 64 m_renderBlock.paintObject(paintInfo, adjustedPaintOffset); 65 } 66 // FIXME: move popContentsClip out of RenderBox. 67 if (pushedClip) 68 m_renderBlock.popContentsClip(paintInfo, phase, adjustedPaintOffset); 69 70 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with 71 // z-index. We paint after we painted the background/border, so that the scrollbars will 72 // sit above the background/border. 73 if (m_renderBlock.hasOverflowClip() && m_renderBlock.style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(&m_renderBlock) && !paintInfo.paintRootBackgroundOnly()) 74 m_renderBlock.layer()->scrollableArea()->paintOverflowControls(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect, false /* paintingOverlayControls */); 75 } 76 77 void BlockPainter::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 78 { 79 for (RenderBox* child = m_renderBlock.firstChildBox(); child; child = child->nextSiblingBox()) 80 paintChild(child, paintInfo, paintOffset); 81 } 82 83 void BlockPainter::paintChild(RenderBox* child, PaintInfo& paintInfo, const LayoutPoint& paintOffset) 84 { 85 LayoutPoint childPoint = m_renderBlock.flipForWritingModeForChild(child, paintOffset); 86 if (!child->hasSelfPaintingLayer() && !child->isFloating()) 87 child->paint(paintInfo, childPoint); 88 } 89 90 void BlockPainter::paintChildrenOfFlexibleBox(RenderFlexibleBox& renderFlexibleBox, PaintInfo& paintInfo, const LayoutPoint& paintOffset) 91 { 92 for (RenderBox* child = renderFlexibleBox.orderIterator().first(); child; child = renderFlexibleBox.orderIterator().next()) 93 BlockPainter(renderFlexibleBox).paintChildAsInlineBlock(child, paintInfo, paintOffset); 94 } 95 96 void BlockPainter::paintChildAsInlineBlock(RenderBox* child, PaintInfo& paintInfo, const LayoutPoint& paintOffset) 97 { 98 LayoutPoint childPoint = m_renderBlock.flipForWritingModeForChild(child, paintOffset); 99 if (!child->hasSelfPaintingLayer() && !child->isFloating()) 100 paintAsInlineBlock(child, paintInfo, childPoint); 101 } 102 103 void BlockPainter::paintInlineBox(InlineBox& inlineBox, PaintInfo& paintInfo, const LayoutPoint& paintOffset) 104 { 105 if (!paintInfo.shouldPaintWithinRoot(&inlineBox.renderer()) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) 106 return; 107 108 LayoutPoint childPoint = paintOffset; 109 if (inlineBox.parent()->renderer().style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock(). 110 childPoint = inlineBox.renderer().containingBlock()->flipForWritingModeForChild(&toRenderBox(inlineBox.renderer()), childPoint); 111 112 paintAsInlineBlock(&inlineBox.renderer(), paintInfo, childPoint); 113 } 114 115 void BlockPainter::paintAsInlineBlock(RenderObject* renderer, PaintInfo& paintInfo, const LayoutPoint& childPoint) 116 { 117 if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection) 118 return; 119 120 // Paint all phases atomically, as though the element established its own 121 // stacking context. (See Appendix E.2, section 7.2.1.4 on 122 // inline block/table/replaced elements in the CSS2.1 specification.) 123 // This is also used by other elements (e.g. flex items and grid items). 124 bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip; 125 PaintInfo info(paintInfo); 126 info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; 127 renderer->paint(info, childPoint); 128 if (!preservePhase) { 129 info.phase = PaintPhaseChildBlockBackgrounds; 130 renderer->paint(info, childPoint); 131 info.phase = PaintPhaseFloat; 132 renderer->paint(info, childPoint); 133 info.phase = PaintPhaseForeground; 134 renderer->paint(info, childPoint); 135 info.phase = PaintPhaseOutline; 136 renderer->paint(info, childPoint); 137 } 138 } 139 140 void BlockPainter::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 141 { 142 PaintPhase paintPhase = paintInfo.phase; 143 144 // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div). 145 LayoutPoint scrolledOffset = paintOffset; 146 if (m_renderBlock.hasOverflowClip()) 147 scrolledOffset.move(-m_renderBlock.scrolledContentOffset()); 148 149 // 1. paint background, borders etc 150 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && m_renderBlock.style()->visibility() == VISIBLE) { 151 if (m_renderBlock.hasBoxDecorationBackground()) 152 m_renderBlock.paintBoxDecorationBackground(paintInfo, paintOffset); 153 if (m_renderBlock.hasColumns() && !paintInfo.paintRootBackgroundOnly()) 154 paintColumnRules(paintInfo, scrolledOffset); 155 } 156 157 if (paintPhase == PaintPhaseMask && m_renderBlock.style()->visibility() == VISIBLE) { 158 m_renderBlock.paintMask(paintInfo, paintOffset); 159 return; 160 } 161 162 if (paintPhase == PaintPhaseClippingMask && m_renderBlock.style()->visibility() == VISIBLE) { 163 m_renderBlock.paintClippingMask(paintInfo, paintOffset); 164 return; 165 } 166 167 // We're done. We don't bother painting any children. 168 if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly()) 169 return; 170 171 // 2. paint contents 172 if (paintPhase != PaintPhaseSelfOutline) { 173 if (m_renderBlock.hasColumns()) 174 paintColumnContents(paintInfo, scrolledOffset); 175 else 176 paintContents(paintInfo, scrolledOffset); 177 } 178 179 // 3. paint selection 180 // FIXME: Make this work with multi column layouts. For now don't fill gaps. 181 bool isPrinting = m_renderBlock.document().printing(); 182 if (!isPrinting && !m_renderBlock.hasColumns()) 183 paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks. 184 185 // 4. paint floats. 186 if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) { 187 if (m_renderBlock.hasColumns()) 188 paintColumnContents(paintInfo, scrolledOffset, true); 189 else 190 m_renderBlock.paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); 191 } 192 193 // 5. paint outline. 194 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && m_renderBlock.style()->hasOutline() && m_renderBlock.style()->visibility() == VISIBLE) { 195 // Don't paint focus ring for anonymous block continuation because the 196 // inline element having outline-style:auto paints the whole focus ring. 197 if (!m_renderBlock.style()->outlineStyleIsAuto() || !m_renderBlock.isAnonymousBlockContinuation()) 198 m_renderBlock.paintOutline(paintInfo, LayoutRect(paintOffset, m_renderBlock.size())); 199 } 200 201 // 6. paint continuation outlines. 202 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) 203 paintContinuationOutlines(paintInfo, paintOffset); 204 205 // 7. paint caret. 206 // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground, 207 // then paint the caret. 208 if (paintPhase == PaintPhaseForeground) 209 paintCarets(paintInfo, paintOffset); 210 } 211 212 static inline bool caretBrowsingEnabled(const Frame* frame) 213 { 214 Settings* settings = frame->settings(); 215 return settings && settings->caretBrowsingEnabled(); 216 } 217 218 static inline bool hasCursorCaret(const FrameSelection& selection, const RenderBlock* block, bool caretBrowsing) 219 { 220 return selection.caretRenderer() == block && (selection.hasEditableStyle() || caretBrowsing); 221 } 222 223 static inline bool hasDragCaret(const DragCaretController& dragCaretController, const RenderBlock* block, bool caretBrowsing) 224 { 225 return dragCaretController.caretRenderer() == block && (dragCaretController.isContentEditable() || caretBrowsing); 226 } 227 228 void BlockPainter::paintCarets(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 229 { 230 bool caretBrowsing = caretBrowsingEnabled(m_renderBlock.frame()); 231 232 FrameSelection& selection = m_renderBlock.frame()->selection(); 233 if (hasCursorCaret(selection, &m_renderBlock, caretBrowsing)) { 234 selection.paintCaret(paintInfo.context, paintOffset, paintInfo.rect); 235 } 236 237 DragCaretController& dragCaretController = m_renderBlock.frame()->page()->dragCaretController(); 238 if (hasDragCaret(dragCaretController, &m_renderBlock, caretBrowsing)) { 239 dragCaretController.paintDragCaret(m_renderBlock.frame(), paintInfo.context, paintOffset, paintInfo.rect); 240 } 241 } 242 243 LayoutRect BlockPainter::overflowRectForPaintRejection() const 244 { 245 LayoutRect overflowRect = m_renderBlock.visualOverflowRect(); 246 if (!m_renderBlock.hasRenderOverflow() || !m_renderBlock.usesCompositedScrolling()) 247 return overflowRect; 248 249 overflowRect.unite(m_renderBlock.layoutOverflowRect()); 250 overflowRect.move(-m_renderBlock.scrolledContentOffset()); 251 return overflowRect; 252 } 253 254 bool BlockPainter::hasCaret() const 255 { 256 bool caretBrowsing = caretBrowsingEnabled(m_renderBlock.frame()); 257 return hasCursorCaret(m_renderBlock.frame()->selection(), &m_renderBlock, caretBrowsing) 258 || hasDragCaret(m_renderBlock.frame()->page()->dragCaretController(), &m_renderBlock, caretBrowsing); 259 } 260 261 void BlockPainter::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 262 { 263 const Color& ruleColor = m_renderBlock.resolveColor(CSSPropertyWebkitColumnRuleColor); 264 bool ruleTransparent = m_renderBlock.style()->columnRuleIsTransparent(); 265 EBorderStyle ruleStyle = m_renderBlock.style()->columnRuleStyle(); 266 LayoutUnit ruleThickness = m_renderBlock.style()->columnRuleWidth(); 267 LayoutUnit colGap = m_renderBlock.columnGap(); 268 bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; 269 if (!renderRule) 270 return; 271 272 ColumnInfo* colInfo = m_renderBlock.columnInfo(); 273 unsigned colCount = m_renderBlock.columnCount(colInfo); 274 275 bool antialias = BoxPainter::shouldAntialiasLines(paintInfo.context); 276 277 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { 278 bool leftToRight = m_renderBlock.style()->isLeftToRightDirection(); 279 LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : m_renderBlock.contentLogicalWidth(); 280 LayoutUnit ruleAdd = m_renderBlock.logicalLeftOffsetForContent(); 281 LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : m_renderBlock.contentLogicalWidth(); 282 LayoutUnit inlineDirectionSize = colInfo->desiredColumnWidth(); 283 BoxSide boxSide = m_renderBlock.isHorizontalWritingMode() 284 ? leftToRight ? BSLeft : BSRight 285 : leftToRight ? BSTop : BSBottom; 286 287 for (unsigned i = 0; i < colCount; i++) { 288 // Move to the next position. 289 if (leftToRight) { 290 ruleLogicalLeft += inlineDirectionSize + colGap / 2; 291 currLogicalLeftOffset += inlineDirectionSize + colGap; 292 } else { 293 ruleLogicalLeft -= (inlineDirectionSize + colGap / 2); 294 currLogicalLeftOffset -= (inlineDirectionSize + colGap); 295 } 296 297 // Now paint the column rule. 298 if (i < colCount - 1) { 299 LayoutUnit ruleLeft = m_renderBlock.isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + m_renderBlock.borderLeft() + m_renderBlock.paddingLeft(); 300 LayoutUnit ruleRight = m_renderBlock.isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + m_renderBlock.contentWidth(); 301 LayoutUnit ruleTop = m_renderBlock.isHorizontalWritingMode() ? paintOffset.y() + m_renderBlock.borderTop() + m_renderBlock.paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd; 302 LayoutUnit ruleBottom = m_renderBlock.isHorizontalWritingMode() ? ruleTop + m_renderBlock.contentHeight() : ruleTop + ruleThickness; 303 IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom); 304 ObjectPainter::drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); 305 } 306 307 ruleLogicalLeft = currLogicalLeftOffset; 308 } 309 } else { 310 bool topToBottom = !m_renderBlock.style()->isFlippedBlocksWritingMode(); 311 LayoutUnit ruleLeft = m_renderBlock.isHorizontalWritingMode() 312 ? m_renderBlock.borderLeft() + m_renderBlock.paddingLeft() 313 : colGap / 2 - colGap - ruleThickness / 2 + m_renderBlock.borderBefore() + m_renderBlock.paddingBefore(); 314 LayoutUnit ruleWidth = m_renderBlock.isHorizontalWritingMode() ? m_renderBlock.contentWidth() : ruleThickness; 315 LayoutUnit ruleTop = m_renderBlock.isHorizontalWritingMode() 316 ? colGap / 2 - colGap - ruleThickness / 2 + m_renderBlock.borderBefore() + m_renderBlock.paddingBefore() 317 : m_renderBlock.borderStart() + m_renderBlock.paddingStart(); 318 LayoutUnit ruleHeight = m_renderBlock.isHorizontalWritingMode() ? ruleThickness : m_renderBlock.contentHeight(); 319 LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight); 320 321 if (!topToBottom) { 322 if (m_renderBlock.isHorizontalWritingMode()) 323 ruleRect.setY(m_renderBlock.height() - ruleRect.maxY()); 324 else 325 ruleRect.setX(m_renderBlock.width() - ruleRect.maxX()); 326 } 327 328 ruleRect.moveBy(paintOffset); 329 330 BoxSide boxSide = m_renderBlock.isHorizontalWritingMode() 331 ? topToBottom ? BSTop : BSBottom 332 : topToBottom ? BSLeft : BSRight; 333 334 LayoutSize step(0, topToBottom ? colInfo->columnHeight() + colGap : -(colInfo->columnHeight() + colGap)); 335 if (!m_renderBlock.isHorizontalWritingMode()) 336 step = step.transposedSize(); 337 338 for (unsigned i = 1; i < colCount; i++) { 339 ruleRect.move(step); 340 IntRect pixelSnappedRuleRect = pixelSnappedIntRect(ruleRect); 341 ObjectPainter::drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); 342 } 343 } 344 } 345 346 void BlockPainter::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool paintingFloats) 347 { 348 // We need to do multiple passes, breaking up our child painting into strips. 349 GraphicsContext* context = paintInfo.context; 350 ColumnInfo* colInfo = m_renderBlock.columnInfo(); 351 unsigned colCount = m_renderBlock.columnCount(colInfo); 352 if (!colCount) 353 return; 354 LayoutUnit currLogicalTopOffset = 0; 355 LayoutUnit colGap = m_renderBlock.columnGap(); 356 for (unsigned i = 0; i < colCount; i++) { 357 // For each rect, we clip to the rect, and then we adjust our coords. 358 LayoutRect colRect = m_renderBlock.columnRectAt(colInfo, i); 359 m_renderBlock.flipForWritingMode(colRect); 360 LayoutUnit logicalLeftOffset = (m_renderBlock.isHorizontalWritingMode() ? colRect.x() : colRect.y()) - m_renderBlock.logicalLeftOffsetForContent(); 361 LayoutSize offset = m_renderBlock.isHorizontalWritingMode() ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset); 362 if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) { 363 if (m_renderBlock.isHorizontalWritingMode()) 364 offset.expand(0, colRect.y() - m_renderBlock.borderTop() - m_renderBlock.paddingTop()); 365 else 366 offset.expand(colRect.x() - m_renderBlock.borderLeft() - m_renderBlock.paddingLeft(), 0); 367 } 368 colRect.moveBy(paintOffset); 369 PaintInfo info(paintInfo); 370 info.rect.intersect(enclosingIntRect(colRect)); 371 372 if (!info.rect.isEmpty()) { 373 GraphicsContextStateSaver stateSaver(*context); 374 LayoutRect clipRect(colRect); 375 376 if (i < colCount - 1) { 377 if (m_renderBlock.isHorizontalWritingMode()) 378 clipRect.expand(colGap / 2, 0); 379 else 380 clipRect.expand(0, colGap / 2); 381 } 382 // Each strip pushes a clip, since column boxes are specified as being 383 // like overflow:hidden. 384 // FIXME: Content and column rules that extend outside column boxes at the edges of the multi-column element 385 // are clipped according to the 'overflow' property. 386 context->clip(enclosingIntRect(clipRect)); 387 388 // Adjust our x and y when painting. 389 LayoutPoint adjustedPaintOffset = paintOffset + offset; 390 if (paintingFloats) 391 m_renderBlock.paintFloats(info, adjustedPaintOffset, paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip); 392 else 393 paintContents(info, adjustedPaintOffset); 394 } 395 396 LayoutUnit blockDelta = (m_renderBlock.isHorizontalWritingMode() ? colRect.height() : colRect.width()); 397 if (m_renderBlock.style()->isFlippedBlocksWritingMode()) 398 currLogicalTopOffset += blockDelta; 399 else 400 currLogicalTopOffset -= blockDelta; 401 } 402 } 403 404 void BlockPainter::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 405 { 406 // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC. 407 // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverChanged() on the Document 408 // will do a full paint invalidation. 409 if (m_renderBlock.document().didLayoutWithPendingStylesheets() && !m_renderBlock.isRenderView()) 410 return; 411 412 if (m_renderBlock.childrenInline()) { 413 LineBoxListPainter(*m_renderBlock.lineBoxes()).paint(&m_renderBlock, paintInfo, paintOffset); 414 } else { 415 PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase; 416 newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase; 417 418 // We don't paint our own background, but we do let the kids paint their backgrounds. 419 PaintInfo paintInfoForChild(paintInfo); 420 paintInfoForChild.phase = newPhase; 421 paintInfoForChild.updatePaintingRootForChildren(&m_renderBlock); 422 m_renderBlock.paintChildren(paintInfoForChild, paintOffset); 423 } 424 } 425 426 void BlockPainter::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 427 { 428 if (m_renderBlock.shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) { 429 LayoutUnit lastTop = 0; 430 LayoutUnit lastLeft = m_renderBlock.logicalLeftSelectionOffset(&m_renderBlock, lastTop); 431 LayoutUnit lastRight = m_renderBlock.logicalRightSelectionOffset(&m_renderBlock, lastTop); 432 GraphicsContextStateSaver stateSaver(*paintInfo.context); 433 434 LayoutRect gapRectsBounds = m_renderBlock.selectionGaps(&m_renderBlock, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo); 435 if (!gapRectsBounds.isEmpty()) { 436 RenderLayer* layer = m_renderBlock.enclosingLayer(); 437 gapRectsBounds.moveBy(-paintOffset); 438 if (!m_renderBlock.hasLayer()) { 439 LayoutRect localBounds(gapRectsBounds); 440 m_renderBlock.flipForWritingMode(localBounds); 441 gapRectsBounds = m_renderBlock.localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox(); 442 if (layer->renderer()->hasOverflowClip()) 443 gapRectsBounds.move(layer->renderBox()->scrolledContentOffset()); 444 } 445 layer->addBlockSelectionGapsBounds(gapRectsBounds); 446 } 447 } 448 } 449 450 void BlockPainter::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& paintOffset) 451 { 452 RenderInline* inlineCont = m_renderBlock.inlineElementContinuation(); 453 if (inlineCont && inlineCont->style()->hasOutline() && inlineCont->style()->visibility() == VISIBLE) { 454 RenderInline* inlineRenderer = toRenderInline(inlineCont->node()->renderer()); 455 RenderBlock* cb = m_renderBlock.containingBlock(); 456 457 bool inlineEnclosedInSelfPaintingLayer = false; 458 for (RenderBoxModelObject* box = inlineRenderer; box != cb; box = box->parent()->enclosingBoxModelObject()) { 459 if (box->hasSelfPaintingLayer()) { 460 inlineEnclosedInSelfPaintingLayer = true; 461 break; 462 } 463 } 464 465 // Do not add continuations for outline painting by our containing block if we are a relative positioned 466 // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being 467 // in the same layer. 468 if (!inlineEnclosedInSelfPaintingLayer && !m_renderBlock.hasLayer()) 469 cb->addContinuationWithOutline(inlineRenderer); 470 else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && m_renderBlock.hasLayer())) 471 InlinePainter(*inlineRenderer).paintOutline(info, paintOffset - m_renderBlock.locationOffset() + inlineRenderer->containingBlock()->location()); 472 } 473 474 ContinuationOutlineTableMap* table = continuationOutlineTable(); 475 if (table->isEmpty()) 476 return; 477 478 OwnPtr<ListHashSet<RenderInline*> > continuations = table->take(&m_renderBlock); 479 if (!continuations) 480 return; 481 482 LayoutPoint accumulatedPaintOffset = paintOffset; 483 // Paint each continuation outline. 484 ListHashSet<RenderInline*>::iterator end = continuations->end(); 485 for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) { 486 // Need to add in the coordinates of the intervening blocks. 487 RenderInline* flow = *it; 488 RenderBlock* block = flow->containingBlock(); 489 for ( ; block && block != &m_renderBlock; block = block->containingBlock()) 490 accumulatedPaintOffset.moveBy(block->location()); 491 ASSERT(block); 492 InlinePainter(*flow).paintOutline(info, accumulatedPaintOffset); 493 } 494 } 495 496 497 } // namespace blink 498