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

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

Issue 2665823002: Invalidate caret during paint invalidation (Closed)
Patch Set: Rebaseline 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); 64 SynchronousMutationObserver::trace(visitor);
70 } 65 }
71 66
72 void FrameCaret::documentAttached(Document* document) { 67 void FrameCaret::documentAttached(Document* document) {
73 setContext(document); 68 setContext(document);
74 } 69 }
75 70
76 const PositionWithAffinity FrameCaret::caretPosition() const { 71 const PositionWithAffinity FrameCaret::caretPosition() const {
77 const VisibleSelection& selection = 72 const VisibleSelection& selection =
78 m_selectionEditor->visibleSelection<EditingStrategy>(); 73 m_selectionEditor->visibleSelection<EditingStrategy>();
79 if (!selection.isCaret()) 74 if (!selection.isCaret())
80 return PositionWithAffinity(); 75 return PositionWithAffinity();
81 return PositionWithAffinity(selection.start(), selection.affinity()); 76 return PositionWithAffinity(selection.start(), selection.affinity());
82 } 77 }
83 78
84 const DisplayItemClient& FrameCaret::displayItemClient() const { 79 void FrameCaret::setVisualRect(const LayoutRect& r) {
80 m_caretBase->setVisualRect(r);
81 }
82
83 const DisplayItemClient& FrameCaret::getDisplayItemClient() const {
85 return *m_caretBase; 84 return *m_caretBase;
86 } 85 }
87 86
88 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame) { 87 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame) {
89 return frame->editor().lastEditCommand() && 88 return frame->editor().lastEditCommand() &&
90 frame->editor().lastEditCommand()->shouldStopCaretBlinking(); 89 frame->editor().lastEditCommand()->shouldStopCaretBlinking();
91 } 90 }
92 91
93 void FrameCaret::updateAppearance() { 92 void FrameCaret::updateAppearance() {
94 // Paint a block cursor instead of a caret in overtype mode unless the caret 93 // Paint a block cursor instead of a caret in overtype mode unless the caret
(...skipping 19 matching lines...) Expand all
114 stopCaretBlinkTimer(); 113 stopCaretBlinkTimer();
115 114
116 // Start blinking with a black caret. Be sure not to restart if we're 115 // Start blinking with a black caret. Be sure not to restart if we're
117 // already blinking in the right location. 116 // already blinking in the right location.
118 if (shouldBlink) 117 if (shouldBlink)
119 startBlinkCaret(); 118 startBlinkCaret();
120 } 119 }
121 120
122 void FrameCaret::stopCaretBlinkTimer() { 121 void FrameCaret::stopCaretBlinkTimer() {
123 if (m_caretBlinkTimer.isActive() || m_shouldPaintCaret) 122 if (m_caretBlinkTimer.isActive() || m_shouldPaintCaret)
124 setCaretRectNeedsUpdate(); 123 setNeedsPaintInvalidation();
125 m_shouldPaintCaret = false; 124 m_shouldPaintCaret = false;
126 m_caretBlinkTimer.stop(); 125 m_caretBlinkTimer.stop();
127 } 126 }
128 127
129 void FrameCaret::startBlinkCaret() { 128 void FrameCaret::startBlinkCaret() {
130 // Start blinking with a black caret. Be sure not to restart if we're 129 // Start blinking with a black caret. Be sure not to restart if we're
131 // already blinking in the right location. 130 // already blinking in the right location.
132 if (m_caretBlinkTimer.isActive()) 131 if (m_caretBlinkTimer.isActive())
133 return; 132 return;
134 133
135 if (double blinkInterval = LayoutTheme::theme().caretBlinkInterval()) 134 if (double blinkInterval = LayoutTheme::theme().caretBlinkInterval())
136 m_caretBlinkTimer.startRepeating(blinkInterval, BLINK_FROM_HERE); 135 m_caretBlinkTimer.startRepeating(blinkInterval, BLINK_FROM_HERE);
137 136
138 m_shouldPaintCaret = true; 137 m_shouldPaintCaret = true;
139 setCaretRectNeedsUpdate(); 138 setNeedsPaintInvalidation();
140 } 139 }
141 140
142 void FrameCaret::setCaretVisibility(CaretVisibility visibility) { 141 void FrameCaret::setCaretVisibility(CaretVisibility visibility) {
143 if (m_caretVisibility == visibility) 142 if (m_caretVisibility == visibility)
144 return; 143 return;
145 144
146 m_caretVisibility = visibility; 145 m_caretVisibility = visibility;
147 146
148 updateAppearance(); 147 updateAppearance();
149 } 148 }
150 149
151 void FrameCaret::setCaretRectNeedsUpdate() { 150 void FrameCaret::setMayNeedPaintInvalidation() {
152 if (m_caretRectDirty) 151 if (LayoutBlock* block = caretLayoutBlock())
yosin_UTC9 2017/01/31 20:12:58 Let's use early-return style to avoid to have (sin
153 return; 152 block->setMayNeedPaintInvalidation();
154 m_caretRectDirty = true; 153 else
154 scheduleVisualUpdate();
155 }
155 156
157 void FrameCaret::setNeedsPaintInvalidation() {
158 if (LayoutBlock* block = caretLayoutBlock())
159 block->setCaretsNeedPaintInvalidation();
yosin_UTC9 2017/01/31 20:12:58 Let's use early-return style to avoid to have (sin
160 else
161 scheduleVisualUpdate();
162 }
163
164 void FrameCaret::scheduleVisualUpdate() {
156 if (Page* page = m_frame->page()) 165 if (Page* page = m_frame->page())
157 page->animator().scheduleVisualUpdate(m_frame->localFrameRoot()); 166 page->animator().scheduleVisualUpdate(m_frame->localFrameRoot());
158
159 // Ensure the frame will be checked for paint invalidation during
160 // PrePaintTreeWalk.
161 if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
chrishtr 2017/01/31 22:51:03 Why is this no longer needed?
Xianzhu 2017/02/02 23:42:06 Now block->setMayNeedPaintInvalidation() marks the
162 if (auto layoutItem = m_frame->ownerLayoutItem())
163 layoutItem.setMayNeedPaintInvalidation();
164 }
165 } 167 }
166 168
167 bool FrameCaret::caretPositionIsValidForDocument( 169 bool FrameCaret::caretPositionIsValidForDocument(
168 const Document& document) const { 170 const Document& document) const {
169 if (!isActive()) 171 if (!isActive())
170 return true; 172 return true;
171 173
172 return caretPosition().document() == document && !caretPosition().isOrphan(); 174 return caretPosition().document() == document && !caretPosition().isOrphan();
173 } 175 }
174 176
175 static bool shouldRepaintCaret(Node& node) { 177 LayoutRect FrameCaret::caretLocalRect() const {
176 // If PositionAnchorType::BeforeAnchor or PositionAnchorType::AfterAnchor, 178 if (!isActive())
177 // carets need to be repainted not only when the node is contentEditable but 179 return LayoutRect();
178 // also when its parentNode() is contentEditable. 180 return CaretDisplayItemClient::computeCaretRect(caretPosition());
179 node.document().updateStyleAndLayoutTree();
180 return hasEditableStyle(node) ||
181 (node.parentNode() && hasEditableStyle(*node.parentNode()));
182 } 181 }
183 182
184 void FrameCaret::invalidateCaretRect(bool forceInvalidation) { 183 LayoutBlock* FrameCaret::caretLayoutBlock() const {
185 if (!m_caretRectDirty) 184 return CaretDisplayItemClient::caretLayoutObject(
186 return; 185 caretPosition().anchorNode());
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) {
233 LayoutBlock* caretPainter = CaretDisplayItemClient::caretLayoutObject(node);
234 if (!caretPainter)
235 return IntRect();
236
237 LayoutRect localRect(rect);
238 caretPainter->flipForWritingMode(localRect);
239 return caretPainter->localToAbsoluteQuad(FloatRect(localRect))
240 .enclosingBoundingBox();
241 } 186 }
242 187
243 IntRect FrameCaret::absoluteCaretBounds() const { 188 IntRect FrameCaret::absoluteCaretBounds() const {
244 DCHECK_NE(m_frame->document()->lifecycle().state(), 189 DCHECK_NE(m_frame->document()->lifecycle().state(),
245 DocumentLifecycle::InPaintInvalidation); 190 DocumentLifecycle::InPaintInvalidation);
246 DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); 191 DCHECK(!m_frame->document()->needsLayoutTreeUpdate());
247 DocumentLifecycle::DisallowTransitionScope disallowTransition( 192 DocumentLifecycle::DisallowTransitionScope disallowTransition(
248 m_frame->document()->lifecycle()); 193 m_frame->document()->lifecycle());
249 194
250 Node* const caretNode = caretPosition().anchorNode();
251 if (!isActive()) 195 if (!isActive())
252 return absoluteBoundsForLocalRect(caretNode, LayoutRect()); 196 return IntRect();
197
198 const LayoutBlock* block = caretLayoutBlock();
199 if (!block)
200 return IntRect();
201
253 // TODO(yosin): We should get rid of text control short path since layout 202 // TODO(yosin): We should get rid of text control short path since layout
254 // tree is clean. 203 // tree is clean.
204 LayoutRect localRect;
255 if (enclosingTextControl(caretPosition().position()) && 205 if (enclosingTextControl(caretPosition().position()) &&
256 isVisuallyEquivalentCandidate(caretPosition().position())) { 206 isVisuallyEquivalentCandidate(caretPosition().position())) {
257 return absoluteBoundsForLocalRect( 207 localRect = this->caretLocalRect();
258 caretNode, CaretDisplayItemClient::computeCaretRect(caretPosition())); 208 } else {
209 localRect = CaretDisplayItemClient::computeCaretRect(
210 createVisiblePosition(caretPosition()).toPositionWithAffinity());
259 } 211 }
260 return absoluteBoundsForLocalRect( 212
261 caretNode, 213 block->flipForWritingMode(localRect);
262 CaretDisplayItemClient::computeCaretRect( 214 return block->localToAbsoluteQuad(FloatRect(localRect))
263 createVisiblePosition(caretPosition()).toPositionWithAffinity())); 215 .enclosingBoundingBox();
264 } 216 }
265 217
266 void FrameCaret::setShouldShowBlockCursor(bool shouldShowBlockCursor) { 218 void FrameCaret::setShouldShowBlockCursor(bool shouldShowBlockCursor) {
267 m_shouldShowBlockCursor = shouldShowBlockCursor; 219 m_shouldShowBlockCursor = shouldShowBlockCursor;
268 220
269 m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets(); 221 m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets();
270 222
271 updateAppearance(); 223 updateAppearance();
272 } 224 }
273 225
274 void FrameCaret::paintCaret(GraphicsContext& context, 226 void FrameCaret::paintCaret(GraphicsContext& context,
275 const LayoutPoint& paintOffset) { 227 const LayoutPoint& paintOffset) {
276 if (m_caretVisibility == CaretVisibility::Hidden) 228 if (m_caretVisibility == CaretVisibility::Hidden)
277 return; 229 return;
278 230
279 if (!(isActive() && m_shouldPaintCaret)) 231 if (!(isActive() && m_shouldPaintCaret))
280 return; 232 return;
281 233
282 const LayoutRect caretLocalRect = 234 m_caretBase->paintCaret(caretPosition().anchorNode(), context,
283 CaretDisplayItemClient::computeCaretRect(caretPosition()); 235 caretLocalRect(), paintOffset, DisplayItem::kCaret);
yosin_UTC9 2017/01/31 20:12:58 Yes, this is what I thought. It is redundant to ca
284 m_caretBase->paintCaret(caretPosition().anchorNode(), context, caretLocalRect,
285 paintOffset, DisplayItem::kCaret);
286 }
287
288 void FrameCaret::dataWillChange(const CharacterData& node) {
289 if (node == m_previousCaretNode) {
290 // This invalidation is eager, and intentionally uses stale state.
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 } 236 }
309 237
310 void FrameCaret::contextDestroyed(Document*) { 238 void FrameCaret::contextDestroyed(Document*) {
311 m_caretBlinkTimer.stop(); 239 m_caretBlinkTimer.stop();
312 m_previousCaretNode.clear();
313 m_previousCaretAnchorNode.clear();
314 } 240 }
315 241
316 bool FrameCaret::shouldBlinkCaret() const { 242 bool FrameCaret::shouldBlinkCaret() const {
317 if (m_caretVisibility != CaretVisibility::Visible || !isActive()) 243 if (m_caretVisibility != CaretVisibility::Visible || !isActive())
318 return false; 244 return false;
319 245
320 Element* root = rootEditableElementOf(caretPosition().position()); 246 Element* root = rootEditableElementOf(caretPosition().position());
321 if (!root) 247 if (!root)
322 return false; 248 return false;
323 249
324 Element* focusedElement = root->document().focusedElement(); 250 Element* focusedElement = root->document().focusedElement();
325 if (!focusedElement) 251 if (!focusedElement)
326 return false; 252 return false;
327 253
328 return focusedElement->isShadowIncludingInclusiveAncestorOf( 254 return focusedElement->isShadowIncludingInclusiveAncestorOf(
329 caretPosition().anchorNode()); 255 caretPosition().anchorNode());
330 } 256 }
331 257
332 void FrameCaret::caretBlinkTimerFired(TimerBase*) { 258 void FrameCaret::caretBlinkTimerFired(TimerBase*) {
333 DCHECK_EQ(m_caretVisibility, CaretVisibility::Visible); 259 DCHECK_EQ(m_caretVisibility, CaretVisibility::Visible);
334 if (isCaretBlinkingSuspended() && m_shouldPaintCaret) 260 if (isCaretBlinkingSuspended() && m_shouldPaintCaret)
335 return; 261 return;
336 m_shouldPaintCaret = !m_shouldPaintCaret; 262 m_shouldPaintCaret = !m_shouldPaintCaret;
337 setCaretRectNeedsUpdate(); 263 setNeedsPaintInvalidation();
338 } 264 }
339 265
340 } // namespace blink 266 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698