OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |