| Index: third_party/WebKit/Source/core/editing/FrameCaret.cpp
|
| diff --git a/third_party/WebKit/Source/core/editing/FrameCaret.cpp b/third_party/WebKit/Source/core/editing/FrameCaret.cpp
|
| index d2073529efe2d7d9646f90be25e7dc9b17de5883..67610628f9fbe8884b6df8edd98cb6833a33fe05 100644
|
| --- a/third_party/WebKit/Source/core/editing/FrameCaret.cpp
|
| +++ b/third_party/WebKit/Source/core/editing/FrameCaret.cpp
|
| @@ -26,6 +26,7 @@
|
| #include "core/editing/FrameCaret.h"
|
|
|
| #include "core/dom/TaskRunnerHelper.h"
|
| +#include "core/editing/CaretBase.h"
|
| #include "core/editing/EditingUtilities.h"
|
| #include "core/editing/Editor.h"
|
| #include "core/editing/SelectionEditor.h"
|
| @@ -34,6 +35,7 @@
|
| #include "core/frame/LocalFrame.h"
|
| #include "core/frame/Settings.h"
|
| #include "core/html/TextControlElement.h"
|
| +#include "core/layout/LayoutBlock.h"
|
| #include "core/layout/LayoutTheme.h"
|
| #include "core/layout/api/LayoutPartItem.h"
|
| #include "core/page/Page.h"
|
| @@ -46,12 +48,13 @@ FrameCaret::FrameCaret(LocalFrame& frame,
|
| const SelectionEditor& selectionEditor)
|
| : m_selectionEditor(&selectionEditor),
|
| m_frame(frame),
|
| + m_caretBase(new CaretBase()),
|
| m_caretVisibility(CaretVisibility::Hidden),
|
| m_previousCaretVisibility(CaretVisibility::Hidden),
|
| m_caretBlinkTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, &frame),
|
| this,
|
| &FrameCaret::caretBlinkTimerFired),
|
| - m_caretRectDirty(true),
|
| + m_didScheduleUpdate(true),
|
| m_shouldPaintCaret(true),
|
| m_isCaretBlinkingSuspended(false),
|
| m_shouldShowBlockCursor(false) {}
|
| @@ -63,36 +66,40 @@ DEFINE_TRACE(FrameCaret) {
|
| visitor->trace(m_frame);
|
| visitor->trace(m_previousCaretNode);
|
| visitor->trace(m_previousCaretAnchorNode);
|
| - CaretBase::trace(visitor);
|
| + SynchronousMutationObserver::trace(visitor);
|
| +}
|
| +
|
| +void FrameCaret::documentAttached(Document* document) {
|
| + setContext(document);
|
| }
|
|
|
| const PositionWithAffinity FrameCaret::caretPosition() const {
|
| const VisibleSelection& selection =
|
| - m_selectionEditor->visibleSelection<EditingStrategy>();
|
| + m_selectionEditor->computeVisibleSelectionInDOMTree();
|
| if (!selection.isCaret())
|
| return PositionWithAffinity();
|
| return PositionWithAffinity(selection.start(), selection.affinity());
|
| }
|
|
|
| +const DisplayItemClient& FrameCaret::displayItemClient() const {
|
| + return *m_caretBase;
|
| +}
|
| +
|
| inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame) {
|
| return frame->editor().lastEditCommand() &&
|
| frame->editor().lastEditCommand()->shouldStopCaretBlinking();
|
| }
|
|
|
| void FrameCaret::updateAppearance() {
|
| + DCHECK_GE(lifecycleContext()->lifecycle().state(),
|
| + DocumentLifecycle::AfterPerformLayout);
|
| // Paint a block cursor instead of a caret in overtype mode unless the caret
|
| // is at the end of a line (in this case the FrameSelection will paint a
|
| // blinking caret as usual).
|
| bool paintBlockCursor = m_shouldShowBlockCursor && isActive();
|
| - if (paintBlockCursor) {
|
| - // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets
|
| - // needs to be audited. see http://crbug.com/590369 for more details.
|
| - // In the long term, we should defer the update of the caret's appearance
|
| - // to prevent synchronous layout.
|
| - m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets();
|
| -
|
| - if (isLogicalEndOfLine(createVisiblePosition(caretPosition())))
|
| - paintBlockCursor = false;
|
| + if (paintBlockCursor &&
|
| + isLogicalEndOfLine(createVisiblePosition(caretPosition()))) {
|
| + paintBlockCursor = false;
|
| }
|
|
|
| bool shouldBlink = !paintBlockCursor && shouldBlinkCaret();
|
| @@ -134,13 +141,13 @@ void FrameCaret::setCaretVisibility(CaretVisibility visibility) {
|
|
|
| m_caretVisibility = visibility;
|
|
|
| - updateAppearance();
|
| + setCaretRectNeedsUpdate();
|
| }
|
|
|
| void FrameCaret::setCaretRectNeedsUpdate() {
|
| - if (m_caretRectDirty)
|
| + if (m_didScheduleUpdate)
|
| return;
|
| - m_caretRectDirty = true;
|
| + m_didScheduleUpdate = true;
|
|
|
| if (Page* page = m_frame->page())
|
| page->animator().scheduleVisualUpdate(m_frame->localFrameRoot());
|
| @@ -161,15 +168,27 @@ bool FrameCaret::caretPositionIsValidForDocument(
|
| return caretPosition().document() == document && !caretPosition().isOrphan();
|
| }
|
|
|
| +static bool shouldRepaintCaret(Node& node) {
|
| + // If PositionAnchorType::BeforeAnchor or PositionAnchorType::AfterAnchor,
|
| + // carets need to be repainted not only when the node is contentEditable but
|
| + // also when its parentNode() is contentEditable.
|
| + node.document().updateStyleAndLayoutTree();
|
| + return hasEditableStyle(node) ||
|
| + (node.parentNode() && hasEditableStyle(*node.parentNode()));
|
| +}
|
| +
|
| void FrameCaret::invalidateCaretRect(bool forceInvalidation) {
|
| - if (!m_caretRectDirty)
|
| - return;
|
| - m_caretRectDirty = false;
|
| + updateAppearance();
|
| + m_didScheduleUpdate = false;
|
|
|
| DCHECK(caretPositionIsValidForDocument(*m_frame->document()));
|
| LayoutObject* layoutObject = nullptr;
|
| LayoutRect newRect;
|
| PositionWithAffinity currentCaretPosition = caretPosition();
|
| + const Node* const colorNode = caretPosition().anchorNode();
|
| + Color newCaretColor =
|
| + colorNode ? colorNode->layoutObject()->resolveColor(CSSPropertyCaretColor)
|
| + : Color();
|
| if (isActive())
|
| newRect = localCaretRectOfPosition(currentCaretPosition, layoutObject);
|
| Node* newNode = layoutObject ? layoutObject->node() : nullptr;
|
| @@ -193,20 +212,34 @@ void FrameCaret::invalidateCaretRect(bool forceInvalidation) {
|
| // of timer state.
|
| if (!forceInvalidation && !m_caretBlinkTimer.isActive() &&
|
| newNode == m_previousCaretNode && newRect == m_previousCaretRect &&
|
| - m_caretVisibility == m_previousCaretVisibility)
|
| + m_caretVisibility == m_previousCaretVisibility &&
|
| + newCaretColor == m_previousCaretColor) {
|
| return;
|
| + }
|
|
|
| if (m_previousCaretAnchorNode &&
|
| shouldRepaintCaret(*m_previousCaretAnchorNode)) {
|
| - invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
|
| - m_previousCaretRect);
|
| + m_caretBase->invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
|
| + m_previousCaretRect);
|
| }
|
| if (newAnchorNode && shouldRepaintCaret(*newAnchorNode))
|
| - invalidateLocalCaretRect(newAnchorNode, newRect);
|
| + m_caretBase->invalidateLocalCaretRect(newAnchorNode, newRect);
|
| m_previousCaretNode = newNode;
|
| m_previousCaretAnchorNode = newAnchorNode;
|
| m_previousCaretRect = newRect;
|
| m_previousCaretVisibility = m_caretVisibility;
|
| + m_previousCaretColor = newCaretColor;
|
| +}
|
| +
|
| +static IntRect absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) {
|
| + LayoutBlock* caretPainter = CaretBase::caretLayoutObject(node);
|
| + if (!caretPainter)
|
| + return IntRect();
|
| +
|
| + LayoutRect localRect(rect);
|
| + caretPainter->flipForWritingMode(localRect);
|
| + return caretPainter->localToAbsoluteQuad(FloatRect(localRect))
|
| + .enclosingBoundingBox();
|
| }
|
|
|
| // TDOO(yosin): We should mark |FrameCaret::absoluteCaretBounds()| to |const|.
|
| @@ -224,11 +257,13 @@ IntRect FrameCaret::absoluteCaretBounds() {
|
| // tree is clean.
|
| if (enclosingTextControl(caretPosition().position()) &&
|
| isVisuallyEquivalentCandidate(caretPosition().position())) {
|
| - return absoluteBoundsForLocalRect(caretNode,
|
| - computeCaretRect(caretPosition()));
|
| + return absoluteBoundsForLocalRect(
|
| + caretNode, CaretBase::computeCaretRect(caretPosition()));
|
| }
|
| return absoluteBoundsForLocalRect(
|
| - caretNode, computeCaretRect(createVisiblePosition(caretPosition())));
|
| + caretNode,
|
| + CaretBase::computeCaretRect(
|
| + createVisiblePosition(caretPosition()).toPositionWithAffinity()));
|
| }
|
|
|
| void FrameCaret::setShouldShowBlockCursor(bool shouldShowBlockCursor) {
|
| @@ -236,7 +271,7 @@ void FrameCaret::setShouldShowBlockCursor(bool shouldShowBlockCursor) {
|
|
|
| m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets();
|
|
|
| - updateAppearance();
|
| + setCaretRectNeedsUpdate();
|
| }
|
|
|
| void FrameCaret::paintCaret(GraphicsContext& context,
|
| @@ -247,17 +282,18 @@ void FrameCaret::paintCaret(GraphicsContext& context,
|
| if (!(isActive() && m_shouldPaintCaret))
|
| return;
|
|
|
| - const LayoutRect caretLocalRect = computeCaretRect(caretPosition());
|
| - CaretBase::paintCaret(caretPosition().anchorNode(), context, *this,
|
| - caretLocalRect, paintOffset, DisplayItem::kCaret);
|
| + const LayoutRect caretLocalRect =
|
| + CaretBase::computeCaretRect(caretPosition());
|
| + m_caretBase->paintCaret(caretPosition().anchorNode(), context, caretLocalRect,
|
| + paintOffset, DisplayItem::kCaret);
|
| }
|
|
|
| void FrameCaret::dataWillChange(const CharacterData& node) {
|
| if (node == m_previousCaretNode) {
|
| // This invalidation is eager, and intentionally uses stale state.
|
| DisableCompositingQueryAsserts disabler;
|
| - invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
|
| - m_previousCaretRect);
|
| + m_caretBase->invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
|
| + m_previousCaretRect);
|
| }
|
| }
|
|
|
| @@ -266,15 +302,15 @@ void FrameCaret::nodeWillBeRemoved(Node& node) {
|
| return;
|
| // Hits in ManualTests/caret-paint-after-last-text-is-removed.html
|
| DisableCompositingQueryAsserts disabler;
|
| - invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
|
| - m_previousCaretRect);
|
| + m_caretBase->invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
|
| + m_previousCaretRect);
|
| m_previousCaretNode = nullptr;
|
| m_previousCaretAnchorNode = nullptr;
|
| m_previousCaretRect = LayoutRect();
|
| m_previousCaretVisibility = CaretVisibility::Hidden;
|
| }
|
|
|
| -void FrameCaret::documentDetached() {
|
| +void FrameCaret::contextDestroyed(Document*) {
|
| m_caretBlinkTimer.stop();
|
| m_previousCaretNode.clear();
|
| m_previousCaretAnchorNode.clear();
|
|
|