OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2005 Apple Computer, 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 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
51 { | 51 { |
52 ASSERT(isTableCell(cell)); | 52 ASSERT(isTableCell(cell)); |
53 return VisiblePosition(firstPositionInNode(cell)).deepEquivalent() == Visibl
ePosition(lastPositionInNode(cell)).deepEquivalent(); | 53 return VisiblePosition(firstPositionInNode(cell)).deepEquivalent() == Visibl
ePosition(lastPositionInNode(cell)).deepEquivalent(); |
54 } | 54 } |
55 | 55 |
56 static bool isTableRowEmpty(Node* row) | 56 static bool isTableRowEmpty(Node* row) |
57 { | 57 { |
58 if (!isHTMLTableRowElement(row)) | 58 if (!isHTMLTableRowElement(row)) |
59 return false; | 59 return false; |
60 | 60 |
61 for (Node* child = row->firstChild(); child; child = child->nextSibling()) | 61 for (Node* child = row->firstChild(); child; child = child->nextSibling()) { |
62 if (isTableCell(child) && !isTableCellEmpty(child)) | 62 if (isTableCell(child) && !isTableCellEmpty(child)) |
63 return false; | 63 return false; |
64 | 64 } |
65 return true; | 65 return true; |
66 } | 66 } |
67 | 67 |
68 DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDel
ete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMa
rkup) | 68 DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDel
ete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMa
rkup) |
69 : CompositeEditCommand(document) | 69 : CompositeEditCommand(document) |
70 , m_hasSelectionToDelete(false) | 70 , m_hasSelectionToDelete(false) |
71 , m_smartDelete(smartDelete) | 71 , m_smartDelete(smartDelete) |
72 , m_mergeBlocksAfterDelete(mergeBlocksAfterDelete) | 72 , m_mergeBlocksAfterDelete(mergeBlocksAfterDelete) |
73 , m_needPlaceholder(false) | 73 , m_needPlaceholder(false) |
74 , m_expandForSpecialElements(expandForSpecialElements) | 74 , m_expandForSpecialElements(expandForSpecialElements) |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
133 break; | 133 break; |
134 | 134 |
135 // If we're going to expand to include the startSpecialContainer, it mus
t be fully selected. | 135 // If we're going to expand to include the startSpecialContainer, it mus
t be fully selected. |
136 if (startSpecialContainer && !endSpecialContainer && comparePositions(po
sitionInParentAfterNode(*startSpecialContainer), end) > -1) | 136 if (startSpecialContainer && !endSpecialContainer && comparePositions(po
sitionInParentAfterNode(*startSpecialContainer), end) > -1) |
137 break; | 137 break; |
138 | 138 |
139 // If we're going to expand to include the endSpecialContainer, it must
be fully selected. | 139 // If we're going to expand to include the endSpecialContainer, it must
be fully selected. |
140 if (endSpecialContainer && !startSpecialContainer && comparePositions(st
art, positionInParentBeforeNode(*endSpecialContainer)) > -1) | 140 if (endSpecialContainer && !startSpecialContainer && comparePositions(st
art, positionInParentBeforeNode(*endSpecialContainer)) > -1) |
141 break; | 141 break; |
142 | 142 |
143 if (startSpecialContainer && startSpecialContainer->isDescendantOf(endSp
ecialContainer)) | 143 if (startSpecialContainer && startSpecialContainer->isDescendantOf(endSp
ecialContainer)) { |
144 // Don't adjust the end yet, it is the end of a special element that
contains the start | 144 // Don't adjust the end yet, it is the end of a special element that
contains the start |
145 // special element (which may or may not be fully selected). | 145 // special element (which may or may not be fully selected). |
146 start = s; | 146 start = s; |
147 else if (endSpecialContainer && endSpecialContainer->isDescendantOf(star
tSpecialContainer)) | 147 } else if (endSpecialContainer && endSpecialContainer->isDescendantOf(st
artSpecialContainer)) { |
148 // Don't adjust the start yet, it is the start of a special element
that contains the end | 148 // Don't adjust the start yet, it is the start of a special element
that contains the end |
149 // special element (which may or may not be fully selected). | 149 // special element (which may or may not be fully selected). |
150 end = e; | 150 end = e; |
151 else { | 151 } else { |
152 start = s; | 152 start = s; |
153 end = e; | 153 end = e; |
154 } | 154 } |
155 } | 155 } |
156 } | 156 } |
157 | 157 |
158 void DeleteSelectionCommand::setStartingSelectionOnSmartDelete(const Position& s
tart, const Position& end) | 158 void DeleteSelectionCommand::setStartingSelectionOnSmartDelete(const Position& s
tart, const Position& end) |
159 { | 159 { |
160 bool isBaseFirst = startingSelection().isBaseFirst(); | 160 bool isBaseFirst = startingSelection().isBaseFirst(); |
161 VisiblePosition newBase(isBaseFirst ? start : end); | 161 VisiblePosition newBase(isBaseFirst ? start : end); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 else | 201 else |
202 m_endingPosition = m_downstreamStart; | 202 m_endingPosition = m_downstreamStart; |
203 | 203 |
204 // We don't want to merge into a block if it will mean changing the quote le
vel of content after deleting | 204 // We don't want to merge into a block if it will mean changing the quote le
vel of content after deleting |
205 // selections that contain a whole number paragraphs plus a line break, sinc
e it is unclear to most users | 205 // selections that contain a whole number paragraphs plus a line break, sinc
e it is unclear to most users |
206 // that such a selection actually ends at the start of the next paragraph. T
his matches TextEdit behavior | 206 // that such a selection actually ends at the start of the next paragraph. T
his matches TextEdit behavior |
207 // for indented paragraphs. | 207 // for indented paragraphs. |
208 // Only apply this rule if the endingSelection is a range selection. If it
is a caret, then other operations have created | 208 // Only apply this rule if the endingSelection is a range selection. If it
is a caret, then other operations have created |
209 // the selection we're deleting (like the process of creating a selection to
delete during a backspace), and the user isn't in the situation described above
. | 209 // the selection we're deleting (like the process of creating a selection to
delete during a backspace), and the user isn't in the situation described above
. |
210 if (numEnclosingMailBlockquotes(start) != numEnclosingMailBlockquotes(end) | 210 if (numEnclosingMailBlockquotes(start) != numEnclosingMailBlockquotes(end) |
211 && isStartOfParagraph(visibleEnd) && isStartOfParagraph(VisiblePosit
ion(start)) | 211 && isStartOfParagraph(visibleEnd) && isStartOfParagraph(VisiblePosition(
start)) |
212 && endingSelection().isRange()) { | 212 && endingSelection().isRange()) { |
213 m_mergeBlocksAfterDelete = false; | 213 m_mergeBlocksAfterDelete = false; |
214 m_pruneStartBlockIfNecessary = true; | 214 m_pruneStartBlockIfNecessary = true; |
215 } | 215 } |
216 | 216 |
217 // Handle leading and trailing whitespace, as well as smart delete adjustmen
ts to the selection | 217 // Handle leading and trailing whitespace, as well as smart delete adjustmen
ts to the selection |
218 m_leadingWhitespace = leadingWhitespacePosition(m_upstreamStart, m_selection
ToDelete.affinity()); | 218 m_leadingWhitespace = leadingWhitespacePosition(m_upstreamStart, m_selection
ToDelete.affinity()); |
219 m_trailingWhitespace = trailingWhitespacePosition(m_downstreamEnd, VP_DEFAUL
T_AFFINITY); | 219 m_trailingWhitespace = trailingWhitespacePosition(m_downstreamEnd, VP_DEFAUL
T_AFFINITY); |
220 | 220 |
221 if (m_smartDelete) { | 221 if (m_smartDelete) { |
222 | 222 |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
484 deleteTextFromNode(text, startOffset, m_downstreamEnd.computeOff
setInContainerNode() - startOffset); | 484 deleteTextFromNode(text, startOffset, m_downstreamEnd.computeOff
setInContainerNode() - startOffset); |
485 } else { | 485 } else { |
486 removeChildrenInRange(startNode, startOffset, m_downstreamEnd.co
mputeEditingOffset()); | 486 removeChildrenInRange(startNode, startOffset, m_downstreamEnd.co
mputeEditingOffset()); |
487 m_endingPosition = m_upstreamStart; | 487 m_endingPosition = m_upstreamStart; |
488 } | 488 } |
489 } | 489 } |
490 | 490 |
491 // The selection to delete is all in one node. | 491 // The selection to delete is all in one node. |
492 if (!startNode->layoutObject() || (!startOffset && m_downstreamEnd.atLas
tEditingPositionForNode())) | 492 if (!startNode->layoutObject() || (!startOffset && m_downstreamEnd.atLas
tEditingPositionForNode())) |
493 removeNode(startNode); | 493 removeNode(startNode); |
494 } | 494 } else { |
495 else { | |
496 bool startNodeWasDescendantOfEndNode = m_upstreamStart.anchorNode()->isD
escendantOf(m_downstreamEnd.anchorNode()); | 495 bool startNodeWasDescendantOfEndNode = m_upstreamStart.anchorNode()->isD
escendantOf(m_downstreamEnd.anchorNode()); |
497 // The selection to delete spans more than one node. | 496 // The selection to delete spans more than one node. |
498 RefPtrWillBeRawPtr<Node> node(startNode); | 497 RefPtrWillBeRawPtr<Node> node(startNode); |
499 | 498 |
500 if (startOffset > 0) { | 499 if (startOffset > 0) { |
501 if (startNode->isTextNode()) { | 500 if (startNode->isTextNode()) { |
502 // in a text node that needs to be trimmed | 501 // in a text node that needs to be trimmed |
503 Text* text = toText(node); | 502 Text* text = toText(node); |
504 deleteTextFromNode(text, startOffset, text->length() - startOffs
et); | 503 deleteTextFromNode(text, startOffset, text->length() - startOffs
et); |
505 node = NodeTraversal::next(*node); | 504 node = NodeTraversal::next(*node); |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
597 m_needPlaceholder = false; | 596 m_needPlaceholder = false; |
598 } | 597 } |
599 return; | 598 return; |
600 } | 599 } |
601 | 600 |
602 // It shouldn't have been asked to both try and merge content into the start
block and prune it. | 601 // It shouldn't have been asked to both try and merge content into the start
block and prune it. |
603 ASSERT(!m_pruneStartBlockIfNecessary); | 602 ASSERT(!m_pruneStartBlockIfNecessary); |
604 | 603 |
605 // FIXME: Deletion should adjust selection endpoints as it removes nodes so
that we never get into this state (4099839). | 604 // FIXME: Deletion should adjust selection endpoints as it removes nodes so
that we never get into this state (4099839). |
606 if (!m_downstreamEnd.inDocument() || !m_upstreamStart.inDocument()) | 605 if (!m_downstreamEnd.inDocument() || !m_upstreamStart.inDocument()) |
607 return; | 606 return; |
608 | 607 |
609 // FIXME: The deletion algorithm shouldn't let this happen. | 608 // FIXME: The deletion algorithm shouldn't let this happen. |
610 if (comparePositions(m_upstreamStart, m_downstreamEnd) > 0) | 609 if (comparePositions(m_upstreamStart, m_downstreamEnd) > 0) |
611 return; | 610 return; |
612 | 611 |
613 // There's nothing to merge. | 612 // There's nothing to merge. |
614 if (m_upstreamStart == m_downstreamEnd) | 613 if (m_upstreamStart == m_downstreamEnd) |
615 return; | 614 return; |
616 | 615 |
617 VisiblePosition startOfParagraphToMove(m_downstreamEnd); | 616 VisiblePosition startOfParagraphToMove(m_downstreamEnd); |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
677 // The endingPosition was likely clobbered by the move, so recompute it (mov
eParagraph selects the moved paragraph). | 676 // The endingPosition was likely clobbered by the move, so recompute it (mov
eParagraph selects the moved paragraph). |
678 m_endingPosition = endingSelection().start(); | 677 m_endingPosition = endingSelection().start(); |
679 } | 678 } |
680 | 679 |
681 void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows() | 680 void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows() |
682 { | 681 { |
683 if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_start
TableRow) { | 682 if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_start
TableRow) { |
684 Node* row = m_endTableRow->previousSibling(); | 683 Node* row = m_endTableRow->previousSibling(); |
685 while (row && row != m_startTableRow) { | 684 while (row && row != m_startTableRow) { |
686 RefPtrWillBeRawPtr<Node> previousRow = row->previousSibling(); | 685 RefPtrWillBeRawPtr<Node> previousRow = row->previousSibling(); |
687 if (isTableRowEmpty(row)) | 686 if (isTableRowEmpty(row)) { |
688 // Use a raw removeNode, instead of DeleteSelectionCommand's, be
cause | 687 // Use a raw removeNode, instead of DeleteSelectionCommand's, |
689 // that won't remove rows, it only empties them in preparation f
or this function. | 688 // because that won't remove rows, it only empties them in |
| 689 // preparation for this function. |
690 CompositeEditCommand::removeNode(row); | 690 CompositeEditCommand::removeNode(row); |
| 691 } |
691 row = previousRow.get(); | 692 row = previousRow.get(); |
692 } | 693 } |
693 } | 694 } |
694 | 695 |
695 // Remove empty rows after the start row. | 696 // Remove empty rows after the start row. |
696 if (m_startTableRow && m_startTableRow->inDocument() && m_startTableRow != m
_endTableRow) { | 697 if (m_startTableRow && m_startTableRow->inDocument() && m_startTableRow != m
_endTableRow) { |
697 Node* row = m_startTableRow->nextSibling(); | 698 Node* row = m_startTableRow->nextSibling(); |
698 while (row && row != m_endTableRow) { | 699 while (row && row != m_endTableRow) { |
699 RefPtrWillBeRawPtr<Node> nextRow = row->nextSibling(); | 700 RefPtrWillBeRawPtr<Node> nextRow = row->nextSibling(); |
700 if (isTableRowEmpty(row)) | 701 if (isTableRowEmpty(row)) |
701 CompositeEditCommand::removeNode(row); | 702 CompositeEditCommand::removeNode(row); |
702 row = nextRow.get(); | 703 row = nextRow.get(); |
703 } | 704 } |
704 } | 705 } |
705 | 706 |
706 if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_start
TableRow) | 707 if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_start
TableRow) { |
707 if (isTableRowEmpty(m_endTableRow.get())) { | 708 if (isTableRowEmpty(m_endTableRow.get())) { |
708 // Don't remove m_endTableRow if it's where we're putting the ending
selection. | 709 // Don't remove m_endTableRow if it's where we're putting the ending |
| 710 // selection. |
709 if (!m_endingPosition.anchorNode()->isDescendantOf(m_endTableRow.get
())) { | 711 if (!m_endingPosition.anchorNode()->isDescendantOf(m_endTableRow.get
())) { |
710 // FIXME: We probably shouldn't remove m_endTableRow unless it's
fully selected, even if it is empty. | 712 // FIXME: We probably shouldn't remove m_endTableRow unless it's |
711 // We'll need to start adjusting the selection endpoints during
deletion to know whether or not m_endTableRow | 713 // fully selected, even if it is empty. We'll need to start |
712 // was fully selected here. | 714 // adjusting the selection endpoints during deletion to know |
| 715 // whether or not m_endTableRow was fully selected here. |
713 CompositeEditCommand::removeNode(m_endTableRow.get()); | 716 CompositeEditCommand::removeNode(m_endTableRow.get()); |
714 } | 717 } |
715 } | 718 } |
| 719 } |
716 } | 720 } |
717 | 721 |
718 void DeleteSelectionCommand::calculateTypingStyleAfterDelete() | 722 void DeleteSelectionCommand::calculateTypingStyleAfterDelete() |
719 { | 723 { |
720 // Clearing any previously set typing style and doing an early return. | 724 // Clearing any previously set typing style and doing an early return. |
721 if (!m_typingStyle) { | 725 if (!m_typingStyle) { |
722 document().frame()->selection().clearTypingStyle(); | 726 document().frame()->selection().clearTypingStyle(); |
723 return; | 727 return; |
724 } | 728 } |
725 | 729 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
762 Node* node = m_endingPosition.computeContainerNode(); | 766 Node* node = m_endingPosition.computeContainerNode(); |
763 Element* rootElement = node->rootEditableElement(); | 767 Element* rootElement = node->rootEditableElement(); |
764 | 768 |
765 while (node != rootElement) { | 769 while (node != rootElement) { |
766 if (isRemovableBlock(node)) { | 770 if (isRemovableBlock(node)) { |
767 if (node == m_endingPosition.anchorNode()) | 771 if (node == m_endingPosition.anchorNode()) |
768 updatePositionForNodeRemovalPreservingChildren(m_endingPosition,
*node); | 772 updatePositionForNodeRemovalPreservingChildren(m_endingPosition,
*node); |
769 | 773 |
770 CompositeEditCommand::removeNodePreservingChildren(node); | 774 CompositeEditCommand::removeNodePreservingChildren(node); |
771 node = m_endingPosition.anchorNode(); | 775 node = m_endingPosition.anchorNode(); |
772 } else | 776 } else { |
773 node = node->parentNode(); | 777 node = node->parentNode(); |
| 778 } |
774 } | 779 } |
775 } | 780 } |
776 | 781 |
777 void DeleteSelectionCommand::doApply() | 782 void DeleteSelectionCommand::doApply() |
778 { | 783 { |
779 // If selection has not been set to a custom selection when the command was
created, | 784 // If selection has not been set to a custom selection when the command was
created, |
780 // use the current ending selection. | 785 // use the current ending selection. |
781 if (!m_hasSelectionToDelete) | 786 if (!m_hasSelectionToDelete) |
782 m_selectionToDelete = endingSelection(); | 787 m_selectionToDelete = endingSelection(); |
783 | 788 |
784 if (!m_selectionToDelete.isNonOrphanedRange() || !m_selectionToDelete.isCont
entEditable()) | 789 if (!m_selectionToDelete.isNonOrphanedRange() || !m_selectionToDelete.isCont
entEditable()) |
785 return; | 790 return; |
786 | 791 |
787 // save this to later make the selection with | 792 // save this to later make the selection with |
788 EAffinity affinity = m_selectionToDelete.affinity(); | 793 EAffinity affinity = m_selectionToDelete.affinity(); |
789 | 794 |
790 Position downstreamEnd = m_selectionToDelete.end().downstream(); | 795 Position downstreamEnd = m_selectionToDelete.end().downstream(); |
791 bool rootWillStayOpenWithoutPlaceholder = downstreamEnd.computeContainerNode
() == downstreamEnd.computeContainerNode()->rootEditableElement() | 796 bool rootWillStayOpenWithoutPlaceholder = downstreamEnd.computeContainerNode
() == downstreamEnd.computeContainerNode()->rootEditableElement() |
792 || (downstreamEnd.computeContainerNode()->isTextNode() && downstreamEnd.
computeContainerNode()->parentNode() == downstreamEnd.computeContainerNode()->ro
otEditableElement()); | 797 || (downstreamEnd.computeContainerNode()->isTextNode() && downstreamEnd.
computeContainerNode()->parentNode() == downstreamEnd.computeContainerNode()->ro
otEditableElement()); |
793 bool lineBreakAtEndOfSelectionToDelete = lineBreakExistsAtVisiblePosition(m_
selectionToDelete.visibleEnd()); | 798 bool lineBreakAtEndOfSelectionToDelete = lineBreakExistsAtVisiblePosition(m_
selectionToDelete.visibleEnd()); |
794 m_needPlaceholder = !rootWillStayOpenWithoutPlaceholder | 799 m_needPlaceholder = !rootWillStayOpenWithoutPlaceholder |
795 && isStartOfParagraph(m_selectionToDelete.visibleStart(), CanCrossEditin
gBoundary) | 800 && isStartOfParagraph(m_selectionToDelete.visibleStart(), CanCrossEditin
gBoundary) |
796 && isEndOfParagraph(m_selectionToDelete.visibleEnd(), CanCrossEditingBou
ndary) | 801 && isEndOfParagraph(m_selectionToDelete.visibleEnd(), CanCrossEditingBou
ndary) |
797 && !lineBreakAtEndOfSelectionToDelete; | 802 && !lineBreakAtEndOfSelectionToDelete; |
798 if (m_needPlaceholder) { | 803 if (m_needPlaceholder) { |
799 // Don't need a placeholder when deleting a selection that starts just b
efore a table | 804 // Don't need a placeholder when deleting a selection that starts just |
800 // and ends inside it (we do need placeholders to hold open empty cells,
but that's | 805 // before a table and ends inside it (we do need placeholders to hold |
801 // handled elsewhere). | 806 // open empty cells, but that's handled elsewhere). |
802 if (Element* table = isLastPositionBeforeTable(m_selectionToDelete.visib
leStart())) | 807 if (Element* table = isLastPositionBeforeTable(m_selectionToDelete.visib
leStart())) { |
803 if (m_selectionToDelete.end().anchorNode()->isDescendantOf(table)) | 808 if (m_selectionToDelete.end().anchorNode()->isDescendantOf(table)) |
804 m_needPlaceholder = false; | 809 m_needPlaceholder = false; |
| 810 } |
805 } | 811 } |
806 | 812 |
807 | 813 |
808 // set up our state | 814 // set up our state |
809 initializePositionData(); | 815 initializePositionData(); |
810 | 816 |
811 bool lineBreakBeforeStart = lineBreakExistsAtVisiblePosition(VisiblePosition
(m_upstreamStart).previous()); | 817 bool lineBreakBeforeStart = lineBreakExistsAtVisiblePosition(VisiblePosition
(m_upstreamStart).previous()); |
812 | 818 |
813 // Delete any text that may hinder our ability to fixup whitespace after the
delete | 819 // Delete any text that may hinder our ability to fixup whitespace after the
delete |
814 deleteInsignificantTextDownstream(m_trailingWhitespace); | 820 deleteInsignificantTextDownstream(m_trailingWhitespace); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
891 visitor->trace(m_deleteIntoBlockquoteStyle); | 897 visitor->trace(m_deleteIntoBlockquoteStyle); |
892 visitor->trace(m_startRoot); | 898 visitor->trace(m_startRoot); |
893 visitor->trace(m_endRoot); | 899 visitor->trace(m_endRoot); |
894 visitor->trace(m_startTableRow); | 900 visitor->trace(m_startTableRow); |
895 visitor->trace(m_endTableRow); | 901 visitor->trace(m_endTableRow); |
896 visitor->trace(m_temporaryPlaceholder); | 902 visitor->trace(m_temporaryPlaceholder); |
897 CompositeEditCommand::trace(visitor); | 903 CompositeEditCommand::trace(visitor); |
898 } | 904 } |
899 | 905 |
900 } // namespace blink | 906 } // namespace blink |
OLD | NEW |