| 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 setNeedsPaintInvalidation(); |
| 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 setNeedsPaintInvalidation(); |
| 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 setNeedsPaintInvalidation(); |
| 149 } | 140 } |
| 150 | 141 |
| 151 void FrameCaret::setCaretRectNeedsUpdate() { | 142 void FrameCaret::setNeedsPaintInvalidation() { |
| 152 if (m_caretRectDirty) | 143 m_caretBase->setNeedsPaintInvalidation(); |
| 153 return; | 144 if (FrameView* frameView = m_frame->view()) |
| 154 m_caretRectDirty = true; | 145 frameView->scheduleVisualUpdateForPaintInvalidationIfNeeded(); |
| 146 } |
| 155 | 147 |
| 156 if (Page* page = m_frame->page()) | 148 void FrameCaret::clearPreviousVisualRect(const LayoutBlock& block) { |
| 157 page->animator().scheduleVisualUpdate(m_frame->localFrameRoot()); | 149 m_caretBase->clearPreviousVisualRect(block); |
| 150 } |
| 158 | 151 |
| 159 // Ensure the frame will be checked for paint invalidation during | 152 void FrameCaret::layoutBlockWillBeDestroyed(const LayoutBlock& block) { |
| 160 // PrePaintTreeWalk. | 153 m_caretBase->layoutBlockWillBeDestroyed(block); |
| 161 if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) { | 154 } |
| 162 if (auto layoutItem = m_frame->ownerLayoutItem()) | 155 |
| 163 layoutItem.setMayNeedPaintInvalidation(); | 156 void FrameCaret::updateForPaintInvalidation() { |
| 164 } | 157 bool shouldPaintCaret = |
| 158 m_shouldPaintCaret && isActive() && |
| 159 m_caretVisibility == CaretVisibility::Visible && |
| 160 m_selectionEditor->visibleSelection<EditingStrategy>().hasEditableStyle(); |
| 161 |
| 162 m_caretBase->updateForPaintInvalidation( |
| 163 shouldPaintCaret ? caretPosition() : PositionWithAffinity()); |
| 164 } |
| 165 |
| 166 void FrameCaret::invalidatePaintIfNeeded(const LayoutBlock& block, |
| 167 const PaintInvalidatorContext& context, |
| 168 PaintInvalidationReason reason) { |
| 169 m_caretBase->invalidatePaintIfNeeded(block, context, reason); |
| 165 } | 170 } |
| 166 | 171 |
| 167 bool FrameCaret::caretPositionIsValidForDocument( | 172 bool FrameCaret::caretPositionIsValidForDocument( |
| 168 const Document& document) const { | 173 const Document& document) const { |
| 169 if (!isActive()) | 174 if (!isActive()) |
| 170 return true; | 175 return true; |
| 171 | 176 |
| 172 return caretPosition().document() == document && !caretPosition().isOrphan(); | 177 return caretPosition().document() == document && !caretPosition().isOrphan(); |
| 173 } | 178 } |
| 174 | 179 |
| 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) { | 180 static IntRect absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) { |
| 233 LayoutBlock* caretPainter = CaretDisplayItemClient::caretLayoutObject(node); | 181 LayoutBlock* caretPainter = CaretDisplayItemClient::caretLayoutBlock(node); |
| 234 if (!caretPainter) | 182 if (!caretPainter) |
| 235 return IntRect(); | 183 return IntRect(); |
| 236 | 184 |
| 237 LayoutRect localRect(rect); | 185 LayoutRect localRect(rect); |
| 238 caretPainter->flipForWritingMode(localRect); | 186 caretPainter->flipForWritingMode(localRect); |
| 239 return caretPainter->localToAbsoluteQuad(FloatRect(localRect)) | 187 return caretPainter->localToAbsoluteQuad(FloatRect(localRect)) |
| 240 .enclosingBoundingBox(); | 188 .enclosingBoundingBox(); |
| 241 } | 189 } |
| 242 | 190 |
| 243 IntRect FrameCaret::absoluteCaretBounds() const { | 191 IntRect FrameCaret::absoluteCaretBounds() const { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 264 } | 212 } |
| 265 | 213 |
| 266 void FrameCaret::setShouldShowBlockCursor(bool shouldShowBlockCursor) { | 214 void FrameCaret::setShouldShowBlockCursor(bool shouldShowBlockCursor) { |
| 267 m_shouldShowBlockCursor = shouldShowBlockCursor; | 215 m_shouldShowBlockCursor = shouldShowBlockCursor; |
| 268 | 216 |
| 269 m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets(); | 217 m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 270 | 218 |
| 271 updateAppearance(); | 219 updateAppearance(); |
| 272 } | 220 } |
| 273 | 221 |
| 274 void FrameCaret::paintCaret(GraphicsContext& context, | 222 bool FrameCaret::shouldPaintCaret(const LayoutBlock& block) const { |
| 275 const LayoutPoint& paintOffset) { | 223 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 } | 224 } |
| 287 | 225 |
| 288 void FrameCaret::dataWillChange(const CharacterData& node) { | 226 void FrameCaret::paintCaret(GraphicsContext& context, |
| 289 if (node == m_previousCaretNode) { | 227 const LayoutPoint& paintOffset) const { |
| 290 // This invalidation is eager, and intentionally uses stale state. | 228 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 } | 229 } |
| 315 | 230 |
| 316 bool FrameCaret::shouldBlinkCaret() const { | 231 bool FrameCaret::shouldBlinkCaret() const { |
| 317 if (m_caretVisibility != CaretVisibility::Visible || !isActive()) | 232 if (m_caretVisibility != CaretVisibility::Visible || !isActive()) |
| 318 return false; | 233 return false; |
| 319 | 234 |
| 320 Element* root = rootEditableElementOf(caretPosition().position()); | 235 Element* root = rootEditableElementOf(caretPosition().position()); |
| 321 if (!root) | 236 if (!root) |
| 322 return false; | 237 return false; |
| 323 | 238 |
| 324 Element* focusedElement = root->document().focusedElement(); | 239 Element* focusedElement = root->document().focusedElement(); |
| 325 if (!focusedElement) | 240 if (!focusedElement) |
| 326 return false; | 241 return false; |
| 327 | 242 |
| 328 return focusedElement->isShadowIncludingInclusiveAncestorOf( | 243 return focusedElement->isShadowIncludingInclusiveAncestorOf( |
| 329 caretPosition().anchorNode()); | 244 caretPosition().anchorNode()); |
| 330 } | 245 } |
| 331 | 246 |
| 332 void FrameCaret::caretBlinkTimerFired(TimerBase*) { | 247 void FrameCaret::caretBlinkTimerFired(TimerBase*) { |
| 333 DCHECK_EQ(m_caretVisibility, CaretVisibility::Visible); | 248 DCHECK_EQ(m_caretVisibility, CaretVisibility::Visible); |
| 334 if (isCaretBlinkingSuspended() && m_shouldPaintCaret) | 249 if (isCaretBlinkingSuspended() && m_shouldPaintCaret) |
| 335 return; | 250 return; |
| 336 m_shouldPaintCaret = !m_shouldPaintCaret; | 251 m_shouldPaintCaret = !m_shouldPaintCaret; |
| 337 setCaretRectNeedsUpdate(); | 252 setNeedsPaintInvalidation(); |
| 338 } | 253 } |
| 339 | 254 |
| 340 } // namespace blink | 255 } // namespace blink |
| OLD | NEW |