OLD | NEW |
---|---|
(Empty) | |
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/rendering/GraphicsContextAnnotator.h" | |
15 #include "core/rendering/PaintInfo.h" | |
16 #include "core/rendering/RenderBlock.h" | |
17 #include "core/rendering/RenderLayer.h" | |
18 #include "platform/geometry/LayoutPoint.h" | |
19 #include "platform/geometry/LayoutRect.h" | |
20 #include "platform/graphics/GraphicsContextCullSaver.h" | |
21 #include "platform/graphics/GraphicsContextStateSaver.h" | |
22 | |
23 namespace blink { | |
24 | |
25 void BlockPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) | |
26 { | |
27 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, &m_renderBlock); | |
28 | |
29 LayoutPoint adjustedPaintOffset = paintOffset + m_renderBlock.location(); | |
30 | |
31 PaintPhase phase = paintInfo.phase; | |
32 | |
33 LayoutRect overflowBox; | |
34 // Check if we need to do anything at all. | |
35 // FIXME: Could eliminate the isDocumentElement() check if we fix background painting so that the RenderView | |
36 // paints the root's background. | |
37 if (!m_renderBlock.isDocumentElement()) { | |
38 overflowBox = overflowRectForPaintRejection(); | |
39 m_renderBlock.flipForWritingMode(overflowBox); | |
40 overflowBox.moveBy(adjustedPaintOffset); | |
41 if (!overflowBox.intersects(paintInfo.rect)) | |
42 return; | |
43 } | |
44 | |
45 // There are some cases where not all clipped visual overflow is accounted f or. | |
46 // FIXME: reduce the number of such cases. | |
47 ContentsClipBehavior contentsClipBehavior = ForceContentsClip; | |
48 if (m_renderBlock.hasOverflowClip() && !m_renderBlock.hasControlClip() && !( m_renderBlock.shouldPaintSelectionGaps() && phase == PaintPhaseForeground) && !h asCaret()) | |
49 contentsClipBehavior = SkipContentsClipIfPossible; | |
50 | |
51 bool pushedClip = m_renderBlock.pushContentsClip(paintInfo, adjustedPaintOff set, contentsClipBehavior); | |
52 { | |
53 GraphicsContextCullSaver cullSaver(*paintInfo.context); | |
54 // Cull if we have more than one child and we didn't already clip. | |
55 bool shouldCull = m_renderBlock.document().settings()->containerCullingE nabled() && !pushedClip && !m_renderBlock.isDocumentElement() | |
56 && m_renderBlock.firstChild() && m_renderBlock.lastChild() && m_rend erBlock.firstChild() != m_renderBlock.lastChild(); | |
57 if (shouldCull) | |
58 cullSaver.cull(overflowBox); | |
59 | |
60 m_renderBlock.paintObject(paintInfo, adjustedPaintOffset); | |
Stephen Chennney
2014/09/15 20:18:05
FIXME on all the refs like this so that we catch t
chrishtr
2014/09/15 20:52:08
This one is due to a virtual method on RenderBlock
| |
61 } | |
62 if (pushedClip) | |
63 m_renderBlock.popContentsClip(paintInfo, phase, adjustedPaintOffset); | |
Stephen Chennney
2014/09/15 20:18:05
Can we move popContentsClip too?
chrishtr
2014/09/15 20:52:08
This one is harder, since popContentsClip is on Re
| |
64 | |
65 // Our scrollbar widgets paint exactly when we tell them to, so that they wo rk properly with | |
66 // z-index. We paint after we painted the background/border, so that the scr ollbars will | |
67 // sit above the background/border. | |
68 if (m_renderBlock.hasOverflowClip() && m_renderBlock.style()->visibility() = = VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBloc kBackground) && paintInfo.shouldPaintWithinRoot(&m_renderBlock) && !paintInfo.pa intRootBackgroundOnly()) | |
69 m_renderBlock.layer()->scrollableArea()->paintOverflowControls(paintInfo .context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect, false /* paintin gOverlayControls */); | |
70 } | |
71 | |
72 void BlockPainter::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOff set) | |
73 { | |
74 PaintPhase paintPhase = paintInfo.phase; | |
75 | |
76 // Adjust our painting position if we're inside a scrolled layer (e.g., an o verflow:auto div). | |
77 LayoutPoint scrolledOffset = paintOffset; | |
78 if (m_renderBlock.hasOverflowClip()) | |
79 scrolledOffset.move(-m_renderBlock.scrolledContentOffset()); | |
80 | |
81 // 1. paint background, borders etc | |
82 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChil dBlockBackground) && m_renderBlock.style()->visibility() == VISIBLE) { | |
83 if (m_renderBlock.hasBoxDecorationBackground()) | |
84 m_renderBlock.paintBoxDecorationBackground(paintInfo, paintOffset); | |
85 if (m_renderBlock.hasColumns() && !paintInfo.paintRootBackgroundOnly()) | |
86 paintColumnRules(paintInfo, scrolledOffset); | |
87 } | |
88 | |
89 if (paintPhase == PaintPhaseMask && m_renderBlock.style()->visibility() == V ISIBLE) { | |
90 m_renderBlock.paintMask(paintInfo, paintOffset); | |
Stephen Chennney
2014/09/15 20:18:05
Whenever I see a m_renderBlock.paintBlah, I presum
chrishtr
2014/09/15 20:52:08
Added the rest of the methods. But paintMask is vi
| |
91 return; | |
92 } | |
93 | |
94 if (paintPhase == PaintPhaseClippingMask && m_renderBlock.style()->visibilit y() == VISIBLE) { | |
95 m_renderBlock.paintClippingMask(paintInfo, paintOffset); | |
96 return; | |
97 } | |
98 | |
99 // We're done. We don't bother painting any children. | |
100 if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackground Only()) | |
101 return; | |
102 | |
103 // 2. paint contents | |
104 if (paintPhase != PaintPhaseSelfOutline) { | |
105 if (m_renderBlock.hasColumns()) | |
106 paintColumnContents(paintInfo, scrolledOffset); | |
107 else | |
108 paintContents(paintInfo, scrolledOffset); | |
109 } | |
110 | |
111 // 3. paint selection | |
112 // FIXME: Make this work with multi column layouts. For now don't fill gaps. | |
113 bool isPrinting = m_renderBlock.document().printing(); | |
114 if (!isPrinting && !m_renderBlock.hasColumns()) | |
115 m_renderBlock.paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks. | |
116 | |
117 // 4. paint floats. | |
118 if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || pa intPhase == PaintPhaseTextClip) { | |
119 if (m_renderBlock.hasColumns()) | |
120 paintColumnContents(paintInfo, scrolledOffset, true); | |
121 else | |
122 m_renderBlock.paintFloats(paintInfo, scrolledOffset, paintPhase == P aintPhaseSelection || paintPhase == PaintPhaseTextClip); | |
123 } | |
124 | |
125 // 5. paint outline. | |
126 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && m_renderBlock.style()->hasOutline() && m_renderBlock.style()->visibility() = = VISIBLE) { | |
127 // Don't paint focus ring for anonymous block continuation because the | |
128 // inline element having outline-style:auto paints the whole focus ring. | |
129 if (!m_renderBlock.style()->outlineStyleIsAuto() || !m_renderBlock.isAno nymousBlockContinuation()) | |
130 m_renderBlock.paintOutline(paintInfo, LayoutRect(paintOffset, m_rend erBlock.size())); | |
131 } | |
132 | |
133 // 6. paint continuation outlines. | |
134 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutline s)) | |
135 m_renderBlock.paintContinuationOutlines(paintInfo, paintOffset); | |
136 | |
137 // 7. paint caret. | |
138 // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground, | |
139 // then paint the caret. | |
140 if (paintPhase == PaintPhaseForeground) | |
141 paintCarets(paintInfo, paintOffset); | |
142 } | |
143 | |
144 static inline bool caretBrowsingEnabled(const Frame* frame) | |
145 { | |
146 Settings* settings = frame->settings(); | |
147 return settings && settings->caretBrowsingEnabled(); | |
148 } | |
149 | |
150 static inline bool hasCursorCaret(const FrameSelection& selection, const RenderB lock* block, bool caretBrowsing) | |
151 { | |
152 return selection.caretRenderer() == block && (selection.hasEditableStyle() | | caretBrowsing); | |
153 } | |
154 | |
155 static inline bool hasDragCaret(const DragCaretController& dragCaretController, const RenderBlock* block, bool caretBrowsing) | |
156 { | |
157 return dragCaretController.caretRenderer() == block && (dragCaretController. isContentEditable() || caretBrowsing); | |
158 } | |
159 | |
160 void BlockPainter::paintCarets(PaintInfo& paintInfo, const LayoutPoint& paintOff set) | |
161 { | |
162 bool caretBrowsing = caretBrowsingEnabled(m_renderBlock.frame()); | |
163 | |
164 FrameSelection& selection = m_renderBlock.frame()->selection(); | |
165 if (hasCursorCaret(selection, &m_renderBlock, caretBrowsing)) { | |
166 selection.paintCaret(paintInfo.context, paintOffset, paintInfo.rect); | |
167 } | |
168 | |
169 DragCaretController& dragCaretController = m_renderBlock.frame()->page()->dr agCaretController(); | |
170 if (hasDragCaret(dragCaretController, &m_renderBlock, caretBrowsing)) { | |
171 dragCaretController.paintDragCaret(m_renderBlock.frame(), paintInfo.cont ext, paintOffset, paintInfo.rect); | |
172 } | |
173 } | |
174 | |
175 LayoutRect BlockPainter::overflowRectForPaintRejection() const | |
176 { | |
177 LayoutRect overflowRect = m_renderBlock.visualOverflowRect(); | |
178 if (!m_renderBlock.hasRenderOverflow() || !m_renderBlock.usesCompositedScrol ling()) | |
179 return overflowRect; | |
180 | |
181 overflowRect.unite(m_renderBlock.layoutOverflowRect()); | |
182 overflowRect.move(-m_renderBlock.scrolledContentOffset()); | |
183 return overflowRect; | |
184 } | |
185 | |
186 bool BlockPainter::hasCaret() const | |
187 { | |
188 bool caretBrowsing = caretBrowsingEnabled(m_renderBlock.frame()); | |
189 return hasCursorCaret(m_renderBlock.frame()->selection(), &m_renderBlock, ca retBrowsing) | |
190 || hasDragCaret(m_renderBlock.frame()->page()->dragCaretController(), &m _renderBlock, caretBrowsing); | |
191 } | |
192 | |
193 void BlockPainter::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pai ntOffset) | |
194 { | |
195 const Color& ruleColor = m_renderBlock.resolveColor(CSSPropertyWebkitColumnR uleColor); | |
196 bool ruleTransparent = m_renderBlock.style()->columnRuleIsTransparent(); | |
197 EBorderStyle ruleStyle = m_renderBlock.style()->columnRuleStyle(); | |
198 LayoutUnit ruleThickness = m_renderBlock.style()->columnRuleWidth(); | |
199 LayoutUnit colGap = m_renderBlock.columnGap(); | |
200 bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; | |
201 if (!renderRule) | |
202 return; | |
203 | |
204 ColumnInfo* colInfo = m_renderBlock.columnInfo(); | |
205 unsigned colCount = m_renderBlock.columnCount(colInfo); | |
206 | |
207 bool antialias = BoxPainter::shouldAntialiasLines(paintInfo.context); | |
208 | |
209 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { | |
210 bool leftToRight = m_renderBlock.style()->isLeftToRightDirection(); | |
211 LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : m_render Block.contentLogicalWidth(); | |
212 LayoutUnit ruleAdd = m_renderBlock.logicalLeftOffsetForContent(); | |
213 LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : m_renderBlock. contentLogicalWidth(); | |
214 LayoutUnit inlineDirectionSize = colInfo->desiredColumnWidth(); | |
215 BoxSide boxSide = m_renderBlock.isHorizontalWritingMode() | |
216 ? leftToRight ? BSLeft : BSRight | |
217 : leftToRight ? BSTop : BSBottom; | |
218 | |
219 for (unsigned i = 0; i < colCount; i++) { | |
220 // Move to the next position. | |
221 if (leftToRight) { | |
222 ruleLogicalLeft += inlineDirectionSize + colGap / 2; | |
223 currLogicalLeftOffset += inlineDirectionSize + colGap; | |
224 } else { | |
225 ruleLogicalLeft -= (inlineDirectionSize + colGap / 2); | |
226 currLogicalLeftOffset -= (inlineDirectionSize + colGap); | |
227 } | |
228 | |
229 // Now paint the column rule. | |
230 if (i < colCount - 1) { | |
231 LayoutUnit ruleLeft = m_renderBlock.isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x( ) + m_renderBlock.borderLeft() + m_renderBlock.paddingLeft(); | |
232 LayoutUnit ruleRight = m_renderBlock.isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + m_renderBlock.contentWidth(); | |
233 LayoutUnit ruleTop = m_renderBlock.isHorizontalWritingMode() ? p aintOffset.y() + m_renderBlock.borderTop() + m_renderBlock.paddingTop() : paintO ffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd; | |
234 LayoutUnit ruleBottom = m_renderBlock.isHorizontalWritingMode() ? ruleTop + m_renderBlock.contentHeight() : ruleTop + ruleThickness; | |
235 IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(rule Left, ruleTop, ruleRight, ruleBottom); | |
236 m_renderBlock.drawLineForBoxSide(paintInfo.context, pixelSnapped RuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappe dRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); | |
237 } | |
238 | |
239 ruleLogicalLeft = currLogicalLeftOffset; | |
240 } | |
241 } else { | |
242 bool topToBottom = !m_renderBlock.style()->isFlippedBlocksWritingMode(); | |
243 LayoutUnit ruleLeft = m_renderBlock.isHorizontalWritingMode() | |
244 ? m_renderBlock.borderLeft() + m_renderBlock.paddingLeft() | |
245 : colGap / 2 - colGap - ruleThickness / 2 + m_renderBlock.borderBefo re() + m_renderBlock.paddingBefore(); | |
246 LayoutUnit ruleWidth = m_renderBlock.isHorizontalWritingMode() ? m_rende rBlock.contentWidth() : ruleThickness; | |
247 LayoutUnit ruleTop = m_renderBlock.isHorizontalWritingMode() | |
248 ? colGap / 2 - colGap - ruleThickness / 2 + m_renderBlock.borderBefo re() + m_renderBlock.paddingBefore() | |
249 : m_renderBlock.borderStart() + m_renderBlock.paddingStart(); | |
250 LayoutUnit ruleHeight = m_renderBlock.isHorizontalWritingMode() ? ruleTh ickness : m_renderBlock.contentHeight(); | |
251 LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight); | |
252 | |
253 if (!topToBottom) { | |
254 if (m_renderBlock.isHorizontalWritingMode()) | |
255 ruleRect.setY(m_renderBlock.height() - ruleRect.maxY()); | |
256 else | |
257 ruleRect.setX(m_renderBlock.width() - ruleRect.maxX()); | |
258 } | |
259 | |
260 ruleRect.moveBy(paintOffset); | |
261 | |
262 BoxSide boxSide = m_renderBlock.isHorizontalWritingMode() | |
263 ? topToBottom ? BSTop : BSBottom | |
264 : topToBottom ? BSLeft : BSRight; | |
265 | |
266 LayoutSize step(0, topToBottom ? colInfo->columnHeight() + colGap : -(co lInfo->columnHeight() + colGap)); | |
267 if (!m_renderBlock.isHorizontalWritingMode()) | |
268 step = step.transposedSize(); | |
269 | |
270 for (unsigned i = 1; i < colCount; i++) { | |
271 ruleRect.move(step); | |
272 IntRect pixelSnappedRuleRect = pixelSnappedIntRect(ruleRect); | |
273 m_renderBlock.drawLineForBoxSide(paintInfo.context, pixelSnappedRule Rect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRul eRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); | |
274 } | |
275 } | |
276 } | |
277 | |
278 void BlockPainter::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool paintingFloats) | |
279 { | |
280 // We need to do multiple passes, breaking up our child painting into strips . | |
281 GraphicsContext* context = paintInfo.context; | |
282 ColumnInfo* colInfo = m_renderBlock.columnInfo(); | |
283 unsigned colCount = m_renderBlock.columnCount(colInfo); | |
284 if (!colCount) | |
285 return; | |
286 LayoutUnit currLogicalTopOffset = 0; | |
287 LayoutUnit colGap = m_renderBlock.columnGap(); | |
288 for (unsigned i = 0; i < colCount; i++) { | |
289 // For each rect, we clip to the rect, and then we adjust our coords. | |
290 LayoutRect colRect = m_renderBlock.columnRectAt(colInfo, i); | |
291 m_renderBlock.flipForWritingMode(colRect); | |
292 LayoutUnit logicalLeftOffset = (m_renderBlock.isHorizontalWritingMode() ? colRect.x() : colRect.y()) - m_renderBlock.logicalLeftOffsetForContent(); | |
293 LayoutSize offset = m_renderBlock.isHorizontalWritingMode() ? LayoutSize (logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, log icalLeftOffset); | |
294 if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) { | |
295 if (m_renderBlock.isHorizontalWritingMode()) | |
296 offset.expand(0, colRect.y() - m_renderBlock.borderTop() - m_ren derBlock.paddingTop()); | |
297 else | |
298 offset.expand(colRect.x() - m_renderBlock.borderLeft() - m_rende rBlock.paddingLeft(), 0); | |
299 } | |
300 colRect.moveBy(paintOffset); | |
301 PaintInfo info(paintInfo); | |
302 info.rect.intersect(enclosingIntRect(colRect)); | |
303 | |
304 if (!info.rect.isEmpty()) { | |
305 GraphicsContextStateSaver stateSaver(*context); | |
306 LayoutRect clipRect(colRect); | |
307 | |
308 if (i < colCount - 1) { | |
309 if (m_renderBlock.isHorizontalWritingMode()) | |
310 clipRect.expand(colGap / 2, 0); | |
311 else | |
312 clipRect.expand(0, colGap / 2); | |
313 } | |
314 // Each strip pushes a clip, since column boxes are specified as bei ng | |
315 // like overflow:hidden. | |
316 // FIXME: Content and column rules that extend outside column boxes at the edges of the multi-column element | |
317 // are clipped according to the 'overflow' property. | |
318 context->clip(enclosingIntRect(clipRect)); | |
319 | |
320 // Adjust our x and y when painting. | |
321 LayoutPoint adjustedPaintOffset = paintOffset + offset; | |
322 if (paintingFloats) | |
323 m_renderBlock.paintFloats(info, adjustedPaintOffset, paintInfo.p hase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip); | |
324 else | |
325 paintContents(info, adjustedPaintOffset); | |
326 } | |
327 | |
328 LayoutUnit blockDelta = (m_renderBlock.isHorizontalWritingMode() ? colRe ct.height() : colRect.width()); | |
329 if (m_renderBlock.style()->isFlippedBlocksWritingMode()) | |
330 currLogicalTopOffset += blockDelta; | |
331 else | |
332 currLogicalTopOffset -= blockDelta; | |
333 } | |
334 } | |
335 | |
336 void BlockPainter::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintO ffset) | |
337 { | |
338 // Avoid painting descendants of the root element when stylesheets haven't l oaded. This eliminates FOUC. | |
339 // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverChanged() on the Document | |
340 // will do a full paint invalidation. | |
341 if (m_renderBlock.document().didLayoutWithPendingStylesheets() && !m_renderB lock.isRenderView()) | |
342 return; | |
343 | |
344 if (m_renderBlock.childrenInline()) { | |
345 m_renderBlock.lineBoxes()->paint(&m_renderBlock, paintInfo, paintOffset) ; | |
346 } else { | |
347 PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? Pai ntPhaseOutline : paintInfo.phase; | |
348 newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChi ldBlockBackground : newPhase; | |
349 | |
350 // We don't paint our own background, but we do let the kids paint their backgrounds. | |
351 PaintInfo paintInfoForChild(paintInfo); | |
352 paintInfoForChild.phase = newPhase; | |
353 paintInfoForChild.updatePaintingRootForChildren(&m_renderBlock); | |
354 m_renderBlock.paintChildren(paintInfoForChild, paintOffset); | |
355 } | |
356 } | |
357 | |
358 } // namespace blink | |
OLD | NEW |