Index: third_party/WebKit/Source/core/editing/SelectionEditor.cpp |
diff --git a/third_party/WebKit/Source/core/editing/SelectionEditor.cpp b/third_party/WebKit/Source/core/editing/SelectionEditor.cpp |
index 7c833994c93ed98299585562a2afc71afda45e92..e34bba5da6c36786ce4d1ba3d03e039e62164cfa 100644 |
--- a/third_party/WebKit/Source/core/editing/SelectionEditor.cpp |
+++ b/third_party/WebKit/Source/core/editing/SelectionEditor.cpp |
@@ -25,6 +25,8 @@ |
#include "core/editing/SelectionEditor.h" |
+#include "core/dom/NodeWithIndex.h" |
+#include "core/dom/Text.h" |
#include "core/editing/EditingUtilities.h" |
#include "core/editing/Editor.h" |
#include "core/editing/SelectionAdjuster.h" |
@@ -32,20 +34,30 @@ |
namespace blink { |
-SelectionEditor::SelectionEditor(LocalFrame& frame) |
- : m_frame(frame), m_observingVisibleSelection(false) { |
+SelectionEditor::SelectionEditor(LocalFrame& frame) : m_frame(frame) { |
clearVisibleSelection(); |
} |
SelectionEditor::~SelectionEditor() {} |
+void SelectionEditor::assertSelectionValid() const { |
+#if DCHECK_IS_ON() |
+ // Since We don't track dom tree version during attribute changes, we can't |
+ // use it for validity of |m_selection|. |
+ const_cast<SelectionEditor*>(this)->m_selection.m_domTreeVersion = |
+ document().domTreeVersion(); |
+#endif |
+ m_selection.assertValidFor(document()); |
+} |
+ |
void SelectionEditor::clearVisibleSelection() { |
- m_selection = VisibleSelection(); |
- m_selectionInFlatTree = VisibleSelectionInFlatTree(); |
+ m_selection = SelectionInDOMTree(); |
+ m_cachedVisibleSelectionInDOMTree = VisibleSelection(); |
+ m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree(); |
+ m_cacheIsDirty = false; |
if (!shouldAlwaysUseDirectionalSelection()) |
return; |
- m_selection.setIsDirectional(true); |
- m_selectionInFlatTree.setIsDirectional(true); |
+ m_selection.m_isDirectional = true; |
} |
void SelectionEditor::dispose() { |
@@ -54,89 +66,114 @@ void SelectionEditor::dispose() { |
clearVisibleSelection(); |
} |
-const Document& SelectionEditor::document() const { |
- DCHECK(m_document); |
- return *m_document; |
+Document& SelectionEditor::document() const { |
+ DCHECK(lifecycleContext()); |
+ return *lifecycleContext(); |
} |
template <> |
const VisibleSelection& SelectionEditor::visibleSelection<EditingStrategy>() |
const { |
- DCHECK_EQ(frame()->document(), document()); |
- DCHECK_EQ(frame(), document().frame()); |
- if (m_selection.isNone()) |
- return m_selection; |
- DCHECK_EQ(m_selection.base().document(), document()); |
- return m_selection; |
+ return computeVisibleSelectionInDOMTree(); |
} |
template <> |
const VisibleSelectionInFlatTree& |
SelectionEditor::visibleSelection<EditingInFlatTreeStrategy>() const { |
+ return computeVisibleSelectionInFlatTree(); |
+} |
+ |
+const VisibleSelection& SelectionEditor::computeVisibleSelectionInDOMTree() |
+ const { |
DCHECK_EQ(frame()->document(), document()); |
DCHECK_EQ(frame(), document().frame()); |
- if (m_selectionInFlatTree.isNone()) |
- return m_selectionInFlatTree; |
- DCHECK_EQ(m_selectionInFlatTree.base().document(), document()); |
- return m_selectionInFlatTree; |
+ updateCachedVisibleSelectionIfNeeded(); |
+ if (m_cachedVisibleSelectionInDOMTree.isNone()) |
+ return m_cachedVisibleSelectionInDOMTree; |
+ DCHECK_EQ(m_cachedVisibleSelectionInDOMTree.base().document(), document()); |
+ return m_cachedVisibleSelectionInDOMTree; |
} |
-void SelectionEditor::setVisibleSelection( |
- const VisibleSelection& newSelection, |
- FrameSelection::SetSelectionOptions options) { |
- DCHECK(newSelection.isValidFor(document())) << newSelection; |
- resetLogicalRange(); |
- clearDocumentCachedRange(); |
+const VisibleSelectionInFlatTree& |
+SelectionEditor::computeVisibleSelectionInFlatTree() const { |
+ DCHECK_EQ(frame()->document(), document()); |
+ DCHECK_EQ(frame(), document().frame()); |
+ updateCachedVisibleSelectionIfNeeded(); |
+ if (m_cachedVisibleSelectionInFlatTree.isNone()) |
+ return m_cachedVisibleSelectionInFlatTree; |
+ DCHECK_EQ(m_cachedVisibleSelectionInFlatTree.base().document(), document()); |
+ return m_cachedVisibleSelectionInFlatTree; |
+} |
- m_selection = newSelection; |
- if (options & FrameSelection::DoNotAdjustInFlatTree) { |
- m_selectionInFlatTree.setWithoutValidation( |
- toPositionInFlatTree(m_selection.base()), |
- toPositionInFlatTree(m_selection.extent())); |
- return; |
- } |
+const SelectionInDOMTree& SelectionEditor::selectionInDOMTree() const { |
+ assertSelectionValid(); |
+ return m_selection; |
+} |
- SelectionAdjuster::adjustSelectionInFlatTree(&m_selectionInFlatTree, |
- m_selection); |
+bool SelectionEditor::hasEditableStyle() const { |
+ return computeVisibleSelectionInDOMTree().hasEditableStyle(); |
} |
-void SelectionEditor::setVisibleSelection( |
- const VisibleSelectionInFlatTree& newSelection, |
- FrameSelection::SetSelectionOptions options) { |
- DCHECK(newSelection.isValidFor(document())) << newSelection; |
- DCHECK(!(options & FrameSelection::DoNotAdjustInFlatTree)); |
- resetLogicalRange(); |
- clearDocumentCachedRange(); |
+bool SelectionEditor::isContentEditable() const { |
+ return computeVisibleSelectionInDOMTree().isContentEditable(); |
+} |
- m_selectionInFlatTree = newSelection; |
- SelectionAdjuster::adjustSelectionInDOMTree(&m_selection, |
- m_selectionInFlatTree); |
+bool SelectionEditor::isContentRichlyEditable() const { |
+ return computeVisibleSelectionInDOMTree().isContentRichlyEditable(); |
} |
-void SelectionEditor::setWithoutValidation(const Position& base, |
- const Position& extent) { |
+void SelectionEditor::markCacheDirty() { |
+ m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree(); |
+ m_cachedVisibleSelectionInDOMTree = VisibleSelection(); |
+ m_cacheIsDirty = true; |
+} |
+ |
+void SelectionEditor::setSelection(const SelectionInDOMTree& newSelection) { |
+ newSelection.assertValidFor(document()); |
+ if (m_selection == newSelection) |
+ return; |
resetLogicalRange(); |
- if (base.isNotNull()) |
- DCHECK_EQ(base.document(), document()); |
- if (extent.isNotNull()) |
- DCHECK_EQ(extent.document(), document()); |
clearDocumentCachedRange(); |
+ markCacheDirty(); |
+ m_selection = newSelection; |
+} |
+ |
+void SelectionEditor::didChangeChildren(const ContainerNode&) { |
+ markCacheDirty(); |
+ didFinishDOMMutation(); |
+} |
+ |
+void SelectionEditor::didFinishTextChange(const Position& newBase, |
+ const Position& newExtent) { |
+ if (newBase == m_selection.m_base && newExtent == m_selection.m_extent) { |
+ didFinishDOMMutation(); |
+ return; |
+ } |
+ m_selection.m_base = newBase; |
+ m_selection.m_extent = newExtent; |
+ markCacheDirty(); |
+ didFinishDOMMutation(); |
+} |
- m_selection.setWithoutValidation(base, extent); |
- m_selectionInFlatTree.setWithoutValidation(toPositionInFlatTree(base), |
- toPositionInFlatTree(extent)); |
+void SelectionEditor::didFinishDOMMutation() { |
+ assertSelectionValid(); |
} |
void SelectionEditor::documentAttached(Document* document) { |
DCHECK(document); |
- DCHECK(!m_document) << m_document; |
- m_document = document; |
+ DCHECK(!lifecycleContext()) << lifecycleContext(); |
+ m_styleVersion = static_cast<uint64_t>(-1); |
+ clearVisibleSelection(); |
+ setContext(document); |
} |
-void SelectionEditor::documentDetached(const Document& document) { |
- DCHECK_EQ(m_document, &document); |
+void SelectionEditor::contextDestroyed(Document*) { |
dispose(); |
- m_document = nullptr; |
+ m_styleVersion = static_cast<uint64_t>(-1); |
+ m_selection = SelectionInDOMTree(); |
+ m_cachedVisibleSelectionInDOMTree = VisibleSelection(); |
+ m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree(); |
+ m_cacheIsDirty = false; |
} |
void SelectionEditor::resetLogicalRange() { |
@@ -157,7 +194,7 @@ void SelectionEditor::setLogicalRange(Range* range) { |
Range* SelectionEditor::firstRange() const { |
if (m_logicalRange) |
return m_logicalRange->cloneRange(); |
- return firstRangeOf(m_selection); |
+ return firstRangeOf(computeVisibleSelectionInDOMTree()); |
} |
bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const { |
@@ -165,10 +202,37 @@ bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const { |
} |
void SelectionEditor::updateIfNeeded() { |
- DCHECK(m_selection.isValidFor(document())) << m_selection; |
- DCHECK(m_selectionInFlatTree.isValidFor(document())) << m_selection; |
- m_selection.updateIfNeeded(); |
- m_selectionInFlatTree.updateIfNeeded(); |
+ // TODO(yosin): We should unify |SelectionEditor::updateIfNeeded()| and |
+ // |updateCachedVisibleSelectionIfNeeded()| |
+ updateCachedVisibleSelectionIfNeeded(); |
+} |
+ |
+bool SelectionEditor::needsUpdateVisibleSelection() const { |
+ return m_cacheIsDirty || m_styleVersion != document().styleVersion(); |
+} |
+ |
+void SelectionEditor::updateCachedVisibleSelectionIfNeeded() const { |
+ // Note: Since we |FrameCaret::updateApperance()| is called from |
+ // |FrameView::performPostLayoutTasks()|, we check lifecycle against |
+ // |AfterPerformLayout| instead of |LayoutClean|. |
+ DCHECK_GE(document().lifecycle().state(), |
+ DocumentLifecycle::AfterPerformLayout); |
+ assertSelectionValid(); |
+ if (!needsUpdateVisibleSelection()) |
+ return; |
+ |
+ m_cachedVisibleSelectionInDOMTree = createVisibleSelection(m_selection); |
+ m_cachedVisibleSelectionInFlatTree = createVisibleSelection( |
+ SelectionInFlatTree::Builder() |
+ .setBaseAndExtent(toPositionInFlatTree(m_selection.base()), |
+ toPositionInFlatTree(m_selection.extent())) |
+ .setAffinity(m_selection.affinity()) |
+ .setHasTrailingWhitespace(m_selection.hasTrailingWhitespace()) |
+ .setGranularity(m_selection.granularity()) |
+ .setIsDirectional(m_selection.isDirectional()) |
+ .build()); |
+ m_styleVersion = document().styleVersion(); |
+ m_cacheIsDirty = false; |
} |
void SelectionEditor::cacheRangeOfDocument(Range* range) { |
@@ -184,12 +248,13 @@ void SelectionEditor::clearDocumentCachedRange() { |
} |
DEFINE_TRACE(SelectionEditor) { |
- visitor->trace(m_document); |
visitor->trace(m_frame); |
visitor->trace(m_selection); |
- visitor->trace(m_selectionInFlatTree); |
+ visitor->trace(m_cachedVisibleSelectionInDOMTree); |
+ visitor->trace(m_cachedVisibleSelectionInFlatTree); |
visitor->trace(m_logicalRange); |
visitor->trace(m_cachedRange); |
+ SynchronousMutationObserver::trace(visitor); |
} |
} // namespace blink |