Chromium Code Reviews| 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 |
| 11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
| 12 * | 12 * |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 */ | 24 */ |
| 25 | 25 |
| 26 #include "core/editing/SelectionEditor.h" | 26 #include "core/editing/SelectionEditor.h" |
| 27 | 27 |
| 28 #include "core/dom/NodeWithIndex.h" | |
| 29 #include "core/dom/Text.h" | |
| 28 #include "core/editing/EditingUtilities.h" | 30 #include "core/editing/EditingUtilities.h" |
| 29 #include "core/editing/Editor.h" | 31 #include "core/editing/Editor.h" |
| 30 #include "core/editing/SelectionAdjuster.h" | 32 #include "core/editing/SelectionAdjuster.h" |
| 31 #include "core/frame/LocalFrame.h" | 33 #include "core/frame/LocalFrame.h" |
| 32 | 34 |
| 33 namespace blink { | 35 namespace blink { |
| 34 | 36 |
| 35 SelectionEditor::SelectionEditor(LocalFrame& frame) | 37 SelectionEditor::SelectionEditor(LocalFrame& frame) : m_frame(frame) { |
| 36 : m_frame(frame), m_observingVisibleSelection(false) { | |
| 37 clearVisibleSelection(); | 38 clearVisibleSelection(); |
| 38 } | 39 } |
| 39 | 40 |
| 40 SelectionEditor::~SelectionEditor() {} | 41 SelectionEditor::~SelectionEditor() {} |
| 41 | 42 |
| 43 void SelectionEditor::assertSelectionValid() const { | |
| 44 #if DCHECK_IS_ON() | |
| 45 // Since We don't track dom tree version during attribute changes, we can't | |
| 46 // use it for validity of |m_selection|. | |
| 47 const_cast<SelectionEditor*>(this)->m_selection.m_domTreeVersion = | |
| 48 document().domTreeVersion(); | |
| 49 #endif | |
| 50 m_selection.assertValidFor(document()); | |
| 51 } | |
| 52 | |
| 42 void SelectionEditor::clearVisibleSelection() { | 53 void SelectionEditor::clearVisibleSelection() { |
| 43 m_selection = VisibleSelection(); | 54 m_selection = SelectionInDOMTree(); |
| 44 m_selectionInFlatTree = VisibleSelectionInFlatTree(); | 55 m_cachedVisibleSelectionInDOMTree = VisibleSelection(); |
| 56 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree(); | |
| 57 m_cacheIsDirty = false; | |
| 45 if (!shouldAlwaysUseDirectionalSelection()) | 58 if (!shouldAlwaysUseDirectionalSelection()) |
| 46 return; | 59 return; |
| 47 m_selection.setIsDirectional(true); | 60 m_selection.m_isDirectional = true; |
| 48 m_selectionInFlatTree.setIsDirectional(true); | |
| 49 } | 61 } |
| 50 | 62 |
| 51 void SelectionEditor::dispose() { | 63 void SelectionEditor::dispose() { |
| 52 resetLogicalRange(); | 64 resetLogicalRange(); |
| 53 clearVisibleSelection(); | 65 clearVisibleSelection(); |
| 54 } | 66 } |
| 55 | 67 |
| 56 const Document& SelectionEditor::document() const { | 68 const Document& SelectionEditor::document() const { |
| 57 DCHECK(m_document); | 69 DCHECK(lifecycleContext()); |
| 58 return *m_document; | 70 return *lifecycleContext(); |
| 71 } | |
| 72 | |
| 73 Document& SelectionEditor::document() { | |
| 74 DCHECK(lifecycleContext()); | |
| 75 return *lifecycleContext(); | |
| 59 } | 76 } |
| 60 | 77 |
| 61 template <> | 78 template <> |
| 62 const VisibleSelection& SelectionEditor::visibleSelection<EditingStrategy>() | 79 const VisibleSelection& SelectionEditor::visibleSelection<EditingStrategy>() |
| 63 const { | 80 const { |
| 64 DCHECK_EQ(frame()->document(), document()); | 81 return computeVisibleSelectionInDOMTree(); |
| 65 DCHECK_EQ(frame(), document().frame()); | |
| 66 if (m_selection.isNone()) | |
| 67 return m_selection; | |
| 68 DCHECK_EQ(m_selection.base().document(), document()); | |
| 69 return m_selection; | |
| 70 } | 82 } |
| 71 | 83 |
| 72 template <> | 84 template <> |
| 73 const VisibleSelectionInFlatTree& | 85 const VisibleSelectionInFlatTree& |
| 74 SelectionEditor::visibleSelection<EditingInFlatTreeStrategy>() const { | 86 SelectionEditor::visibleSelection<EditingInFlatTreeStrategy>() const { |
| 87 return computeVisibleSelectionInFlatTree(); | |
| 88 } | |
| 89 | |
| 90 const VisibleSelection& SelectionEditor::computeVisibleSelectionInDOMTree() | |
| 91 const { | |
| 75 DCHECK_EQ(frame()->document(), document()); | 92 DCHECK_EQ(frame()->document(), document()); |
| 76 DCHECK_EQ(frame(), document().frame()); | 93 DCHECK_EQ(frame(), document().frame()); |
| 77 if (m_selectionInFlatTree.isNone()) | 94 const_cast<SelectionEditor*>(this)->updateCachedVisibleSelectionIfNeeded(); |
|
yoichio
2017/02/10 05:24:52
Why do you const_cast the same type?
yosin_UTC9
2017/02/10 06:38:41
This is const member function, |this| is type |con
yoichio
2017/02/13 04:38:19
I prefer const-clean code flow.
Could you rm |cons
| |
| 78 return m_selectionInFlatTree; | 95 if (m_cachedVisibleSelectionInDOMTree.isNone()) |
| 79 DCHECK_EQ(m_selectionInFlatTree.base().document(), document()); | 96 return m_cachedVisibleSelectionInDOMTree; |
| 80 return m_selectionInFlatTree; | 97 DCHECK_EQ(m_cachedVisibleSelectionInDOMTree.base().document(), document()); |
| 98 return m_cachedVisibleSelectionInDOMTree; | |
| 81 } | 99 } |
| 82 | 100 |
| 83 void SelectionEditor::setVisibleSelection( | 101 const VisibleSelectionInFlatTree& |
| 84 const VisibleSelection& newSelection, | 102 SelectionEditor::computeVisibleSelectionInFlatTree() const { |
| 85 FrameSelection::SetSelectionOptions options) { | 103 DCHECK_EQ(frame()->document(), document()); |
| 86 DCHECK(newSelection.isValidFor(document())) << newSelection; | 104 DCHECK_EQ(frame(), document().frame()); |
| 105 const_cast<SelectionEditor*>(this)->updateCachedVisibleSelectionIfNeeded(); | |
| 106 if (m_cachedVisibleSelectionInFlatTree.isNone()) | |
| 107 return m_cachedVisibleSelectionInFlatTree; | |
| 108 DCHECK_EQ(m_cachedVisibleSelectionInFlatTree.base().document(), document()); | |
| 109 return m_cachedVisibleSelectionInFlatTree; | |
| 110 } | |
| 111 | |
| 112 const SelectionInDOMTree& SelectionEditor::selectionInDOMTree() const { | |
| 113 assertSelectionValid(); | |
| 114 return m_selection; | |
| 115 } | |
| 116 | |
| 117 bool SelectionEditor::hasEditableStyle() const { | |
| 118 return computeVisibleSelectionInDOMTree().hasEditableStyle(); | |
| 119 } | |
| 120 | |
| 121 bool SelectionEditor::isContentEditable() const { | |
| 122 return computeVisibleSelectionInDOMTree().isContentEditable(); | |
| 123 } | |
| 124 | |
| 125 bool SelectionEditor::isContentRichlyEditable() const { | |
| 126 return computeVisibleSelectionInDOMTree().isContentRichlyEditable(); | |
| 127 } | |
| 128 | |
| 129 void SelectionEditor::markCacheDirty() { | |
| 130 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree(); | |
| 131 m_cachedVisibleSelectionInDOMTree = VisibleSelection(); | |
| 132 m_cacheIsDirty = true; | |
| 133 } | |
| 134 | |
| 135 void SelectionEditor::setSelection(const SelectionInDOMTree& newSelection) { | |
| 136 newSelection.assertValidFor(document()); | |
| 137 if (m_selection == newSelection) | |
| 138 return; | |
| 87 resetLogicalRange(); | 139 resetLogicalRange(); |
| 140 markCacheDirty(); | |
| 88 m_selection = newSelection; | 141 m_selection = newSelection; |
| 89 if (options & FrameSelection::DoNotAdjustInFlatTree) { | 142 } |
| 90 m_selectionInFlatTree.setWithoutValidation( | 143 |
| 91 toPositionInFlatTree(m_selection.base()), | 144 void SelectionEditor::didChangeChildren(const ContainerNode&) { |
| 92 toPositionInFlatTree(m_selection.extent())); | 145 markCacheDirty(); |
| 146 didFinishDOMMutation(); | |
| 147 } | |
| 148 | |
| 149 static Position positionAfterPreviousSiblingOf(const Node& node) { | |
| 150 Node* const previousSibling = NodeTraversal::previousSibling(node); | |
| 151 if (!previousSibling) { | |
| 152 // Returns |BeforeChildren| of a parent of |node| since |node| is the | |
| 153 // first child. | |
| 154 return Position::firstPositionInNode(NodeTraversal::parent(node)); | |
| 155 } | |
| 156 if (previousSibling->isCharacterDataNode()) { | |
| 157 return Position(previousSibling, | |
| 158 toCharacterData(previousSibling)->length()); | |
| 159 } | |
| 160 return Position::afterNode(previousSibling); | |
| 161 } | |
| 162 | |
| 163 static Position computePositionForNodeRemoval(const Position& position, | |
| 164 Node& nodeToBeRemoved) { | |
| 165 Node* const anchorNode = position.anchorNode(); | |
| 166 if (anchorNode == nodeToBeRemoved) | |
| 167 return positionAfterPreviousSiblingOf(nodeToBeRemoved); | |
| 168 if (position.isOffsetInAnchor() && | |
| 169 anchorNode == nodeToBeRemoved.parentNode()) { | |
| 170 const int positionOffset = position.offsetInContainerNode(); | |
| 171 if (positionOffset == 0) | |
| 172 return position; | |
| 173 // |nodeToBeRemoved| is sibling or itself of |position|. | |
| 174 int offset = 0; | |
| 175 for (Node& child : | |
| 176 NodeTraversal::childrenOf(*nodeToBeRemoved.parentNode())) { | |
| 177 if (nodeToBeRemoved == child) { | |
| 178 // |position| is after |nodeToBeRemoved|. | |
| 179 return Position(anchorNode, positionOffset - 1); | |
| 180 } | |
| 181 if (positionOffset == offset) { | |
| 182 // |position| is before |nodeToBeRemoved|. | |
| 183 return position; | |
| 184 } | |
| 185 ++offset; | |
| 186 } | |
| 187 NOTREACHED() << position << " should be handled."; | |
| 188 } | |
| 189 if (!nodeToBeRemoved.isContainerNode()) | |
| 190 return position; | |
| 191 if (toContainerNode(nodeToBeRemoved) | |
| 192 .containsIncludingHostElements(*anchorNode)) | |
| 193 return positionAfterPreviousSiblingOf(nodeToBeRemoved); | |
| 194 return position; | |
| 195 } | |
| 196 | |
| 197 void SelectionEditor::didFinishTextChange(const Position& newBase, | |
| 198 const Position& newExtent) { | |
| 199 if (newBase == m_selection.m_base && newExtent == m_selection.m_extent) { | |
| 200 didFinishDOMMutation(); | |
| 93 return; | 201 return; |
| 94 } | 202 } |
| 95 | 203 m_selection.m_base = newBase; |
| 96 SelectionAdjuster::adjustSelectionInFlatTree(&m_selectionInFlatTree, | 204 m_selection.m_extent = newExtent; |
| 97 m_selection); | 205 markCacheDirty(); |
| 206 didFinishDOMMutation(); | |
| 98 } | 207 } |
| 99 | 208 |
| 100 void SelectionEditor::setVisibleSelection( | 209 void SelectionEditor::didFinishDOMMutation() { |
| 101 const VisibleSelectionInFlatTree& newSelection, | 210 assertSelectionValid(); |
| 102 FrameSelection::SetSelectionOptions options) { | |
| 103 DCHECK(newSelection.isValidFor(document())) << newSelection; | |
| 104 DCHECK(!(options & FrameSelection::DoNotAdjustInFlatTree)); | |
| 105 resetLogicalRange(); | |
| 106 m_selectionInFlatTree = newSelection; | |
| 107 SelectionAdjuster::adjustSelectionInDOMTree(&m_selection, | |
| 108 m_selectionInFlatTree); | |
| 109 } | 211 } |
| 110 | 212 |
| 111 void SelectionEditor::setWithoutValidation(const Position& base, | 213 void SelectionEditor::nodeWillBeRemoved(Node& nodeToBeRemoved) { |
| 112 const Position& extent) { | 214 if (m_selection.isNone()) |
| 113 resetLogicalRange(); | 215 return; |
| 114 if (base.isNotNull()) | 216 const Position oldBase = m_selection.m_base; |
| 115 DCHECK_EQ(base.document(), document()); | 217 const Position oldExtent = m_selection.m_extent; |
| 116 if (extent.isNotNull()) | 218 const Position& newBase = |
| 117 DCHECK_EQ(extent.document(), document()); | 219 computePositionForNodeRemoval(oldBase, nodeToBeRemoved); |
| 118 m_selection.setWithoutValidation(base, extent); | 220 const Position& newExtent = |
| 119 m_selectionInFlatTree.setWithoutValidation(toPositionInFlatTree(base), | 221 computePositionForNodeRemoval(oldExtent, nodeToBeRemoved); |
| 120 toPositionInFlatTree(extent)); | 222 if (newBase == oldBase && newExtent == oldExtent) |
| 223 return; | |
| 224 m_selection = SelectionInDOMTree::Builder() | |
| 225 .setBaseAndExtent(newBase, newExtent) | |
| 226 .build(); | |
| 227 markCacheDirty(); | |
| 121 } | 228 } |
| 122 | 229 |
| 123 void SelectionEditor::documentAttached(Document* document) { | 230 void SelectionEditor::documentAttached(Document* document) { |
| 124 DCHECK(document); | 231 DCHECK(document); |
| 125 DCHECK(!m_document) << m_document; | 232 DCHECK(!lifecycleContext()) << lifecycleContext(); |
| 126 m_document = document; | 233 m_styleVersion = static_cast<uint64_t>(-1); |
| 234 clearVisibleSelection(); | |
| 235 setContext(document); | |
| 127 } | 236 } |
| 128 | 237 |
| 129 void SelectionEditor::documentDetached(const Document& document) { | 238 void SelectionEditor::contextDestroyed(Document*) { |
| 130 DCHECK_EQ(m_document, &document); | |
| 131 dispose(); | 239 dispose(); |
| 132 m_document = nullptr; | 240 m_styleVersion = static_cast<uint64_t>(-1); |
| 241 m_selection = SelectionInDOMTree(); | |
| 242 m_cachedVisibleSelectionInDOMTree = VisibleSelection(); | |
| 243 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree(); | |
| 244 m_cacheIsDirty = false; | |
| 133 } | 245 } |
| 134 | 246 |
| 135 void SelectionEditor::resetLogicalRange() { | 247 void SelectionEditor::resetLogicalRange() { |
| 136 // Non-collapsed ranges are not allowed to start at the end of a line that | 248 // Non-collapsed ranges are not allowed to start at the end of a line that |
| 137 // is wrapped, they start at the beginning of the next line instead | 249 // is wrapped, they start at the beginning of the next line instead |
| 138 if (!m_logicalRange) | 250 if (!m_logicalRange) |
| 139 return; | 251 return; |
| 140 m_logicalRange->dispose(); | 252 m_logicalRange->dispose(); |
| 141 m_logicalRange = nullptr; | 253 m_logicalRange = nullptr; |
| 142 } | 254 } |
| 143 | 255 |
| 144 void SelectionEditor::setLogicalRange(Range* range) { | 256 void SelectionEditor::setLogicalRange(Range* range) { |
| 145 DCHECK_EQ(range->ownerDocument(), document()); | 257 DCHECK_EQ(range->ownerDocument(), document()); |
| 146 DCHECK(!m_logicalRange) << "A logical range should be one."; | 258 DCHECK(!m_logicalRange) << "A logical range should be one."; |
| 147 m_logicalRange = range; | 259 m_logicalRange = range; |
| 148 } | 260 } |
| 149 | 261 |
| 150 Range* SelectionEditor::firstRange() const { | 262 Range* SelectionEditor::firstRange() const { |
| 151 if (m_logicalRange) | 263 if (m_logicalRange) |
| 152 return m_logicalRange->cloneRange(); | 264 return m_logicalRange->cloneRange(); |
| 153 return firstRangeOf(m_selection); | 265 return firstRangeOf(computeVisibleSelectionInDOMTree()); |
| 154 } | 266 } |
| 155 | 267 |
| 156 bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const { | 268 bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const { |
| 157 return frame()->editor().behavior().shouldConsiderSelectionAsDirectional(); | 269 return frame()->editor().behavior().shouldConsiderSelectionAsDirectional(); |
| 158 } | 270 } |
| 159 | 271 |
| 160 void SelectionEditor::updateIfNeeded() { | 272 void SelectionEditor::updateIfNeeded() { |
| 161 DCHECK(m_selection.isValidFor(document())) << m_selection; | 273 // TODO(yosin): We should unify |SelectionEditor::updateIfNeeded()| and |
| 162 DCHECK(m_selectionInFlatTree.isValidFor(document())) << m_selection; | 274 // |updateCachedVisibleSelectionIfNeeded()| |
| 163 m_selection.updateIfNeeded(); | 275 updateCachedVisibleSelectionIfNeeded(); |
| 164 m_selectionInFlatTree.updateIfNeeded(); | 276 } |
| 277 | |
| 278 bool SelectionEditor::needsUpdateVisibleSelection() const { | |
| 279 return m_cacheIsDirty || m_styleVersion != document().styleVersion(); | |
| 280 } | |
| 281 | |
| 282 void SelectionEditor::updateCachedVisibleSelectionIfNeeded() { | |
| 283 // Note: Since we |FrameCaret::updateApperance()| is called from | |
| 284 // |FrameView::performPostLayoutTasks()|, we check lifecycle against | |
| 285 // |AfterPerformLayout| instead of |LayoutClean|. | |
| 286 DCHECK_GE(document().lifecycle().state(), | |
| 287 DocumentLifecycle::AfterPerformLayout); | |
| 288 assertSelectionValid(); | |
| 289 if (!needsUpdateVisibleSelection()) | |
| 290 return; | |
| 291 | |
| 292 m_cachedVisibleSelectionInDOMTree = createVisibleSelection(m_selection); | |
| 293 m_cachedVisibleSelectionInFlatTree = createVisibleSelection( | |
| 294 SelectionInFlatTree::Builder() | |
| 295 .setBaseAndExtent(toPositionInFlatTree(m_selection.base()), | |
| 296 toPositionInFlatTree(m_selection.extent())) | |
| 297 .setAffinity(m_selection.affinity()) | |
| 298 .setHasTrailingWhitespace(m_selection.hasTrailingWhitespace()) | |
| 299 .setGranularity(m_selection.granularity()) | |
| 300 .setIsDirectional(m_selection.isDirectional()) | |
| 301 .build()); | |
| 302 m_styleVersion = document().styleVersion(); | |
| 303 m_cacheIsDirty = false; | |
| 165 } | 304 } |
| 166 | 305 |
| 167 DEFINE_TRACE(SelectionEditor) { | 306 DEFINE_TRACE(SelectionEditor) { |
| 168 visitor->trace(m_document); | |
| 169 visitor->trace(m_frame); | 307 visitor->trace(m_frame); |
| 170 visitor->trace(m_selection); | 308 visitor->trace(m_selection); |
| 171 visitor->trace(m_selectionInFlatTree); | 309 visitor->trace(m_cachedVisibleSelectionInDOMTree); |
| 310 visitor->trace(m_cachedVisibleSelectionInFlatTree); | |
| 172 visitor->trace(m_logicalRange); | 311 visitor->trace(m_logicalRange); |
| 312 SynchronousMutationObserver::trace(visitor); | |
| 173 } | 313 } |
| 174 | 314 |
| 175 } // namespace blink | 315 } // namespace blink |
| OLD | NEW |