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