| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserv
ed. | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserv
ed. |
| 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 15 matching lines...) Expand all Loading... |
| 26 #include "core/editing/VisibleUnits.h" | 26 #include "core/editing/VisibleUnits.h" |
| 27 | 27 |
| 28 #include "core/HTMLNames.h" | 28 #include "core/HTMLNames.h" |
| 29 #include "core/dom/Document.h" | 29 #include "core/dom/Document.h" |
| 30 #include "core/dom/Element.h" | 30 #include "core/dom/Element.h" |
| 31 #include "core/dom/FirstLetterPseudoElement.h" | 31 #include "core/dom/FirstLetterPseudoElement.h" |
| 32 #include "core/dom/NodeTraversal.h" | 32 #include "core/dom/NodeTraversal.h" |
| 33 #include "core/dom/Text.h" | 33 #include "core/dom/Text.h" |
| 34 #include "core/editing/EditingUtilities.h" | 34 #include "core/editing/EditingUtilities.h" |
| 35 #include "core/editing/FrameSelection.h" | 35 #include "core/editing/FrameSelection.h" |
| 36 #include "core/editing/LayoutPosition.h" |
| 36 #include "core/editing/Position.h" | 37 #include "core/editing/Position.h" |
| 37 #include "core/editing/PositionIterator.h" | 38 #include "core/editing/PositionIterator.h" |
| 38 #include "core/editing/RenderedPosition.h" | 39 #include "core/editing/RenderedPosition.h" |
| 39 #include "core/editing/TextAffinity.h" | 40 #include "core/editing/TextAffinity.h" |
| 40 #include "core/editing/VisiblePosition.h" | 41 #include "core/editing/VisiblePosition.h" |
| 41 #include "core/editing/iterators/BackwardsCharacterIterator.h" | 42 #include "core/editing/iterators/BackwardsCharacterIterator.h" |
| 42 #include "core/editing/iterators/BackwardsTextBuffer.h" | 43 #include "core/editing/iterators/BackwardsTextBuffer.h" |
| 43 #include "core/editing/iterators/CharacterIterator.h" | 44 #include "core/editing/iterators/CharacterIterator.h" |
| 44 #include "core/editing/iterators/ForwardsTextBuffer.h" | 45 #include "core/editing/iterators/ForwardsTextBuffer.h" |
| 45 #include "core/editing/iterators/SimplifiedBackwardsTextIterator.h" | 46 #include "core/editing/iterators/SimplifiedBackwardsTextIterator.h" |
| (...skipping 21 matching lines...) Expand all Loading... |
| 67 #include "platform/heap/Handle.h" | 68 #include "platform/heap/Handle.h" |
| 68 #include "platform/text/TextBoundaries.h" | 69 #include "platform/text/TextBoundaries.h" |
| 69 | 70 |
| 70 namespace blink { | 71 namespace blink { |
| 71 | 72 |
| 72 template <typename PositionType> | 73 template <typename PositionType> |
| 73 static PositionType canonicalizeCandidate(const PositionType& candidate) | 74 static PositionType canonicalizeCandidate(const PositionType& candidate) |
| 74 { | 75 { |
| 75 if (candidate.isNull()) | 76 if (candidate.isNull()) |
| 76 return PositionType(); | 77 return PositionType(); |
| 77 DCHECK(isVisuallyEquivalentCandidate(candidate)); | 78 ASSERT(isVisuallyEquivalentCandidate(candidate)); |
| 78 PositionType upstream = mostBackwardCaretPosition(candidate); | 79 PositionType upstream = mostBackwardCaretPosition(candidate); |
| 79 if (isVisuallyEquivalentCandidate(upstream)) | 80 if (isVisuallyEquivalentCandidate(upstream)) |
| 80 return upstream; | 81 return upstream; |
| 81 return candidate; | 82 return candidate; |
| 82 } | 83 } |
| 83 | 84 |
| 84 template <typename PositionType> | 85 template <typename PositionType> |
| 85 static PositionType canonicalPosition(const PositionType& passedPosition) | 86 static PositionType canonicalPosition(const PositionType& passedPosition) |
| 86 { | 87 { |
| 87 // Sometimes updating selection positions can be extremely expensive and | 88 // Sometimes updating selection positions can be extremely expensive and |
| 88 // occur frequently. Often calling preventDefault on mousedown events can | 89 // occur frequently. Often calling preventDefault on mousedown events can |
| 89 // avoid doing unnecessary text selection work. http://crbug.com/472258. | 90 // avoid doing unnecessary text selection work. http://crbug.com/472258. |
| 90 TRACE_EVENT0("input", "VisibleUnits::canonicalPosition"); | 91 TRACE_EVENT0("input", "VisibleUnits::canonicalPosition"); |
| 91 | 92 |
| 92 // The updateLayout call below can do so much that even the position passed | 93 // The updateLayout call below can do so much that even the position passed |
| 93 // in to us might get changed as a side effect. Specifically, there are code | 94 // in to us might get changed as a side effect. Specifically, there are code |
| 94 // paths that pass selection endpoints, and updateLayout can change the | 95 // paths that pass selection endpoints, and updateLayout can change the |
| 95 // selection. | 96 // selection. |
| 96 PositionType position = passedPosition; | 97 PositionType position = passedPosition; |
| 97 | 98 |
| 98 // FIXME (9535): Canonicalizing to the leftmost candidate means that if | 99 // FIXME (9535): Canonicalizing to the leftmost candidate means that if |
| 99 // we're at a line wrap, we will ask layoutObjects to paint downstream | 100 // we're at a line wrap, we will ask layoutObjects to paint downstream |
| 100 // carets for other layoutObjects. To fix this, we need to either a) add | 101 // carets for other layoutObjects. To fix this, we need to either a) add |
| 101 // code to all paintCarets to pass the responsibility off to the appropriate | 102 // code to all paintCarets to pass the responsibility off to the appropriate |
| 102 // layoutObject for VisiblePosition's like these, or b) canonicalize to the | 103 // layoutObject for VisiblePosition's like these, or b) canonicalize to the |
| 103 // rightmost candidate unless the affinity is upstream. | 104 // rightmost candidate unless the affinity is upstream. |
| 104 if (position.isNull()) | 105 if (position.isNull()) |
| 105 return PositionType(); | 106 return PositionType(); |
| 106 | 107 |
| 107 DCHECK(position.document()); | 108 ASSERT(position.document()); |
| 108 position.document()->updateLayoutIgnorePendingStylesheets(); | 109 position.document()->updateLayoutIgnorePendingStylesheets(); |
| 109 | 110 |
| 110 Node* node = position.computeContainerNode(); | 111 Node* node = position.computeContainerNode(); |
| 111 | 112 |
| 112 PositionType candidate = mostBackwardCaretPosition(position); | 113 PositionType candidate = mostBackwardCaretPosition(position); |
| 113 if (isVisuallyEquivalentCandidate(candidate)) | 114 if (isVisuallyEquivalentCandidate(candidate)) |
| 114 return candidate; | 115 return candidate; |
| 115 candidate = mostForwardCaretPosition(position); | 116 candidate = mostForwardCaretPosition(position); |
| 116 if (isVisuallyEquivalentCandidate(candidate)) | 117 if (isVisuallyEquivalentCandidate(candidate)) |
| 117 return candidate; | 118 return candidate; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 152 // The new position should be in the same block flow element. Favor that. | 153 // The new position should be in the same block flow element. Favor that. |
| 153 Element* originalBlock = node ? enclosingBlockFlowElement(*node) : 0; | 154 Element* originalBlock = node ? enclosingBlockFlowElement(*node) : 0; |
| 154 bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) &
& nextNode != originalBlock; | 155 bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) &
& nextNode != originalBlock; |
| 155 bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) &
& prevNode != originalBlock; | 156 bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) &
& prevNode != originalBlock; |
| 156 if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock) | 157 if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock) |
| 157 return prev; | 158 return prev; |
| 158 | 159 |
| 159 return next; | 160 return next; |
| 160 } | 161 } |
| 161 | 162 |
| 163 template <typename Strategy> |
| 164 struct BackwardMovement { |
| 165 using PositionType = PositionTemplate<Strategy>; |
| 166 |
| 167 static LayoutPosition advance(const LayoutPosition& position) |
| 168 { |
| 169 return previousPositionOf(position); |
| 170 } |
| 171 |
| 172 // TODO(yosin): |previousPositionOf()| should be implemented as |
| 173 // |BackwardMovement::advance()|. |
| 174 static PositionTemplate<Strategy> advance(const PositionTemplate<Strategy>&
position) |
| 175 { |
| 176 ASSERT(position.isNotNull()); |
| 177 Node* const anchorNode = position.anchorNode(); |
| 178 switch (position.anchorType()) { |
| 179 case PositionAnchorType::AfterAnchor: |
| 180 if (anchorNode->isTextNode()) |
| 181 return advance(PositionTemplate<Strategy>(anchorNode, toText(anc
horNode)->length())); |
| 182 // fallthrough |
| 183 case PositionAnchorType::AfterChildren: |
| 184 if (Node* lastChild = Strategy::lastChild(*anchorNode)) |
| 185 return PositionTemplate<Strategy>::afterNode(lastChild); |
| 186 return PositionTemplate<Strategy>::beforeNode(anchorNode); |
| 187 case PositionAnchorType::BeforeAnchor: { |
| 188 Node* const previous = Strategy::previous(*anchorNode); |
| 189 if (!previous || previous->isDocumentNode()) |
| 190 return PositionTemplate<Strategy>(); |
| 191 if (previous == Strategy::parent(*anchorNode)) |
| 192 return PositionTemplate<Strategy>::beforeNode(previous); |
| 193 return PositionTemplate<Strategy>::afterNode(previous); |
| 194 } |
| 195 case PositionAnchorType::BeforeChildren: |
| 196 return PositionTemplate<Strategy>::beforeNode(anchorNode); |
| 197 case PositionAnchorType::OffsetInAnchor: { |
| 198 const int offsetInAnchor = position.offsetInContainerNode(); |
| 199 if (anchorNode->isTextNode()) { |
| 200 if (offsetInAnchor > 0) { |
| 201 // TODO(yosin): Should we check Unicode surrogate pair? |
| 202 return PositionTemplate<Strategy>(anchorNode, offsetInAnchor
- 1); |
| 203 } |
| 204 return advance(PositionTemplate<Strategy>::beforeNode(anchorNode
)); |
| 205 } |
| 206 if (Node* child = Strategy::childAt(*anchorNode, offsetInAnchor)) |
| 207 return advance(PositionTemplate<Strategy>::beforeNode(child)); |
| 208 if (Node* lastChild = Strategy::lastChild(*anchorNode)) |
| 209 return advance(PositionTemplate<Strategy>::afterNode(lastChild))
; |
| 210 if (anchorNode->isDocumentNode()) |
| 211 return PositionTemplate<Strategy>(); |
| 212 return PositionTemplate<Strategy>::beforeNode(anchorNode); |
| 213 } |
| 214 } |
| 215 NOTREACHED() << position.anchorType(); |
| 216 return PositionTemplate<Strategy>(); |
| 217 } |
| 218 }; |
| 219 |
| 220 template <typename Strategy> |
| 221 struct ForwardMovement { |
| 222 using PositionType = PositionTemplate<Strategy>; |
| 223 |
| 224 static LayoutPosition advance(const LayoutPosition& position) |
| 225 { |
| 226 return nextPositionOf(position); |
| 227 } |
| 228 |
| 229 // TODO(yosin): |nextPositionOf()| should be implemented as |
| 230 // |ForwardMovement::advance()|. |
| 231 static PositionTemplate<Strategy> advance(const PositionTemplate<Strategy>&
position) |
| 232 { |
| 233 ASSERT(position.isNotNull()); |
| 234 Node* const anchorNode = position.anchorNode(); |
| 235 switch (position.anchorType()) { |
| 236 case PositionAnchorType::AfterAnchor: |
| 237 case PositionAnchorType::AfterChildren: |
| 238 if (Node* nextNode = Strategy::nextSkippingChildren(*anchorNode)) |
| 239 return PositionTemplate<Strategy>::beforeNode(nextNode); |
| 240 return PositionTemplate<Strategy>(); |
| 241 case PositionAnchorType::BeforeAnchor: |
| 242 if (anchorNode->isTextNode()) |
| 243 return PositionTemplate<Strategy>(anchorNode, 0); |
| 244 // fallthrough |
| 245 case PositionAnchorType::BeforeChildren: |
| 246 if (Node* nextNode = Strategy::next(*anchorNode)) |
| 247 return PositionTemplate<Strategy>::beforeNode(nextNode); |
| 248 return PositionTemplate<Strategy>(); |
| 249 case PositionAnchorType::OffsetInAnchor: { |
| 250 const int offsetInAnchor = position.offsetInContainerNode(); |
| 251 if (anchorNode->isTextNode()) { |
| 252 if (offsetInAnchor < static_cast<int>(toText(anchorNode)->length
())) { |
| 253 // TODO(yosin): Should we check Unicode surrogate pair? |
| 254 return PositionTemplate<Strategy>(anchorNode, offsetInAnchor
+ 1); |
| 255 } |
| 256 return advance(PositionTemplate<Strategy>::afterNode(anchorNode)
); |
| 257 } |
| 258 if (Node* child = Strategy::childAt(*anchorNode, offsetInAnchor)) { |
| 259 if (child->isTextNode()) |
| 260 return advance(PositionTemplate<Strategy>(child, 0)); |
| 261 return advance(PositionTemplate<Strategy>::beforeNode(child)); |
| 262 } |
| 263 if (Node* lastChild = Strategy::lastChild(*anchorNode)) |
| 264 return advance(PositionTemplate<Strategy>::afterNode(lastChild))
; |
| 265 if (anchorNode->isDocumentNode()) |
| 266 return PositionTemplate<Strategy>(); |
| 267 return PositionTemplate<Strategy>::afterNode(anchorNode); |
| 268 } |
| 269 } |
| 270 NOTREACHED() << position.anchorType(); |
| 271 return PositionTemplate<Strategy>(); |
| 272 } |
| 273 }; |
| 274 |
| 275 // |Direction| is specified tree traversal direction. There are |
| 276 // - |BackwardMovement<Strategy>| |
| 277 // - |ForwardMovement<Strategy>| |
| 278 // for backward/forward with DOM tree/flat tree traversal. |
| 279 // |
| 280 // |Rule| corresponds to |enum EditingBoundaryCrossingRule|. |
| 281 // There are: |
| 282 // - |CanCrossEditingBoundaryRule|, |
| 283 // - |CannotCrossEditingBoundaryRule| |
| 284 // - |CanSkipOverEditingBoundary| |
| 285 template <typename Direction, typename Rule> |
| 286 class CaretPositionFinder final { |
| 287 STATIC_ONLY(CaretPositionFinder); |
| 288 |
| 289 public: |
| 290 using PositionType = typename Direction::PositionType; |
| 291 |
| 292 static LayoutPosition find(const PositionType&); |
| 293 |
| 294 private: |
| 295 static LayoutPosition advance(const LayoutPosition& position) |
| 296 { |
| 297 return Direction::advance(position); |
| 298 } |
| 299 |
| 300 static PositionType advance(const PositionType& position) |
| 301 { |
| 302 return Direction::advance(position); |
| 303 } |
| 304 static LayoutPosition findStart(const PositionType&); |
| 305 static LayoutPosition locate(const LayoutPosition&); |
| 306 }; |
| 307 |
| 308 template <typename Direction, typename Rule> |
| 309 LayoutPosition CaretPositionFinder<Direction, Rule>::find(const PositionType& po
sition) |
| 310 { |
| 311 DCHECK(position.isNotNull()); |
| 312 const LayoutPosition& start = findStart(position); |
| 313 if (start.isNull()) |
| 314 return LayoutPosition(); |
| 315 LayoutPosition candidate = locate(start); |
| 316 if (candidate.isNull()) |
| 317 return LayoutPosition(); |
| 318 Rule rule(candidate); |
| 319 for (LayoutPosition runner = advance(candidate); runner.isNotNull(); runner
= advance(runner)) { |
| 320 if (!runner.canBePosition()) |
| 321 continue; |
| 322 if (!rule.satisfies(runner)) |
| 323 return candidate; |
| 324 if (!candidate.isCaretEquivalent(runner)) |
| 325 return candidate; |
| 326 candidate = runner; |
| 327 } |
| 328 return candidate; |
| 329 } |
| 330 |
| 331 template <typename Direction, typename Rule> |
| 332 LayoutPosition CaretPositionFinder<Direction, Rule>::findStart(const PositionTyp
e& position) |
| 333 { |
| 334 Rule rule(position); |
| 335 for (PositionType runner = position; runner.isNotNull(); runner = advance(ru
nner)) { |
| 336 const LayoutPosition& layoutPosition = LayoutPosition::create(runner); |
| 337 if (!layoutPosition.canBePosition()) |
| 338 continue; |
| 339 if (!rule.satisfies(runner)) |
| 340 return LayoutPosition(); |
| 341 if (layoutPosition.isNotNull()) |
| 342 return layoutPosition; |
| 343 } |
| 344 return LayoutPosition(); |
| 345 } |
| 346 |
| 347 template <typename Direction, typename Rule> |
| 348 LayoutPosition CaretPositionFinder<Direction, Rule>::locate(const LayoutPosition
& position) |
| 349 { |
| 350 DCHECK(position.isNotNull()); |
| 351 Rule rule(position); |
| 352 for (LayoutPosition runner = position; runner.isNotNull(); runner = advance(
runner)) { |
| 353 if (!runner.canBePosition()) |
| 354 continue; |
| 355 if (!rule.satisfies(runner)) |
| 356 return LayoutPosition(); |
| 357 if (runner.canPlaceCaret()) |
| 358 return runner; |
| 359 } |
| 360 return LayoutPosition(); |
| 361 } |
| 362 |
| 363 template <typename Strategy> |
| 364 class CannotCrossEditingBoundaryRule final { |
| 365 WTF_MAKE_NONCOPYABLE(CannotCrossEditingBoundaryRule); |
| 366 STACK_ALLOCATED(); |
| 367 |
| 368 public: |
| 369 explicit CannotCrossEditingBoundaryRule(const LayoutPosition& position) |
| 370 : CannotCrossEditingBoundaryRule(position.toPosition(PositionTemplate<St
rategy>())) |
| 371 { |
| 372 } |
| 373 |
| 374 explicit CannotCrossEditingBoundaryRule(const PositionTemplate<Strategy>& po
sition) |
| 375 : m_rootEditableElement(rootEditableElementOf(position)) |
| 376 { |
| 377 } |
| 378 |
| 379 bool satisfies(const PositionTemplate<Strategy>& position) const |
| 380 { |
| 381 return m_rootEditableElement == rootEditableElementOf(position); |
| 382 } |
| 383 |
| 384 bool satisfies(const LayoutPosition& position) const |
| 385 { |
| 386 return satisfies(position.toPosition(PositionTemplate<Strategy>())); |
| 387 } |
| 388 |
| 389 private: |
| 390 Element* const m_rootEditableElement; |
| 391 }; |
| 392 |
| 393 template <typename Strategy> |
| 394 static PositionTemplate<Strategy> caretPositionOfTemplate(const PositionTemplate
<Strategy>& position) |
| 395 { |
| 396 if (position.isNull()) |
| 397 return PositionTemplate<Strategy>(); |
| 398 position.document()->updateLayoutIgnorePendingStylesheets(); |
| 399 const auto& backwardCandidate = CaretPositionFinder<BackwardMovement<Strateg
y>, CannotCrossEditingBoundaryRule<Strategy>>::find(position); |
| 400 if (backwardCandidate.isNotNull()) |
| 401 return backwardCandidate.toPosition(PositionTemplate<Strategy>()); |
| 402 const auto& forwardCandidate = CaretPositionFinder<ForwardMovement<Strategy>
, CannotCrossEditingBoundaryRule<Strategy>>::find(position); |
| 403 // |forwardCandidate| can be null if |position| in uneditable and parent |
| 404 // is editable, e.g. |
| 405 // <div contentEditable><span contentEditable=false>|</span></div> |
| 406 return forwardCandidate.toPosition(PositionTemplate<Strategy>()); |
| 407 } |
| 408 |
| 409 Position caretPositionOf(const Position& position) |
| 410 { |
| 411 return caretPositionOfTemplate<EditingStrategy>(position); |
| 412 } |
| 413 |
| 414 PositionInFlatTree caretPositionOf(const PositionInFlatTree& position) |
| 415 { |
| 416 return caretPositionOfTemplate<EditingInFlatTreeStrategy>(position); |
| 417 } |
| 418 |
| 162 Position canonicalPositionOf(const Position& position) | 419 Position canonicalPositionOf(const Position& position) |
| 163 { | 420 { |
| 164 return canonicalPosition(position); | 421 #if 0 |
| 422 const auto& expected = canonicalPosition(position); |
| 423 #endif |
| 424 const auto& actual = caretPositionOf(position); |
| 425 return actual; |
| 165 } | 426 } |
| 166 | 427 |
| 167 PositionInFlatTree canonicalPositionOf(const PositionInFlatTree& position) | 428 PositionInFlatTree canonicalPositionOf(const PositionInFlatTree& position) |
| 168 { | 429 { |
| 169 return canonicalPosition(position); | 430 #if 0 |
| 170 } | 431 const auto& expected = canonicalPosition(position); |
| 171 | 432 #endif |
| 172 template <typename Strategy> | 433 const auto& actual = caretPositionOf(position); |
| 434 return actual; |
| 435 } |
| 436 |
| 437 template <typename Strategy> |
| 173 static PositionWithAffinityTemplate<Strategy> honorEditingBoundaryAtOrBefore(con
st PositionWithAffinityTemplate<Strategy>& pos, const PositionTemplate<Strategy>
& anchor) | 438 static PositionWithAffinityTemplate<Strategy> honorEditingBoundaryAtOrBefore(con
st PositionWithAffinityTemplate<Strategy>& pos, const PositionTemplate<Strategy>
& anchor) |
| 174 { | 439 { |
| 175 if (pos.isNull()) | 440 if (pos.isNull()) |
| 176 return pos; | 441 return pos; |
| 177 | 442 |
| 178 ContainerNode* highestRoot = highestEditableRoot(anchor); | 443 ContainerNode* highestRoot = highestEditableRoot(anchor); |
| 179 | 444 |
| 180 // Return empty position if |pos| is not somewhere inside the editable | 445 // Return empty position if |pos| is not somewhere inside the editable |
| 181 // region containing this position | 446 // region containing this position |
| 182 if (highestRoot && !pos.position().anchorNode()->isDescendantOf(highestRoot)
) | 447 if (highestRoot && !pos.position().anchorNode()->isDescendantOf(highestRoot)
) |
| (...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 719 next = 0; | 984 next = 0; |
| 720 } | 985 } |
| 721 it.advance(); | 986 it.advance(); |
| 722 } | 987 } |
| 723 if (needMoreContext) { | 988 if (needMoreContext) { |
| 724 // The last search returned the beginning of the buffer and asked for | 989 // The last search returned the beginning of the buffer and asked for |
| 725 // more context, but there is no earlier text. Force a search with | 990 // more context, but there is no earlier text. Force a search with |
| 726 // what's available. | 991 // what's available. |
| 727 // TODO(xiaochengh): Do we have to search the whole string? | 992 // TODO(xiaochengh): Do we have to search the whole string? |
| 728 next = searchFunction(string.data(), string.size(), string.size() - suff
ixLength, DontHaveMoreContext, needMoreContext); | 993 next = searchFunction(string.data(), string.size(), string.size() - suff
ixLength, DontHaveMoreContext, needMoreContext); |
| 729 DCHECK(!needMoreContext); | 994 ASSERT(!needMoreContext); |
| 730 } | 995 } |
| 731 | 996 |
| 732 if (!next) | 997 if (!next) |
| 733 return createVisiblePosition(it.atEnd() ? it.startPosition() : pos); | 998 return createVisiblePosition(it.atEnd() ? it.startPosition() : pos); |
| 734 | 999 |
| 735 Node* node = it.startContainer(); | 1000 Node* node = it.startContainer(); |
| 736 int boundaryOffset = remainingLength + next; | 1001 int boundaryOffset = remainingLength + next; |
| 737 if (node->isTextNode() && boundaryOffset <= node->maxCharacterOffset()) { | 1002 if (node->isTextNode() && boundaryOffset <= node->maxCharacterOffset()) { |
| 738 // The next variable contains a usable index into a text node | 1003 // The next variable contains a usable index into a text node |
| 739 return createVisiblePosition(PositionTemplate<Strategy>(node, boundaryOf
fset)); | 1004 return createVisiblePosition(PositionTemplate<Strategy>(node, boundaryOf
fset)); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 813 next = string.size(); | 1078 next = string.size(); |
| 814 } | 1079 } |
| 815 it.advance(); | 1080 it.advance(); |
| 816 } | 1081 } |
| 817 if (needMoreContext) { | 1082 if (needMoreContext) { |
| 818 // The last search returned the end of the buffer and asked for more | 1083 // The last search returned the end of the buffer and asked for more |
| 819 // context, but there is no further text. Force a search with what's | 1084 // context, but there is no further text. Force a search with what's |
| 820 // available. | 1085 // available. |
| 821 // TODO(xiaochengh): Do we still have to search the whole string? | 1086 // TODO(xiaochengh): Do we still have to search the whole string? |
| 822 next = searchFunction(string.data(), string.size(), prefixLength, DontHa
veMoreContext, needMoreContext); | 1087 next = searchFunction(string.data(), string.size(), prefixLength, DontHa
veMoreContext, needMoreContext); |
| 823 DCHECK(!needMoreContext); | 1088 ASSERT(!needMoreContext); |
| 824 } | 1089 } |
| 825 | 1090 |
| 826 if (it.atEnd() && next == string.size()) { | 1091 if (it.atEnd() && next == string.size()) { |
| 827 pos = it.startPositionInCurrentContainer(); | 1092 pos = it.startPositionInCurrentContainer(); |
| 828 } else if (next != invalidOffset && next != prefixLength) { | 1093 } else if (next != invalidOffset && next != prefixLength) { |
| 829 // TODO(dglazkov): The use of updateLayoutIgnorePendingStylesheets needs
to be audited. | 1094 // TODO(dglazkov): The use of updateLayoutIgnorePendingStylesheets needs
to be audited. |
| 830 // see http://crbug.com/590369 for more details. | 1095 // see http://crbug.com/590369 for more details. |
| 831 searchStart.document()->updateLayoutIgnorePendingStylesheets(); | 1096 searchStart.document()->updateLayoutIgnorePendingStylesheets(); |
| 832 // Use the character iterator to translate the next value into a DOM | 1097 // Use the character iterator to translate the next value into a DOM |
| 833 // position. | 1098 // position. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 849 | 1114 |
| 850 // generate VisiblePosition, use TextAffinity::Upstream affinity if possible | 1115 // generate VisiblePosition, use TextAffinity::Upstream affinity if possible |
| 851 return createVisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); | 1116 return createVisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); |
| 852 } | 1117 } |
| 853 | 1118 |
| 854 // --------- | 1119 // --------- |
| 855 | 1120 |
| 856 static unsigned startWordBoundary(const UChar* characters, unsigned length, unsi
gned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMor
eContext) | 1121 static unsigned startWordBoundary(const UChar* characters, unsigned length, unsi
gned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMor
eContext) |
| 857 { | 1122 { |
| 858 TRACE_EVENT0("blink", "startWordBoundary"); | 1123 TRACE_EVENT0("blink", "startWordBoundary"); |
| 859 DCHECK(offset); | 1124 ASSERT(offset); |
| 860 if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset
)) { | 1125 if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset
)) { |
| 861 needMoreContext = true; | 1126 needMoreContext = true; |
| 862 return 0; | 1127 return 0; |
| 863 } | 1128 } |
| 864 needMoreContext = false; | 1129 needMoreContext = false; |
| 865 int start, end; | 1130 int start, end; |
| 866 U16_BACK_1(characters, 0, offset); | 1131 U16_BACK_1(characters, 0, offset); |
| 867 findWordBoundary(characters, length, offset, &start, &end); | 1132 findWordBoundary(characters, length, offset, &start, &end); |
| 868 return start; | 1133 return start; |
| 869 } | 1134 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 891 return startOfWordAlgorithm<EditingStrategy>(c, side); | 1156 return startOfWordAlgorithm<EditingStrategy>(c, side); |
| 892 } | 1157 } |
| 893 | 1158 |
| 894 VisiblePositionInFlatTree startOfWord(const VisiblePositionInFlatTree& c, EWordS
ide side) | 1159 VisiblePositionInFlatTree startOfWord(const VisiblePositionInFlatTree& c, EWordS
ide side) |
| 895 { | 1160 { |
| 896 return startOfWordAlgorithm<EditingInFlatTreeStrategy>(c, side); | 1161 return startOfWordAlgorithm<EditingInFlatTreeStrategy>(c, side); |
| 897 } | 1162 } |
| 898 | 1163 |
| 899 static unsigned endWordBoundary(const UChar* characters, unsigned length, unsign
ed offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreC
ontext) | 1164 static unsigned endWordBoundary(const UChar* characters, unsigned length, unsign
ed offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreC
ontext) |
| 900 { | 1165 { |
| 901 DCHECK_LE(offset, length); | 1166 ASSERT(offset <= length); |
| 902 if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset,
length - offset) == static_cast<int>(length - offset)) { | 1167 if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset,
length - offset) == static_cast<int>(length - offset)) { |
| 903 needMoreContext = true; | 1168 needMoreContext = true; |
| 904 return length; | 1169 return length; |
| 905 } | 1170 } |
| 906 needMoreContext = false; | 1171 needMoreContext = false; |
| 907 return findWordEndBoundary(characters, length, offset); | 1172 return findWordEndBoundary(characters, length, offset); |
| 908 } | 1173 } |
| 909 | 1174 |
| 910 template <typename Strategy> | 1175 template <typename Strategy> |
| 911 static VisiblePositionTemplate<Strategy> endOfWordAlgorithm(const VisiblePositio
nTemplate<Strategy>& c, EWordSide side) | 1176 static VisiblePositionTemplate<Strategy> endOfWordAlgorithm(const VisiblePositio
nTemplate<Strategy>& c, EWordSide side) |
| (...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1279 return isLogicalEndOfLineAlgorithm<EditingStrategy>(p); | 1544 return isLogicalEndOfLineAlgorithm<EditingStrategy>(p); |
| 1280 } | 1545 } |
| 1281 | 1546 |
| 1282 bool isLogicalEndOfLine(const VisiblePositionInFlatTree& p) | 1547 bool isLogicalEndOfLine(const VisiblePositionInFlatTree& p) |
| 1283 { | 1548 { |
| 1284 return isLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | 1549 return isLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); |
| 1285 } | 1550 } |
| 1286 | 1551 |
| 1287 static inline LayoutPoint absoluteLineDirectionPointToLocalPointInBlock(RootInli
neBox* root, LayoutUnit lineDirectionPoint) | 1552 static inline LayoutPoint absoluteLineDirectionPointToLocalPointInBlock(RootInli
neBox* root, LayoutUnit lineDirectionPoint) |
| 1288 { | 1553 { |
| 1289 DCHECK(root); | 1554 ASSERT(root); |
| 1290 LineLayoutBlockFlow containingBlock = root->block(); | 1555 LineLayoutBlockFlow containingBlock = root->block(); |
| 1291 FloatPoint absoluteBlockPoint = containingBlock.localToAbsolute(FloatPoint()
); | 1556 FloatPoint absoluteBlockPoint = containingBlock.localToAbsolute(FloatPoint()
); |
| 1292 if (containingBlock.hasOverflowClip()) | 1557 if (containingBlock.hasOverflowClip()) |
| 1293 absoluteBlockPoint -= FloatSize(containingBlock.scrolledContentOffset())
; | 1558 absoluteBlockPoint -= FloatSize(containingBlock.scrolledContentOffset())
; |
| 1294 | 1559 |
| 1295 if (root->block().isHorizontalWritingMode()) | 1560 if (root->block().isHorizontalWritingMode()) |
| 1296 return LayoutPoint(LayoutUnit(lineDirectionPoint - absoluteBlockPoint.x(
)), root->blockDirectionPointInLine()); | 1561 return LayoutPoint(LayoutUnit(lineDirectionPoint - absoluteBlockPoint.x(
)), root->blockDirectionPointInLine()); |
| 1297 | 1562 |
| 1298 return LayoutPoint(root->blockDirectionPointInLine(), LayoutUnit(lineDirecti
onPoint - absoluteBlockPoint.y())); | 1563 return LayoutPoint(root->blockDirectionPointInLine(), LayoutUnit(lineDirecti
onPoint - absoluteBlockPoint.y())); |
| 1299 } | 1564 } |
| (...skipping 913 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2213 // appeared in layout tree. | 2478 // appeared in layout tree. |
| 2214 // | 2479 // |
| 2215 // When |Text| node contains only first-letter characters and whitespaces, e.g. | 2480 // When |Text| node contains only first-letter characters and whitespaces, e.g. |
| 2216 // "B\n", associated |LayoutTextFragment| is first-letter part instead of | 2481 // "B\n", associated |LayoutTextFragment| is first-letter part instead of |
| 2217 // remaining part. | 2482 // remaining part. |
| 2218 // | 2483 // |
| 2219 // Punctuation characters are considered as first-letter. For "(1)ab", | 2484 // Punctuation characters are considered as first-letter. For "(1)ab", |
| 2220 // "(1)" are first-letter part and "ab" are remaining part. | 2485 // "(1)" are first-letter part and "ab" are remaining part. |
| 2221 LayoutObject* associatedLayoutObjectOf(const Node& node, int offsetInNode) | 2486 LayoutObject* associatedLayoutObjectOf(const Node& node, int offsetInNode) |
| 2222 { | 2487 { |
| 2223 DCHECK_GE(offsetInNode, 0); | 2488 ASSERT(offsetInNode >= 0); |
| 2224 LayoutObject* layoutObject = node.layoutObject(); | 2489 LayoutObject* layoutObject = node.layoutObject(); |
| 2225 if (!node.isTextNode() || !layoutObject || !toLayoutText(layoutObject)->isTe
xtFragment()) | 2490 if (!node.isTextNode() || !layoutObject || !toLayoutText(layoutObject)->isTe
xtFragment()) |
| 2226 return layoutObject; | 2491 return layoutObject; |
| 2227 LayoutTextFragment* layoutTextFragment = toLayoutTextFragment(layoutObject); | 2492 LayoutTextFragment* layoutTextFragment = toLayoutTextFragment(layoutObject); |
| 2228 if (!layoutTextFragment->isRemainingTextLayoutObject()) { | 2493 if (!layoutTextFragment->isRemainingTextLayoutObject()) { |
| 2229 DCHECK_LE(static_cast<unsigned>(offsetInNode), layoutTextFragment->start
() + layoutTextFragment->fragmentLength()); | 2494 ASSERT(static_cast<unsigned>(offsetInNode) <= layoutTextFragment->start(
) + layoutTextFragment->fragmentLength()); |
| 2230 return layoutTextFragment; | 2495 return layoutTextFragment; |
| 2231 } | 2496 } |
| 2232 if (layoutTextFragment->fragmentLength() && static_cast<unsigned>(offsetInNo
de) >= layoutTextFragment->start()) | 2497 if (layoutTextFragment->fragmentLength() && static_cast<unsigned>(offsetInNo
de) >= layoutTextFragment->start()) |
| 2233 return layoutObject; | 2498 return layoutObject; |
| 2234 LayoutObject* firstLetterLayoutObject = layoutTextFragment->firstLetterPseud
oElement()->layoutObject(); | 2499 LayoutObject* firstLetterLayoutObject = layoutTextFragment->firstLetterPseud
oElement()->layoutObject(); |
| 2235 // TODO(yosin): We're not sure when |firstLetterLayoutObject| has | 2500 // TODO(yosin): We're not sure when |firstLetterLayoutObject| has |
| 2236 // multiple child layout object. | 2501 // multiple child layout object. |
| 2237 DCHECK_EQ(firstLetterLayoutObject->slowFirstChild(), firstLetterLayoutObject
->slowLastChild()); | 2502 ASSERT(firstLetterLayoutObject->slowFirstChild() == firstLetterLayoutObject-
>slowLastChild()); |
| 2238 return firstLetterLayoutObject->slowFirstChild(); | 2503 return firstLetterLayoutObject->slowFirstChild(); |
| 2239 } | 2504 } |
| 2240 | 2505 |
| 2241 int caretMinOffset(const Node* node) | 2506 int caretMinOffset(const Node* node) |
| 2242 { | 2507 { |
| 2243 LayoutObject* layoutObject = associatedLayoutObjectOf(*node, 0); | 2508 LayoutObject* layoutObject = associatedLayoutObjectOf(*node, 0); |
| 2244 return layoutObject ? layoutObject->caretMinOffset() : 0; | 2509 return layoutObject ? layoutObject->caretMinOffset() : 0; |
| 2245 } | 2510 } |
| 2246 | 2511 |
| 2247 int caretMaxOffset(const Node* n) | 2512 int caretMaxOffset(const Node* n) |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2422 | 2687 |
| 2423 // return current position if it is in laid out text | 2688 // return current position if it is in laid out text |
| 2424 if (layoutObject->isText() && toLayoutText(layoutObject)->firstTextBox()
) { | 2689 if (layoutObject->isText() && toLayoutText(layoutObject)->firstTextBox()
) { |
| 2425 LayoutText* const textLayoutObject = toLayoutText(layoutObject); | 2690 LayoutText* const textLayoutObject = toLayoutText(layoutObject); |
| 2426 const unsigned textStartOffset = textLayoutObject->textStartOffset()
; | 2691 const unsigned textStartOffset = textLayoutObject->textStartOffset()
; |
| 2427 if (currentNode != startNode) { | 2692 if (currentNode != startNode) { |
| 2428 // This assertion fires in layout tests in the case-transform.ht
ml test because | 2693 // This assertion fires in layout tests in the case-transform.ht
ml test because |
| 2429 // of a mix-up between offsets in the text in the DOM tree with
text in the | 2694 // of a mix-up between offsets in the text in the DOM tree with
text in the |
| 2430 // layout tree which can have a different length due to case tra
nsformation. | 2695 // layout tree which can have a different length due to case tra
nsformation. |
| 2431 // Until we resolve that, disable this so we can run the layout
tests! | 2696 // Until we resolve that, disable this so we can run the layout
tests! |
| 2432 // DCHECK_GE(currentOffset, layoutObject->caretMaxOffset()); | 2697 // ASSERT(currentOffset >= layoutObject->caretMaxOffset()); |
| 2433 return PositionTemplate<Strategy>(currentNode, layoutObject->car
etMaxOffset() + textStartOffset); | 2698 return PositionTemplate<Strategy>(currentNode, layoutObject->car
etMaxOffset() + textStartOffset); |
| 2434 } | 2699 } |
| 2435 | 2700 |
| 2436 // Map offset in DOM node to offset in InlineBox. | 2701 // Map offset in DOM node to offset in InlineBox. |
| 2437 DCHECK_GE(currentPos.offsetInLeafNode(), static_cast<int>(textStartO
ffset)); | 2702 ASSERT(currentPos.offsetInLeafNode() >= static_cast<int>(textStartOf
fset)); |
| 2438 const unsigned textOffset = currentPos.offsetInLeafNode() - textStar
tOffset; | 2703 const unsigned textOffset = currentPos.offsetInLeafNode() - textStar
tOffset; |
| 2439 InlineTextBox* lastTextBox = textLayoutObject->lastTextBox(); | 2704 InlineTextBox* lastTextBox = textLayoutObject->lastTextBox(); |
| 2440 for (InlineTextBox* box = textLayoutObject->firstTextBox(); box; box
= box->nextTextBox()) { | 2705 for (InlineTextBox* box = textLayoutObject->firstTextBox(); box; box
= box->nextTextBox()) { |
| 2441 if (textOffset == box->start()) { | 2706 if (textOffset == box->start()) { |
| 2442 if (textLayoutObject->isTextFragment() && toLayoutTextFragme
nt(layoutObject)->isRemainingTextLayoutObject()) { | 2707 if (textLayoutObject->isTextFragment() && toLayoutTextFragme
nt(layoutObject)->isRemainingTextLayoutObject()) { |
| 2443 // |currentPos| is at start of remaining text of | 2708 // |currentPos| is at start of remaining text of |
| 2444 // |Text| node with :first-letter. | 2709 // |Text| node with :first-letter. |
| 2445 DCHECK_GE(currentPos.offsetInLeafNode(), 1); | 2710 ASSERT(currentPos.offsetInLeafNode() >= 1); |
| 2446 LayoutObject* firstLetterLayoutObject = toLayoutTextFrag
ment(layoutObject)->firstLetterPseudoElement()->layoutObject(); | 2711 LayoutObject* firstLetterLayoutObject = toLayoutTextFrag
ment(layoutObject)->firstLetterPseudoElement()->layoutObject(); |
| 2447 if (firstLetterLayoutObject && firstLetterLayoutObject->
style()->visibility() == VISIBLE) | 2712 if (firstLetterLayoutObject && firstLetterLayoutObject->
style()->visibility() == VISIBLE) |
| 2448 return currentPos.computePosition(); | 2713 return currentPos.computePosition(); |
| 2449 } | 2714 } |
| 2450 continue; | 2715 continue; |
| 2451 } | 2716 } |
| 2452 if (textOffset <= box->start() + box->len()) { | 2717 if (textOffset <= box->start() + box->len()) { |
| 2453 if (textOffset > box->start()) | 2718 if (textOffset > box->start()) |
| 2454 return currentPos.computePosition(); | 2719 return currentPos.computePosition(); |
| 2455 continue; | 2720 continue; |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2564 if (currentPos.offsetInLeafNode() <= layoutObject->caretMinOffset()) | 2829 if (currentPos.offsetInLeafNode() <= layoutObject->caretMinOffset()) |
| 2565 return PositionTemplate<Strategy>::editingPositionOf(currentNode
, layoutObject->caretMinOffset()); | 2830 return PositionTemplate<Strategy>::editingPositionOf(currentNode
, layoutObject->caretMinOffset()); |
| 2566 continue; | 2831 continue; |
| 2567 } | 2832 } |
| 2568 | 2833 |
| 2569 // return current position if it is in laid out text | 2834 // return current position if it is in laid out text |
| 2570 if (layoutObject->isText() && toLayoutText(layoutObject)->firstTextBox()
) { | 2835 if (layoutObject->isText() && toLayoutText(layoutObject)->firstTextBox()
) { |
| 2571 LayoutText* const textLayoutObject = toLayoutText(layoutObject); | 2836 LayoutText* const textLayoutObject = toLayoutText(layoutObject); |
| 2572 const unsigned textStartOffset = textLayoutObject->textStartOffset()
; | 2837 const unsigned textStartOffset = textLayoutObject->textStartOffset()
; |
| 2573 if (currentNode != startNode) { | 2838 if (currentNode != startNode) { |
| 2574 DCHECK(currentPos.atStartOfNode()); | 2839 ASSERT(currentPos.atStartOfNode()); |
| 2575 return PositionTemplate<Strategy>(currentNode, layoutObject->car
etMinOffset() + textStartOffset); | 2840 return PositionTemplate<Strategy>(currentNode, layoutObject->car
etMinOffset() + textStartOffset); |
| 2576 } | 2841 } |
| 2577 | 2842 |
| 2578 // Map offset in DOM node to offset in InlineBox. | 2843 // Map offset in DOM node to offset in InlineBox. |
| 2579 DCHECK_GE(currentPos.offsetInLeafNode(), static_cast<int>(textStartO
ffset)); | 2844 ASSERT(currentPos.offsetInLeafNode() >= static_cast<int>(textStartOf
fset)); |
| 2580 const unsigned textOffset = currentPos.offsetInLeafNode() - textStar
tOffset; | 2845 const unsigned textOffset = currentPos.offsetInLeafNode() - textStar
tOffset; |
| 2581 InlineTextBox* lastTextBox = textLayoutObject->lastTextBox(); | 2846 InlineTextBox* lastTextBox = textLayoutObject->lastTextBox(); |
| 2582 for (InlineTextBox* box = textLayoutObject->firstTextBox(); box; box
= box->nextTextBox()) { | 2847 for (InlineTextBox* box = textLayoutObject->firstTextBox(); box; box
= box->nextTextBox()) { |
| 2583 if (textOffset <= box->end()) { | 2848 if (textOffset <= box->end()) { |
| 2584 if (textOffset >= box->start()) | 2849 if (textOffset >= box->start()) |
| 2585 return currentPos.computePosition(); | 2850 return currentPos.computePosition(); |
| 2586 continue; | 2851 continue; |
| 2587 } | 2852 } |
| 2588 | 2853 |
| 2589 if (box == lastTextBox || textOffset != box->start() + box->len(
)) | 2854 if (box == lastTextBox || textOffset != box->start() + box->len(
)) |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2762 // or both are non-editable. | 3027 // or both are non-editable. |
| 2763 if (highestRootOfPos == highestRoot) | 3028 if (highestRootOfPos == highestRoot) |
| 2764 return pos; | 3029 return pos; |
| 2765 | 3030 |
| 2766 // If this is not editable but |pos| has an editable root, skip to the end | 3031 // If this is not editable but |pos| has an editable root, skip to the end |
| 2767 if (!highestRoot && highestRootOfPos) | 3032 if (!highestRoot && highestRootOfPos) |
| 2768 return createVisiblePosition(PositionTemplate<Strategy>(highestRootOfPos
, PositionAnchorType::AfterAnchor).parentAnchoredEquivalent()); | 3033 return createVisiblePosition(PositionTemplate<Strategy>(highestRootOfPos
, PositionAnchorType::AfterAnchor).parentAnchoredEquivalent()); |
| 2769 | 3034 |
| 2770 // That must mean that |pos| is not editable. Return the next position after | 3035 // That must mean that |pos| is not editable. Return the next position after |
| 2771 // |pos| that is in the same editable region as this position | 3036 // |pos| that is in the same editable region as this position |
| 2772 DCHECK(highestRoot); | 3037 ASSERT(highestRoot); |
| 2773 return firstEditableVisiblePositionAfterPositionInRoot(pos.deepEquivalent(),
*highestRoot); | 3038 return firstEditableVisiblePositionAfterPositionInRoot(pos.deepEquivalent(),
*highestRoot); |
| 2774 } | 3039 } |
| 2775 | 3040 |
| 2776 template <typename Strategy> | 3041 template <typename Strategy> |
| 2777 static UChar32 characterAfterAlgorithm(const VisiblePositionTemplate<Strategy>&
visiblePosition) | 3042 static UChar32 characterAfterAlgorithm(const VisiblePositionTemplate<Strategy>&
visiblePosition) |
| 2778 { | 3043 { |
| 2779 // We canonicalize to the first of two equivalent candidates, but the second | 3044 // We canonicalize to the first of two equivalent candidates, but the second |
| 2780 // of the two candidates is the one that will be inside the text node | 3045 // of the two candidates is the one that will be inside the text node |
| 2781 // containing the character after this visible position. | 3046 // containing the character after this visible position. |
| 2782 const PositionTemplate<Strategy> pos = mostForwardCaretPosition(visiblePosit
ion.deepEquivalent()); | 3047 const PositionTemplate<Strategy> pos = mostForwardCaretPosition(visiblePosit
ion.deepEquivalent()); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2814 { | 3079 { |
| 2815 return characterBeforeAlgorithm<EditingStrategy>(visiblePosition); | 3080 return characterBeforeAlgorithm<EditingStrategy>(visiblePosition); |
| 2816 } | 3081 } |
| 2817 | 3082 |
| 2818 UChar32 characterBefore(const VisiblePositionInFlatTree& visiblePosition) | 3083 UChar32 characterBefore(const VisiblePositionInFlatTree& visiblePosition) |
| 2819 { | 3084 { |
| 2820 return characterBeforeAlgorithm<EditingInFlatTreeStrategy>(visiblePosition); | 3085 return characterBeforeAlgorithm<EditingInFlatTreeStrategy>(visiblePosition); |
| 2821 } | 3086 } |
| 2822 | 3087 |
| 2823 template <typename Strategy> | 3088 template <typename Strategy> |
| 2824 static PositionTemplate<Strategy> leftVisuallyDistinctCandidate(const VisiblePos
itionTemplate<Strategy>& visiblePosition) | |
| 2825 { | |
| 2826 const PositionTemplate<Strategy> deepPosition = visiblePosition.deepEquivale
nt(); | |
| 2827 PositionTemplate<Strategy> p = deepPosition; | |
| 2828 | |
| 2829 if (p.isNull()) | |
| 2830 return PositionTemplate<Strategy>(); | |
| 2831 | |
| 2832 const PositionTemplate<Strategy> downstreamStart = mostForwardCaretPosition(
p); | |
| 2833 TextDirection primaryDirection = primaryDirectionOf(*p.anchorNode()); | |
| 2834 const TextAffinity affinity = visiblePosition.affinity(); | |
| 2835 | |
| 2836 while (true) { | |
| 2837 InlineBoxPosition boxPosition = computeInlineBoxPosition(p, affinity, pr
imaryDirection); | |
| 2838 InlineBox* box = boxPosition.inlineBox; | |
| 2839 int offset = boxPosition.offsetInBox; | |
| 2840 if (!box) | |
| 2841 return primaryDirection == LTR ? previousVisuallyDistinctCandidate(d
eepPosition) : nextVisuallyDistinctCandidate(deepPosition); | |
| 2842 | |
| 2843 LineLayoutItem lineLayoutItem = box->getLineLayoutItem(); | |
| 2844 | |
| 2845 while (true) { | |
| 2846 if ((lineLayoutItem.isAtomicInlineLevel() || lineLayoutItem.isBR())
&& offset == box->caretRightmostOffset()) | |
| 2847 return box->isLeftToRightDirection() ? previousVisuallyDistinctC
andidate(deepPosition) : nextVisuallyDistinctCandidate(deepPosition); | |
| 2848 | |
| 2849 if (!lineLayoutItem.node()) { | |
| 2850 box = box->prevLeafChild(); | |
| 2851 if (!box) | |
| 2852 return primaryDirection == LTR ? previousVisuallyDistinctCan
didate(deepPosition) : nextVisuallyDistinctCandidate(deepPosition); | |
| 2853 lineLayoutItem = box->getLineLayoutItem(); | |
| 2854 offset = box->caretRightmostOffset(); | |
| 2855 continue; | |
| 2856 } | |
| 2857 | |
| 2858 offset = box->isLeftToRightDirection() ? previousGraphemeBoundaryOf(
lineLayoutItem.node(), offset) : nextGraphemeBoundaryOf(lineLayoutItem.node(), o
ffset); | |
| 2859 | |
| 2860 int caretMinOffset = box->caretMinOffset(); | |
| 2861 int caretMaxOffset = box->caretMaxOffset(); | |
| 2862 | |
| 2863 if (offset > caretMinOffset && offset < caretMaxOffset) | |
| 2864 break; | |
| 2865 | |
| 2866 if (box->isLeftToRightDirection() ? offset < caretMinOffset : offset
> caretMaxOffset) { | |
| 2867 // Overshot to the left. | |
| 2868 InlineBox* prevBox = box->prevLeafChildIgnoringLineBreak(); | |
| 2869 if (!prevBox) { | |
| 2870 PositionTemplate<Strategy> positionOnLeft = primaryDirection
== LTR ? previousVisuallyDistinctCandidate(visiblePosition.deepEquivalent()) :
nextVisuallyDistinctCandidate(visiblePosition.deepEquivalent()); | |
| 2871 if (positionOnLeft.isNull()) | |
| 2872 return PositionTemplate<Strategy>(); | |
| 2873 | |
| 2874 InlineBox* boxOnLeft = computeInlineBoxPosition(positionOnLe
ft, affinity, primaryDirection).inlineBox; | |
| 2875 if (boxOnLeft && boxOnLeft->root() == box->root()) | |
| 2876 return PositionTemplate<Strategy>(); | |
| 2877 return positionOnLeft; | |
| 2878 } | |
| 2879 | |
| 2880 // Reposition at the other logical position corresponding to our | |
| 2881 // edge's visual position and go for another round. | |
| 2882 box = prevBox; | |
| 2883 lineLayoutItem = box->getLineLayoutItem(); | |
| 2884 offset = prevBox->caretRightmostOffset(); | |
| 2885 continue; | |
| 2886 } | |
| 2887 | |
| 2888 DCHECK_EQ(offset, box->caretLeftmostOffset()); | |
| 2889 | |
| 2890 unsigned char level = box->bidiLevel(); | |
| 2891 InlineBox* prevBox = box->prevLeafChild(); | |
| 2892 | |
| 2893 if (box->direction() == primaryDirection) { | |
| 2894 if (!prevBox) { | |
| 2895 InlineBox* logicalStart = 0; | |
| 2896 if (primaryDirection == LTR ? box->root().getLogicalStartBox
WithNode(logicalStart) : box->root().getLogicalEndBoxWithNode(logicalStart)) { | |
| 2897 box = logicalStart; | |
| 2898 lineLayoutItem = box->getLineLayoutItem(); | |
| 2899 offset = primaryDirection == LTR ? box->caretMinOffset()
: box->caretMaxOffset(); | |
| 2900 } | |
| 2901 break; | |
| 2902 } | |
| 2903 if (prevBox->bidiLevel() >= level) | |
| 2904 break; | |
| 2905 | |
| 2906 level = prevBox->bidiLevel(); | |
| 2907 | |
| 2908 InlineBox* nextBox = box; | |
| 2909 do { | |
| 2910 nextBox = nextBox->nextLeafChild(); | |
| 2911 } while (nextBox && nextBox->bidiLevel() > level); | |
| 2912 | |
| 2913 if (nextBox && nextBox->bidiLevel() == level) | |
| 2914 break; | |
| 2915 | |
| 2916 box = prevBox; | |
| 2917 lineLayoutItem = box->getLineLayoutItem(); | |
| 2918 offset = box->caretRightmostOffset(); | |
| 2919 if (box->direction() == primaryDirection) | |
| 2920 break; | |
| 2921 continue; | |
| 2922 } | |
| 2923 | |
| 2924 while (prevBox && !prevBox->getLineLayoutItem().node()) | |
| 2925 prevBox = prevBox->prevLeafChild(); | |
| 2926 | |
| 2927 if (prevBox) { | |
| 2928 box = prevBox; | |
| 2929 lineLayoutItem = box->getLineLayoutItem(); | |
| 2930 offset = box->caretRightmostOffset(); | |
| 2931 if (box->bidiLevel() > level) { | |
| 2932 do { | |
| 2933 prevBox = prevBox->prevLeafChild(); | |
| 2934 } while (prevBox && prevBox->bidiLevel() > level); | |
| 2935 | |
| 2936 if (!prevBox || prevBox->bidiLevel() < level) | |
| 2937 continue; | |
| 2938 } | |
| 2939 } else { | |
| 2940 // Trailing edge of a secondary run. Set to the leading edge of | |
| 2941 // the entire run. | |
| 2942 while (true) { | |
| 2943 while (InlineBox* nextBox = box->nextLeafChild()) { | |
| 2944 if (nextBox->bidiLevel() < level) | |
| 2945 break; | |
| 2946 box = nextBox; | |
| 2947 } | |
| 2948 if (box->bidiLevel() == level) | |
| 2949 break; | |
| 2950 level = box->bidiLevel(); | |
| 2951 while (InlineBox* prevBox = box->prevLeafChild()) { | |
| 2952 if (prevBox->bidiLevel() < level) | |
| 2953 break; | |
| 2954 box = prevBox; | |
| 2955 } | |
| 2956 if (box->bidiLevel() == level) | |
| 2957 break; | |
| 2958 level = box->bidiLevel(); | |
| 2959 } | |
| 2960 lineLayoutItem = box->getLineLayoutItem(); | |
| 2961 offset = primaryDirection == LTR ? box->caretMinOffset() : box->
caretMaxOffset(); | |
| 2962 } | |
| 2963 break; | |
| 2964 } | |
| 2965 | |
| 2966 p = PositionTemplate<Strategy>::editingPositionOf(lineLayoutItem.node(),
offset); | |
| 2967 | |
| 2968 if ((isVisuallyEquivalentCandidate(p) && mostForwardCaretPosition(p) !=
downstreamStart) || p.atStartOfTree() || p.atEndOfTree()) | |
| 2969 return p; | |
| 2970 | |
| 2971 DCHECK_NE(p, deepPosition); | |
| 2972 } | |
| 2973 } | |
| 2974 | |
| 2975 template <typename Strategy> | |
| 2976 VisiblePositionTemplate<Strategy> leftPositionOfAlgorithm(const VisiblePositionT
emplate<Strategy>& visiblePosition) | 3089 VisiblePositionTemplate<Strategy> leftPositionOfAlgorithm(const VisiblePositionT
emplate<Strategy>& visiblePosition) |
| 2977 { | 3090 { |
| 2978 const PositionTemplate<Strategy> pos = leftVisuallyDistinctCandidate(visible
Position); | 3091 if (visiblePosition.isNull()) |
| 2979 // TODO(yosin) Why can't we move left from the last position in a tree? | |
| 2980 if (pos.atStartOfTree() || pos.atEndOfTree()) | |
| 2981 return VisiblePositionTemplate<Strategy>(); | 3092 return VisiblePositionTemplate<Strategy>(); |
| 2982 | 3093 const PositionTemplate<Strategy>& startPosition = visiblePosition.deepEquiva
lent(); |
| 2983 const VisiblePositionTemplate<Strategy> left = createVisiblePosition(pos); | 3094 const LayoutPosition& startLayoutPostion = LayoutPosition::create(startPosit
ion); |
| 2984 DCHECK_NE(left.deepEquivalent(), visiblePosition.deepEquivalent()); | 3095 for (LayoutPosition runner = leftPositionOf(startLayoutPostion); |
| 2985 | 3096 runner.isNotNull(); |
| 2986 return directionOfEnclosingBlock(left.deepEquivalent()) == LTR ? honorEditin
gBoundaryAtOrBefore(left, visiblePosition.deepEquivalent()) : honorEditingBounda
ryAtOrAfter(left, visiblePosition.deepEquivalent()); | 3097 runner = leftPositionOf(runner)) { |
| 3098 if (!runner.canPlaceCaret() || runner.isCaretEquivalent(startLayoutPosti
on)) |
| 3099 continue; |
| 3100 const PositionTemplate<Strategy>& position = runner.toPosition(PositionT
emplate<Strategy>()); |
| 3101 // TODO(yosin): Why can't we move left from the last position in a tree? |
| 3102 if (position.atStartOfTree() || position.atEndOfTree()) |
| 3103 return VisiblePositionTemplate<Strategy>(); |
| 3104 const VisiblePositionTemplate<Strategy>& visibleLeft = createVisiblePosi
tion(position); |
| 3105 if (visibleLeft.isNull()) |
| 3106 return VisiblePositionTemplate<Strategy>(); |
| 3107 const PositionTemplate<Strategy>& left = visibleLeft.deepEquivalent(); |
| 3108 if (left == startPosition) { |
| 3109 // |runner| is a member of canonical position set of |startPosition|
. |
| 3110 continue; |
| 3111 } |
| 3112 if (directionOfEnclosingBlock(left) == LTR) |
| 3113 return honorEditingBoundaryAtOrBefore(visibleLeft, startPosition); |
| 3114 return honorEditingBoundaryAtOrAfter(visibleLeft, startPosition); |
| 3115 } |
| 3116 return VisiblePositionTemplate<Strategy>(); |
| 2987 } | 3117 } |
| 2988 | 3118 |
| 2989 VisiblePosition leftPositionOf(const VisiblePosition& visiblePosition) | 3119 VisiblePosition leftPositionOf(const VisiblePosition& visiblePosition) |
| 2990 { | 3120 { |
| 2991 return leftPositionOfAlgorithm<EditingStrategy>(visiblePosition); | 3121 return leftPositionOfAlgorithm<EditingStrategy>(visiblePosition); |
| 2992 } | 3122 } |
| 2993 | 3123 |
| 2994 VisiblePositionInFlatTree leftPositionOf(const VisiblePositionInFlatTree& visibl
ePosition) | 3124 VisiblePositionInFlatTree leftPositionOf(const VisiblePositionInFlatTree& visibl
ePosition) |
| 2995 { | 3125 { |
| 2996 return leftPositionOfAlgorithm<EditingInFlatTreeStrategy>(visiblePosition); | 3126 return leftPositionOfAlgorithm<EditingInFlatTreeStrategy>(visiblePosition); |
| 2997 } | 3127 } |
| 2998 | 3128 |
| 2999 template <typename Strategy> | 3129 template <typename Strategy> |
| 3000 static PositionTemplate<Strategy> rightVisuallyDistinctCandidate(const VisiblePo
sitionTemplate<Strategy>& visiblePosition) | |
| 3001 { | |
| 3002 const PositionTemplate<Strategy> deepPosition = visiblePosition.deepEquivale
nt(); | |
| 3003 PositionTemplate<Strategy> p = deepPosition; | |
| 3004 if (p.isNull()) | |
| 3005 return PositionTemplate<Strategy>(); | |
| 3006 | |
| 3007 const PositionTemplate<Strategy> downstreamStart = mostForwardCaretPosition(
p); | |
| 3008 TextDirection primaryDirection = primaryDirectionOf(*p.anchorNode()); | |
| 3009 const TextAffinity affinity = visiblePosition.affinity(); | |
| 3010 | |
| 3011 while (true) { | |
| 3012 InlineBoxPosition boxPosition = computeInlineBoxPosition(p, affinity, pr
imaryDirection); | |
| 3013 InlineBox* box = boxPosition.inlineBox; | |
| 3014 int offset = boxPosition.offsetInBox; | |
| 3015 if (!box) | |
| 3016 return primaryDirection == LTR ? nextVisuallyDistinctCandidate(deepP
osition) : previousVisuallyDistinctCandidate(deepPosition); | |
| 3017 | |
| 3018 LayoutObject* layoutObject = LineLayoutAPIShim::layoutObjectFrom(box->ge
tLineLayoutItem()); | |
| 3019 | |
| 3020 while (true) { | |
| 3021 if ((layoutObject->isAtomicInlineLevel() || layoutObject->isBR()) &&
offset == box->caretLeftmostOffset()) | |
| 3022 return box->isLeftToRightDirection() ? nextVisuallyDistinctCandi
date(deepPosition) : previousVisuallyDistinctCandidate(deepPosition); | |
| 3023 | |
| 3024 if (!layoutObject->node()) { | |
| 3025 box = box->nextLeafChild(); | |
| 3026 if (!box) | |
| 3027 return primaryDirection == LTR ? nextVisuallyDistinctCandida
te(deepPosition) : previousVisuallyDistinctCandidate(deepPosition); | |
| 3028 layoutObject = LineLayoutAPIShim::layoutObjectFrom(box->getLineL
ayoutItem()); | |
| 3029 offset = box->caretLeftmostOffset(); | |
| 3030 continue; | |
| 3031 } | |
| 3032 | |
| 3033 offset = box->isLeftToRightDirection() ? nextGraphemeBoundaryOf(layo
utObject->node(), offset) : previousGraphemeBoundaryOf(layoutObject->node(), off
set); | |
| 3034 | |
| 3035 int caretMinOffset = box->caretMinOffset(); | |
| 3036 int caretMaxOffset = box->caretMaxOffset(); | |
| 3037 | |
| 3038 if (offset > caretMinOffset && offset < caretMaxOffset) | |
| 3039 break; | |
| 3040 | |
| 3041 if (box->isLeftToRightDirection() ? offset > caretMaxOffset : offset
< caretMinOffset) { | |
| 3042 // Overshot to the right. | |
| 3043 InlineBox* nextBox = box->nextLeafChildIgnoringLineBreak(); | |
| 3044 if (!nextBox) { | |
| 3045 PositionTemplate<Strategy> positionOnRight = primaryDirectio
n == LTR ? nextVisuallyDistinctCandidate(deepPosition) : previousVisuallyDistinc
tCandidate(deepPosition); | |
| 3046 if (positionOnRight.isNull()) | |
| 3047 return PositionTemplate<Strategy>(); | |
| 3048 | |
| 3049 InlineBox* boxOnRight = computeInlineBoxPosition(positionOnR
ight, affinity, primaryDirection).inlineBox; | |
| 3050 if (boxOnRight && boxOnRight->root() == box->root()) | |
| 3051 return PositionTemplate<Strategy>(); | |
| 3052 return positionOnRight; | |
| 3053 } | |
| 3054 | |
| 3055 // Reposition at the other logical position corresponding to our | |
| 3056 // edge's visual position and go for another round. | |
| 3057 box = nextBox; | |
| 3058 layoutObject = LineLayoutAPIShim::layoutObjectFrom(box->getLineL
ayoutItem()); | |
| 3059 offset = nextBox->caretLeftmostOffset(); | |
| 3060 continue; | |
| 3061 } | |
| 3062 | |
| 3063 DCHECK_EQ(offset, box->caretRightmostOffset()); | |
| 3064 | |
| 3065 unsigned char level = box->bidiLevel(); | |
| 3066 InlineBox* nextBox = box->nextLeafChild(); | |
| 3067 | |
| 3068 if (box->direction() == primaryDirection) { | |
| 3069 if (!nextBox) { | |
| 3070 InlineBox* logicalEnd = 0; | |
| 3071 if (primaryDirection == LTR ? box->root().getLogicalEndBoxWi
thNode(logicalEnd) : box->root().getLogicalStartBoxWithNode(logicalEnd)) { | |
| 3072 box = logicalEnd; | |
| 3073 layoutObject = LineLayoutAPIShim::layoutObjectFrom(box->
getLineLayoutItem()); | |
| 3074 offset = primaryDirection == LTR ? box->caretMaxOffset()
: box->caretMinOffset(); | |
| 3075 } | |
| 3076 break; | |
| 3077 } | |
| 3078 | |
| 3079 if (nextBox->bidiLevel() >= level) | |
| 3080 break; | |
| 3081 | |
| 3082 level = nextBox->bidiLevel(); | |
| 3083 | |
| 3084 InlineBox* prevBox = box; | |
| 3085 do { | |
| 3086 prevBox = prevBox->prevLeafChild(); | |
| 3087 } while (prevBox && prevBox->bidiLevel() > level); | |
| 3088 | |
| 3089 // For example, abc FED 123 ^ CBA | |
| 3090 if (prevBox && prevBox->bidiLevel() == level) | |
| 3091 break; | |
| 3092 | |
| 3093 // For example, abc 123 ^ CBA or 123 ^ CBA abc | |
| 3094 box = nextBox; | |
| 3095 layoutObject = LineLayoutAPIShim::layoutObjectFrom(box->getLineL
ayoutItem()); | |
| 3096 offset = box->caretLeftmostOffset(); | |
| 3097 if (box->direction() == primaryDirection) | |
| 3098 break; | |
| 3099 continue; | |
| 3100 } | |
| 3101 | |
| 3102 while (nextBox && !nextBox->getLineLayoutItem().node()) | |
| 3103 nextBox = nextBox->nextLeafChild(); | |
| 3104 | |
| 3105 if (nextBox) { | |
| 3106 box = nextBox; | |
| 3107 layoutObject = LineLayoutAPIShim::layoutObjectFrom(box->getLineL
ayoutItem()); | |
| 3108 offset = box->caretLeftmostOffset(); | |
| 3109 | |
| 3110 if (box->bidiLevel() > level) { | |
| 3111 do { | |
| 3112 nextBox = nextBox->nextLeafChild(); | |
| 3113 } while (nextBox && nextBox->bidiLevel() > level); | |
| 3114 | |
| 3115 if (!nextBox || nextBox->bidiLevel() < level) | |
| 3116 continue; | |
| 3117 } | |
| 3118 } else { | |
| 3119 // Trailing edge of a secondary run. Set to the leading edge of
the entire run. | |
| 3120 while (true) { | |
| 3121 while (InlineBox* prevBox = box->prevLeafChild()) { | |
| 3122 if (prevBox->bidiLevel() < level) | |
| 3123 break; | |
| 3124 box = prevBox; | |
| 3125 } | |
| 3126 if (box->bidiLevel() == level) | |
| 3127 break; | |
| 3128 level = box->bidiLevel(); | |
| 3129 while (InlineBox* nextBox = box->nextLeafChild()) { | |
| 3130 if (nextBox->bidiLevel() < level) | |
| 3131 break; | |
| 3132 box = nextBox; | |
| 3133 } | |
| 3134 if (box->bidiLevel() == level) | |
| 3135 break; | |
| 3136 level = box->bidiLevel(); | |
| 3137 } | |
| 3138 layoutObject = LineLayoutAPIShim::layoutObjectFrom(box->getLineL
ayoutItem()); | |
| 3139 offset = primaryDirection == LTR ? box->caretMaxOffset() : box->
caretMinOffset(); | |
| 3140 } | |
| 3141 break; | |
| 3142 } | |
| 3143 | |
| 3144 p = PositionTemplate<Strategy>::editingPositionOf(layoutObject->node(),
offset); | |
| 3145 | |
| 3146 if ((isVisuallyEquivalentCandidate(p) && mostForwardCaretPosition(p) !=
downstreamStart) || p.atStartOfTree() || p.atEndOfTree()) | |
| 3147 return p; | |
| 3148 | |
| 3149 DCHECK_NE(p, deepPosition); | |
| 3150 } | |
| 3151 } | |
| 3152 | |
| 3153 template <typename Strategy> | |
| 3154 static VisiblePositionTemplate<Strategy> rightPositionOfAlgorithm(const VisibleP
ositionTemplate<Strategy>& visiblePosition) | 3130 static VisiblePositionTemplate<Strategy> rightPositionOfAlgorithm(const VisibleP
ositionTemplate<Strategy>& visiblePosition) |
| 3155 { | 3131 { |
| 3156 const PositionTemplate<Strategy> pos = rightVisuallyDistinctCandidate(visibl
ePosition); | 3132 if (visiblePosition.isNull()) |
| 3157 // FIXME: Why can't we move left from the last position in a tree? | |
| 3158 if (pos.atStartOfTree() || pos.atEndOfTree()) | |
| 3159 return VisiblePositionTemplate<Strategy>(); | 3133 return VisiblePositionTemplate<Strategy>(); |
| 3160 | 3134 const PositionTemplate<Strategy>& startPosition = visiblePosition.deepEquiva
lent(); |
| 3161 const VisiblePositionTemplate<Strategy> right = createVisiblePosition(pos); | 3135 const LayoutPosition& startLayoutPostion = LayoutPosition::create(startPosit
ion); |
| 3162 DCHECK_NE(right.deepEquivalent(), visiblePosition.deepEquivalent()); | 3136 for (LayoutPosition runner = rightPositionOf(startLayoutPostion); |
| 3163 | 3137 runner.isNotNull(); |
| 3164 return directionOfEnclosingBlock(right.deepEquivalent()) == LTR ? honorEditi
ngBoundaryAtOrAfter(right, visiblePosition.deepEquivalent()) : honorEditingBound
aryAtOrBefore(right, visiblePosition.deepEquivalent()); | 3138 runner = rightPositionOf(runner)) { |
| 3139 if (!runner.canPlaceCaret() || runner.isCaretEquivalent(startLayoutPosti
on)) |
| 3140 continue; |
| 3141 const PositionTemplate<Strategy>& position = runner.toPosition(PositionT
emplate<Strategy>()); |
| 3142 // TODO(yosin): Why can't we move left from the last position in a tree? |
| 3143 if (position.atStartOfTree() || position.atEndOfTree()) |
| 3144 return VisiblePositionTemplate<Strategy>(); |
| 3145 // We should always be able to make the affinity |
| 3146 // |TextAffinity::Downstream|, because going right from an |
| 3147 // |TextAffinity::Upstream| position can never yield another |
| 3148 // |TextAffinity::Upstream position| (unless line wrap length is 0). |
| 3149 const VisiblePositionTemplate<Strategy>& visibleRight = createVisiblePos
ition(position); |
| 3150 if (visibleRight.isNull()) |
| 3151 return VisiblePositionTemplate<Strategy>(); |
| 3152 const PositionTemplate<Strategy>& right = visibleRight.deepEquivalent(); |
| 3153 if (right == startPosition) { |
| 3154 // |runner| is a member of canonical position set of |startPosition|
. |
| 3155 continue; |
| 3156 } |
| 3157 if (directionOfEnclosingBlock(right) == LTR) |
| 3158 return honorEditingBoundaryAtOrAfter(visibleRight, startPosition); |
| 3159 return honorEditingBoundaryAtOrBefore(visibleRight, startPosition); |
| 3160 } |
| 3161 return VisiblePositionTemplate<Strategy>(); |
| 3165 } | 3162 } |
| 3166 | 3163 |
| 3167 VisiblePosition rightPositionOf(const VisiblePosition& visiblePosition) | 3164 VisiblePosition rightPositionOf(const VisiblePosition& visiblePosition) |
| 3168 { | 3165 { |
| 3169 return rightPositionOfAlgorithm<EditingStrategy>(visiblePosition); | 3166 return rightPositionOfAlgorithm<EditingStrategy>(visiblePosition); |
| 3170 } | 3167 } |
| 3171 | 3168 |
| 3172 VisiblePositionInFlatTree rightPositionOf(const VisiblePositionInFlatTree& visib
lePosition) | 3169 VisiblePositionInFlatTree rightPositionOf(const VisiblePositionInFlatTree& visib
lePosition) |
| 3173 { | 3170 { |
| 3174 return rightPositionOfAlgorithm<EditingInFlatTreeStrategy>(visiblePosition); | 3171 return rightPositionOfAlgorithm<EditingInFlatTreeStrategy>(visiblePosition); |
| 3175 } | 3172 } |
| 3176 | 3173 |
| 3177 template <typename Strategy> | 3174 template <typename Strategy> |
| 3178 static VisiblePositionTemplate<Strategy> nextPositionOfAlgorithm(const VisiblePo
sitionTemplate<Strategy>& visiblePosition, EditingBoundaryCrossingRule rule) | 3175 static VisiblePositionTemplate<Strategy> nextPositionOfAlgorithm(const VisiblePo
sitionTemplate<Strategy>& visiblePosition, EditingBoundaryCrossingRule rule) |
| 3179 { | 3176 { |
| 3180 const VisiblePositionTemplate<Strategy> next = createVisiblePosition(nextVis
uallyDistinctCandidate(visiblePosition.deepEquivalent()), visiblePosition.affini
ty()); | 3177 if (visiblePosition.isNull()) |
| 3178 return VisiblePositionTemplate<Strategy>(); |
| 3179 const PositionTemplate<Strategy>& startPosition = visiblePosition.deepEquiva
lent(); |
| 3180 const LayoutPosition& startLayoutPostion = LayoutPosition::create(startPosit
ion); |
| 3181 for (LayoutPosition runner = nextPositionOf(startLayoutPostion); |
| 3182 runner.isNotNull(); |
| 3183 runner = nextPositionOf(runner)) { |
| 3184 if (!runner.canPlaceCaret() || runner.isCaretEquivalent(startLayoutPosti
on)) |
| 3185 continue; |
| 3186 const PositionTemplate<Strategy>& position = runner.toPosition(PositionT
emplate<Strategy>()); |
| 3187 const VisiblePositionTemplate<Strategy>& visibleNext = createVisiblePosi
tion(position); |
| 3188 if (visibleNext.isNull()) |
| 3189 return VisiblePositionTemplate<Strategy>(); |
| 3190 if (visibleNext.deepEquivalent() == startPosition) { |
| 3191 // |runner| is a member of canonical position set of |startPosition|
. |
| 3192 continue; |
| 3193 } |
| 3194 switch (rule) { |
| 3195 case CanCrossEditingBoundary: |
| 3196 return visibleNext; |
| 3197 case CannotCrossEditingBoundary: |
| 3198 return honorEditingBoundaryAtOrAfter(visibleNext, startPosition); |
| 3199 case CanSkipOverEditingBoundary: |
| 3200 return skipToEndOfEditingBoundary(visibleNext, startPosition); |
| 3201 } |
| 3181 | 3202 |
| 3182 switch (rule) { | 3203 ASSERT_NOT_REACHED(); |
| 3183 case CanCrossEditingBoundary: | 3204 return honorEditingBoundaryAtOrAfter(visibleNext, startPosition); |
| 3184 return next; | |
| 3185 case CannotCrossEditingBoundary: | |
| 3186 return honorEditingBoundaryAtOrAfter(next, visiblePosition.deepEquivalen
t()); | |
| 3187 case CanSkipOverEditingBoundary: | |
| 3188 return skipToEndOfEditingBoundary(next, visiblePosition.deepEquivalent()
); | |
| 3189 } | 3205 } |
| 3190 ASSERT_NOT_REACHED(); | 3206 return VisiblePositionTemplate<Strategy>(); |
| 3191 return honorEditingBoundaryAtOrAfter(next, visiblePosition.deepEquivalent())
; | |
| 3192 } | 3207 } |
| 3193 | 3208 |
| 3194 VisiblePosition nextPositionOf(const VisiblePosition& visiblePosition, EditingBo
undaryCrossingRule rule) | 3209 VisiblePosition nextPositionOf(const VisiblePosition& visiblePosition, EditingBo
undaryCrossingRule rule) |
| 3195 { | 3210 { |
| 3196 return nextPositionOfAlgorithm<EditingStrategy>(visiblePosition, rule); | 3211 return nextPositionOfAlgorithm<EditingStrategy>(visiblePosition, rule); |
| 3197 } | 3212 } |
| 3198 | 3213 |
| 3199 VisiblePositionInFlatTree nextPositionOf(const VisiblePositionInFlatTree& visibl
ePosition, EditingBoundaryCrossingRule rule) | 3214 VisiblePositionInFlatTree nextPositionOf(const VisiblePositionInFlatTree& visibl
ePosition, EditingBoundaryCrossingRule rule) |
| 3200 { | 3215 { |
| 3201 return nextPositionOfAlgorithm<EditingInFlatTreeStrategy>(visiblePosition, r
ule); | 3216 return nextPositionOfAlgorithm<EditingInFlatTreeStrategy>(visiblePosition, r
ule); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 3214 // both are non-editable. | 3229 // both are non-editable. |
| 3215 if (highestRootOfPos == highestRoot) | 3230 if (highestRootOfPos == highestRoot) |
| 3216 return pos; | 3231 return pos; |
| 3217 | 3232 |
| 3218 // If this is not editable but |pos| has an editable root, skip to the start | 3233 // If this is not editable but |pos| has an editable root, skip to the start |
| 3219 if (!highestRoot && highestRootOfPos) | 3234 if (!highestRoot && highestRootOfPos) |
| 3220 return createVisiblePosition(previousVisuallyDistinctCandidate(PositionT
emplate<Strategy>(highestRootOfPos, PositionAnchorType::BeforeAnchor).parentAnch
oredEquivalent())); | 3235 return createVisiblePosition(previousVisuallyDistinctCandidate(PositionT
emplate<Strategy>(highestRootOfPos, PositionAnchorType::BeforeAnchor).parentAnch
oredEquivalent())); |
| 3221 | 3236 |
| 3222 // That must mean that |pos| is not editable. Return the last position | 3237 // That must mean that |pos| is not editable. Return the last position |
| 3223 // before |pos| that is in the same editable region as this position | 3238 // before |pos| that is in the same editable region as this position |
| 3224 DCHECK(highestRoot); | 3239 ASSERT(highestRoot); |
| 3225 return lastEditableVisiblePositionBeforePositionInRoot(pos.deepEquivalent(),
*highestRoot); | 3240 return lastEditableVisiblePositionBeforePositionInRoot(pos.deepEquivalent(),
*highestRoot); |
| 3226 } | 3241 } |
| 3227 | 3242 |
| 3228 template <typename Strategy> | 3243 template <typename Strategy> |
| 3229 static VisiblePositionTemplate<Strategy> previousPositionOfAlgorithm(const Visib
lePositionTemplate<Strategy>& visiblePosition, EditingBoundaryCrossingRule rule) | 3244 static VisiblePositionTemplate<Strategy> previousPositionOfAlgorithm(const Visib
lePositionTemplate<Strategy>& visiblePosition, EditingBoundaryCrossingRule rule) |
| 3230 { | 3245 { |
| 3231 const PositionTemplate<Strategy> pos = previousVisuallyDistinctCandidate(vis
iblePosition.deepEquivalent()); | 3246 if (visiblePosition.isNull()) |
| 3247 return VisiblePositionTemplate<Strategy>(); |
| 3248 const PositionTemplate<Strategy>& startPosition = visiblePosition.deepEquiva
lent(); |
| 3249 const LayoutPosition& startLayoutPosition = LayoutPosition::create(startPosi
tion); |
| 3250 for (LayoutPosition runner = previousPositionOf(startLayoutPosition); |
| 3251 runner.isNotNull(); |
| 3252 runner = previousPositionOf(runner)) { |
| 3253 if (!runner.canPlaceCaret() || runner.isCaretEquivalent(startLayoutPosit
ion)) |
| 3254 continue; |
| 3255 const PositionTemplate<Strategy>& position = runner.toPosition(PositionT
emplate<Strategy>()); |
| 3256 if (position.atStartOfTree()) |
| 3257 return VisiblePositionTemplate<Strategy>(); |
| 3258 // We should always be able to make the affinity |
| 3259 // |TextAffinity::Downstream|, because going previous from an |
| 3260 // |TextAffinity::Upstream| position can never yield another |
| 3261 // |TextAffinity::Upstream position| (unless line wrap length is 0). |
| 3262 const VisiblePositionTemplate<Strategy>& visiblePrevious = createVisible
Position(position); |
| 3263 if (visiblePrevious.isNull()) |
| 3264 return VisiblePositionTemplate<Strategy>(); |
| 3265 if (visiblePrevious.deepEquivalent() == startPosition) { |
| 3266 // |runner| is a member of canonical position set of |startPosition|
. |
| 3267 continue; |
| 3268 } |
| 3269 switch (rule) { |
| 3270 case CanCrossEditingBoundary: |
| 3271 return visiblePrevious; |
| 3272 case CannotCrossEditingBoundary: |
| 3273 return honorEditingBoundaryAtOrBefore(visiblePrevious, startPosition
); |
| 3274 case CanSkipOverEditingBoundary: |
| 3275 return skipToStartOfEditingBoundary(visiblePrevious, startPosition); |
| 3276 } |
| 3232 | 3277 |
| 3233 // return null visible position if there is no previous visible position | 3278 ASSERT_NOT_REACHED(); |
| 3234 if (pos.atStartOfTree()) | 3279 return honorEditingBoundaryAtOrBefore(visiblePrevious, startPosition); |
| 3235 return VisiblePositionTemplate<Strategy>(); | |
| 3236 | |
| 3237 // we should always be able to make the affinity |TextAffinity::Downstream|, | |
| 3238 // because going previous from an |TextAffinity::Upstream| position can | |
| 3239 // never yield another |TextAffinity::Upstream position| (unless line wrap | |
| 3240 // length is 0!). | |
| 3241 const VisiblePositionTemplate<Strategy> prev = createVisiblePosition(pos); | |
| 3242 DCHECK_NE(prev.deepEquivalent(), visiblePosition.deepEquivalent()); | |
| 3243 | |
| 3244 switch (rule) { | |
| 3245 case CanCrossEditingBoundary: | |
| 3246 return prev; | |
| 3247 case CannotCrossEditingBoundary: | |
| 3248 return honorEditingBoundaryAtOrBefore(prev, visiblePosition.deepEquivale
nt()); | |
| 3249 case CanSkipOverEditingBoundary: | |
| 3250 return skipToStartOfEditingBoundary(prev, visiblePosition.deepEquivalent
()); | |
| 3251 } | 3280 } |
| 3252 | 3281 return VisiblePositionTemplate<Strategy>(); |
| 3253 ASSERT_NOT_REACHED(); | |
| 3254 return honorEditingBoundaryAtOrBefore(prev, visiblePosition.deepEquivalent()
); | |
| 3255 } | 3282 } |
| 3256 | 3283 |
| 3257 VisiblePosition previousPositionOf(const VisiblePosition& visiblePosition, Editi
ngBoundaryCrossingRule rule) | 3284 VisiblePosition previousPositionOf(const VisiblePosition& visiblePosition, Editi
ngBoundaryCrossingRule rule) |
| 3258 { | 3285 { |
| 3259 return previousPositionOfAlgorithm<EditingStrategy>(visiblePosition, rule); | 3286 return previousPositionOfAlgorithm<EditingStrategy>(visiblePosition, rule); |
| 3260 } | 3287 } |
| 3261 | 3288 |
| 3262 VisiblePositionInFlatTree previousPositionOf(const VisiblePositionInFlatTree& vi
siblePosition, EditingBoundaryCrossingRule rule) | 3289 VisiblePositionInFlatTree previousPositionOf(const VisiblePositionInFlatTree& vi
siblePosition, EditingBoundaryCrossingRule rule) |
| 3263 { | 3290 { |
| 3264 return previousPositionOfAlgorithm<EditingInFlatTreeStrategy>(visiblePositio
n, rule); | 3291 return previousPositionOfAlgorithm<EditingInFlatTreeStrategy>(visiblePositio
n, rule); |
| 3265 } | 3292 } |
| 3266 | 3293 |
| 3267 } // namespace blink | 3294 } // namespace blink |
| OLD | NEW |