• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
24  */
25 
26 #include "config.h"
27 #include "core/editing/CompositeEditCommand.h"
28 
29 #include "HTMLNames.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/DocumentFragment.h"
33 #include "core/dom/DocumentMarkerController.h"
34 #include "core/dom/NodeTraversal.h"
35 #include "core/dom/Range.h"
36 #include "core/events/ScopedEventQueue.h"
37 #include "core/dom/Text.h"
38 #include "core/editing/AppendNodeCommand.h"
39 #include "core/editing/ApplyStyleCommand.h"
40 #include "core/editing/DeleteFromTextNodeCommand.h"
41 #include "core/editing/DeleteSelectionCommand.h"
42 #include "core/editing/Editor.h"
43 #include "core/editing/InsertIntoTextNodeCommand.h"
44 #include "core/editing/InsertLineBreakCommand.h"
45 #include "core/editing/InsertNodeBeforeCommand.h"
46 #include "core/editing/InsertParagraphSeparatorCommand.h"
47 #include "core/editing/MergeIdenticalElementsCommand.h"
48 #include "core/editing/PlainTextRange.h"
49 #include "core/editing/RemoveCSSPropertyCommand.h"
50 #include "core/editing/RemoveNodeCommand.h"
51 #include "core/editing/RemoveNodePreservingChildrenCommand.h"
52 #include "core/editing/ReplaceNodeWithSpanCommand.h"
53 #include "core/editing/ReplaceSelectionCommand.h"
54 #include "core/editing/SetNodeAttributeCommand.h"
55 #include "core/editing/SpellChecker.h"
56 #include "core/editing/SplitElementCommand.h"
57 #include "core/editing/SplitTextNodeCommand.h"
58 #include "core/editing/SplitTextNodeContainingElementCommand.h"
59 #include "core/editing/TextIterator.h"
60 #include "core/editing/VisibleUnits.h"
61 #include "core/editing/WrapContentsInDummySpanCommand.h"
62 #include "core/editing/htmlediting.h"
63 #include "core/editing/markup.h"
64 #include "core/html/HTMLElement.h"
65 #include "core/frame/Frame.h"
66 #include "core/rendering/InlineTextBox.h"
67 #include "core/rendering/RenderBlock.h"
68 #include "core/rendering/RenderText.h"
69 
70 using namespace std;
71 
72 namespace WebCore {
73 
74 using namespace HTMLNames;
75 
76 namespace {
77 class ReentrancyGuard {
78 public:
isRecursiveCall()79     static bool isRecursiveCall() { return s_nestingCounter; }
80 
81     class Scope {
82     public:
Scope()83         Scope() { ++s_nestingCounter; }
~Scope()84         ~Scope() { --s_nestingCounter; }
85     };
86     friend class Scope;
87 
88 private:
89     static int s_nestingCounter;
90 };
91 int ReentrancyGuard::s_nestingCounter;
92 }
93 
create(Document * document,const VisibleSelection & startingSelection,const VisibleSelection & endingSelection,EditAction editAction)94 PassRefPtr<EditCommandComposition> EditCommandComposition::create(Document* document,
95     const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
96 {
97     return adoptRef(new EditCommandComposition(document, startingSelection, endingSelection, editAction));
98 }
99 
EditCommandComposition(Document * document,const VisibleSelection & startingSelection,const VisibleSelection & endingSelection,EditAction editAction)100 EditCommandComposition::EditCommandComposition(Document* document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
101     : m_document(document)
102     , m_startingSelection(startingSelection)
103     , m_endingSelection(endingSelection)
104     , m_startingRootEditableElement(startingSelection.rootEditableElement())
105     , m_endingRootEditableElement(endingSelection.rootEditableElement())
106     , m_editAction(editAction)
107 {
108 }
109 
belongsTo(const Frame & frame) const110 bool EditCommandComposition::belongsTo(const Frame& frame) const
111 {
112     ASSERT(m_document);
113     return m_document->frame() == &frame;
114 }
115 
unapply()116 void EditCommandComposition::unapply()
117 {
118     ASSERT(m_document);
119     RefPtr<Frame> frame = m_document->frame();
120     ASSERT(frame);
121 
122     // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
123     // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
124     // if one is necessary (like for the creation of VisiblePositions).
125     m_document->updateLayoutIgnorePendingStylesheets();
126 
127     {
128         size_t size = m_commands.size();
129         for (size_t i = size; i; --i)
130             m_commands[i - 1]->doUnapply();
131     }
132 
133     frame->editor().unappliedEditing(this);
134 }
135 
reapply()136 void EditCommandComposition::reapply()
137 {
138     ASSERT(m_document);
139     RefPtr<Frame> frame = m_document->frame();
140     ASSERT(frame);
141 
142     // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
143     // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
144     // if one is necessary (like for the creation of VisiblePositions).
145     m_document->updateLayoutIgnorePendingStylesheets();
146 
147     {
148         size_t size = m_commands.size();
149         for (size_t i = 0; i != size; ++i)
150             m_commands[i]->doReapply();
151     }
152 
153     frame->editor().reappliedEditing(this);
154 }
155 
append(SimpleEditCommand * command)156 void EditCommandComposition::append(SimpleEditCommand* command)
157 {
158     m_commands.append(command);
159 }
160 
setStartingSelection(const VisibleSelection & selection)161 void EditCommandComposition::setStartingSelection(const VisibleSelection& selection)
162 {
163     m_startingSelection = selection;
164     m_startingRootEditableElement = selection.rootEditableElement();
165 }
166 
setEndingSelection(const VisibleSelection & selection)167 void EditCommandComposition::setEndingSelection(const VisibleSelection& selection)
168 {
169     m_endingSelection = selection;
170     m_endingRootEditableElement = selection.rootEditableElement();
171 }
172 
CompositeEditCommand(Document & document)173 CompositeEditCommand::CompositeEditCommand(Document& document)
174     : EditCommand(document)
175 {
176 }
177 
~CompositeEditCommand()178 CompositeEditCommand::~CompositeEditCommand()
179 {
180     ASSERT(isTopLevelCommand() || !m_composition);
181 }
182 
apply()183 void CompositeEditCommand::apply()
184 {
185     // We don't allow recusrive |apply()| to protect against attack code.
186     // Recursive call of |apply()| could be happened by moving iframe
187     // with script triggered by insertion, e.g. <iframe src="javascript:...">
188     // <iframe onload="...">. This usage is valid as of the specification
189     // although, it isn't common use case, rather it is used as attack code.
190     if (ReentrancyGuard::isRecursiveCall())
191         return;
192 
193     if (!endingSelection().isContentRichlyEditable()) {
194         switch (editingAction()) {
195         case EditActionTyping:
196         case EditActionPaste:
197         case EditActionDrag:
198         case EditActionSetWritingDirection:
199         case EditActionCut:
200         case EditActionUnspecified:
201             break;
202         default:
203             ASSERT_NOT_REACHED();
204             return;
205         }
206     }
207     ensureComposition();
208 
209     // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
210     // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
211     // if one is necessary (like for the creation of VisiblePositions).
212     document().updateLayoutIgnorePendingStylesheets();
213 
214     Frame* frame = document().frame();
215     ASSERT(frame);
216     {
217         EventQueueScope eventQueueScope;
218         ReentrancyGuard::Scope reentrancyGuardScope;
219         doApply();
220     }
221 
222     // Only need to call appliedEditing for top-level commands,
223     // and TypingCommands do it on their own (see TypingCommand::typingAddedToOpenCommand).
224     if (!isTypingCommand())
225         frame->editor().appliedEditing(this);
226     setShouldRetainAutocorrectionIndicator(false);
227 }
228 
ensureComposition()229 EditCommandComposition* CompositeEditCommand::ensureComposition()
230 {
231     CompositeEditCommand* command = this;
232     while (command && command->parent())
233         command = command->parent();
234     if (!command->m_composition)
235         command->m_composition = EditCommandComposition::create(&document(), startingSelection(), endingSelection(), editingAction());
236     return command->m_composition.get();
237 }
238 
isCreateLinkCommand() const239 bool CompositeEditCommand::isCreateLinkCommand() const
240 {
241     return false;
242 }
243 
preservesTypingStyle() const244 bool CompositeEditCommand::preservesTypingStyle() const
245 {
246     return false;
247 }
248 
isTypingCommand() const249 bool CompositeEditCommand::isTypingCommand() const
250 {
251     return false;
252 }
253 
shouldRetainAutocorrectionIndicator() const254 bool CompositeEditCommand::shouldRetainAutocorrectionIndicator() const
255 {
256     return false;
257 }
258 
setShouldRetainAutocorrectionIndicator(bool)259 void CompositeEditCommand::setShouldRetainAutocorrectionIndicator(bool)
260 {
261 }
262 
263 //
264 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
265 //
applyCommandToComposite(PassRefPtr<EditCommand> prpCommand)266 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> prpCommand)
267 {
268     RefPtr<EditCommand> command = prpCommand;
269     command->setParent(this);
270     command->doApply();
271     if (command->isSimpleEditCommand()) {
272         command->setParent(0);
273         ensureComposition()->append(toSimpleEditCommand(command.get()));
274     }
275     m_commands.append(command.release());
276 }
277 
applyCommandToComposite(PassRefPtr<CompositeEditCommand> command,const VisibleSelection & selection)278 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<CompositeEditCommand> command, const VisibleSelection& selection)
279 {
280     command->setParent(this);
281     if (selection != command->endingSelection()) {
282         command->setStartingSelection(selection);
283         command->setEndingSelection(selection);
284     }
285     command->doApply();
286     m_commands.append(command);
287 }
288 
applyStyle(const EditingStyle * style,EditAction editingAction)289 void CompositeEditCommand::applyStyle(const EditingStyle* style, EditAction editingAction)
290 {
291     applyCommandToComposite(ApplyStyleCommand::create(document(), style, editingAction));
292 }
293 
applyStyle(const EditingStyle * style,const Position & start,const Position & end,EditAction editingAction)294 void CompositeEditCommand::applyStyle(const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction)
295 {
296     applyCommandToComposite(ApplyStyleCommand::create(document(), style, start, end, editingAction));
297 }
298 
applyStyledElement(PassRefPtr<Element> element)299 void CompositeEditCommand::applyStyledElement(PassRefPtr<Element> element)
300 {
301     applyCommandToComposite(ApplyStyleCommand::create(element, false));
302 }
303 
removeStyledElement(PassRefPtr<Element> element)304 void CompositeEditCommand::removeStyledElement(PassRefPtr<Element> element)
305 {
306     applyCommandToComposite(ApplyStyleCommand::create(element, true));
307 }
308 
insertParagraphSeparator(bool useDefaultParagraphElement,bool pasteBlockqutoeIntoUnquotedArea)309 void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
310 {
311     applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
312 }
313 
insertLineBreak()314 void CompositeEditCommand::insertLineBreak()
315 {
316     applyCommandToComposite(InsertLineBreakCommand::create(document()));
317 }
318 
isRemovableBlock(const Node * node)319 bool CompositeEditCommand::isRemovableBlock(const Node* node)
320 {
321     if (!node->hasTagName(divTag))
322         return false;
323 
324     Node* parentNode = node->parentNode();
325     if (parentNode && parentNode->firstChild() != parentNode->lastChild())
326         return false;
327 
328     if (!toElement(node)->hasAttributes())
329         return true;
330 
331     return false;
332 }
333 
insertNodeBefore(PassRefPtr<Node> insertChild,PassRefPtr<Node> refChild,ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)334 void CompositeEditCommand::insertNodeBefore(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
335 {
336     ASSERT(!refChild->hasTagName(bodyTag));
337     applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable));
338 }
339 
insertNodeAfter(PassRefPtr<Node> insertChild,PassRefPtr<Node> refChild)340 void CompositeEditCommand::insertNodeAfter(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild)
341 {
342     ASSERT(insertChild);
343     ASSERT(refChild);
344     ASSERT(!refChild->hasTagName(bodyTag));
345     ContainerNode* parent = refChild->parentNode();
346     ASSERT(parent);
347     ASSERT(!parent->isShadowRoot());
348     if (parent->lastChild() == refChild)
349         appendNode(insertChild, parent);
350     else {
351         ASSERT(refChild->nextSibling());
352         insertNodeBefore(insertChild, refChild->nextSibling());
353     }
354 }
355 
insertNodeAt(PassRefPtr<Node> insertChild,const Position & editingPosition)356 void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Position& editingPosition)
357 {
358     ASSERT(isEditablePosition(editingPosition));
359     // For editing positions like [table, 0], insert before the table,
360     // likewise for replaced elements, brs, etc.
361     Position p = editingPosition.parentAnchoredEquivalent();
362     Node* refChild = p.deprecatedNode();
363     int offset = p.deprecatedEditingOffset();
364 
365     if (canHaveChildrenForEditing(refChild)) {
366         Node* child = refChild->firstChild();
367         for (int i = 0; child && i < offset; i++)
368             child = child->nextSibling();
369         if (child)
370             insertNodeBefore(insertChild, child);
371         else
372             appendNode(insertChild, toContainerNode(refChild));
373     } else if (caretMinOffset(refChild) >= offset)
374         insertNodeBefore(insertChild, refChild);
375     else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) {
376         splitTextNode(toText(refChild), offset);
377 
378         // Mutation events (bug 22634) from the text node insertion may have removed the refChild
379         if (!refChild->inDocument())
380             return;
381         insertNodeBefore(insertChild, refChild);
382     } else
383         insertNodeAfter(insertChild, refChild);
384 }
385 
appendNode(PassRefPtr<Node> node,PassRefPtr<ContainerNode> parent)386 void CompositeEditCommand::appendNode(PassRefPtr<Node> node, PassRefPtr<ContainerNode> parent)
387 {
388     ASSERT(canHaveChildrenForEditing(parent.get()));
389     applyCommandToComposite(AppendNodeCommand::create(parent, node));
390 }
391 
removeChildrenInRange(PassRefPtr<Node> node,unsigned from,unsigned to)392 void CompositeEditCommand::removeChildrenInRange(PassRefPtr<Node> node, unsigned from, unsigned to)
393 {
394     Vector<RefPtr<Node> > children;
395     Node* child = node->childNode(from);
396     for (unsigned i = from; child && i < to; i++, child = child->nextSibling())
397         children.append(child);
398 
399     size_t size = children.size();
400     for (size_t i = 0; i < size; ++i)
401         removeNode(children[i].release());
402 }
403 
removeNode(PassRefPtr<Node> node,ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)404 void CompositeEditCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
405 {
406     if (!node || !node->nonShadowBoundaryParentNode())
407         return;
408     applyCommandToComposite(RemoveNodeCommand::create(node, shouldAssumeContentIsAlwaysEditable));
409 }
410 
removeNodePreservingChildren(PassRefPtr<Node> node,ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)411 void CompositeEditCommand::removeNodePreservingChildren(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
412 {
413     applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable));
414 }
415 
removeNodeAndPruneAncestors(PassRefPtr<Node> node,Node * excludeNode)416 void CompositeEditCommand::removeNodeAndPruneAncestors(PassRefPtr<Node> node, Node* excludeNode)
417 {
418     ASSERT(node.get() != excludeNode);
419     RefPtr<ContainerNode> parent = node->parentNode();
420     removeNode(node);
421     prune(parent.release(), excludeNode);
422 }
423 
moveRemainingSiblingsToNewParent(Node * node,Node * pastLastNodeToMove,PassRefPtr<Element> prpNewParent)424 void CompositeEditCommand::moveRemainingSiblingsToNewParent(Node* node, Node* pastLastNodeToMove, PassRefPtr<Element> prpNewParent)
425 {
426     NodeVector nodesToRemove;
427     RefPtr<Element> newParent = prpNewParent;
428 
429     for (; node && node != pastLastNodeToMove; node = node->nextSibling())
430         nodesToRemove.append(node);
431 
432     for (unsigned i = 0; i < nodesToRemove.size(); i++) {
433         removeNode(nodesToRemove[i]);
434         appendNode(nodesToRemove[i], newParent);
435     }
436 }
437 
updatePositionForNodeRemovalPreservingChildren(Position & position,Node * node)438 void CompositeEditCommand::updatePositionForNodeRemovalPreservingChildren(Position& position, Node* node)
439 {
440     int offset = (position.anchorType() == Position::PositionIsOffsetInAnchor) ? position.offsetInContainerNode() : 0;
441     updatePositionForNodeRemoval(position, node);
442     if (offset)
443         position.moveToOffset(offset);
444 }
445 
replaceElementWithSpanPreservingChildrenAndAttributes(PassRefPtr<HTMLElement> node)446 HTMLElement* CompositeEditCommand::replaceElementWithSpanPreservingChildrenAndAttributes(PassRefPtr<HTMLElement> node)
447 {
448     // It would also be possible to implement all of ReplaceNodeWithSpanCommand
449     // as a series of existing smaller edit commands.  Someone who wanted to
450     // reduce the number of edit commands could do so here.
451     RefPtr<ReplaceNodeWithSpanCommand> command = ReplaceNodeWithSpanCommand::create(node);
452     applyCommandToComposite(command);
453     // Returning a raw pointer here is OK because the command is retained by
454     // applyCommandToComposite (thus retaining the span), and the span is also
455     // in the DOM tree, and thus alive whie it has a parent.
456     ASSERT(command->spanElement()->inDocument());
457     return command->spanElement();
458 }
459 
prune(PassRefPtr<Node> node,Node * excludeNode)460 void CompositeEditCommand::prune(PassRefPtr<Node> node, Node* excludeNode)
461 {
462     if (RefPtr<Node> highestNodeToRemove = highestNodeToRemoveInPruning(node.get(), excludeNode))
463         removeNode(highestNodeToRemove.release());
464 }
465 
splitTextNode(PassRefPtr<Text> node,unsigned offset)466 void CompositeEditCommand::splitTextNode(PassRefPtr<Text> node, unsigned offset)
467 {
468     applyCommandToComposite(SplitTextNodeCommand::create(node, offset));
469 }
470 
splitElement(PassRefPtr<Element> element,PassRefPtr<Node> atChild)471 void CompositeEditCommand::splitElement(PassRefPtr<Element> element, PassRefPtr<Node> atChild)
472 {
473     applyCommandToComposite(SplitElementCommand::create(element, atChild));
474 }
475 
mergeIdenticalElements(PassRefPtr<Element> prpFirst,PassRefPtr<Element> prpSecond)476 void CompositeEditCommand::mergeIdenticalElements(PassRefPtr<Element> prpFirst, PassRefPtr<Element> prpSecond)
477 {
478     RefPtr<Element> first = prpFirst;
479     RefPtr<Element> second = prpSecond;
480     ASSERT(!first->isDescendantOf(second.get()) && second != first);
481     if (first->nextSibling() != second) {
482         removeNode(second);
483         insertNodeAfter(second, first);
484     }
485     applyCommandToComposite(MergeIdenticalElementsCommand::create(first, second));
486 }
487 
wrapContentsInDummySpan(PassRefPtr<Element> element)488 void CompositeEditCommand::wrapContentsInDummySpan(PassRefPtr<Element> element)
489 {
490     applyCommandToComposite(WrapContentsInDummySpanCommand::create(element));
491 }
492 
splitTextNodeContainingElement(PassRefPtr<Text> text,unsigned offset)493 void CompositeEditCommand::splitTextNodeContainingElement(PassRefPtr<Text> text, unsigned offset)
494 {
495     applyCommandToComposite(SplitTextNodeContainingElementCommand::create(text, offset));
496 }
497 
insertTextIntoNode(PassRefPtr<Text> node,unsigned offset,const String & text)498 void CompositeEditCommand::insertTextIntoNode(PassRefPtr<Text> node, unsigned offset, const String& text)
499 {
500     if (!text.isEmpty())
501         applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text));
502 }
503 
deleteTextFromNode(PassRefPtr<Text> node,unsigned offset,unsigned count)504 void CompositeEditCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned offset, unsigned count)
505 {
506     applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
507 }
508 
replaceTextInNode(PassRefPtr<Text> prpNode,unsigned offset,unsigned count,const String & replacementText)509 void CompositeEditCommand::replaceTextInNode(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
510 {
511     RefPtr<Text> node(prpNode);
512     applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
513     if (!replacementText.isEmpty())
514         applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, replacementText));
515 }
516 
replaceSelectedTextInNode(const String & text)517 Position CompositeEditCommand::replaceSelectedTextInNode(const String& text)
518 {
519     Position start = endingSelection().start();
520     Position end = endingSelection().end();
521     if (start.containerNode() != end.containerNode() || !start.containerNode()->isTextNode() || isTabSpanTextNode(start.containerNode()))
522         return Position();
523 
524     RefPtr<Text> textNode = start.containerText();
525     replaceTextInNode(textNode, start.offsetInContainerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), text);
526 
527     return Position(textNode.release(), start.offsetInContainerNode() + text.length());
528 }
529 
copyMarkers(const Vector<DocumentMarker * > & markerPointers,Vector<DocumentMarker> & markers)530 static void copyMarkers(const Vector<DocumentMarker*>& markerPointers, Vector<DocumentMarker>& markers)
531 {
532     size_t arraySize = markerPointers.size();
533     markers.reserveCapacity(arraySize);
534     for (size_t i = 0; i < arraySize; ++i)
535         markers.append(*markerPointers[i]);
536 }
537 
replaceTextInNodePreservingMarkers(PassRefPtr<Text> prpNode,unsigned offset,unsigned count,const String & replacementText)538 void CompositeEditCommand::replaceTextInNodePreservingMarkers(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
539 {
540     RefPtr<Text> node(prpNode);
541     DocumentMarkerController* markerController = document().markers();
542     Vector<DocumentMarker> markers;
543     copyMarkers(markerController->markersInRange(Range::create(document(), node.get(), offset, node.get(), offset + count).get(), DocumentMarker::AllMarkers()), markers);
544     replaceTextInNode(node, offset, count, replacementText);
545     RefPtr<Range> newRange = Range::create(document(), node.get(), offset, node.get(), offset + replacementText.length());
546     for (size_t i = 0; i < markers.size(); ++i)
547         markerController->addMarker(newRange.get(), markers[i].type(), markers[i].description());
548 }
549 
positionOutsideTabSpan(const Position & pos)550 Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
551 {
552     if (!isTabSpanTextNode(pos.anchorNode()))
553         return pos;
554 
555     switch (pos.anchorType()) {
556     case Position::PositionIsBeforeChildren:
557     case Position::PositionIsAfterChildren:
558         ASSERT_NOT_REACHED();
559         return pos;
560     case Position::PositionIsOffsetInAnchor:
561         break;
562     case Position::PositionIsBeforeAnchor:
563         return positionInParentBeforeNode(pos.anchorNode());
564     case Position::PositionIsAfterAnchor:
565         return positionInParentAfterNode(pos.anchorNode());
566     }
567 
568     Node* tabSpan = tabSpanNode(pos.containerNode());
569 
570     if (pos.offsetInContainerNode() <= caretMinOffset(pos.containerNode()))
571         return positionInParentBeforeNode(tabSpan);
572 
573     if (pos.offsetInContainerNode() >= caretMaxOffset(pos.containerNode()))
574         return positionInParentAfterNode(tabSpan);
575 
576     splitTextNodeContainingElement(toText(pos.containerNode()), pos.offsetInContainerNode());
577     return positionInParentBeforeNode(tabSpan);
578 }
579 
insertNodeAtTabSpanPosition(PassRefPtr<Node> node,const Position & pos)580 void CompositeEditCommand::insertNodeAtTabSpanPosition(PassRefPtr<Node> node, const Position& pos)
581 {
582     // insert node before, after, or at split of tab span
583     insertNodeAt(node, positionOutsideTabSpan(pos));
584 }
585 
deleteSelection(bool smartDelete,bool mergeBlocksAfterDelete,bool replace,bool expandForSpecialElements,bool sanitizeMarkup)586 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
587 {
588     if (endingSelection().isRange())
589         applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
590 }
591 
deleteSelection(const VisibleSelection & selection,bool smartDelete,bool mergeBlocksAfterDelete,bool replace,bool expandForSpecialElements,bool sanitizeMarkup)592 void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
593 {
594     if (selection.isRange())
595         applyCommandToComposite(DeleteSelectionCommand::create(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
596 }
597 
removeCSSProperty(PassRefPtr<Element> element,CSSPropertyID property)598 void CompositeEditCommand::removeCSSProperty(PassRefPtr<Element> element, CSSPropertyID property)
599 {
600     applyCommandToComposite(RemoveCSSPropertyCommand::create(document(), element, property));
601 }
602 
removeNodeAttribute(PassRefPtr<Element> element,const QualifiedName & attribute)603 void CompositeEditCommand::removeNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute)
604 {
605     setNodeAttribute(element, attribute, AtomicString());
606 }
607 
setNodeAttribute(PassRefPtr<Element> element,const QualifiedName & attribute,const AtomicString & value)608 void CompositeEditCommand::setNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute, const AtomicString& value)
609 {
610     applyCommandToComposite(SetNodeAttributeCommand::create(element, attribute, value));
611 }
612 
containsOnlyWhitespace(const String & text)613 static inline bool containsOnlyWhitespace(const String& text)
614 {
615     for (unsigned i = 0; i < text.length(); ++i) {
616         if (!isWhitespace(text[i]))
617             return false;
618     }
619 
620     return true;
621 }
622 
shouldRebalanceLeadingWhitespaceFor(const String & text) const623 bool CompositeEditCommand::shouldRebalanceLeadingWhitespaceFor(const String& text) const
624 {
625     return containsOnlyWhitespace(text);
626 }
627 
canRebalance(const Position & position) const628 bool CompositeEditCommand::canRebalance(const Position& position) const
629 {
630     Node* node = position.containerNode();
631     if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node || !node->isTextNode())
632         return false;
633 
634     Text* textNode = toText(node);
635     if (textNode->length() == 0)
636         return false;
637 
638     RenderObject* renderer = textNode->renderer();
639     if (renderer && !renderer->style()->collapseWhiteSpace())
640         return false;
641 
642     return true;
643 }
644 
645 // FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc).
rebalanceWhitespaceAt(const Position & position)646 void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
647 {
648     Node* node = position.containerNode();
649     if (!canRebalance(position))
650         return;
651 
652     // If the rebalance is for the single offset, and neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
653     int offset = position.deprecatedEditingOffset();
654     String text = toText(node)->data();
655     if (!isWhitespace(text[offset])) {
656         offset--;
657         if (offset < 0 || !isWhitespace(text[offset]))
658             return;
659     }
660 
661     rebalanceWhitespaceOnTextSubstring(toText(node), position.offsetInContainerNode(), position.offsetInContainerNode());
662 }
663 
rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> prpTextNode,int startOffset,int endOffset)664 void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> prpTextNode, int startOffset, int endOffset)
665 {
666     RefPtr<Text> textNode = prpTextNode;
667 
668     String text = textNode->data();
669     ASSERT(!text.isEmpty());
670 
671     // Set upstream and downstream to define the extent of the whitespace surrounding text[offset].
672     int upstream = startOffset;
673     while (upstream > 0 && isWhitespace(text[upstream - 1]))
674         upstream--;
675 
676     int downstream = endOffset;
677     while ((unsigned)downstream < text.length() && isWhitespace(text[downstream]))
678         downstream++;
679 
680     int length = downstream - upstream;
681     if (!length)
682         return;
683 
684     VisiblePosition visibleUpstreamPos(Position(textNode, upstream));
685     VisiblePosition visibleDownstreamPos(Position(textNode, downstream));
686 
687     String string = text.substring(upstream, length);
688     String rebalancedString = stringWithRebalancedWhitespace(string,
689     // FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because
690     // this function doesn't get all surrounding whitespace, just the whitespace in the current text node.
691                                                              isStartOfParagraph(visibleUpstreamPos) || upstream == 0,
692                                                              isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length());
693 
694     if (string != rebalancedString)
695         replaceTextInNodePreservingMarkers(textNode.release(), upstream, length, rebalancedString);
696 }
697 
prepareWhitespaceAtPositionForSplit(Position & position)698 void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position)
699 {
700     Node* node = position.deprecatedNode();
701     if (!node || !node->isTextNode())
702         return;
703     Text* textNode = toText(node);
704 
705     if (textNode->length() == 0)
706         return;
707     RenderObject* renderer = textNode->renderer();
708     if (renderer && !renderer->style()->collapseWhiteSpace())
709         return;
710 
711     // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it.
712     Position upstreamPos = position.upstream();
713     deleteInsignificantText(upstreamPos, position.downstream());
714     position = upstreamPos.downstream();
715 
716     VisiblePosition visiblePos(position);
717     VisiblePosition previousVisiblePos(visiblePos.previous());
718     replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(previousVisiblePos);
719     replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(visiblePos);
720 }
721 
replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(const VisiblePosition & visiblePosition)722 void CompositeEditCommand::replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(const VisiblePosition& visiblePosition)
723 {
724     if (!isCollapsibleWhitespace(visiblePosition.characterAfter()))
725         return;
726     Position pos = visiblePosition.deepEquivalent().downstream();
727     if (!pos.containerNode() || !pos.containerNode()->isTextNode() || pos.containerNode()->hasTagName(brTag))
728         return;
729     replaceTextInNodePreservingMarkers(pos.containerText(), pos.offsetInContainerNode(), 1, nonBreakingSpaceString());
730 }
731 
rebalanceWhitespace()732 void CompositeEditCommand::rebalanceWhitespace()
733 {
734     VisibleSelection selection = endingSelection();
735     if (selection.isNone())
736         return;
737 
738     rebalanceWhitespaceAt(selection.start());
739     if (selection.isRange())
740         rebalanceWhitespaceAt(selection.end());
741 }
742 
deleteInsignificantText(PassRefPtr<Text> textNode,unsigned start,unsigned end)743 void CompositeEditCommand::deleteInsignificantText(PassRefPtr<Text> textNode, unsigned start, unsigned end)
744 {
745     if (!textNode || start >= end)
746         return;
747 
748     document().updateLayout();
749 
750     RenderText* textRenderer = toRenderText(textNode->renderer());
751     if (!textRenderer)
752         return;
753 
754     Vector<InlineTextBox*> sortedTextBoxes;
755     size_t sortedTextBoxesPosition = 0;
756 
757     for (InlineTextBox* textBox = textRenderer->firstTextBox(); textBox; textBox = textBox->nextTextBox())
758         sortedTextBoxes.append(textBox);
759 
760     // If there is mixed directionality text, the boxes can be out of order,
761     // (like Arabic with embedded LTR), so sort them first.
762     if (textRenderer->containsReversedText())
763         std::sort(sortedTextBoxes.begin(), sortedTextBoxes.end(), InlineTextBox::compareByStart);
764     InlineTextBox* box = sortedTextBoxes.isEmpty() ? 0 : sortedTextBoxes[sortedTextBoxesPosition];
765 
766     if (!box) {
767         // whole text node is empty
768         removeNode(textNode);
769         return;
770     }
771 
772     unsigned length = textNode->length();
773     if (start >= length || end > length)
774         return;
775 
776     unsigned removed = 0;
777     InlineTextBox* prevBox = 0;
778     String str;
779 
780     // This loop structure works to process all gaps preceding a box,
781     // and also will look at the gap after the last box.
782     while (prevBox || box) {
783         unsigned gapStart = prevBox ? prevBox->start() + prevBox->len() : 0;
784         if (end < gapStart)
785             // No more chance for any intersections
786             break;
787 
788         unsigned gapEnd = box ? box->start() : length;
789         bool indicesIntersect = start <= gapEnd && end >= gapStart;
790         int gapLen = gapEnd - gapStart;
791         if (indicesIntersect && gapLen > 0) {
792             gapStart = max(gapStart, start);
793             gapEnd = min(gapEnd, end);
794             if (str.isNull())
795                 str = textNode->data().substring(start, end - start);
796             // remove text in the gap
797             str.remove(gapStart - start - removed, gapLen);
798             removed += gapLen;
799         }
800 
801         prevBox = box;
802         if (box) {
803             if (++sortedTextBoxesPosition < sortedTextBoxes.size())
804                 box = sortedTextBoxes[sortedTextBoxesPosition];
805             else
806                 box = 0;
807         }
808     }
809 
810     if (!str.isNull()) {
811         // Replace the text between start and end with our pruned version.
812         if (!str.isEmpty())
813             replaceTextInNode(textNode, start, end - start, str);
814         else {
815             // Assert that we are not going to delete all of the text in the node.
816             // If we were, that should have been done above with the call to
817             // removeNode and return.
818             ASSERT(start > 0 || end - start < textNode->length());
819             deleteTextFromNode(textNode, start, end - start);
820         }
821     }
822 }
823 
deleteInsignificantText(const Position & start,const Position & end)824 void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end)
825 {
826     if (start.isNull() || end.isNull())
827         return;
828 
829     if (comparePositions(start, end) >= 0)
830         return;
831 
832     Vector<RefPtr<Text> > nodes;
833     for (Node* node = start.deprecatedNode(); node; node = NodeTraversal::next(*node)) {
834         if (node->isTextNode())
835             nodes.append(toText(node));
836         if (node == end.deprecatedNode())
837             break;
838     }
839 
840     for (size_t i = 0; i < nodes.size(); ++i) {
841         Text* textNode = nodes[i].get();
842         int startOffset = textNode == start.deprecatedNode() ? start.deprecatedEditingOffset() : 0;
843         int endOffset = textNode == end.deprecatedNode() ? end.deprecatedEditingOffset() : static_cast<int>(textNode->length());
844         deleteInsignificantText(textNode, startOffset, endOffset);
845     }
846 }
847 
deleteInsignificantTextDownstream(const Position & pos)848 void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos)
849 {
850     Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream();
851     deleteInsignificantText(pos, end);
852 }
853 
appendBlockPlaceholder(PassRefPtr<Element> container)854 PassRefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(PassRefPtr<Element> container)
855 {
856     if (!container)
857         return 0;
858 
859     document().updateLayoutIgnorePendingStylesheets();
860 
861     // Should assert isRenderBlockFlow || isInlineFlow when deletion improves. See 4244964.
862     ASSERT(container->renderer());
863 
864     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
865     appendNode(placeholder, container);
866     return placeholder.release();
867 }
868 
insertBlockPlaceholder(const Position & pos)869 PassRefPtr<Node> CompositeEditCommand::insertBlockPlaceholder(const Position& pos)
870 {
871     if (pos.isNull())
872         return 0;
873 
874     // Should assert isRenderBlockFlow || isInlineFlow when deletion improves. See 4244964.
875     ASSERT(pos.deprecatedNode()->renderer());
876 
877     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
878     insertNodeAt(placeholder, pos);
879     return placeholder.release();
880 }
881 
addBlockPlaceholderIfNeeded(Element * container)882 PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* container)
883 {
884     if (!container)
885         return 0;
886 
887     document().updateLayoutIgnorePendingStylesheets();
888 
889     RenderObject* renderer = container->renderer();
890     if (!renderer || !renderer->isRenderBlockFlow())
891         return 0;
892 
893     // append the placeholder to make sure it follows
894     // any unrendered blocks
895     RenderBlock* block = toRenderBlock(renderer);
896     if (block->height() == 0 || (block->isListItem() && block->isEmpty()))
897         return appendBlockPlaceholder(container);
898 
899     return 0;
900 }
901 
902 // Assumes that the position is at a placeholder and does the removal without much checking.
removePlaceholderAt(const Position & p)903 void CompositeEditCommand::removePlaceholderAt(const Position& p)
904 {
905     ASSERT(lineBreakExistsAtPosition(p));
906 
907     // We are certain that the position is at a line break, but it may be a br or a preserved newline.
908     if (p.anchorNode()->hasTagName(brTag)) {
909         removeNode(p.anchorNode());
910         return;
911     }
912 
913     deleteTextFromNode(toText(p.anchorNode()), p.offsetInContainerNode(), 1);
914 }
915 
insertNewDefaultParagraphElementAt(const Position & position)916 PassRefPtr<Node> CompositeEditCommand::insertNewDefaultParagraphElementAt(const Position& position)
917 {
918     RefPtr<Element> paragraphElement = createDefaultParagraphElement(document());
919     paragraphElement->appendChild(createBreakElement(document()));
920     insertNodeAt(paragraphElement, position);
921     return paragraphElement.release();
922 }
923 
924 // If the paragraph is not entirely within it's own block, create one and move the paragraph into
925 // it, and return that block.  Otherwise return 0.
moveParagraphContentsToNewBlockIfNecessary(const Position & pos)926 PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos)
927 {
928     if (pos.isNull())
929         return 0;
930 
931     document().updateLayoutIgnorePendingStylesheets();
932 
933     // It's strange that this function is responsible for verifying that pos has not been invalidated
934     // by an earlier call to this function.  The caller, applyBlockStyle, should do this.
935     VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
936     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
937     VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
938     VisiblePosition next = visibleParagraphEnd.next();
939     VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
940 
941     Position upstreamStart = visibleParagraphStart.deepEquivalent().upstream();
942     Position upstreamEnd = visibleEnd.deepEquivalent().upstream();
943 
944     // If there are no VisiblePositions in the same block as pos then
945     // upstreamStart will be outside the paragraph
946     if (comparePositions(pos, upstreamStart) < 0)
947         return 0;
948 
949     // Perform some checks to see if we need to perform work in this function.
950     if (isBlock(upstreamStart.deprecatedNode())) {
951         // If the block is the root editable element, always move content to a new block,
952         // since it is illegal to modify attributes on the root editable element for editing.
953         if (upstreamStart.deprecatedNode() == editableRootForPosition(upstreamStart)) {
954             // If the block is the root editable element and it contains no visible content, create a new
955             // block but don't try and move content into it, since there's nothing for moveParagraphs to move.
956             if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(upstreamStart.deprecatedNode()->renderer()))
957                 return insertNewDefaultParagraphElementAt(upstreamStart);
958         } else if (isBlock(upstreamEnd.deprecatedNode())) {
959             if (!upstreamEnd.deprecatedNode()->isDescendantOf(upstreamStart.deprecatedNode())) {
960                 // If the paragraph end is a descendant of paragraph start, then we need to run
961                 // the rest of this function. If not, we can bail here.
962                 return 0;
963             }
964         } else if (enclosingBlock(upstreamEnd.deprecatedNode()) != upstreamStart.deprecatedNode()) {
965             // The visibleEnd.  It must be an ancestor of the paragraph start.
966             // We can bail as we have a full block to work with.
967             ASSERT(upstreamStart.deprecatedNode()->isDescendantOf(enclosingBlock(upstreamEnd.deprecatedNode())));
968             return 0;
969         } else if (isEndOfEditableOrNonEditableContent(visibleEnd)) {
970             // At the end of the editable region. We can bail here as well.
971             return 0;
972         }
973     }
974 
975     RefPtr<Node> newBlock = insertNewDefaultParagraphElementAt(upstreamStart);
976 
977     bool endWasBr = visibleParagraphEnd.deepEquivalent().deprecatedNode()->hasTagName(brTag);
978 
979     moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(firstPositionInNode(newBlock.get())));
980 
981     if (newBlock->lastChild() && newBlock->lastChild()->hasTagName(brTag) && !endWasBr)
982         removeNode(newBlock->lastChild());
983 
984     return newBlock.release();
985 }
986 
pushAnchorElementDown(Node * anchorNode)987 void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
988 {
989     if (!anchorNode)
990         return;
991 
992     ASSERT(anchorNode->isLink());
993 
994     setEndingSelection(VisibleSelection::selectionFromContentsOfNode(anchorNode));
995     applyStyledElement(toElement(anchorNode));
996     // Clones of anchorNode have been pushed down, now remove it.
997     if (anchorNode->inDocument())
998         removeNodePreservingChildren(anchorNode);
999 }
1000 
1001 // Clone the paragraph between start and end under blockElement,
1002 // preserving the hierarchy up to outerNode.
1003 
cloneParagraphUnderNewElement(Position & start,Position & end,Node * passedOuterNode,Element * blockElement)1004 void CompositeEditCommand::cloneParagraphUnderNewElement(Position& start, Position& end, Node* passedOuterNode, Element* blockElement)
1005 {
1006     // First we clone the outerNode
1007     RefPtr<Node> lastNode;
1008     RefPtr<Node> outerNode = passedOuterNode;
1009 
1010     if (outerNode->isRootEditableElement()) {
1011         lastNode = blockElement;
1012     } else {
1013         lastNode = outerNode->cloneNode(isTableElement(outerNode.get()));
1014         appendNode(lastNode, blockElement);
1015     }
1016 
1017     if (start.anchorNode() != outerNode && lastNode->isElementNode() && start.anchorNode()->isDescendantOf(outerNode.get())) {
1018         Vector<RefPtr<Node> > ancestors;
1019 
1020         // Insert each node from innerNode to outerNode (excluded) in a list.
1021         for (Node* n = start.deprecatedNode(); n && n != outerNode; n = n->parentNode())
1022             ancestors.append(n);
1023 
1024         // Clone every node between start.deprecatedNode() and outerBlock.
1025 
1026         for (size_t i = ancestors.size(); i != 0; --i) {
1027             Node* item = ancestors[i - 1].get();
1028             RefPtr<Node> child = item->cloneNode(isTableElement(item));
1029             appendNode(child, toElement(lastNode));
1030             lastNode = child.release();
1031         }
1032     }
1033 
1034     // Scripts specified in javascript protocol may remove |outerNode|
1035     // during insertion, e.g. <iframe src="javascript:...">
1036     if (!outerNode->inDocument())
1037         return;
1038 
1039     // Handle the case of paragraphs with more than one node,
1040     // cloning all the siblings until end.deprecatedNode() is reached.
1041 
1042     if (start.deprecatedNode() != end.deprecatedNode() && !start.deprecatedNode()->isDescendantOf(end.deprecatedNode())) {
1043         // If end is not a descendant of outerNode we need to
1044         // find the first common ancestor to increase the scope
1045         // of our nextSibling traversal.
1046         while (!end.deprecatedNode()->isDescendantOf(outerNode.get())) {
1047             outerNode = outerNode->parentNode();
1048         }
1049 
1050         RefPtr<Node> startNode = start.deprecatedNode();
1051         for (RefPtr<Node> node = NodeTraversal::nextSkippingChildren(*startNode, outerNode.get()); node; node = NodeTraversal::nextSkippingChildren(*node, outerNode.get())) {
1052             // Move lastNode up in the tree as much as node was moved up in the
1053             // tree by NodeTraversal::nextSkippingChildren, so that the relative depth between
1054             // node and the original start node is maintained in the clone.
1055             while (startNode->parentNode() != node->parentNode()) {
1056                 startNode = startNode->parentNode();
1057                 lastNode = lastNode->parentNode();
1058             }
1059 
1060             RefPtr<Node> clonedNode = node->cloneNode(true);
1061             insertNodeAfter(clonedNode, lastNode);
1062             lastNode = clonedNode.release();
1063             if (node == end.deprecatedNode() || end.deprecatedNode()->isDescendantOf(node.get()))
1064                 break;
1065         }
1066     }
1067 }
1068 
1069 
1070 // There are bugs in deletion when it removes a fully selected table/list.
1071 // It expands and removes the entire table/list, but will let content
1072 // before and after the table/list collapse onto one line.
1073 // Deleting a paragraph will leave a placeholder. Remove it (and prune
1074 // empty or unrendered parents).
1075 
cleanupAfterDeletion(VisiblePosition destination)1076 void CompositeEditCommand::cleanupAfterDeletion(VisiblePosition destination)
1077 {
1078     VisiblePosition caretAfterDelete = endingSelection().visibleStart();
1079     Node* destinationNode = destination.deepEquivalent().anchorNode();
1080     if (caretAfterDelete != destination && isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) {
1081         // Note: We want the rightmost candidate.
1082         Position position = caretAfterDelete.deepEquivalent().downstream();
1083         Node* node = position.deprecatedNode();
1084 
1085         // Bail if we'd remove an ancestor of our destination.
1086         if (destinationNode && destinationNode->isDescendantOf(node))
1087             return;
1088 
1089         // Normally deletion will leave a br as a placeholder.
1090         if (node->hasTagName(brTag)) {
1091             removeNodeAndPruneAncestors(node, destinationNode);
1092 
1093             // If the selection to move was empty and in an empty block that
1094             // doesn't require a placeholder to prop itself open (like a bordered
1095             // div or an li), remove it during the move (the list removal code
1096             // expects this behavior).
1097         } else if (isBlock(node)) {
1098             // If caret position after deletion and destination position coincides,
1099             // node should not be removed.
1100             if (!position.rendersInDifferentPosition(destination.deepEquivalent())) {
1101                 prune(node, destinationNode);
1102                 return;
1103             }
1104             removeNodeAndPruneAncestors(node, destinationNode);
1105         }
1106         else if (lineBreakExistsAtPosition(position)) {
1107             // There is a preserved '\n' at caretAfterDelete.
1108             // We can safely assume this is a text node.
1109             Text* textNode = toText(node);
1110             if (textNode->length() == 1)
1111                 removeNodeAndPruneAncestors(node, destinationNode);
1112             else
1113                 deleteTextFromNode(textNode, position.deprecatedEditingOffset(), 1);
1114         }
1115     }
1116 }
1117 
1118 // This is a version of moveParagraph that preserves style by keeping the original markup
1119 // It is currently used only by IndentOutdentCommand but it is meant to be used in the
1120 // future by several other commands such as InsertList and the align commands.
1121 // The blockElement parameter is the element to move the paragraph to,
1122 // outerNode is the top element of the paragraph hierarchy.
1123 
moveParagraphWithClones(const VisiblePosition & startOfParagraphToMove,const VisiblePosition & endOfParagraphToMove,Element * blockElement,Node * outerNode)1124 void CompositeEditCommand::moveParagraphWithClones(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, Element* blockElement, Node* outerNode)
1125 {
1126     ASSERT(outerNode);
1127     ASSERT(blockElement);
1128 
1129     VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
1130     VisiblePosition afterParagraph(endOfParagraphToMove.next());
1131 
1132     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
1133     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.
1134     Position start = startOfParagraphToMove.deepEquivalent().downstream();
1135     Position end = endOfParagraphToMove.deepEquivalent().upstream();
1136 
1137     cloneParagraphUnderNewElement(start, end, outerNode, blockElement);
1138 
1139     setEndingSelection(VisibleSelection(start, end, DOWNSTREAM));
1140     deleteSelection(false, false, false, false);
1141 
1142     // There are bugs in deletion when it removes a fully selected table/list.
1143     // It expands and removes the entire table/list, but will let content
1144     // before and after the table/list collapse onto one line.
1145 
1146     cleanupAfterDeletion();
1147 
1148     // Add a br if pruning an empty block level element caused a collapse.  For example:
1149     // foo^
1150     // <div>bar</div>
1151     // baz
1152     // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
1153     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
1154     // Must recononicalize these two VisiblePositions after the pruning above.
1155     beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
1156     afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
1157 
1158     if (beforeParagraph.isNotNull() && !isRenderedTable(beforeParagraph.deepEquivalent().deprecatedNode())
1159         && ((!isEndOfParagraph(beforeParagraph) && !isStartOfParagraph(beforeParagraph)) || beforeParagraph == afterParagraph)) {
1160         // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
1161         insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent());
1162     }
1163 }
1164 
moveParagraph(const VisiblePosition & startOfParagraphToMove,const VisiblePosition & endOfParagraphToMove,const VisiblePosition & destination,bool preserveSelection,bool preserveStyle,Node * constrainingAncestor)1165 void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle, Node* constrainingAncestor)
1166 {
1167     ASSERT(isStartOfParagraph(startOfParagraphToMove));
1168     ASSERT(isEndOfParagraph(endOfParagraphToMove));
1169     moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle, constrainingAncestor);
1170 }
1171 
moveParagraphs(const VisiblePosition & startOfParagraphToMove,const VisiblePosition & endOfParagraphToMove,const VisiblePosition & destination,bool preserveSelection,bool preserveStyle,Node * constrainingAncestor)1172 void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle, Node* constrainingAncestor)
1173 {
1174     if (startOfParagraphToMove == destination)
1175         return;
1176 
1177     int startIndex = -1;
1178     int endIndex = -1;
1179     int destinationIndex = -1;
1180     bool originalIsDirectional = endingSelection().isDirectional();
1181     if (preserveSelection && !endingSelection().isNone()) {
1182         VisiblePosition visibleStart = endingSelection().visibleStart();
1183         VisiblePosition visibleEnd = endingSelection().visibleEnd();
1184 
1185         bool startAfterParagraph = comparePositions(visibleStart, endOfParagraphToMove) > 0;
1186         bool endBeforeParagraph = comparePositions(visibleEnd, startOfParagraphToMove) < 0;
1187 
1188         if (!startAfterParagraph && !endBeforeParagraph) {
1189             bool startInParagraph = comparePositions(visibleStart, startOfParagraphToMove) >= 0;
1190             bool endInParagraph = comparePositions(visibleEnd, endOfParagraphToMove) <= 0;
1191 
1192             startIndex = 0;
1193             if (startInParagraph) {
1194                 RefPtr<Range> startRange = Range::create(document(), startOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent(), visibleStart.deepEquivalent().parentAnchoredEquivalent());
1195                 startIndex = TextIterator::rangeLength(startRange.get(), true);
1196             }
1197 
1198             endIndex = 0;
1199             if (endInParagraph) {
1200                 RefPtr<Range> endRange = Range::create(document(), startOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent(), visibleEnd.deepEquivalent().parentAnchoredEquivalent());
1201                 endIndex = TextIterator::rangeLength(endRange.get(), true);
1202             }
1203         }
1204     }
1205 
1206     VisiblePosition beforeParagraph = startOfParagraphToMove.previous(CannotCrossEditingBoundary);
1207     VisiblePosition afterParagraph(endOfParagraphToMove.next(CannotCrossEditingBoundary));
1208 
1209     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
1210     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.
1211     Position start = startOfParagraphToMove.deepEquivalent().downstream();
1212     Position end = endOfParagraphToMove.deepEquivalent().upstream();
1213 
1214     // start and end can't be used directly to create a Range; they are "editing positions"
1215     Position startRangeCompliant = start.parentAnchoredEquivalent();
1216     Position endRangeCompliant = end.parentAnchoredEquivalent();
1217     RefPtr<Range> range = Range::create(document(), startRangeCompliant.deprecatedNode(), startRangeCompliant.deprecatedEditingOffset(), endRangeCompliant.deprecatedNode(), endRangeCompliant.deprecatedEditingOffset());
1218 
1219     // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move. It
1220     // shouldn't matter though, since moved paragraphs will usually be quite small.
1221     RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ?
1222         createFragmentFromMarkup(document(), createMarkup(range.get(), 0, DoNotAnnotateForInterchange, true, DoNotResolveURLs, constrainingAncestor), "") : 0;
1223 
1224     // A non-empty paragraph's style is moved when we copy and move it.  We don't move
1225     // anything if we're given an empty paragraph, but an empty paragraph can have style
1226     // too, <div><b><br></b></div> for example.  Save it so that we can preserve it later.
1227     RefPtr<EditingStyle> styleInEmptyParagraph;
1228     if (startOfParagraphToMove == endOfParagraphToMove && preserveStyle) {
1229         styleInEmptyParagraph = EditingStyle::create(startOfParagraphToMove.deepEquivalent());
1230         styleInEmptyParagraph->mergeTypingStyle(&document());
1231         // The moved paragraph should assume the block style of the destination.
1232         styleInEmptyParagraph->removeBlockProperties();
1233     }
1234 
1235     // FIXME (5098931): We should add a new insert action "WebViewInsertActionMoved" and call shouldInsertFragment here.
1236 
1237     setEndingSelection(VisibleSelection(start, end, DOWNSTREAM));
1238     document().frame()->spellChecker().clearMisspellingsAndBadGrammar(endingSelection());
1239     deleteSelection(false, false, false, false);
1240 
1241     ASSERT(destination.deepEquivalent().inDocument());
1242     cleanupAfterDeletion(destination);
1243     ASSERT(destination.deepEquivalent().inDocument());
1244 
1245     // Add a br if pruning an empty block level element caused a collapse. For example:
1246     // foo^
1247     // <div>bar</div>
1248     // baz
1249     // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That would
1250     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
1251     // Must recononicalize these two VisiblePositions after the pruning above.
1252     beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
1253     afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
1254     if (beforeParagraph.isNotNull() && (!isEndOfParagraph(beforeParagraph) || beforeParagraph == afterParagraph)) {
1255         // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
1256         insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent());
1257         // Need an updateLayout here in case inserting the br has split a text node.
1258         document().updateLayoutIgnorePendingStylesheets();
1259     }
1260 
1261     RefPtr<Range> startToDestinationRange(Range::create(document(), firstPositionInNode(document().documentElement()), destination.deepEquivalent().parentAnchoredEquivalent()));
1262     destinationIndex = TextIterator::rangeLength(startToDestinationRange.get(), true);
1263 
1264     setEndingSelection(VisibleSelection(destination, originalIsDirectional));
1265     ASSERT(endingSelection().isCaretOrRange());
1266     ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MovingParagraph;
1267     if (!preserveStyle)
1268         options |= ReplaceSelectionCommand::MatchStyle;
1269     applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment, options));
1270 
1271     document().frame()->spellChecker().markMisspellingsAndBadGrammar(endingSelection());
1272 
1273     // If the selection is in an empty paragraph, restore styles from the old empty paragraph to the new empty paragraph.
1274     bool selectionIsEmptyParagraph = endingSelection().isCaret() && isStartOfParagraph(endingSelection().visibleStart()) && isEndOfParagraph(endingSelection().visibleStart());
1275     if (styleInEmptyParagraph && selectionIsEmptyParagraph)
1276         applyStyle(styleInEmptyParagraph.get());
1277 
1278     if (preserveSelection && startIndex != -1) {
1279         // Fragment creation (using createMarkup) incorrectly uses regular
1280         // spaces instead of nbsps for some spaces that were rendered (11475), which
1281         // causes spaces to be collapsed during the move operation.  This results
1282         // in a call to rangeFromLocationAndLength with a location past the end
1283         // of the document (which will return null).
1284         RefPtr<Range> start = PlainTextRange(destinationIndex + startIndex).createRangeForSelection(*document().documentElement());
1285         RefPtr<Range> end = PlainTextRange(destinationIndex + endIndex).createRangeForSelection(*document().documentElement());
1286         if (start && end)
1287             setEndingSelection(VisibleSelection(start->startPosition(), end->startPosition(), DOWNSTREAM, originalIsDirectional));
1288     }
1289 }
1290 
1291 // FIXME: Send an appropriate shouldDeleteRange call.
breakOutOfEmptyListItem()1292 bool CompositeEditCommand::breakOutOfEmptyListItem()
1293 {
1294     RefPtr<Node> emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
1295     if (!emptyListItem)
1296         return false;
1297 
1298     RefPtr<EditingStyle> style = EditingStyle::create(endingSelection().start());
1299     style->mergeTypingStyle(&document());
1300 
1301     RefPtr<ContainerNode> listNode = emptyListItem->parentNode();
1302     // FIXME: Can't we do something better when the immediate parent wasn't a list node?
1303     if (!listNode
1304         || (!listNode->hasTagName(ulTag) && !listNode->hasTagName(olTag))
1305         || !listNode->rendererIsEditable()
1306         || listNode == emptyListItem->rootEditableElement())
1307         return false;
1308 
1309     RefPtr<Element> newBlock = 0;
1310     if (ContainerNode* blockEnclosingList = listNode->parentNode()) {
1311         if (blockEnclosingList->hasTagName(liTag)) { // listNode is inside another list item
1312             if (visiblePositionAfterNode(blockEnclosingList) == visiblePositionAfterNode(listNode.get())) {
1313                 // If listNode appears at the end of the outer list item, then move listNode outside of this list item
1314                 // e.g. <ul><li>hello <ul><li><br></li></ul> </li></ul> should become <ul><li>hello</li> <ul><li><br></li></ul> </ul> after this section
1315                 // If listNode does NOT appear at the end, then we should consider it as a regular paragraph.
1316                 // e.g. <ul><li> <ul><li><br></li></ul> hello</li></ul> should become <ul><li> <div><br></div> hello</li></ul> at the end
1317                 splitElement(toElement(blockEnclosingList), listNode);
1318                 removeNodePreservingChildren(listNode->parentNode());
1319                 newBlock = createListItemElement(document());
1320             }
1321             // If listNode does NOT appear at the end of the outer list item, then behave as if in a regular paragraph.
1322         } else if (blockEnclosingList->hasTagName(olTag) || blockEnclosingList->hasTagName(ulTag)) {
1323             newBlock = createListItemElement(document());
1324         }
1325     }
1326     if (!newBlock)
1327         newBlock = createDefaultParagraphElement(document());
1328 
1329     RefPtr<Node> previousListNode = emptyListItem->isElementNode() ? toElement(emptyListItem)->previousElementSibling(): emptyListItem->previousSibling();
1330     RefPtr<Node> nextListNode = emptyListItem->isElementNode() ? toElement(emptyListItem)->nextElementSibling(): emptyListItem->nextSibling();
1331     if (isListItem(nextListNode.get()) || isListElement(nextListNode.get())) {
1332         // If emptyListItem follows another list item or nested list, split the list node.
1333         if (isListItem(previousListNode.get()) || isListElement(previousListNode.get()))
1334             splitElement(toElement(listNode), emptyListItem);
1335 
1336         // If emptyListItem is followed by other list item or nested list, then insert newBlock before the list node.
1337         // Because we have splitted the element, emptyListItem is the first element in the list node.
1338         // i.e. insert newBlock before ul or ol whose first element is emptyListItem
1339         insertNodeBefore(newBlock, listNode);
1340         removeNode(emptyListItem);
1341     } else {
1342         // When emptyListItem does not follow any list item or nested list, insert newBlock after the enclosing list node.
1343         // Remove the enclosing node if emptyListItem is the only child; otherwise just remove emptyListItem.
1344         insertNodeAfter(newBlock, listNode);
1345         removeNode(isListItem(previousListNode.get()) || isListElement(previousListNode.get()) ? emptyListItem.get() : listNode.get());
1346     }
1347 
1348     appendBlockPlaceholder(newBlock);
1349     setEndingSelection(VisibleSelection(firstPositionInNode(newBlock.get()), DOWNSTREAM, endingSelection().isDirectional()));
1350 
1351     style->prepareToApplyAt(endingSelection().start());
1352     if (!style->isEmpty())
1353         applyStyle(style.get());
1354 
1355     return true;
1356 }
1357 
1358 // If the caret is in an empty quoted paragraph, and either there is nothing before that
1359 // paragraph, or what is before is unquoted, and the user presses delete, unquote that paragraph.
breakOutOfEmptyMailBlockquotedParagraph()1360 bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
1361 {
1362     if (!endingSelection().isCaret())
1363         return false;
1364 
1365     VisiblePosition caret(endingSelection().visibleStart());
1366     Node* highestBlockquote = highestEnclosingNodeOfType(caret.deepEquivalent(), &isMailBlockquote);
1367     if (!highestBlockquote)
1368         return false;
1369 
1370     if (!isStartOfParagraph(caret) || !isEndOfParagraph(caret))
1371         return false;
1372 
1373     VisiblePosition previous(caret.previous(CannotCrossEditingBoundary));
1374     // Only move forward if there's nothing before the caret, or if there's unquoted content before it.
1375     if (enclosingNodeOfType(previous.deepEquivalent(), &isMailBlockquote))
1376         return false;
1377 
1378     RefPtr<Node> br = createBreakElement(document());
1379     // We want to replace this quoted paragraph with an unquoted one, so insert a br
1380     // to hold the caret before the highest blockquote.
1381     insertNodeBefore(br, highestBlockquote);
1382     VisiblePosition atBR(positionBeforeNode(br.get()));
1383     // If the br we inserted collapsed, for example foo<br><blockquote>...</blockquote>, insert
1384     // a second one.
1385     if (!isStartOfParagraph(atBR))
1386         insertNodeBefore(createBreakElement(document()), br);
1387     setEndingSelection(VisibleSelection(atBR, endingSelection().isDirectional()));
1388 
1389     // If this is an empty paragraph there must be a line break here.
1390     if (!lineBreakExistsAtVisiblePosition(caret))
1391         return false;
1392 
1393     Position caretPos(caret.deepEquivalent().downstream());
1394     // A line break is either a br or a preserved newline.
1395     ASSERT(caretPos.deprecatedNode()->hasTagName(brTag) || (caretPos.deprecatedNode()->isTextNode() && caretPos.deprecatedNode()->renderer()->style()->preserveNewline()));
1396 
1397     if (caretPos.deprecatedNode()->hasTagName(brTag))
1398         removeNodeAndPruneAncestors(caretPos.deprecatedNode());
1399     else if (caretPos.deprecatedNode()->isTextNode()) {
1400         ASSERT(caretPos.deprecatedEditingOffset() == 0);
1401         Text* textNode = toText(caretPos.deprecatedNode());
1402         ContainerNode* parentNode = textNode->parentNode();
1403         // The preserved newline must be the first thing in the node, since otherwise the previous
1404         // paragraph would be quoted, and we verified that it wasn't above.
1405         deleteTextFromNode(textNode, 0, 1);
1406         prune(parentNode);
1407     }
1408 
1409     return true;
1410 }
1411 
1412 // Operations use this function to avoid inserting content into an anchor when at the start or the end of
1413 // that anchor, as in NSTextView.
1414 // FIXME: This is only an approximation of NSTextViews insertion behavior, which varies depending on how
1415 // the caret was made.
positionAvoidingSpecialElementBoundary(const Position & original)1416 Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Position& original)
1417 {
1418     if (original.isNull())
1419         return original;
1420 
1421     VisiblePosition visiblePos(original);
1422     Node* enclosingAnchor = enclosingAnchorElement(original);
1423     Position result = original;
1424 
1425     if (!enclosingAnchor)
1426         return result;
1427 
1428     // Don't avoid block level anchors, because that would insert content into the wrong paragraph.
1429     if (enclosingAnchor && !isBlock(enclosingAnchor)) {
1430         VisiblePosition firstInAnchor(firstPositionInNode(enclosingAnchor));
1431         VisiblePosition lastInAnchor(lastPositionInNode(enclosingAnchor));
1432         // If visually just after the anchor, insert *inside* the anchor unless it's the last
1433         // VisiblePosition in the document, to match NSTextView.
1434         if (visiblePos == lastInAnchor) {
1435             // Make sure anchors are pushed down before avoiding them so that we don't
1436             // also avoid structural elements like lists and blocks (5142012).
1437             if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) {
1438                 pushAnchorElementDown(enclosingAnchor);
1439                 enclosingAnchor = enclosingAnchorElement(original);
1440                 if (!enclosingAnchor)
1441                     return original;
1442             }
1443             // Don't insert outside an anchor if doing so would skip over a line break.  It would
1444             // probably be safe to move the line break so that we could still avoid the anchor here.
1445             Position downstream(visiblePos.deepEquivalent().downstream());
1446             if (lineBreakExistsAtVisiblePosition(visiblePos) && downstream.deprecatedNode()->isDescendantOf(enclosingAnchor))
1447                 return original;
1448 
1449             result = positionInParentAfterNode(enclosingAnchor);
1450         }
1451         // If visually just before an anchor, insert *outside* the anchor unless it's the first
1452         // VisiblePosition in a paragraph, to match NSTextView.
1453         if (visiblePos == firstInAnchor) {
1454             // Make sure anchors are pushed down before avoiding them so that we don't
1455             // also avoid structural elements like lists and blocks (5142012).
1456             if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) {
1457                 pushAnchorElementDown(enclosingAnchor);
1458                 enclosingAnchor = enclosingAnchorElement(original);
1459             }
1460             if (!enclosingAnchor)
1461                 return original;
1462 
1463             result = positionInParentBeforeNode(enclosingAnchor);
1464         }
1465     }
1466 
1467     if (result.isNull() || !editableRootForPosition(result))
1468         result = original;
1469 
1470     return result;
1471 }
1472 
1473 // Splits the tree parent by parent until we reach the specified ancestor. We use VisiblePositions
1474 // to determine if the split is necessary. Returns the last split node.
splitTreeToNode(Node * start,Node * end,bool shouldSplitAncestor)1475 PassRefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool shouldSplitAncestor)
1476 {
1477     ASSERT(start);
1478     ASSERT(end);
1479     ASSERT(start != end);
1480 
1481     RefPtr<Node> node;
1482     if (shouldSplitAncestor && end->parentNode())
1483         end = end->parentNode();
1484 
1485     RefPtr<Node> endNode = end;
1486     for (node = start; node && node->parentNode() != endNode; node = node->parentNode()) {
1487         if (!node->parentNode()->isElementNode())
1488             break;
1489         // Do not split a node when doing so introduces an empty node.
1490         VisiblePosition positionInParent = firstPositionInNode(node->parentNode());
1491         VisiblePosition positionInNode = firstPositionInOrBeforeNode(node.get());
1492         if (positionInParent != positionInNode)
1493             splitElement(toElement(node->parentNode()), node);
1494     }
1495 
1496     return node.release();
1497 }
1498 
createBlockPlaceholderElement(Document & document)1499 PassRefPtr<Element> createBlockPlaceholderElement(Document& document)
1500 {
1501     RefPtr<Element> breakNode = document.createElement(brTag, false);
1502     return breakNode.release();
1503 }
1504 
1505 } // namespace WebCore
1506