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