Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(213)

Side by Side Diff: third_party/WebKit/Source/core/editing/FrameCaret.cpp

Issue 2665823002: Invalidate caret during paint invalidation (Closed)
Patch Set: NeedsRebaseline Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved. 2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 21 matching lines...) Expand all
32 #include "core/editing/SelectionEditor.h" 32 #include "core/editing/SelectionEditor.h"
33 #include "core/editing/commands/CompositeEditCommand.h" 33 #include "core/editing/commands/CompositeEditCommand.h"
34 #include "core/frame/FrameView.h" 34 #include "core/frame/FrameView.h"
35 #include "core/frame/LocalFrame.h" 35 #include "core/frame/LocalFrame.h"
36 #include "core/frame/Settings.h" 36 #include "core/frame/Settings.h"
37 #include "core/html/TextControlElement.h" 37 #include "core/html/TextControlElement.h"
38 #include "core/layout/LayoutBlock.h" 38 #include "core/layout/LayoutBlock.h"
39 #include "core/layout/LayoutTheme.h" 39 #include "core/layout/LayoutTheme.h"
40 #include "core/layout/api/LayoutPartItem.h" 40 #include "core/layout/api/LayoutPartItem.h"
41 #include "core/page/Page.h" 41 #include "core/page/Page.h"
42 #include "core/paint/PaintLayer.h"
43 #include "public/platform/WebTraceLocation.h" 42 #include "public/platform/WebTraceLocation.h"
44 43
45 namespace blink { 44 namespace blink {
46 45
47 FrameCaret::FrameCaret(LocalFrame& frame, 46 FrameCaret::FrameCaret(LocalFrame& frame,
48 const SelectionEditor& selectionEditor) 47 const SelectionEditor& selectionEditor)
49 : m_selectionEditor(&selectionEditor), 48 : m_selectionEditor(&selectionEditor),
50 m_frame(frame), 49 m_frame(frame),
51 m_caretBase(new CaretDisplayItemClient()), 50 m_caretBase(new CaretDisplayItemClient()),
52 m_caretVisibility(CaretVisibility::Hidden), 51 m_caretVisibility(CaretVisibility::Hidden),
53 m_previousCaretVisibility(CaretVisibility::Hidden),
54 m_caretBlinkTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, &frame), 52 m_caretBlinkTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, &frame),
55 this, 53 this,
56 &FrameCaret::caretBlinkTimerFired), 54 &FrameCaret::caretBlinkTimerFired),
57 m_caretRectDirty(true),
58 m_shouldPaintCaret(true), 55 m_shouldPaintCaret(true),
59 m_isCaretBlinkingSuspended(false), 56 m_isCaretBlinkingSuspended(false),
60 m_shouldShowBlockCursor(false) {} 57 m_shouldShowBlockCursor(false) {}
61 58
62 FrameCaret::~FrameCaret() = default; 59 FrameCaret::~FrameCaret() = default;
63 60
64 DEFINE_TRACE(FrameCaret) { 61 DEFINE_TRACE(FrameCaret) {
65 visitor->trace(m_selectionEditor); 62 visitor->trace(m_selectionEditor);
66 visitor->trace(m_frame); 63 visitor->trace(m_frame);
67 visitor->trace(m_previousCaretNode);
68 visitor->trace(m_previousCaretAnchorNode);
69 SynchronousMutationObserver::trace(visitor);
70 } 64 }
71 65
72 void FrameCaret::documentAttached(Document* document) { 66 const DisplayItemClient& FrameCaret::displayItemClient() const {
73 setContext(document); 67 return *m_caretBase;
74 } 68 }
75 69
76 const PositionWithAffinity FrameCaret::caretPosition() const { 70 const PositionWithAffinity FrameCaret::caretPosition() const {
77 const VisibleSelection& selection = 71 const VisibleSelection& selection =
78 m_selectionEditor->visibleSelection<EditingStrategy>(); 72 m_selectionEditor->visibleSelection<EditingStrategy>();
79 if (!selection.isCaret()) 73 if (!selection.isCaret())
80 return PositionWithAffinity(); 74 return PositionWithAffinity();
81 return PositionWithAffinity(selection.start(), selection.affinity()); 75 return PositionWithAffinity(selection.start(), selection.affinity());
82 } 76 }
83 77
84 const DisplayItemClient& FrameCaret::displayItemClient() const {
85 return *m_caretBase;
86 }
87
88 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame) { 78 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame) {
89 return frame->editor().lastEditCommand() && 79 return frame->editor().lastEditCommand() &&
90 frame->editor().lastEditCommand()->shouldStopCaretBlinking(); 80 frame->editor().lastEditCommand()->shouldStopCaretBlinking();
91 } 81 }
92 82
93 void FrameCaret::updateAppearance() { 83 void FrameCaret::updateAppearance() {
94 // Paint a block cursor instead of a caret in overtype mode unless the caret 84 // Paint a block cursor instead of a caret in overtype mode unless the caret
95 // is at the end of a line (in this case the FrameSelection will paint a 85 // is at the end of a line (in this case the FrameSelection will paint a
96 // blinking caret as usual). 86 // blinking caret as usual).
97 bool paintBlockCursor = m_shouldShowBlockCursor && isActive(); 87 bool paintBlockCursor = m_shouldShowBlockCursor && isActive();
(...skipping 16 matching lines...) Expand all
114 stopCaretBlinkTimer(); 104 stopCaretBlinkTimer();
115 105
116 // Start blinking with a black caret. Be sure not to restart if we're 106 // Start blinking with a black caret. Be sure not to restart if we're
117 // already blinking in the right location. 107 // already blinking in the right location.
118 if (shouldBlink) 108 if (shouldBlink)
119 startBlinkCaret(); 109 startBlinkCaret();
120 } 110 }
121 111
122 void FrameCaret::stopCaretBlinkTimer() { 112 void FrameCaret::stopCaretBlinkTimer() {
123 if (m_caretBlinkTimer.isActive() || m_shouldPaintCaret) 113 if (m_caretBlinkTimer.isActive() || m_shouldPaintCaret)
124 setCaretRectNeedsUpdate(); 114 scheduleVisualUpdateForPaintInvalidationIfNeeded();
125 m_shouldPaintCaret = false; 115 m_shouldPaintCaret = false;
126 m_caretBlinkTimer.stop(); 116 m_caretBlinkTimer.stop();
127 } 117 }
128 118
129 void FrameCaret::startBlinkCaret() { 119 void FrameCaret::startBlinkCaret() {
130 // Start blinking with a black caret. Be sure not to restart if we're 120 // Start blinking with a black caret. Be sure not to restart if we're
131 // already blinking in the right location. 121 // already blinking in the right location.
132 if (m_caretBlinkTimer.isActive()) 122 if (m_caretBlinkTimer.isActive())
133 return; 123 return;
134 124
135 if (double blinkInterval = LayoutTheme::theme().caretBlinkInterval()) 125 if (double blinkInterval = LayoutTheme::theme().caretBlinkInterval())
136 m_caretBlinkTimer.startRepeating(blinkInterval, BLINK_FROM_HERE); 126 m_caretBlinkTimer.startRepeating(blinkInterval, BLINK_FROM_HERE);
137 127
138 m_shouldPaintCaret = true; 128 m_shouldPaintCaret = true;
139 setCaretRectNeedsUpdate(); 129 scheduleVisualUpdateForPaintInvalidationIfNeeded();
140 } 130 }
141 131
142 void FrameCaret::setCaretVisibility(CaretVisibility visibility) { 132 void FrameCaret::setCaretVisibility(CaretVisibility visibility) {
143 if (m_caretVisibility == visibility) 133 if (m_caretVisibility == visibility)
144 return; 134 return;
145 135
146 m_caretVisibility = visibility; 136 m_caretVisibility = visibility;
147 137
148 updateAppearance(); 138 updateAppearance();
139 scheduleVisualUpdateForPaintInvalidationIfNeeded();
149 } 140 }
150 141
151 void FrameCaret::setCaretRectNeedsUpdate() { 142 void FrameCaret::clearPreviousVisualRect(const LayoutBlock& block) {
152 if (m_caretRectDirty) 143 m_caretBase->clearPreviousVisualRect(block);
153 return; 144 }
154 m_caretRectDirty = true;
155 145
156 if (Page* page = m_frame->page()) 146 void FrameCaret::layoutBlockWillBeDestroyed(const LayoutBlock& block) {
157 page->animator().scheduleVisualUpdate(m_frame->localFrameRoot()); 147 m_caretBase->layoutBlockWillBeDestroyed(block);
148 }
158 149
159 // Ensure the frame will be checked for paint invalidation during 150 void FrameCaret::updateStyleAndLayoutIfNeeded() {
160 // PrePaintTreeWalk. 151 bool shouldPaintCaret =
161 if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) { 152 m_shouldPaintCaret && isActive() &&
162 if (auto layoutItem = m_frame->ownerLayoutItem()) 153 m_caretVisibility == CaretVisibility::Visible &&
163 layoutItem.setMayNeedPaintInvalidation(); 154 m_selectionEditor->visibleSelection<EditingStrategy>().hasEditableStyle();
164 } 155
156 m_caretBase->updateStyleAndLayoutIfNeeded(
157 shouldPaintCaret ? caretPosition() : PositionWithAffinity());
158 }
159
160 void FrameCaret::invalidatePaintIfNeeded(const LayoutBlock& block,
161 const PaintInvalidatorContext& context,
162 PaintInvalidationReason reason) {
163 m_caretBase->invalidatePaintIfNeeded(block, context, reason);
165 } 164 }
166 165
167 bool FrameCaret::caretPositionIsValidForDocument( 166 bool FrameCaret::caretPositionIsValidForDocument(
168 const Document& document) const { 167 const Document& document) const {
169 if (!isActive()) 168 if (!isActive())
170 return true; 169 return true;
171 170
172 return caretPosition().document() == document && !caretPosition().isOrphan(); 171 return caretPosition().document() == document && !caretPosition().isOrphan();
173 } 172 }
174 173
175 static bool shouldRepaintCaret(Node& node) {
176 // If PositionAnchorType::BeforeAnchor or PositionAnchorType::AfterAnchor,
177 // carets need to be repainted not only when the node is contentEditable but
178 // also when its parentNode() is contentEditable.
179 node.document().updateStyleAndLayoutTree();
180 return hasEditableStyle(node) ||
181 (node.parentNode() && hasEditableStyle(*node.parentNode()));
182 }
183
184 void FrameCaret::invalidateCaretRect(bool forceInvalidation) {
185 if (!m_caretRectDirty)
186 return;
187 m_caretRectDirty = false;
188
189 DCHECK(caretPositionIsValidForDocument(*m_frame->document()));
190 LayoutObject* layoutObject = nullptr;
191 LayoutRect newRect;
192 PositionWithAffinity currentCaretPosition = caretPosition();
193 if (isActive())
194 newRect = localCaretRectOfPosition(currentCaretPosition, layoutObject);
195 Node* newNode = layoutObject ? layoutObject->node() : nullptr;
196 // The current selected node |newNode| could be a child multiple levels below
197 // its associated "anchor node" ancestor, so we reference and keep around the
198 // anchor node for checking editability.
199 // TODO(wkorman): Consider storing previous Position, rather than Node, and
200 // making use of EditingUtilies::isEditablePosition() directly.
201 Node* newAnchorNode =
202 currentCaretPosition.position().parentAnchoredEquivalent().anchorNode();
203 if (newNode && newAnchorNode && newNode != newAnchorNode &&
204 newAnchorNode->layoutObject() && newAnchorNode->layoutObject()->isBox()) {
205 newNode->layoutObject()->mapToVisualRectInAncestorSpace(
206 toLayoutBoxModelObject(newAnchorNode->layoutObject()), newRect);
207 }
208 // It's possible for the timer to be inactive even though we want to
209 // invalidate the caret. For example, when running as a layout test the
210 // caret blink interval could be zero and thus |m_caretBlinkTimer| will
211 // never be started. We provide |forceInvalidation| for use by paint
212 // invalidation internals where we need to invalidate the caret regardless
213 // of timer state.
214 if (!forceInvalidation && !m_caretBlinkTimer.isActive() &&
215 newNode == m_previousCaretNode && newRect == m_previousCaretRect &&
216 m_caretVisibility == m_previousCaretVisibility)
217 return;
218
219 if (m_previousCaretAnchorNode &&
220 shouldRepaintCaret(*m_previousCaretAnchorNode)) {
221 m_caretBase->invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
222 m_previousCaretRect);
223 }
224 if (newAnchorNode && shouldRepaintCaret(*newAnchorNode))
225 m_caretBase->invalidateLocalCaretRect(newAnchorNode, newRect);
226 m_previousCaretNode = newNode;
227 m_previousCaretAnchorNode = newAnchorNode;
228 m_previousCaretRect = newRect;
229 m_previousCaretVisibility = m_caretVisibility;
230 }
231
232 static IntRect absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) { 174 static IntRect absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) {
233 LayoutBlock* caretPainter = CaretDisplayItemClient::caretLayoutObject(node); 175 LayoutBlock* caretPainter = CaretDisplayItemClient::caretLayoutBlock(node);
234 if (!caretPainter) 176 if (!caretPainter)
235 return IntRect(); 177 return IntRect();
236 178
237 LayoutRect localRect(rect); 179 LayoutRect localRect(rect);
238 caretPainter->flipForWritingMode(localRect); 180 caretPainter->flipForWritingMode(localRect);
239 return caretPainter->localToAbsoluteQuad(FloatRect(localRect)) 181 return caretPainter->localToAbsoluteQuad(FloatRect(localRect))
240 .enclosingBoundingBox(); 182 .enclosingBoundingBox();
241 } 183 }
242 184
243 IntRect FrameCaret::absoluteCaretBounds() const { 185 IntRect FrameCaret::absoluteCaretBounds() const {
(...skipping 20 matching lines...) Expand all
264 } 206 }
265 207
266 void FrameCaret::setShouldShowBlockCursor(bool shouldShowBlockCursor) { 208 void FrameCaret::setShouldShowBlockCursor(bool shouldShowBlockCursor) {
267 m_shouldShowBlockCursor = shouldShowBlockCursor; 209 m_shouldShowBlockCursor = shouldShowBlockCursor;
268 210
269 m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets(); 211 m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets();
270 212
271 updateAppearance(); 213 updateAppearance();
272 } 214 }
273 215
274 void FrameCaret::paintCaret(GraphicsContext& context, 216 bool FrameCaret::shouldPaintCaret(const LayoutBlock& block) const {
275 const LayoutPoint& paintOffset) { 217 return m_caretBase->shouldPaintCaret(block);
276 if (m_caretVisibility == CaretVisibility::Hidden)
277 return;
278
279 if (!(isActive() && m_shouldPaintCaret))
280 return;
281
282 const LayoutRect caretLocalRect =
283 CaretDisplayItemClient::computeCaretRect(caretPosition());
284 m_caretBase->paintCaret(caretPosition().anchorNode(), context, caretLocalRect,
285 paintOffset, DisplayItem::kCaret);
286 } 218 }
287 219
288 void FrameCaret::dataWillChange(const CharacterData& node) { 220 void FrameCaret::paintCaret(GraphicsContext& context,
289 if (node == m_previousCaretNode) { 221 const LayoutPoint& paintOffset) const {
290 // This invalidation is eager, and intentionally uses stale state. 222 m_caretBase->paintCaret(context, paintOffset, DisplayItem::kCaret);
291 DisableCompositingQueryAsserts disabler;
292 m_caretBase->invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
293 m_previousCaretRect);
294 }
295 }
296
297 void FrameCaret::nodeWillBeRemoved(Node& node) {
298 if (node != m_previousCaretNode && node != m_previousCaretAnchorNode)
299 return;
300 // Hits in ManualTests/caret-paint-after-last-text-is-removed.html
301 DisableCompositingQueryAsserts disabler;
302 m_caretBase->invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
303 m_previousCaretRect);
304 m_previousCaretNode = nullptr;
305 m_previousCaretAnchorNode = nullptr;
306 m_previousCaretRect = LayoutRect();
307 m_previousCaretVisibility = CaretVisibility::Hidden;
308 }
309
310 void FrameCaret::contextDestroyed(Document*) {
311 m_caretBlinkTimer.stop();
312 m_previousCaretNode.clear();
313 m_previousCaretAnchorNode.clear();
314 } 223 }
315 224
316 bool FrameCaret::shouldBlinkCaret() const { 225 bool FrameCaret::shouldBlinkCaret() const {
317 if (m_caretVisibility != CaretVisibility::Visible || !isActive()) 226 if (m_caretVisibility != CaretVisibility::Visible || !isActive())
318 return false; 227 return false;
319 228
320 Element* root = rootEditableElementOf(caretPosition().position()); 229 Element* root = rootEditableElementOf(caretPosition().position());
321 if (!root) 230 if (!root)
322 return false; 231 return false;
323 232
324 Element* focusedElement = root->document().focusedElement(); 233 Element* focusedElement = root->document().focusedElement();
325 if (!focusedElement) 234 if (!focusedElement)
326 return false; 235 return false;
327 236
328 return focusedElement->isShadowIncludingInclusiveAncestorOf( 237 return focusedElement->isShadowIncludingInclusiveAncestorOf(
329 caretPosition().anchorNode()); 238 caretPosition().anchorNode());
330 } 239 }
331 240
332 void FrameCaret::caretBlinkTimerFired(TimerBase*) { 241 void FrameCaret::caretBlinkTimerFired(TimerBase*) {
333 DCHECK_EQ(m_caretVisibility, CaretVisibility::Visible); 242 DCHECK_EQ(m_caretVisibility, CaretVisibility::Visible);
334 if (isCaretBlinkingSuspended() && m_shouldPaintCaret) 243 if (isCaretBlinkingSuspended() && m_shouldPaintCaret)
335 return; 244 return;
336 m_shouldPaintCaret = !m_shouldPaintCaret; 245 m_shouldPaintCaret = !m_shouldPaintCaret;
337 setCaretRectNeedsUpdate(); 246 scheduleVisualUpdateForPaintInvalidationIfNeeded();
247 }
248
249 void FrameCaret::scheduleVisualUpdateForPaintInvalidationIfNeeded() {
250 if (FrameView* frameView = m_frame->view())
251 frameView->scheduleVisualUpdateForPaintInvalidationIfNeeded();
338 } 252 }
339 253
340 } // namespace blink 254 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/FrameCaret.h ('k') | third_party/WebKit/Source/core/editing/FrameSelection.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698