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