Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(707)

Side by Side Diff: Source/WebCore/editing/CompositeEditCommand.cpp

Issue 9564016: Merge 107761 (Closed) Base URL: http://svn.webkit.org/repository/webkit/branches/chromium/963/
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « LayoutTests/editing/inserting/delete-insignificant-text-crash-expected.txt ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 2 * Copyright (C) 2005, 2006, 2007, 2008 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 10 matching lines...) Expand all
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */ 24 */
25 25
26 #include "config.h" 26 #include "config.h"
27 #include "CompositeEditCommand.h" 27 #include "CompositeEditCommand.h"
28 28
29 #include "AppendNodeCommand.h" 29 #include "AppendNodeCommand.h"
30 #include "ApplyStyleCommand.h" 30 #include "ApplyStyleCommand.h"
31 #include "DeleteButtonController.h"
31 #include "DeleteFromTextNodeCommand.h" 32 #include "DeleteFromTextNodeCommand.h"
32 #include "DeleteSelectionCommand.h" 33 #include "DeleteSelectionCommand.h"
33 #include "Document.h" 34 #include "Document.h"
34 #include "DocumentFragment.h" 35 #include "DocumentFragment.h"
35 #include "DocumentMarkerController.h" 36 #include "DocumentMarkerController.h"
36 #include "EditorInsertAction.h" 37 #include "EditorInsertAction.h"
37 #include "Frame.h" 38 #include "Frame.h"
38 #include "HTMLElement.h" 39 #include "HTMLElement.h"
39 #include "HTMLNames.h" 40 #include "HTMLNames.h"
40 #include "InlineTextBox.h" 41 #include "InlineTextBox.h"
41 #include "InsertIntoTextNodeCommand.h" 42 #include "InsertIntoTextNodeCommand.h"
42 #include "InsertLineBreakCommand.h" 43 #include "InsertLineBreakCommand.h"
43 #include "InsertNodeBeforeCommand.h" 44 #include "InsertNodeBeforeCommand.h"
44 #include "InsertParagraphSeparatorCommand.h" 45 #include "InsertParagraphSeparatorCommand.h"
45 #include "InsertTextCommand.h" 46 #include "InsertTextCommand.h"
46 #include "MergeIdenticalElementsCommand.h" 47 #include "MergeIdenticalElementsCommand.h"
47 #include "Range.h" 48 #include "Range.h"
48 #include "RemoveCSSPropertyCommand.h" 49 #include "RemoveCSSPropertyCommand.h"
49 #include "RemoveNodeCommand.h" 50 #include "RemoveNodeCommand.h"
50 #include "RemoveNodePreservingChildrenCommand.h" 51 #include "RemoveNodePreservingChildrenCommand.h"
51 #include "ReplaceNodeWithSpanCommand.h" 52 #include "ReplaceNodeWithSpanCommand.h"
52 #include "ReplaceSelectionCommand.h" 53 #include "ReplaceSelectionCommand.h"
53 #include "RenderBlock.h" 54 #include "RenderBlock.h"
54 #include "RenderText.h" 55 #include "RenderText.h"
56 #include "ScopedEventQueue.h"
55 #include "SetNodeAttributeCommand.h" 57 #include "SetNodeAttributeCommand.h"
56 #include "SplitElementCommand.h" 58 #include "SplitElementCommand.h"
57 #include "SplitTextNodeCommand.h" 59 #include "SplitTextNodeCommand.h"
58 #include "SplitTextNodeContainingElementCommand.h" 60 #include "SplitTextNodeContainingElementCommand.h"
59 #include "Text.h" 61 #include "Text.h"
60 #include "TextIterator.h" 62 #include "TextIterator.h"
61 #include "WrapContentsInDummySpanCommand.h" 63 #include "WrapContentsInDummySpanCommand.h"
62 #include "htmlediting.h" 64 #include "htmlediting.h"
63 #include "markup.h" 65 #include "markup.h"
64 #include "visible_units.h" 66 #include "visible_units.h"
65 #include <wtf/unicode/CharacterNames.h> 67 #include <wtf/unicode/CharacterNames.h>
66 68
67 using namespace std; 69 using namespace std;
68 70
69 namespace WebCore { 71 namespace WebCore {
70 72
71 using namespace HTMLNames; 73 using namespace HTMLNames;
72 74
75 PassRefPtr<EditCommandComposition> EditCommandComposition::create(Document* docu ment,
76 const VisibleSelection& startingSelection, const VisibleSelection& endingSel ection, EditAction editAction)
77 {
78 return adoptRef(new EditCommandComposition(document, startingSelection, endi ngSelection, editAction));
79 }
80
81 EditCommandComposition::EditCommandComposition(Document* document, const Visible Selection& startingSelection, const VisibleSelection& endingSelection, EditActio n editAction)
82 : m_document(document)
83 , m_startingSelection(startingSelection)
84 , m_endingSelection(endingSelection)
85 , m_startingRootEditableElement(startingSelection.rootEditableElement())
86 , m_endingRootEditableElement(endingSelection.rootEditableElement())
87 , m_editAction(editAction)
88 {
89 }
90
91 void EditCommandComposition::unapply()
92 {
93 ASSERT(m_document);
94 Frame* frame = m_document->frame();
95 ASSERT(frame);
96
97 // Changes to the document may have been made since the last editing operati on that require a layout, as in <rdar://problem/5658603>.
98 // Low level operations, like RemoveNodeCommand, don't require a layout beca use the high level operations that use them perform one
99 // if one is necessary (like for the creation of VisiblePositions).
100 m_document->updateLayoutIgnorePendingStylesheets();
101
102 DeleteButtonController* deleteButtonController = frame->editor()->deleteButt onController();
103 deleteButtonController->disable();
104 size_t size = m_commands.size();
105 for (size_t i = size; i != 0; --i)
106 m_commands[i - 1]->doUnapply();
107 deleteButtonController->enable();
108
109 frame->editor()->unappliedEditing(this);
110 }
111
112 void EditCommandComposition::reapply()
113 {
114 ASSERT(m_document);
115 Frame* frame = m_document->frame();
116 ASSERT(frame);
117
118 // Changes to the document may have been made since the last editing operati on that require a layout, as in <rdar://problem/5658603>.
119 // Low level operations, like RemoveNodeCommand, don't require a layout beca use the high level operations that use them perform one
120 // if one is necessary (like for the creation of VisiblePositions).
121 m_document->updateLayoutIgnorePendingStylesheets();
122
123 DeleteButtonController* deleteButtonController = frame->editor()->deleteButt onController();
124 deleteButtonController->disable();
125 size_t size = m_commands.size();
126 for (size_t i = 0; i != size; ++i)
127 m_commands[i]->doReapply();
128 deleteButtonController->enable();
129
130 frame->editor()->reappliedEditing(this);
131 }
132
133 void EditCommandComposition::append(SimpleEditCommand* command)
134 {
135 m_commands.append(command);
136 }
137
138 void EditCommandComposition::setStartingSelection(const VisibleSelection& select ion)
139 {
140 m_startingSelection = selection;
141 m_startingRootEditableElement = selection.rootEditableElement();
142 }
143
144 void EditCommandComposition::setEndingSelection(const VisibleSelection& selectio n)
145 {
146 m_endingSelection = selection;
147 m_endingRootEditableElement = selection.rootEditableElement();
148 }
149
150 #ifndef NDEBUG
151 void EditCommandComposition::getNodesInCommand(HashSet<Node*>& nodes)
152 {
153 size_t size = m_commands.size();
154 for (size_t i = 0; i < size; ++i)
155 m_commands[i]->getNodesInCommand(nodes);
156 }
157 #endif
158
159 void applyCommand(PassRefPtr<CompositeEditCommand> command)
160 {
161 command->apply();
162 }
163
73 CompositeEditCommand::CompositeEditCommand(Document *document) 164 CompositeEditCommand::CompositeEditCommand(Document *document)
74 : EditCommand(document) 165 : EditCommand(document)
75 { 166 {
76 } 167 }
77 168
78 CompositeEditCommand::~CompositeEditCommand() 169 CompositeEditCommand::~CompositeEditCommand()
79 { 170 {
171 ASSERT(isTopLevelCommand() || !m_composition);
80 } 172 }
81 173
82 void CompositeEditCommand::doUnapply() 174 void CompositeEditCommand::apply()
83 { 175 {
84 size_t size = m_commands.size(); 176 if (!endingSelection().isContentRichlyEditable()) {
85 for (size_t i = size; i != 0; --i) 177 switch (editingAction()) {
86 m_commands[i - 1]->unapply(); 178 case EditActionTyping:
179 case EditActionPaste:
180 case EditActionDrag:
181 case EditActionSetWritingDirection:
182 case EditActionCut:
183 case EditActionUnspecified:
184 break;
185 default:
186 ASSERT_NOT_REACHED();
187 return;
188 }
189 }
190 ensureComposition();
191
192 // Changes to the document may have been made since the last editing operati on that require a layout, as in <rdar://problem/5658603>.
193 // Low level operations, like RemoveNodeCommand, don't require a layout beca use the high level operations that use them perform one
194 // if one is necessary (like for the creation of VisiblePositions).
195 ASSERT(document());
196 document()->updateLayoutIgnorePendingStylesheets();
197
198 Frame* frame = document()->frame();
199 ASSERT(frame);
200 {
201 EventQueueScope scope;
202 DeleteButtonController* deleteButtonController = frame->editor()->delete ButtonController();
203 deleteButtonController->disable();
204 doApply();
205 deleteButtonController->enable();
206 }
207
208 // Only need to call appliedEditing for top-level commands,
209 // and TypingCommands do it on their own (see TypingCommand::typingAddedToOp enCommand).
210 if (!isTypingCommand())
211 frame->editor()->appliedEditing(this);
212 setShouldRetainAutocorrectionIndicator(false);
87 } 213 }
88 214
89 void CompositeEditCommand::doReapply() 215 EditCommandComposition* CompositeEditCommand::ensureComposition()
90 { 216 {
91 size_t size = m_commands.size(); 217 CompositeEditCommand* command = this;
92 for (size_t i = 0; i != size; ++i) 218 while (command && command->parent())
93 m_commands[i]->reapply(); 219 command = command->parent();
220 if (!command->m_composition)
221 command->m_composition = EditCommandComposition::create(document(), star tingSelection(), endingSelection(), editingAction());
222 return command->m_composition.get();
223 }
224
225 bool CompositeEditCommand::isCreateLinkCommand() const
226 {
227 return false;
228 }
229
230 bool CompositeEditCommand::preservesTypingStyle() const
231 {
232 return false;
233 }
234
235 bool CompositeEditCommand::isTypingCommand() const
236 {
237 return false;
238 }
239
240 bool CompositeEditCommand::shouldRetainAutocorrectionIndicator() const
241 {
242 return false;
243 }
244
245 void CompositeEditCommand::setShouldRetainAutocorrectionIndicator(bool)
246 {
94 } 247 }
95 248
96 // 249 //
97 // sugary-sweet convenience functions to help create and apply edit commands in composite commands 250 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
98 // 251 //
99 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> cmd) 252 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> prpCo mmand)
100 { 253 {
101 cmd->setParent(this); 254 RefPtr<EditCommand> command = prpCommand;
102 cmd->apply(); 255 command->setParent(this);
103 m_commands.append(cmd); 256 command->doApply();
257 if (command->isSimpleEditCommand()) {
258 command->setParent(0);
259 ensureComposition()->append(toSimpleEditCommand(command.get()));
260 }
261 m_commands.append(command.release());
104 } 262 }
105 263
106 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<CompositeEditComma nd> command, const VisibleSelection& selection) 264 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<CompositeEditComma nd> command, const VisibleSelection& selection)
107 { 265 {
108 command->setParent(this); 266 command->setParent(this);
109 if (selection != command->endingSelection()) { 267 if (selection != command->endingSelection()) {
110 command->setStartingSelection(selection); 268 command->setStartingSelection(selection);
111 command->setEndingSelection(selection); 269 command->setEndingSelection(selection);
112 } 270 }
113 command->apply(); 271 command->doApply();
114 m_commands.append(command); 272 m_commands.append(command);
115 } 273 }
116 274
117 void CompositeEditCommand::applyStyle(const EditingStyle* style, EditAction edit ingAction) 275 void CompositeEditCommand::applyStyle(const EditingStyle* style, EditAction edit ingAction)
118 { 276 {
119 applyCommandToComposite(ApplyStyleCommand::create(document(), style, editing Action)); 277 applyCommandToComposite(ApplyStyleCommand::create(document(), style, editing Action));
120 } 278 }
121 279
122 void CompositeEditCommand::applyStyle(const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction) 280 void CompositeEditCommand::applyStyle(const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction)
123 { 281 {
(...skipping 13 matching lines...) Expand all
137 void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElem ent) 295 void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElem ent)
138 { 296 {
139 applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement)); 297 applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement));
140 } 298 }
141 299
142 void CompositeEditCommand::insertLineBreak() 300 void CompositeEditCommand::insertLineBreak()
143 { 301 {
144 applyCommandToComposite(InsertLineBreakCommand::create(document())); 302 applyCommandToComposite(InsertLineBreakCommand::create(document()));
145 } 303 }
146 304
305 bool CompositeEditCommand::isRemovableBlock(const Node* node)
306 {
307 Node* parentNode = node->parentNode();
308 if ((parentNode && parentNode->firstChild() != parentNode->lastChild()) || ! node->hasTagName(divTag))
309 return false;
310
311 if (!node->isElementNode() || !toElement(node)->hasAttributes())
312 return true;
313
314 return false;
315 }
316
147 void CompositeEditCommand::insertNodeBefore(PassRefPtr<Node> insertChild, PassRe fPtr<Node> refChild) 317 void CompositeEditCommand::insertNodeBefore(PassRefPtr<Node> insertChild, PassRe fPtr<Node> refChild)
148 { 318 {
149 ASSERT(!refChild->hasTagName(bodyTag)); 319 ASSERT(!refChild->hasTagName(bodyTag));
150 applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChil d)); 320 applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChil d));
151 } 321 }
152 322
153 void CompositeEditCommand::insertNodeAfter(PassRefPtr<Node> insertChild, PassRef Ptr<Node> refChild) 323 void CompositeEditCommand::insertNodeAfter(PassRefPtr<Node> insertChild, PassRef Ptr<Node> refChild)
154 { 324 {
155 ASSERT(insertChild); 325 ASSERT(insertChild);
156 ASSERT(refChild); 326 ASSERT(refChild);
(...skipping 22 matching lines...) Expand all
179 Node* child = refChild->firstChild(); 349 Node* child = refChild->firstChild();
180 for (int i = 0; child && i < offset; i++) 350 for (int i = 0; child && i < offset; i++)
181 child = child->nextSibling(); 351 child = child->nextSibling();
182 if (child) 352 if (child)
183 insertNodeBefore(insertChild, child); 353 insertNodeBefore(insertChild, child);
184 else 354 else
185 appendNode(insertChild, static_cast<Element*>(refChild)); 355 appendNode(insertChild, static_cast<Element*>(refChild));
186 } else if (caretMinOffset(refChild) >= offset) 356 } else if (caretMinOffset(refChild) >= offset)
187 insertNodeBefore(insertChild, refChild); 357 insertNodeBefore(insertChild, refChild);
188 else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) { 358 else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) {
189 splitTextNode(static_cast<Text *>(refChild), offset); 359 splitTextNode(toText(refChild), offset);
190 360
191 // Mutation events (bug 22634) from the text node insertion may have rem oved the refChild 361 // Mutation events (bug 22634) from the text node insertion may have rem oved the refChild
192 if (!refChild->inDocument()) 362 if (!refChild->inDocument())
193 return; 363 return;
194 insertNodeBefore(insertChild, refChild); 364 insertNodeBefore(insertChild, refChild);
195 } else 365 } else
196 insertNodeAfter(insertChild, refChild); 366 insertNodeAfter(insertChild, refChild);
197 } 367 }
198 368
199 void CompositeEditCommand::appendNode(PassRefPtr<Node> node, PassRefPtr<Containe rNode> parent) 369 void CompositeEditCommand::appendNode(PassRefPtr<Node> node, PassRefPtr<Containe rNode> parent)
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 } 525 }
356 526
357 Node* tabSpan = tabSpanNode(pos.containerNode()); 527 Node* tabSpan = tabSpanNode(pos.containerNode());
358 528
359 if (pos.offsetInContainerNode() <= caretMinOffset(pos.containerNode())) 529 if (pos.offsetInContainerNode() <= caretMinOffset(pos.containerNode()))
360 return positionInParentBeforeNode(tabSpan); 530 return positionInParentBeforeNode(tabSpan);
361 531
362 if (pos.offsetInContainerNode() >= caretMaxOffset(pos.containerNode())) 532 if (pos.offsetInContainerNode() >= caretMaxOffset(pos.containerNode()))
363 return positionInParentAfterNode(tabSpan); 533 return positionInParentAfterNode(tabSpan);
364 534
365 splitTextNodeContainingElement(static_cast<Text *>(pos.containerNode()), pos .offsetInContainerNode()); 535 splitTextNodeContainingElement(toText(pos.containerNode()), pos.offsetInCont ainerNode());
366 return positionInParentBeforeNode(tabSpan); 536 return positionInParentBeforeNode(tabSpan);
367 } 537 }
368 538
369 void CompositeEditCommand::insertNodeAtTabSpanPosition(PassRefPtr<Node> node, co nst Position& pos) 539 void CompositeEditCommand::insertNodeAtTabSpanPosition(PassRefPtr<Node> node, co nst Position& pos)
370 { 540 {
371 // insert node before, after, or at split of tab span 541 // insert node before, after, or at split of tab span
372 insertNodeAt(node, positionOutsideTabSpan(pos)); 542 insertNodeAt(node, positionOutsideTabSpan(pos));
373 } 543 }
374 544
375 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAft erDelete, bool replace, bool expandForSpecialElements) 545 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAft erDelete, bool replace, bool expandForSpecialElements)
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
413 { 583 {
414 return containsOnlyWhitespace(text); 584 return containsOnlyWhitespace(text);
415 } 585 }
416 586
417 bool CompositeEditCommand::canRebalance(const Position& position) const 587 bool CompositeEditCommand::canRebalance(const Position& position) const
418 { 588 {
419 Node* node = position.containerNode(); 589 Node* node = position.containerNode();
420 if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node || !node->isTextNode()) 590 if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node || !node->isTextNode())
421 return false; 591 return false;
422 592
423 Text* textNode = static_cast<Text*>(node); 593 Text* textNode = toText(node);
424 if (textNode->length() == 0) 594 if (textNode->length() == 0)
425 return false; 595 return false;
426 596
427 RenderObject* renderer = textNode->renderer(); 597 RenderObject* renderer = textNode->renderer();
428 if (renderer && !renderer->style()->collapseWhiteSpace()) 598 if (renderer && !renderer->style()->collapseWhiteSpace())
429 return false; 599 return false;
430 600
431 return true; 601 return true;
432 } 602 }
433 603
434 // FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, co usins, etc). 604 // FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, co usins, etc).
435 void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position) 605 void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
436 { 606 {
437 Node* node = position.containerNode(); 607 Node* node = position.containerNode();
438 if (!canRebalance(position)) 608 if (!canRebalance(position))
439 return; 609 return;
440 610
441 // If the rebalance is for the single offset, and neither text[offset] nor t ext[offset - 1] are some form of whitespace, do nothing. 611 // If the rebalance is for the single offset, and neither text[offset] nor t ext[offset - 1] are some form of whitespace, do nothing.
442 int offset = position.deprecatedEditingOffset(); 612 int offset = position.deprecatedEditingOffset();
443 String text = static_cast<Text*>(node)->data(); 613 String text = toText(node)->data();
444 if (!isWhitespace(text[offset])) { 614 if (!isWhitespace(text[offset])) {
445 offset--; 615 offset--;
446 if (offset < 0 || !isWhitespace(text[offset])) 616 if (offset < 0 || !isWhitespace(text[offset]))
447 return; 617 return;
448 } 618 }
449 619
450 rebalanceWhitespaceOnTextSubstring(static_cast<Text*>(node), position.offset InContainerNode(), position.offsetInContainerNode()); 620 rebalanceWhitespaceOnTextSubstring(toText(node), position.offsetInContainerN ode(), position.offsetInContainerNode());
451 } 621 }
452 622
453 void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> p rpTextNode, int startOffset, int endOffset) 623 void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> p rpTextNode, int startOffset, int endOffset)
454 { 624 {
455 RefPtr<Text> textNode = prpTextNode; 625 RefPtr<Text> textNode = prpTextNode;
456 626
457 String text = textNode->data(); 627 String text = textNode->data();
458 ASSERT(!text.isEmpty()); 628 ASSERT(!text.isEmpty());
459 629
460 // Set upstream and downstream to define the extent of the whitespace surrou nding text[offset]. 630 // Set upstream and downstream to define the extent of the whitespace surrou nding text[offset].
(...skipping 21 matching lines...) Expand all
482 652
483 if (string != rebalancedString) 653 if (string != rebalancedString)
484 replaceTextInNodePreservingMarkers(textNode.release(), upstream, length, rebalancedString); 654 replaceTextInNodePreservingMarkers(textNode.release(), upstream, length, rebalancedString);
485 } 655 }
486 656
487 void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& positio n) 657 void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& positio n)
488 { 658 {
489 Node* node = position.deprecatedNode(); 659 Node* node = position.deprecatedNode();
490 if (!node || !node->isTextNode()) 660 if (!node || !node->isTextNode())
491 return; 661 return;
492 Text* textNode = static_cast<Text*>(node); 662 Text* textNode = toText(node);
493 663
494 if (textNode->length() == 0) 664 if (textNode->length() == 0)
495 return; 665 return;
496 RenderObject* renderer = textNode->renderer(); 666 RenderObject* renderer = textNode->renderer();
497 if (renderer && !renderer->style()->collapseWhiteSpace()) 667 if (renderer && !renderer->style()->collapseWhiteSpace())
498 return; 668 return;
499 669
500 // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it . 670 // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it .
501 Position upstreamPos = position.upstream(); 671 Position upstreamPos = position.upstream();
502 deleteInsignificantText(position.upstream(), position.downstream()); 672 deleteInsignificantText(position.upstream(), position.downstream());
503 position = upstreamPos.downstream(); 673 position = upstreamPos.downstream();
504 674
505 VisiblePosition visiblePos(position); 675 VisiblePosition visiblePos(position);
506 VisiblePosition previousVisiblePos(visiblePos.previous()); 676 VisiblePosition previousVisiblePos(visiblePos.previous());
507 Position previous(previousVisiblePos.deepEquivalent()); 677 Position previous(previousVisiblePos.deepEquivalent());
508 678
509 if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous .deprecatedNode()->isTextNode() && !previous.deprecatedNode()->hasTagName(brTag) ) 679 if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous .deprecatedNode()->isTextNode() && !previous.deprecatedNode()->hasTagName(brTag) )
510 replaceTextInNodePreservingMarkers(static_cast<Text*>(previous.deprecate dNode()), previous.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); 680 replaceTextInNodePreservingMarkers(toText(previous.deprecatedNode()), pr evious.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
511 if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.depreca tedNode()->isTextNode() && !position.deprecatedNode()->hasTagName(brTag)) 681 if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.depreca tedNode()->isTextNode() && !position.deprecatedNode()->hasTagName(brTag))
512 replaceTextInNodePreservingMarkers(static_cast<Text*>(position.deprecate dNode()), position.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); 682 replaceTextInNodePreservingMarkers(toText(position.deprecatedNode()), po sition.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
513 } 683 }
514 684
515 void CompositeEditCommand::rebalanceWhitespace() 685 void CompositeEditCommand::rebalanceWhitespace()
516 { 686 {
517 VisibleSelection selection = endingSelection(); 687 VisibleSelection selection = endingSelection();
518 if (selection.isNone()) 688 if (selection.isNone())
519 return; 689 return;
520 690
521 rebalanceWhitespaceAt(selection.start()); 691 rebalanceWhitespaceAt(selection.start());
522 if (selection.isRange()) 692 if (selection.isRange())
523 rebalanceWhitespaceAt(selection.end()); 693 rebalanceWhitespaceAt(selection.end());
524 } 694 }
525 695
526 void CompositeEditCommand::deleteInsignificantText(PassRefPtr<Text> textNode, un signed start, unsigned end) 696 void CompositeEditCommand::deleteInsignificantText(PassRefPtr<Text> textNode, un signed start, unsigned end)
527 { 697 {
528 if (!textNode || start >= end) 698 if (!textNode || start >= end)
529 return; 699 return;
530 700
701 document()->updateLayout();
702
531 RenderText* textRenderer = toRenderText(textNode->renderer()); 703 RenderText* textRenderer = toRenderText(textNode->renderer());
532 if (!textRenderer) 704 if (!textRenderer)
533 return; 705 return;
534 706
535 Vector<InlineTextBox*> sortedTextBoxes; 707 Vector<InlineTextBox*> sortedTextBoxes;
536 size_t sortedTextBoxesPosition = 0; 708 size_t sortedTextBoxesPosition = 0;
537 709
538 for (InlineTextBox* textBox = textRenderer->firstTextBox(); textBox; textBox = textBox->nextTextBox()) 710 for (InlineTextBox* textBox = textRenderer->firstTextBox(); textBox; textBox = textBox->nextTextBox())
539 sortedTextBoxes.append(textBox); 711 sortedTextBoxes.append(textBox);
540 712
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
603 } 775 }
604 776
605 void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end) 777 void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end)
606 { 778 {
607 if (start.isNull() || end.isNull()) 779 if (start.isNull() || end.isNull())
608 return; 780 return;
609 781
610 if (comparePositions(start, end) >= 0) 782 if (comparePositions(start, end) >= 0)
611 return; 783 return;
612 784
613 Node* next; 785 Vector<RefPtr<Text> > nodes;
614 for (Node* node = start.deprecatedNode(); node; node = next) { 786 for (Node* node = start.deprecatedNode(); node; node = node->traverseNextNod e()) {
615 next = node->traverseNextNode(); 787 if (node->isTextNode())
616 if (node->isTextNode()) { 788 nodes.append(toText(node));
617 Text* textNode = static_cast<Text*>(node);
618 int startOffset = node == start.deprecatedNode() ? start.deprecatedE ditingOffset() : 0;
619 int endOffset = node == end.deprecatedNode() ? end.deprecatedEditing Offset() : static_cast<int>(textNode->length());
620 deleteInsignificantText(textNode, startOffset, endOffset);
621 }
622 if (node == end.deprecatedNode()) 789 if (node == end.deprecatedNode())
623 break; 790 break;
624 } 791 }
792
793 for (size_t i = 0; i < nodes.size(); ++i) {
794 Text* textNode = nodes[i].get();
795 int startOffset = textNode == start.deprecatedNode() ? start.deprecatedE ditingOffset() : 0;
796 int endOffset = textNode == end.deprecatedNode() ? end.deprecatedEditing Offset() : static_cast<int>(textNode->length());
797 deleteInsignificantText(textNode, startOffset, endOffset);
798 }
625 } 799 }
626 800
627 void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos ) 801 void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos )
628 { 802 {
629 Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivale nt().downstream(); 803 Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivale nt().downstream();
630 deleteInsignificantText(pos, end); 804 deleteInsignificantText(pos, end);
631 } 805 }
632 806
633 PassRefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(PassRefPtr<Element > container) 807 PassRefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(PassRefPtr<Element > container)
634 { 808 {
(...skipping 19 matching lines...) Expand all
654 RefPtr<Node> placeholder = createBlockPlaceholderElement(document()); 828 RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
655 insertNodeAt(placeholder, pos); 829 insertNodeAt(placeholder, pos);
656 return placeholder.release(); 830 return placeholder.release();
657 } 831 }
658 832
659 PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* cont ainer) 833 PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* cont ainer)
660 { 834 {
661 if (!container) 835 if (!container)
662 return 0; 836 return 0;
663 837
664 updateLayout(); 838 document()->updateLayoutIgnorePendingStylesheets();
665 839
666 RenderObject* renderer = container->renderer(); 840 RenderObject* renderer = container->renderer();
667 if (!renderer || !renderer->isBlockFlow()) 841 if (!renderer || !renderer->isBlockFlow())
668 return 0; 842 return 0;
669 843
670 // append the placeholder to make sure it follows 844 // append the placeholder to make sure it follows
671 // any unrendered blocks 845 // any unrendered blocks
672 RenderBlock* block = toRenderBlock(renderer); 846 RenderBlock* block = toRenderBlock(renderer);
673 if (block->height() == 0 || (block->isListItem() && block->isEmpty())) 847 if (block->height() == 0 || (block->isListItem() && block->isEmpty()))
674 return appendBlockPlaceholder(container); 848 return appendBlockPlaceholder(container);
675 849
676 return 0; 850 return 0;
677 } 851 }
678 852
679 // Assumes that the position is at a placeholder and does the removal without mu ch checking. 853 // Assumes that the position is at a placeholder and does the removal without mu ch checking.
680 void CompositeEditCommand::removePlaceholderAt(const Position& p) 854 void CompositeEditCommand::removePlaceholderAt(const Position& p)
681 { 855 {
682 ASSERT(lineBreakExistsAtPosition(p)); 856 ASSERT(lineBreakExistsAtPosition(p));
683 857
684 // We are certain that the position is at a line break, but it may be a br o r a preserved newline. 858 // We are certain that the position is at a line break, but it may be a br o r a preserved newline.
685 if (p.anchorNode()->hasTagName(brTag)) { 859 if (p.anchorNode()->hasTagName(brTag)) {
686 removeNode(p.anchorNode()); 860 removeNode(p.anchorNode());
687 return; 861 return;
688 } 862 }
689 863
690 deleteTextFromNode(static_cast<Text*>(p.anchorNode()), p.offsetInContainerNo de(), 1); 864 deleteTextFromNode(toText(p.anchorNode()), p.offsetInContainerNode(), 1);
691 } 865 }
692 866
693 PassRefPtr<Node> CompositeEditCommand::insertNewDefaultParagraphElementAt(const Position& position) 867 PassRefPtr<Node> CompositeEditCommand::insertNewDefaultParagraphElementAt(const Position& position)
694 { 868 {
695 RefPtr<Element> paragraphElement = createDefaultParagraphElement(document()) ; 869 RefPtr<Element> paragraphElement = createDefaultParagraphElement(document()) ;
696 ExceptionCode ec; 870 ExceptionCode ec;
697 paragraphElement->appendChild(createBreakElement(document()), ec); 871 paragraphElement->appendChild(createBreakElement(document()), ec);
698 insertNodeAt(paragraphElement, position); 872 insertNodeAt(paragraphElement, position);
699 return paragraphElement.release(); 873 return paragraphElement.release();
700 } 874 }
701 875
702 // If the paragraph is not entirely within it's own block, create one and move t he paragraph into 876 // If the paragraph is not entirely within it's own block, create one and move t he paragraph into
703 // it, and return that block. Otherwise return 0. 877 // it, and return that block. Otherwise return 0.
704 PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessar y(const Position& pos) 878 PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessar y(const Position& pos)
705 { 879 {
706 if (pos.isNull()) 880 if (pos.isNull())
707 return 0; 881 return 0;
708 882
709 updateLayout(); 883 document()->updateLayoutIgnorePendingStylesheets();
710 884
711 // It's strange that this function is responsible for verifying that pos has not been invalidated 885 // It's strange that this function is responsible for verifying that pos has not been invalidated
712 // by an earlier call to this function. The caller, applyBlockStyle, should do this. 886 // by an earlier call to this function. The caller, applyBlockStyle, should do this.
713 VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY); 887 VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
714 VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos)); 888 VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
715 VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos); 889 VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
716 VisiblePosition next = visibleParagraphEnd.next(); 890 VisiblePosition next = visibleParagraphEnd.next();
717 VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd; 891 VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
718 892
719 Position upstreamStart = visibleParagraphStart.deepEquivalent().upstream(); 893 Position upstreamStart = visibleParagraphStart.deepEquivalent().upstream();
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
856 removeNodeAndPruneAncestors(node); 1030 removeNodeAndPruneAncestors(node);
857 // If the selection to move was empty and in an empty block that 1031 // If the selection to move was empty and in an empty block that
858 // doesn't require a placeholder to prop itself open (like a bordered 1032 // doesn't require a placeholder to prop itself open (like a bordered
859 // div or an li), remove it during the move (the list removal code 1033 // div or an li), remove it during the move (the list removal code
860 // expects this behavior). 1034 // expects this behavior).
861 else if (isBlock(node)) 1035 else if (isBlock(node))
862 removeNodeAndPruneAncestors(node); 1036 removeNodeAndPruneAncestors(node);
863 else if (lineBreakExistsAtPosition(position)) { 1037 else if (lineBreakExistsAtPosition(position)) {
864 // There is a preserved '\n' at caretAfterDelete. 1038 // There is a preserved '\n' at caretAfterDelete.
865 // We can safely assume this is a text node. 1039 // We can safely assume this is a text node.
866 Text* textNode = static_cast<Text*>(node); 1040 Text* textNode = toText(node);
867 if (textNode->length() == 1) 1041 if (textNode->length() == 1)
868 removeNodeAndPruneAncestors(node); 1042 removeNodeAndPruneAncestors(node);
869 else 1043 else
870 deleteTextFromNode(textNode, position.deprecatedEditingOffset(), 1); 1044 deleteTextFromNode(textNode, position.deprecatedEditingOffset(), 1);
871 } 1045 }
872 } 1046 }
873 } 1047 }
874 1048
875 // This is a version of moveParagraph that preserves style by keeping the origin al markup 1049 // This is a version of moveParagraph that preserves style by keeping the origin al markup
876 // It is currently used only by IndentOutdentCommand but it is meant to be used in the 1050 // It is currently used only by IndentOutdentCommand but it is meant to be used in the
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
1009 // baz 1183 // baz
1010 // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That would 1184 // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That would
1011 // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br . 1185 // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br .
1012 // Must recononicalize these two VisiblePositions after the pruning above. 1186 // Must recononicalize these two VisiblePositions after the pruning above.
1013 beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent()); 1187 beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
1014 afterParagraph = VisiblePosition(afterParagraph.deepEquivalent()); 1188 afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
1015 if (beforeParagraph.isNotNull() && (!isEndOfParagraph(beforeParagraph) || be foreParagraph == afterParagraph)) { 1189 if (beforeParagraph.isNotNull() && (!isEndOfParagraph(beforeParagraph) || be foreParagraph == afterParagraph)) {
1016 // FIXME: Trim text between beforeParagraph and afterParagraph if they a ren't equal. 1190 // FIXME: Trim text between beforeParagraph and afterParagraph if they a ren't equal.
1017 insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquival ent()); 1191 insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquival ent());
1018 // Need an updateLayout here in case inserting the br has split a text n ode. 1192 // Need an updateLayout here in case inserting the br has split a text n ode.
1019 updateLayout(); 1193 document()->updateLayoutIgnorePendingStylesheets();
1020 } 1194 }
1021 1195
1022 RefPtr<Range> startToDestinationRange(Range::create(document(), firstPositio nInNode(document()->documentElement()), destination.deepEquivalent().parentAncho redEquivalent())); 1196 RefPtr<Range> startToDestinationRange(Range::create(document(), firstPositio nInNode(document()->documentElement()), destination.deepEquivalent().parentAncho redEquivalent()));
1023 destinationIndex = TextIterator::rangeLength(startToDestinationRange.get(), true); 1197 destinationIndex = TextIterator::rangeLength(startToDestinationRange.get(), true);
1024 1198
1025 setEndingSelection(VisibleSelection(destination, originalIsDirectional)); 1199 setEndingSelection(VisibleSelection(destination, originalIsDirectional));
1026 ASSERT(endingSelection().isCaretOrRange()); 1200 ASSERT(endingSelection().isCaretOrRange());
1027 ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::S electReplacement | ReplaceSelectionCommand::MovingParagraph; 1201 ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::S electReplacement | ReplaceSelectionCommand::MovingParagraph;
1028 if (!preserveStyle) 1202 if (!preserveStyle)
1029 options |= ReplaceSelectionCommand::MatchStyle; 1203 options |= ReplaceSelectionCommand::MatchStyle;
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
1149 return false; 1323 return false;
1150 1324
1151 Position caretPos(caret.deepEquivalent().downstream()); 1325 Position caretPos(caret.deepEquivalent().downstream());
1152 // A line break is either a br or a preserved newline. 1326 // A line break is either a br or a preserved newline.
1153 ASSERT(caretPos.deprecatedNode()->hasTagName(brTag) || (caretPos.deprecatedN ode()->isTextNode() && caretPos.deprecatedNode()->renderer()->style()->preserveN ewline())); 1327 ASSERT(caretPos.deprecatedNode()->hasTagName(brTag) || (caretPos.deprecatedN ode()->isTextNode() && caretPos.deprecatedNode()->renderer()->style()->preserveN ewline()));
1154 1328
1155 if (caretPos.deprecatedNode()->hasTagName(brTag)) 1329 if (caretPos.deprecatedNode()->hasTagName(brTag))
1156 removeNodeAndPruneAncestors(caretPos.deprecatedNode()); 1330 removeNodeAndPruneAncestors(caretPos.deprecatedNode());
1157 else if (caretPos.deprecatedNode()->isTextNode()) { 1331 else if (caretPos.deprecatedNode()->isTextNode()) {
1158 ASSERT(caretPos.deprecatedEditingOffset() == 0); 1332 ASSERT(caretPos.deprecatedEditingOffset() == 0);
1159 Text* textNode = static_cast<Text*>(caretPos.deprecatedNode()); 1333 Text* textNode = toText(caretPos.deprecatedNode());
1160 ContainerNode* parentNode = textNode->parentNode(); 1334 ContainerNode* parentNode = textNode->parentNode();
1161 // The preserved newline must be the first thing in the node, since othe rwise the previous 1335 // The preserved newline must be the first thing in the node, since othe rwise the previous
1162 // paragraph would be quoted, and we verified that it wasn't above. 1336 // paragraph would be quoted, and we verified that it wasn't above.
1163 deleteTextFromNode(textNode, 0, 1); 1337 deleteTextFromNode(textNode, 0, 1);
1164 prune(parentNode); 1338 prune(parentNode);
1165 } 1339 }
1166 1340
1167 return true; 1341 return true;
1168 } 1342 }
1169 1343
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1254 return node.release(); 1428 return node.release();
1255 } 1429 }
1256 1430
1257 PassRefPtr<Element> createBlockPlaceholderElement(Document* document) 1431 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
1258 { 1432 {
1259 RefPtr<Element> breakNode = document->createElement(brTag, false); 1433 RefPtr<Element> breakNode = document->createElement(brTag, false);
1260 return breakNode.release(); 1434 return breakNode.release();
1261 } 1435 }
1262 1436
1263 } // namespace WebCore 1437 } // namespace WebCore
OLDNEW
« no previous file with comments | « LayoutTests/editing/inserting/delete-insignificant-text-crash-expected.txt ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698