| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. | 2 * Copyright (C) 2004, 2005, 2006, 2007 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 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 #include "core/editing/PlainTextRange.h" | 37 #include "core/editing/PlainTextRange.h" |
| 38 #include "core/editing/PositionIterator.h" | 38 #include "core/editing/PositionIterator.h" |
| 39 #include "core/editing/VisiblePosition.h" | 39 #include "core/editing/VisiblePosition.h" |
| 40 #include "core/editing/VisibleSelection.h" | 40 #include "core/editing/VisibleSelection.h" |
| 41 #include "core/editing/VisibleUnits.h" | 41 #include "core/editing/VisibleUnits.h" |
| 42 #include "core/editing/iterators/TextIterator.h" | 42 #include "core/editing/iterators/TextIterator.h" |
| 43 #include "core/editing/serializers/HTMLInterchange.h" | 43 #include "core/editing/serializers/HTMLInterchange.h" |
| 44 #include "core/editing/state_machines/BackspaceStateMachine.h" | 44 #include "core/editing/state_machines/BackspaceStateMachine.h" |
| 45 #include "core/editing/state_machines/BackwardGraphemeBoundaryStateMachine.h" | 45 #include "core/editing/state_machines/BackwardGraphemeBoundaryStateMachine.h" |
| 46 #include "core/editing/state_machines/ForwardGraphemeBoundaryStateMachine.h" | 46 #include "core/editing/state_machines/ForwardGraphemeBoundaryStateMachine.h" |
| 47 #include "core/frame/FrameView.h" |
| 47 #include "core/frame/LocalFrame.h" | 48 #include "core/frame/LocalFrame.h" |
| 48 #include "core/frame/UseCounter.h" | 49 #include "core/frame/UseCounter.h" |
| 49 #include "core/html/HTMLBRElement.h" | 50 #include "core/html/HTMLBRElement.h" |
| 50 #include "core/html/HTMLDivElement.h" | 51 #include "core/html/HTMLDivElement.h" |
| 51 #include "core/html/HTMLLIElement.h" | 52 #include "core/html/HTMLLIElement.h" |
| 52 #include "core/html/HTMLParagraphElement.h" | 53 #include "core/html/HTMLParagraphElement.h" |
| 53 #include "core/html/HTMLSpanElement.h" | 54 #include "core/html/HTMLSpanElement.h" |
| 54 #include "core/html/HTMLTableCellElement.h" | 55 #include "core/html/HTMLTableCellElement.h" |
| 55 #include "core/html/HTMLUListElement.h" | 56 #include "core/html/HTMLUListElement.h" |
| 56 #include "core/layout/LayoutObject.h" | 57 #include "core/layout/LayoutObject.h" |
| 57 #include "core/layout/LayoutTableCell.h" | 58 #include "core/layout/LayoutTableCell.h" |
| 58 #include "wtf/Assertions.h" | 59 #include "wtf/Assertions.h" |
| 59 #include "wtf/StdLibExtras.h" | 60 #include "wtf/StdLibExtras.h" |
| 60 #include "wtf/text/StringBuilder.h" | 61 #include "wtf/text/StringBuilder.h" |
| 61 #include "wtf/text/Unicode.h" | 62 #include "wtf/text/Unicode.h" |
| 62 | 63 |
| 63 namespace blink { | 64 namespace blink { |
| 64 | 65 |
| 65 using namespace HTMLNames; | 66 using namespace HTMLNames; |
| 66 | 67 |
| 67 namespace { | 68 namespace { |
| 69 |
| 68 std::ostream& operator<<(std::ostream& os, PositionMoveType type) | 70 std::ostream& operator<<(std::ostream& os, PositionMoveType type) |
| 69 { | 71 { |
| 70 static const char* const texts[] = { | 72 static const char* const texts[] = { |
| 71 "CodeUnit", "BackwardDeletion", "GraphemeCluster" | 73 "CodeUnit", "BackwardDeletion", "GraphemeCluster" |
| 72 }; | 74 }; |
| 73 const auto& it = std::begin(texts) + static_cast<size_t>(type); | 75 const auto& it = std::begin(texts) + static_cast<size_t>(type); |
| 74 DCHECK_GE(it, std::begin(texts)) << "Unknown PositionMoveType value"; | 76 DCHECK_GE(it, std::begin(texts)) << "Unknown PositionMoveType value"; |
| 75 DCHECK_LT(it, std::end(texts)) << "Unknown PositionMoveType value"; | 77 DCHECK_LT(it, std::end(texts)) << "Unknown PositionMoveType value"; |
| 76 return os << *it; | 78 return os << *it; |
| 77 } | 79 } |
| 80 |
| 78 } // namespace | 81 } // namespace |
| 79 | 82 |
| 83 bool needsLayoutTreeUpdate(const Node& node) |
| 84 { |
| 85 const Document& document = node.document(); |
| 86 if (document.needsLayoutTreeUpdate()) |
| 87 return true; |
| 88 // TODO(yosin): We should make |document::needsLayoutTreeUpdate()| to |
| 89 // check |LayoutView::needsLayout()|. |
| 90 return document.view() && document.view()->needsLayout(); |
| 91 } |
| 92 |
| 93 bool needsLayoutTreeUpdate(const Position& position) |
| 94 { |
| 95 const Node* node = position.anchorNode(); |
| 96 if (!node) |
| 97 return false; |
| 98 return needsLayoutTreeUpdate(*node); |
| 99 } |
| 100 |
| 80 // Atomic means that the node has no children, or has children which are ignored
for the | 101 // Atomic means that the node has no children, or has children which are ignored
for the |
| 81 // purposes of editing. | 102 // purposes of editing. |
| 82 bool isAtomicNode(const Node *node) | 103 bool isAtomicNode(const Node *node) |
| 83 { | 104 { |
| 84 return node && (!node->hasChildren() || editingIgnoresContent(node)); | 105 return node && (!node->hasChildren() || editingIgnoresContent(node)); |
| 85 } | 106 } |
| 86 | 107 |
| 87 template <typename Traversal> | 108 template <typename Traversal> |
| 88 static int comparePositions(Node* containerA, int offsetA, Node* containerB, int
offsetB, bool* disconnected) | 109 static int comparePositions(Node* containerA, int offsetA, Node* containerB, int
offsetB, bool* disconnected) |
| 89 { | 110 { |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 } | 276 } |
| 256 | 277 |
| 257 return highestRoot; | 278 return highestRoot; |
| 258 } | 279 } |
| 259 | 280 |
| 260 ContainerNode* highestEditableRoot(const PositionInFlatTree& position, EditableT
ype editableType) | 281 ContainerNode* highestEditableRoot(const PositionInFlatTree& position, EditableT
ype editableType) |
| 261 { | 282 { |
| 262 return highestEditableRoot(toPositionInDOMTree(position), editableType); | 283 return highestEditableRoot(toPositionInDOMTree(position), editableType); |
| 263 } | 284 } |
| 264 | 285 |
| 265 bool isEditablePosition(const Position& p, EditableType editableType, EUpdateSty
le updateStyle) | 286 bool isEditablePosition(const Position& position, EditableType editableType) |
| 266 { | 287 { |
| 267 Node* node = p.parentAnchoredEquivalent().anchorNode(); | 288 Node* node = position.parentAnchoredEquivalent().anchorNode(); |
| 268 if (!node) | 289 if (!node) |
| 269 return false; | 290 return false; |
| 270 if (updateStyle == UpdateStyle) | 291 DCHECK(node->document().isActive()); |
| 271 node->document().updateStyleAndLayoutIgnorePendingStylesheets(); | 292 if (node->document().lifecycle().state() >= DocumentLifecycle::InStyleRecalc
) { |
| 272 else | 293 // TODO(yosin): Once we change |LayoutObject::adjustStyleDifference()| |
| 273 DCHECK_EQ(updateStyle, DoNotUpdateStyle); | 294 // not to call |FrameSelection::hasCaret()|, we should not assume |
| 295 // calling |isEditablePosition()| in |InStyleRecalc| is safe. |
| 296 } else { |
| 297 DCHECK(!needsLayoutTreeUpdate(position)) << position; |
| 298 } |
| 274 | 299 |
| 275 if (isDisplayInsideTable(node)) | 300 if (isDisplayInsideTable(node)) |
| 276 node = node->parentNode(); | 301 node = node->parentNode(); |
| 277 | 302 |
| 278 if (node->isDocumentNode()) | 303 if (node->isDocumentNode()) |
| 279 return false; | 304 return false; |
| 280 return node->hasEditableStyle(editableType); | 305 return node->hasEditableStyle(editableType); |
| 281 } | 306 } |
| 282 | 307 |
| 283 bool isEditablePosition(const PositionInFlatTree& p, EditableType editableType,
EUpdateStyle updateStyle) | 308 bool isEditablePosition(const PositionInFlatTree& p, EditableType editableType) |
| 284 { | 309 { |
| 285 return isEditablePosition(toPositionInDOMTree(p), editableType, updateStyle)
; | 310 return isEditablePosition(toPositionInDOMTree(p), editableType); |
| 286 } | 311 } |
| 287 | 312 |
| 288 bool isAtUnsplittableElement(const Position& pos) | 313 bool isAtUnsplittableElement(const Position& pos) |
| 289 { | 314 { |
| 290 Node* node = pos.anchorNode(); | 315 Node* node = pos.anchorNode(); |
| 291 return (node == rootEditableElementOf(pos) || node == enclosingNodeOfType(po
s, &isTableCell)); | 316 return (node == rootEditableElementOf(pos) || node == enclosingNodeOfType(po
s, &isTableCell)); |
| 292 } | 317 } |
| 293 | 318 |
| 294 | 319 |
| 295 bool isRichlyEditablePosition(const Position& p, EditableType editableType) | 320 bool isRichlyEditablePosition(const Position& p, EditableType editableType) |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 474 } | 499 } |
| 475 | 500 |
| 476 VisiblePositionInFlatTree firstEditableVisiblePositionAfterPositionInRoot(const
PositionInFlatTree& position, ContainerNode& highestRoot) | 501 VisiblePositionInFlatTree firstEditableVisiblePositionAfterPositionInRoot(const
PositionInFlatTree& position, ContainerNode& highestRoot) |
| 477 { | 502 { |
| 478 return createVisiblePosition(firstEditablePositionAfterPositionInRoot(positi
on, highestRoot)); | 503 return createVisiblePosition(firstEditablePositionAfterPositionInRoot(positi
on, highestRoot)); |
| 479 } | 504 } |
| 480 | 505 |
| 481 template <typename Strategy> | 506 template <typename Strategy> |
| 482 PositionTemplate<Strategy> firstEditablePositionAfterPositionInRootAlgorithm(con
st PositionTemplate<Strategy>& position, Node& highestRoot) | 507 PositionTemplate<Strategy> firstEditablePositionAfterPositionInRootAlgorithm(con
st PositionTemplate<Strategy>& position, Node& highestRoot) |
| 483 { | 508 { |
| 509 DCHECK(!needsLayoutTreeUpdate(highestRoot)) << position << ' ' << highestRoo
t; |
| 484 // position falls before highestRoot. | 510 // position falls before highestRoot. |
| 485 if (position.compareTo(PositionTemplate<Strategy>::firstPositionInNode(&high
estRoot)) == -1 && highestRoot.hasEditableStyle()) | 511 if (position.compareTo(PositionTemplate<Strategy>::firstPositionInNode(&high
estRoot)) == -1 && highestRoot.hasEditableStyle()) |
| 486 return PositionTemplate<Strategy>::firstPositionInNode(&highestRoot); | 512 return PositionTemplate<Strategy>::firstPositionInNode(&highestRoot); |
| 487 | 513 |
| 488 PositionTemplate<Strategy> editablePosition = position; | 514 PositionTemplate<Strategy> editablePosition = position; |
| 489 | 515 |
| 490 if (position.anchorNode()->treeScope() != highestRoot.treeScope()) { | 516 if (position.anchorNode()->treeScope() != highestRoot.treeScope()) { |
| 491 Node* shadowAncestor = highestRoot.treeScope().ancestorInThisScope(edita
blePosition.anchorNode()); | 517 Node* shadowAncestor = highestRoot.treeScope().ancestorInThisScope(edita
blePosition.anchorNode()); |
| 492 if (!shadowAncestor) | 518 if (!shadowAncestor) |
| 493 return PositionTemplate<Strategy>(); | 519 return PositionTemplate<Strategy>(); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 520 } | 546 } |
| 521 | 547 |
| 522 VisiblePositionInFlatTree lastEditableVisiblePositionBeforePositionInRoot(const
PositionInFlatTree& position, ContainerNode& highestRoot) | 548 VisiblePositionInFlatTree lastEditableVisiblePositionBeforePositionInRoot(const
PositionInFlatTree& position, ContainerNode& highestRoot) |
| 523 { | 549 { |
| 524 return createVisiblePosition(lastEditablePositionBeforePositionInRoot(positi
on, highestRoot)); | 550 return createVisiblePosition(lastEditablePositionBeforePositionInRoot(positi
on, highestRoot)); |
| 525 } | 551 } |
| 526 | 552 |
| 527 template <typename Strategy> | 553 template <typename Strategy> |
| 528 PositionTemplate<Strategy> lastEditablePositionBeforePositionInRootAlgorithm(con
st PositionTemplate<Strategy>& position, Node& highestRoot) | 554 PositionTemplate<Strategy> lastEditablePositionBeforePositionInRootAlgorithm(con
st PositionTemplate<Strategy>& position, Node& highestRoot) |
| 529 { | 555 { |
| 556 DCHECK(!needsLayoutTreeUpdate(highestRoot)) << position << ' ' << highestRoo
t; |
| 530 // When position falls after highestRoot, the result is easy to compute. | 557 // When position falls after highestRoot, the result is easy to compute. |
| 531 if (position.compareTo(PositionTemplate<Strategy>::lastPositionInNode(&highe
stRoot)) == 1) | 558 if (position.compareTo(PositionTemplate<Strategy>::lastPositionInNode(&highe
stRoot)) == 1) |
| 532 return PositionTemplate<Strategy>::lastPositionInNode(&highestRoot); | 559 return PositionTemplate<Strategy>::lastPositionInNode(&highestRoot); |
| 533 | 560 |
| 534 PositionTemplate<Strategy> editablePosition = position; | 561 PositionTemplate<Strategy> editablePosition = position; |
| 535 | 562 |
| 536 if (position.anchorNode()->treeScope() != highestRoot.treeScope()) { | 563 if (position.anchorNode()->treeScope() != highestRoot.treeScope()) { |
| 537 Node* shadowAncestor = highestRoot.treeScope().ancestorInThisScope(edita
blePosition.anchorNode()); | 564 Node* shadowAncestor = highestRoot.treeScope().ancestorInThisScope(edita
blePosition.anchorNode()); |
| 538 if (!shadowAncestor) | 565 if (!shadowAncestor) |
| 539 return PositionTemplate<Strategy>(); | 566 return PositionTemplate<Strategy>(); |
| (...skipping 870 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1410 return currentPos; | 1437 return currentPos; |
| 1411 } | 1438 } |
| 1412 } | 1439 } |
| 1413 | 1440 |
| 1414 return position; | 1441 return position; |
| 1415 } | 1442 } |
| 1416 | 1443 |
| 1417 // This assumes that it starts in editable content. | 1444 // This assumes that it starts in editable content. |
| 1418 Position leadingWhitespacePosition(const Position& position, TextAffinity affini
ty, WhitespacePositionOption option) | 1445 Position leadingWhitespacePosition(const Position& position, TextAffinity affini
ty, WhitespacePositionOption option) |
| 1419 { | 1446 { |
| 1420 DCHECK(isEditablePosition(position, ContentIsEditable, DoNotUpdateStyle)) <<
position; | 1447 DCHECK(isEditablePosition(position, ContentIsEditable)) << position; |
| 1421 if (position.isNull()) | 1448 if (position.isNull()) |
| 1422 return Position(); | 1449 return Position(); |
| 1423 | 1450 |
| 1424 if (isHTMLBRElement(*mostBackwardCaretPosition(position).anchorNode())) | 1451 if (isHTMLBRElement(*mostBackwardCaretPosition(position).anchorNode())) |
| 1425 return Position(); | 1452 return Position(); |
| 1426 | 1453 |
| 1427 const Position& prev = previousCharacterPosition(position, affinity); | 1454 const Position& prev = previousCharacterPosition(position, affinity); |
| 1428 if (prev == position) | 1455 if (prev == position) |
| 1429 return Position(); | 1456 return Position(); |
| 1430 const Node* const anchorNode = prev.anchorNode(); | 1457 const Node* const anchorNode = prev.anchorNode(); |
| 1431 if (!anchorNode || !anchorNode->isTextNode()) | 1458 if (!anchorNode || !anchorNode->isTextNode()) |
| 1432 return Position(); | 1459 return Position(); |
| 1433 if (enclosingBlockFlowElement(*anchorNode) != enclosingBlockFlowElement(*pos
ition.anchorNode())) | 1460 if (enclosingBlockFlowElement(*anchorNode) != enclosingBlockFlowElement(*pos
ition.anchorNode())) |
| 1434 return Position(); | 1461 return Position(); |
| 1435 if (option == NotConsiderNonCollapsibleWhitespace && anchorNode->layoutObjec
t() && !anchorNode->layoutObject()->style()->collapseWhiteSpace()) | 1462 if (option == NotConsiderNonCollapsibleWhitespace && anchorNode->layoutObjec
t() && !anchorNode->layoutObject()->style()->collapseWhiteSpace()) |
| 1436 return Position(); | 1463 return Position(); |
| 1437 const String& string = toText(anchorNode)->data(); | 1464 const String& string = toText(anchorNode)->data(); |
| 1438 const UChar previousCharacter = string[prev.computeOffsetInContainerNode()]; | 1465 const UChar previousCharacter = string[prev.computeOffsetInContainerNode()]; |
| 1439 const bool isSpace = option == ConsiderNonCollapsibleWhitespace ? (isSpaceOr
Newline(previousCharacter) || previousCharacter == noBreakSpaceCharacter) : isCo
llapsibleWhitespace(previousCharacter); | 1466 const bool isSpace = option == ConsiderNonCollapsibleWhitespace ? (isSpaceOr
Newline(previousCharacter) || previousCharacter == noBreakSpaceCharacter) : isCo
llapsibleWhitespace(previousCharacter); |
| 1440 if (!isSpace || !isEditablePosition(prev)) | 1467 if (!isSpace || !isEditablePosition(prev)) |
| 1441 return Position(); | 1468 return Position(); |
| 1442 return prev; | 1469 return prev; |
| 1443 } | 1470 } |
| 1444 | 1471 |
| 1445 // This assumes that it starts in editable content. | 1472 // This assumes that it starts in editable content. |
| 1446 Position trailingWhitespacePosition(const Position& position, TextAffinity, Whit
espacePositionOption option) | 1473 Position trailingWhitespacePosition(const Position& position, TextAffinity, Whit
espacePositionOption option) |
| 1447 { | 1474 { |
| 1448 DCHECK(isEditablePosition(position, ContentIsEditable, DoNotUpdateStyle)) <<
position; | 1475 DCHECK(isEditablePosition(position, ContentIsEditable)) << position; |
| 1449 if (position.isNull()) | 1476 if (position.isNull()) |
| 1450 return Position(); | 1477 return Position(); |
| 1451 | 1478 |
| 1452 VisiblePosition visiblePosition = createVisiblePosition(position); | 1479 VisiblePosition visiblePosition = createVisiblePosition(position); |
| 1453 UChar characterAfterVisiblePosition = characterAfter(visiblePosition); | 1480 UChar characterAfterVisiblePosition = characterAfter(visiblePosition); |
| 1454 bool isSpace = option == ConsiderNonCollapsibleWhitespace ? (isSpaceOrNewlin
e(characterAfterVisiblePosition) || characterAfterVisiblePosition == noBreakSpac
eCharacter) : isCollapsibleWhitespace(characterAfterVisiblePosition); | 1481 bool isSpace = option == ConsiderNonCollapsibleWhitespace ? (isSpaceOrNewlin
e(characterAfterVisiblePosition) || characterAfterVisiblePosition == noBreakSpac
eCharacter) : isCollapsibleWhitespace(characterAfterVisiblePosition); |
| 1455 // The space must not be in another paragraph and it must be editable. | 1482 // The space must not be in another paragraph and it must be editable. |
| 1456 if (isSpace && !isEndOfParagraph(visiblePosition) && nextPositionOf(visibleP
osition, CannotCrossEditingBoundary).isNotNull()) | 1483 if (isSpace && !isEndOfParagraph(visiblePosition) && nextPositionOf(visibleP
osition, CannotCrossEditingBoundary).isNotNull()) |
| 1457 return position; | 1484 return position; |
| 1458 return Position(); | 1485 return Position(); |
| (...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1784 { | 1811 { |
| 1785 if (!RuntimeEnabledFeatures::inputEventEnabled()) | 1812 if (!RuntimeEnabledFeatures::inputEventEnabled()) |
| 1786 return DispatchEventResult::NotCanceled; | 1813 return DispatchEventResult::NotCanceled; |
| 1787 if (!target) | 1814 if (!target) |
| 1788 return DispatchEventResult::NotCanceled; | 1815 return DispatchEventResult::NotCanceled; |
| 1789 InputEvent* beforeInputEvent = InputEvent::createBeforeInput(inputType, data
, InputEvent::EventCancelable::IsCancelable, InputEvent::EventIsComposing::NotCo
mposing, ranges); | 1816 InputEvent* beforeInputEvent = InputEvent::createBeforeInput(inputType, data
, InputEvent::EventCancelable::IsCancelable, InputEvent::EventIsComposing::NotCo
mposing, ranges); |
| 1790 return target->dispatchEvent(beforeInputEvent); | 1817 return target->dispatchEvent(beforeInputEvent); |
| 1791 } | 1818 } |
| 1792 | 1819 |
| 1793 } // namespace blink | 1820 } // namespace blink |
| OLD | NEW |