Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
|
tkent
2016/01/27 05:12:48
Please use the copyright headers of SelectionEdito
yosin_UTC9
2016/01/27 05:58:22
Done
| |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "core/editing/SelectionAdjuster.h" | |
| 6 | |
| 7 #include "core/editing/EditingUtilities.h" | |
| 8 | |
| 9 namespace blink { | |
| 10 | |
| 11 namespace { | |
| 12 | |
| 13 Node* enclosingShadowHost(Node* node) | |
| 14 { | |
| 15 for (Node* runner = node; runner; runner = ComposedTreeTraversal::parent(*ru nner)) { | |
| 16 if (isShadowHost(runner)) | |
| 17 return runner; | |
| 18 } | |
| 19 return nullptr; | |
| 20 } | |
| 21 | |
| 22 bool isEnclosedBy(const PositionInComposedTree& position, const Node& node) | |
| 23 { | |
| 24 ASSERT(position.isNotNull()); | |
| 25 Node* anchorNode = position.anchorNode(); | |
| 26 if (anchorNode == node) | |
| 27 return !position.isAfterAnchor() && !position.isBeforeAnchor(); | |
| 28 | |
| 29 return ComposedTreeTraversal::isDescendantOf(*anchorNode, node); | |
| 30 } | |
| 31 | |
| 32 bool isSelectionBoundary(const Node& node) | |
| 33 { | |
| 34 return isHTMLTextAreaElement(node) || isHTMLInputElement(node) || isHTMLSele ctElement(node); | |
| 35 } | |
| 36 | |
| 37 Node* enclosingShadowHostForStart(const PositionInComposedTree& position) | |
| 38 { | |
| 39 Node* node = position.nodeAsRangeFirstNode(); | |
| 40 if (!node) | |
| 41 return nullptr; | |
| 42 Node* shadowHost = enclosingShadowHost(node); | |
| 43 if (!shadowHost) | |
| 44 return nullptr; | |
| 45 if (!isEnclosedBy(position, *shadowHost)) | |
| 46 return nullptr; | |
| 47 return isSelectionBoundary(*shadowHost) ? shadowHost : nullptr; | |
| 48 } | |
| 49 | |
| 50 Node* enclosingShadowHostForEnd(const PositionInComposedTree& position) | |
| 51 { | |
| 52 Node* node = position.nodeAsRangeLastNode(); | |
| 53 if (!node) | |
| 54 return nullptr; | |
| 55 Node* shadowHost = enclosingShadowHost(node); | |
| 56 if (!shadowHost) | |
| 57 return nullptr; | |
| 58 if (!isEnclosedBy(position, *shadowHost)) | |
| 59 return nullptr; | |
| 60 return isSelectionBoundary(*shadowHost) ? shadowHost : nullptr; | |
| 61 } | |
| 62 | |
| 63 PositionInComposedTree adjustPositionInComposedTreeForStart(const PositionInComp osedTree& position, Node* shadowHost) | |
| 64 { | |
| 65 if (isEnclosedBy(position, *shadowHost)) { | |
| 66 if (position.isBeforeChildren()) | |
| 67 return PositionInComposedTree::beforeNode(shadowHost); | |
| 68 return PositionInComposedTree::afterNode(shadowHost); | |
| 69 } | |
| 70 | |
| 71 // We use |firstChild|'s after instead of beforeAllChildren for backward | |
| 72 // compatibility. The positions are same but the anchors would be different, | |
| 73 // and selection painting uses anchor nodes. | |
| 74 if (Node* firstChild = ComposedTreeTraversal::firstChild(*shadowHost)) | |
| 75 return PositionInComposedTree::beforeNode(firstChild); | |
| 76 return PositionInComposedTree(); | |
| 77 } | |
| 78 | |
| 79 Position adjustPositionForEnd(const Position& currentPosition, Node* startContai nerNode) | |
| 80 { | |
| 81 TreeScope& treeScope = startContainerNode->treeScope(); | |
| 82 | |
| 83 ASSERT(currentPosition.computeContainerNode()->treeScope() != treeScope); | |
| 84 | |
| 85 if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.computeCo ntainerNode())) { | |
| 86 if (ancestor->contains(startContainerNode)) | |
| 87 return positionAfterNode(ancestor); | |
| 88 return positionBeforeNode(ancestor); | |
| 89 } | |
| 90 | |
| 91 if (Node* lastChild = treeScope.rootNode().lastChild()) | |
| 92 return positionAfterNode(lastChild); | |
| 93 | |
| 94 return Position(); | |
| 95 } | |
| 96 | |
| 97 PositionInComposedTree adjustPositionInComposedTreeForEnd(const PositionInCompos edTree& position, Node* shadowHost) | |
| 98 { | |
| 99 if (isEnclosedBy(position, *shadowHost)) { | |
| 100 if (position.isAfterChildren()) | |
| 101 return PositionInComposedTree::afterNode(shadowHost); | |
| 102 return PositionInComposedTree::beforeNode(shadowHost); | |
| 103 } | |
| 104 | |
| 105 // We use |lastChild|'s after instead of afterAllChildren for backward | |
| 106 // compatibility. The positions are same but the anchors would be different, | |
| 107 // and selection painting uses anchor nodes. | |
| 108 if (Node* lastChild = ComposedTreeTraversal::lastChild(*shadowHost)) | |
| 109 return PositionInComposedTree::afterNode(lastChild); | |
| 110 return PositionInComposedTree(); | |
| 111 } | |
| 112 | |
| 113 Position adjustPositionForStart(const Position& currentPosition, Node* endContai nerNode) | |
| 114 { | |
| 115 TreeScope& treeScope = endContainerNode->treeScope(); | |
| 116 | |
| 117 ASSERT(currentPosition.computeContainerNode()->treeScope() != treeScope); | |
| 118 | |
| 119 if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.computeCo ntainerNode())) { | |
| 120 if (ancestor->contains(endContainerNode)) | |
| 121 return positionBeforeNode(ancestor); | |
| 122 return positionAfterNode(ancestor); | |
| 123 } | |
| 124 | |
| 125 if (Node* firstChild = treeScope.rootNode().firstChild()) | |
| 126 return positionBeforeNode(firstChild); | |
| 127 | |
| 128 return Position(); | |
| 129 } | |
| 130 | |
| 131 } // namespace | |
| 132 | |
| 133 // Updates |selectionInComposedTree| to match with |selection|. | |
| 134 void SelectionAdjuster::adjustSelectionInComposedTree(VisibleSelectionInComposed Tree* selectionInComposedTree, const VisibleSelection& selection) | |
| 135 { | |
| 136 if (selection.isNone()) { | |
| 137 *selectionInComposedTree = VisibleSelectionInComposedTree(); | |
| 138 return; | |
| 139 } | |
| 140 | |
| 141 const PositionInComposedTree& base = toPositionInComposedTree(selection.base ()); | |
| 142 const PositionInComposedTree& extent = toPositionInComposedTree(selection.ex tent()); | |
| 143 const PositionInComposedTree& position1 = toPositionInComposedTree(selection .start()); | |
| 144 const PositionInComposedTree& position2 = toPositionInComposedTree(selection .end()); | |
| 145 position1.anchorNode()->updateDistribution(); | |
| 146 position2.anchorNode()->updateDistribution(); | |
| 147 selectionInComposedTree->m_base = base; | |
| 148 selectionInComposedTree->m_extent = extent; | |
| 149 selectionInComposedTree->m_affinity = selection.m_affinity; | |
| 150 selectionInComposedTree->m_isDirectional = selection.m_isDirectional; | |
| 151 selectionInComposedTree->m_baseIsFirst = base.isNull() || base.compareTo(ext ent) <= 0; | |
| 152 if (position1.compareTo(position2) <= 0) { | |
| 153 selectionInComposedTree->m_start = position1; | |
| 154 selectionInComposedTree->m_end = position2; | |
| 155 } else { | |
| 156 selectionInComposedTree->m_start = position2; | |
| 157 selectionInComposedTree->m_end = position1; | |
| 158 } | |
| 159 selectionInComposedTree->updateSelectionType(); | |
| 160 } | |
| 161 | |
| 162 static bool isCrossingShadowBoundaries(const VisibleSelectionInComposedTree& sel ection) | |
| 163 { | |
| 164 if (!selection.isRange()) | |
| 165 return false; | |
| 166 TreeScope& treeScope = selection.base().anchorNode()->treeScope(); | |
| 167 return selection.extent().anchorNode()->treeScope() != treeScope | |
| 168 || selection.start().anchorNode()->treeScope() != treeScope | |
| 169 || selection.end().anchorNode()->treeScope() != treeScope; | |
| 170 } | |
| 171 | |
| 172 void SelectionAdjuster::adjustSelectionInDOMTree(VisibleSelection* selection, co nst VisibleSelectionInComposedTree& selectionInComposedTree) | |
| 173 { | |
| 174 if (selectionInComposedTree.isNone()) { | |
| 175 *selection = VisibleSelection(); | |
| 176 return; | |
| 177 } | |
| 178 | |
| 179 const Position& base = toPositionInDOMTree(selectionInComposedTree.base()); | |
| 180 const Position& extent = toPositionInDOMTree(selectionInComposedTree.extent( )); | |
| 181 | |
| 182 if (isCrossingShadowBoundaries(selectionInComposedTree)) { | |
| 183 *selection = VisibleSelection(base, extent); | |
| 184 return; | |
| 185 } | |
| 186 | |
| 187 const Position& position1 = toPositionInDOMTree(selectionInComposedTree.star t()); | |
| 188 const Position& position2 = toPositionInDOMTree(selectionInComposedTree.end( )); | |
| 189 selection->m_base = base; | |
| 190 selection->m_extent = extent; | |
| 191 selection->m_affinity = selectionInComposedTree.m_affinity; | |
| 192 selection->m_isDirectional = selectionInComposedTree.m_isDirectional; | |
| 193 selection->m_baseIsFirst = base.isNull() || base.compareTo(extent) <= 0; | |
| 194 if (position1.compareTo(position2) <= 0) { | |
| 195 selection->m_start = position1; | |
| 196 selection->m_end = position2; | |
| 197 } else { | |
| 198 selection->m_start = position2; | |
| 199 selection->m_end = position1; | |
| 200 } | |
| 201 selection->updateSelectionType(); | |
| 202 } | |
| 203 | |
| 204 void SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(VisibleSe lection* selection) | |
| 205 { | |
| 206 // Note: |m_selectionType| isn't computed yet. | |
| 207 ASSERT(selection->base().isNotNull()); | |
| 208 ASSERT(selection->extent().isNotNull()); | |
| 209 ASSERT(selection->start().isNotNull()); | |
| 210 ASSERT(selection->end().isNotNull()); | |
| 211 | |
| 212 // TODO(hajimehoshi): Checking treeScope is wrong when a node is | |
| 213 // distributed, but we leave it as it is for backward compatibility. | |
| 214 if (selection->start().anchorNode()->treeScope() == selection->end().anchorN ode()->treeScope()) | |
| 215 return; | |
| 216 | |
| 217 if (selection->isBaseFirst()) { | |
| 218 const Position& newEnd = adjustPositionForEnd(selection->end(), selectio n->start().computeContainerNode()); | |
| 219 selection->m_extent = newEnd; | |
| 220 selection->m_end = newEnd; | |
| 221 return; | |
| 222 } | |
| 223 | |
| 224 const Position& newStart = adjustPositionForStart(selection->start(), select ion->end().computeContainerNode()); | |
| 225 selection->m_extent = newStart; | |
| 226 selection->m_start = newStart; | |
| 227 } | |
| 228 | |
| 229 // This function is called twice. The first is called when |m_start| and |m_end| | |
| 230 // or |m_extent| are same, and the second when |m_start| and |m_end| are changed | |
| 231 // after downstream/upstream. | |
| 232 void SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(VisibleSe lectionInComposedTree* selection) | |
| 233 { | |
| 234 Node* const shadowHostStart = enclosingShadowHostForStart(selection->start() ); | |
| 235 Node* const shadowHostEnd = enclosingShadowHostForEnd(selection->end()); | |
| 236 if (shadowHostStart == shadowHostEnd) | |
| 237 return; | |
| 238 | |
| 239 if (selection->isBaseFirst()) { | |
| 240 Node* const shadowHost = shadowHostStart ? shadowHostStart : shadowHostE nd; | |
| 241 const PositionInComposedTree& newEnd = adjustPositionInComposedTreeForEn d(selection->end(), shadowHost); | |
| 242 selection->m_extent = newEnd; | |
| 243 selection->m_end = newEnd; | |
| 244 return; | |
| 245 } | |
| 246 Node* const shadowHost = shadowHostEnd ? shadowHostEnd : shadowHostStart; | |
| 247 const PositionInComposedTree& newStart = adjustPositionInComposedTreeForStar t(selection->start(), shadowHost); | |
| 248 selection->m_extent = newStart; | |
| 249 selection->m_start = newStart; | |
| 250 } | |
| 251 | |
| 252 } // namespace blink | |
| OLD | NEW |