dc10102071ebdf28649dd542ba1a2eb3d51ce078
[WebKit-https.git] / Source / WebCore / editing / ReplaceSelectionCommand.cpp
1 /*
2  * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2009, 2010, 2011 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "ReplaceSelectionCommand.h"
29
30 #include "ApplyStyleCommand.h"
31 #include "BeforeTextInsertedEvent.h"
32 #include "BreakBlockquoteCommand.h"
33 #include "CSSStyleDeclaration.h"
34 #include "Document.h"
35 #include "DocumentFragment.h"
36 #include "Element.h"
37 #include "ElementIterator.h"
38 #include "EventNames.h"
39 #include "ExceptionCodePlaceholder.h"
40 #include "Frame.h"
41 #include "FrameSelection.h"
42 #include "HTMLInputElement.h"
43 #include "HTMLNames.h"
44 #include "HTMLTitleElement.h"
45 #include "NodeList.h"
46 #include "NodeRenderStyle.h"
47 #include "RenderInline.h"
48 #include "RenderObject.h"
49 #include "RenderText.h"
50 #include "SimplifyMarkupCommand.h"
51 #include "SmartReplace.h"
52 #include "StyleProperties.h"
53 #include "Text.h"
54 #include "TextIterator.h"
55 #include "VisibleUnits.h"
56 #include "htmlediting.h"
57 #include "markup.h"
58 #include <wtf/StdLibExtras.h>
59
60 namespace WebCore {
61
62 using namespace HTMLNames;
63
64 enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment };
65
66 // --- ReplacementFragment helper class
67
68 class ReplacementFragment {
69     WTF_MAKE_NONCOPYABLE(ReplacementFragment);
70 public:
71     ReplacementFragment(Document&, DocumentFragment*, const VisibleSelection&);
72
73     DocumentFragment* fragment() { return m_fragment.get(); }
74
75     Node* firstChild() const;
76     Node* lastChild() const;
77
78     bool isEmpty() const;
79     
80     bool hasInterchangeNewlineAtStart() const { return m_hasInterchangeNewlineAtStart; }
81     bool hasInterchangeNewlineAtEnd() const { return m_hasInterchangeNewlineAtEnd; }
82     
83     void removeNode(PassRefPtr<Node>);
84     void removeNodePreservingChildren(PassRefPtr<Node>);
85
86 private:
87     PassRefPtr<StyledElement> insertFragmentForTestRendering(Node* rootEditableNode);
88     void removeUnrenderedNodes(Node*);
89     void restoreAndRemoveTestRenderingNodesToFragment(StyledElement*);
90     void removeInterchangeNodes(Node*);
91     
92     void insertNodeBefore(PassRefPtr<Node> node, Node* refNode);
93
94     Document& document() { return *m_document; }
95
96     RefPtr<Document> m_document;
97     RefPtr<DocumentFragment> m_fragment;
98     bool m_hasInterchangeNewlineAtStart;
99     bool m_hasInterchangeNewlineAtEnd;
100 };
101
102 static bool isInterchangeNewlineNode(const Node *node)
103 {
104     DEPRECATED_DEFINE_STATIC_LOCAL(String, interchangeNewlineClassString, (AppleInterchangeNewline));
105     return node && node->hasTagName(brTag) && 
106            static_cast<const Element *>(node)->getAttribute(classAttr) == interchangeNewlineClassString;
107 }
108
109 static bool isInterchangeConvertedSpaceSpan(const Node *node)
110 {
111     DEPRECATED_DEFINE_STATIC_LOCAL(String, convertedSpaceSpanClassString, (AppleConvertedSpace));
112     return node->isHTMLElement() && 
113            static_cast<const HTMLElement *>(node)->getAttribute(classAttr) == convertedSpaceSpanClassString;
114 }
115
116 static Position positionAvoidingPrecedingNodes(Position pos)
117 {
118     // If we're already on a break, it's probably a placeholder and we shouldn't change our position.
119     if (editingIgnoresContent(pos.deprecatedNode()))
120         return pos;
121
122     // We also stop when changing block flow elements because even though the visual position is the
123     // same.  E.g.,
124     //   <div>foo^</div>^
125     // The two positions above are the same visual position, but we want to stay in the same block.
126     Node* enclosingBlockNode = enclosingBlock(pos.containerNode());
127     for (Position nextPosition = pos; nextPosition.containerNode() != enclosingBlockNode; pos = nextPosition) {
128         if (lineBreakExistsAtPosition(pos))
129             break;
130
131         if (pos.containerNode()->nonShadowBoundaryParentNode())
132             nextPosition = positionInParentAfterNode(pos.containerNode());
133         
134         if (nextPosition == pos 
135             || enclosingBlock(nextPosition.containerNode()) != enclosingBlockNode
136             || VisiblePosition(pos) != VisiblePosition(nextPosition))
137             break;
138     }
139     return pos;
140 }
141
142 ReplacementFragment::ReplacementFragment(Document& document, DocumentFragment* fragment, const VisibleSelection& selection)
143     : m_document(&document)
144     , m_fragment(fragment)
145     , m_hasInterchangeNewlineAtStart(false)
146     , m_hasInterchangeNewlineAtEnd(false)
147 {
148     if (!m_fragment)
149         return;
150     if (!m_fragment->firstChild())
151         return;
152     
153     RefPtr<Element> editableRoot = selection.rootEditableElement();
154     ASSERT(editableRoot);
155     if (!editableRoot)
156         return;
157     
158     Node* shadowAncestorNode = editableRoot->deprecatedShadowAncestorNode();
159     
160     if (!editableRoot->getAttributeEventListener(eventNames().webkitBeforeTextInsertedEvent) &&
161         // FIXME: Remove these checks once textareas and textfields actually register an event handler.
162         !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextControl()) &&
163         editableRoot->hasRichlyEditableStyle()) {
164         removeInterchangeNodes(m_fragment.get());
165         return;
166     }
167
168     RefPtr<StyledElement> holder = insertFragmentForTestRendering(editableRoot.get());
169     if (!holder) {
170         removeInterchangeNodes(m_fragment.get());
171         return;
172     }
173     
174     RefPtr<Range> range = VisibleSelection::selectionFromContentsOfNode(holder.get()).toNormalizedRange();
175     String text = plainText(range.get(), static_cast<TextIteratorBehavior>(TextIteratorEmitsOriginalText | TextIteratorIgnoresStyleVisibility));
176
177     removeInterchangeNodes(holder.get());
178     removeUnrenderedNodes(holder.get());
179     restoreAndRemoveTestRenderingNodesToFragment(holder.get());
180
181     // Give the root a chance to change the text.
182     RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
183     editableRoot->dispatchEvent(evt, ASSERT_NO_EXCEPTION);
184     if (text != evt->text() || !editableRoot->hasRichlyEditableStyle()) {
185         restoreAndRemoveTestRenderingNodesToFragment(holder.get());
186
187         RefPtr<Range> range = selection.toNormalizedRange();
188         if (!range)
189             return;
190
191         m_fragment = createFragmentFromText(*range, evt->text());
192         if (!m_fragment->firstChild())
193             return;
194
195         holder = insertFragmentForTestRendering(editableRoot.get());
196         removeInterchangeNodes(holder.get());
197         removeUnrenderedNodes(holder.get());
198         restoreAndRemoveTestRenderingNodesToFragment(holder.get());
199     }
200 }
201
202 bool ReplacementFragment::isEmpty() const
203 {
204     return (!m_fragment || !m_fragment->firstChild()) && !m_hasInterchangeNewlineAtStart && !m_hasInterchangeNewlineAtEnd;
205 }
206
207 Node *ReplacementFragment::firstChild() const 
208
209     return m_fragment ? m_fragment->firstChild() : 0; 
210 }
211
212 Node *ReplacementFragment::lastChild() const 
213
214     return m_fragment ? m_fragment->lastChild() : 0; 
215 }
216
217 void ReplacementFragment::removeNodePreservingChildren(PassRefPtr<Node> node)
218 {
219     if (!node)
220         return;
221
222     while (RefPtr<Node> n = node->firstChild()) {
223         removeNode(n);
224         insertNodeBefore(n.release(), node.get());
225     }
226     removeNode(node);
227 }
228
229 void ReplacementFragment::removeNode(PassRefPtr<Node> node)
230 {
231     if (!node)
232         return;
233     
234     ContainerNode* parent = node->nonShadowBoundaryParentNode();
235     if (!parent)
236         return;
237     
238     parent->removeChild(node.get(), ASSERT_NO_EXCEPTION);
239 }
240
241 void ReplacementFragment::insertNodeBefore(PassRefPtr<Node> node, Node* refNode)
242 {
243     if (!node || !refNode)
244         return;
245         
246     ContainerNode* parent = refNode->nonShadowBoundaryParentNode();
247     if (!parent)
248         return;
249         
250     parent->insertBefore(node, refNode, ASSERT_NO_EXCEPTION);
251 }
252
253 PassRefPtr<StyledElement> ReplacementFragment::insertFragmentForTestRendering(Node* rootEditableElement)
254 {
255     RefPtr<StyledElement> holder = createDefaultParagraphElement(document());
256
257     holder->appendChild(m_fragment, ASSERT_NO_EXCEPTION);
258     rootEditableElement->appendChild(holder.get(), ASSERT_NO_EXCEPTION);
259     document().updateLayoutIgnorePendingStylesheets();
260
261     return holder.release();
262 }
263
264 void ReplacementFragment::restoreAndRemoveTestRenderingNodesToFragment(StyledElement* holder)
265 {
266     if (!holder)
267         return;
268     
269     while (RefPtr<Node> node = holder->firstChild()) {
270         holder->removeChild(node.get(), ASSERT_NO_EXCEPTION);
271         m_fragment->appendChild(node.get(), ASSERT_NO_EXCEPTION);
272     }
273
274     removeNode(holder);
275 }
276
277 void ReplacementFragment::removeUnrenderedNodes(Node* holder)
278 {
279     Vector<RefPtr<Node>> unrendered;
280
281     for (Node* node = holder->firstChild(); node; node = NodeTraversal::next(node, holder))
282         if (!isNodeRendered(node) && !isTableStructureNode(node))
283             unrendered.append(node);
284
285     size_t n = unrendered.size();
286     for (size_t i = 0; i < n; ++i)
287         removeNode(unrendered[i]);
288 }
289
290 void ReplacementFragment::removeInterchangeNodes(Node* container)
291 {
292     m_hasInterchangeNewlineAtStart = false;
293     m_hasInterchangeNewlineAtEnd = false;
294
295     // Interchange newlines at the "start" of the incoming fragment must be
296     // either the first node in the fragment or the first leaf in the fragment.
297     Node* node = container->firstChild();
298     while (node) {
299         if (isInterchangeNewlineNode(node)) {
300             m_hasInterchangeNewlineAtStart = true;
301             removeNode(node);
302             break;
303         }
304         node = node->firstChild();
305     }
306     if (!container->hasChildNodes())
307         return;
308     // Interchange newlines at the "end" of the incoming fragment must be
309     // either the last node in the fragment or the last leaf in the fragment.
310     node = container->lastChild();
311     while (node) {
312         if (isInterchangeNewlineNode(node)) {
313             m_hasInterchangeNewlineAtEnd = true;
314             removeNode(node);
315             break;
316         }
317         node = node->lastChild();
318     }
319     
320     node = container->firstChild();
321     while (node) {
322         RefPtr<Node> next = NodeTraversal::next(node);
323         if (isInterchangeConvertedSpaceSpan(node)) {
324             next = NodeTraversal::nextSkippingChildren(node);
325             removeNodePreservingChildren(node);
326         }
327         node = next.get();
328     }
329 }
330
331 inline void ReplaceSelectionCommand::InsertedNodes::respondToNodeInsertion(Node* node)
332 {
333     if (!node)
334         return;
335     
336     if (!m_firstNodeInserted)
337         m_firstNodeInserted = node;
338     
339     m_lastNodeInserted = node;
340 }
341
342 inline void ReplaceSelectionCommand::InsertedNodes::willRemoveNodePreservingChildren(Node* node)
343 {
344     if (m_firstNodeInserted == node)
345         m_firstNodeInserted = NodeTraversal::next(node);
346     if (m_lastNodeInserted == node)
347         m_lastNodeInserted = node->lastChild() ? node->lastChild() : NodeTraversal::nextSkippingChildren(node);
348 }
349
350 inline void ReplaceSelectionCommand::InsertedNodes::willRemoveNode(Node* node)
351 {
352     if (m_firstNodeInserted == node && m_lastNodeInserted == node) {
353         m_firstNodeInserted = 0;
354         m_lastNodeInserted = 0;
355     } else if (m_firstNodeInserted == node)
356         m_firstNodeInserted = NodeTraversal::nextSkippingChildren(m_firstNodeInserted.get());
357     else if (m_lastNodeInserted == node)
358         m_lastNodeInserted = NodeTraversal::previousSkippingChildren(m_lastNodeInserted.get());
359 }
360
361 inline void ReplaceSelectionCommand::InsertedNodes::didReplaceNode(Node* node, Node* newNode)
362 {
363     if (m_firstNodeInserted == node)
364         m_firstNodeInserted = newNode;
365     if (m_lastNodeInserted == node)
366         m_lastNodeInserted = newNode;
367 }
368
369 ReplaceSelectionCommand::ReplaceSelectionCommand(Document& document, PassRefPtr<DocumentFragment> fragment, CommandOptions options, EditAction editAction)
370     : CompositeEditCommand(document)
371     , m_selectReplacement(options & SelectReplacement)
372     , m_smartReplace(options & SmartReplace)
373     , m_matchStyle(options & MatchStyle)
374     , m_documentFragment(fragment)
375     , m_preventNesting(options & PreventNesting)
376     , m_movingParagraph(options & MovingParagraph)
377     , m_editAction(editAction)
378     , m_sanitizeFragment(options & SanitizeFragment)
379     , m_shouldMergeEnd(false)
380 {
381 }
382
383 static bool hasMatchingQuoteLevel(VisiblePosition endOfExistingContent, VisiblePosition endOfInsertedContent)
384 {
385     Position existing = endOfExistingContent.deepEquivalent();
386     Position inserted = endOfInsertedContent.deepEquivalent();
387     bool isInsideMailBlockquote = enclosingNodeOfType(inserted, isMailBlockquote, CanCrossEditingBoundary);
388     return isInsideMailBlockquote && (numEnclosingMailBlockquotes(existing) == numEnclosingMailBlockquotes(inserted));
389 }
390
391 bool ReplaceSelectionCommand::shouldMergeStart(bool selectionStartWasStartOfParagraph, bool fragmentHasInterchangeNewlineAtStart, bool selectionStartWasInsideMailBlockquote)
392 {
393     if (m_movingParagraph)
394         return false;
395     
396     VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent());
397     VisiblePosition prev = startOfInsertedContent.previous(CannotCrossEditingBoundary);
398     if (prev.isNull())
399         return false;
400     
401     // When we have matching quote levels, its ok to merge more frequently.
402     // For a successful merge, we still need to make sure that the inserted content starts with the beginning of a paragraph.
403     // And we should only merge here if the selection start was inside a mail blockquote.  This prevents against removing a 
404     // blockquote from newly pasted quoted content that was pasted into an unquoted position.  If that unquoted position happens 
405     // to be right after another blockquote, we don't want to merge and risk stripping a valid block (and newline) from the pasted content.
406     if (isStartOfParagraph(startOfInsertedContent) && selectionStartWasInsideMailBlockquote && hasMatchingQuoteLevel(prev, positionAtEndOfInsertedContent()))
407         return true;
408
409     return !selectionStartWasStartOfParagraph
410         && !fragmentHasInterchangeNewlineAtStart
411         && isStartOfParagraph(startOfInsertedContent)
412         && !startOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName(brTag)
413         && shouldMerge(startOfInsertedContent, prev);
414 }
415
416 bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph)
417 {
418     VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent());
419     VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary);
420     if (next.isNull())
421         return false;
422
423     return !selectionEndWasEndOfParagraph
424         && isEndOfParagraph(endOfInsertedContent)
425         && !endOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName(brTag)
426         && shouldMerge(endOfInsertedContent, next);
427 }
428
429 static bool isMailPasteAsQuotationNode(const Node* node)
430 {
431     return node && node->hasTagName(blockquoteTag) && node->isElementNode() && toElement(node)->getAttribute(classAttr) == ApplePasteAsQuotation;
432 }
433
434 static bool isHeaderElement(const Node* a)
435 {
436     if (!a)
437         return false;
438         
439     return a->hasTagName(h1Tag)
440         || a->hasTagName(h2Tag)
441         || a->hasTagName(h3Tag)
442         || a->hasTagName(h4Tag)
443         || a->hasTagName(h5Tag)
444         || a->hasTagName(h6Tag);
445 }
446
447 static bool haveSameTagName(Node* a, Node* b)
448 {
449     return a && b && a->isElementNode() && b->isElementNode() && toElement(a)->tagName() == toElement(b)->tagName();
450 }
451
452 bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const VisiblePosition& destination)
453 {
454     if (source.isNull() || destination.isNull())
455         return false;
456         
457     Node* sourceNode = source.deepEquivalent().deprecatedNode();
458     Node* destinationNode = destination.deepEquivalent().deprecatedNode();
459     Node* sourceBlock = enclosingBlock(sourceNode);
460     Node* destinationBlock = enclosingBlock(destinationNode);
461     return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotationNode) &&
462            sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || isMailBlockquote(sourceBlock))  &&
463            enclosingListChild(sourceBlock) == enclosingListChild(destinationNode) &&
464            enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(destination.deepEquivalent()) &&
465            (!isHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, destinationBlock)) &&
466            // Don't merge to or from a position before or after a block because it would
467            // be a no-op and cause infinite recursion.
468            !isBlock(sourceNode) && !isBlock(destinationNode);
469 }
470
471 // Style rules that match just inserted elements could change their appearance, like
472 // a div inserted into a document with div { display:inline; }.
473 void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(InsertedNodes& insertedNodes)
474 {
475     RefPtr<Node> pastEndNode = insertedNodes.pastLastLeaf();
476     RefPtr<Node> next;
477     for (RefPtr<Node> node = insertedNodes.firstNodeInserted(); node && node != pastEndNode; node = next) {
478         // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
479
480         next = NodeTraversal::next(node.get());
481         if (!node->isStyledElement())
482             continue;
483
484         StyledElement* element = toStyledElement(node.get());
485
486         const StyleProperties* inlineStyle = element->inlineStyle();
487         RefPtr<EditingStyle> newInlineStyle = EditingStyle::create(inlineStyle);
488         if (inlineStyle) {
489             if (element->isHTMLElement()) {
490                 Vector<QualifiedName> attributes;
491                 HTMLElement* htmlElement = toHTMLElement(element);
492
493                 if (newInlineStyle->conflictsWithImplicitStyleOfElement(htmlElement)) {
494                     // e.g. <b style="font-weight: normal;"> is converted to <span style="font-weight: normal;">
495                     node = replaceElementWithSpanPreservingChildrenAndAttributes(htmlElement);
496                     element = toStyledElement(node.get());
497                     insertedNodes.didReplaceNode(htmlElement, node.get());
498                 } else if (newInlineStyle->extractConflictingImplicitStyleOfAttributes(htmlElement, EditingStyle::PreserveWritingDirection, 0, attributes,
499                     EditingStyle::DoNotExtractMatchingStyle)) {
500                     // e.g. <font size="3" style="font-size: 20px;"> is converted to <font style="font-size: 20px;">
501                     for (size_t i = 0; i < attributes.size(); i++)
502                         removeNodeAttribute(element, attributes[i]);
503                 }
504             }
505
506             ContainerNode* context = element->parentNode();
507
508             // If Mail wraps the fragment with a Paste as Quotation blockquote, or if you're pasting into a quoted region,
509             // styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>.
510             Node* blockquoteNode = isMailPasteAsQuotationNode(context) ? context : enclosingNodeOfType(firstPositionInNode(context), isMailBlockquote, CanCrossEditingBoundary);
511             if (blockquoteNode)
512                 newInlineStyle->removeStyleFromRulesAndContext(element, document().documentElement());
513
514             newInlineStyle->removeStyleFromRulesAndContext(element, context);
515         }
516
517         if (!inlineStyle || newInlineStyle->isEmpty()) {
518             if (isStyleSpanOrSpanWithOnlyStyleAttribute(element) || isEmptyFontTag(element, AllowNonEmptyStyleAttribute)) {
519                 insertedNodes.willRemoveNodePreservingChildren(element);
520                 removeNodePreservingChildren(element);
521                 continue;
522             }
523             removeNodeAttribute(element, styleAttr);
524         } else if (newInlineStyle->style()->propertyCount() != inlineStyle->propertyCount())
525             setNodeAttribute(element, styleAttr, newInlineStyle->style()->asText());
526
527         // FIXME: Tolerate differences in id, class, and style attributes.
528         if (isNonTableCellHTMLBlockElement(element) && areIdenticalElements(element, element->parentNode())
529             && VisiblePosition(firstPositionInNode(element->parentNode())) == VisiblePosition(firstPositionInNode(element))
530             && VisiblePosition(lastPositionInNode(element->parentNode())) == VisiblePosition(lastPositionInNode(element))) {
531             insertedNodes.willRemoveNodePreservingChildren(element);
532             removeNodePreservingChildren(element);
533             continue;
534         }
535
536         if (element->parentNode()->hasRichlyEditableStyle())
537             removeNodeAttribute(element, contenteditableAttr);
538
539         // WebKit used to not add display: inline and float: none on copy.
540         // Keep this code around for backward compatibility
541         if (isLegacyAppleStyleSpan(element)) {
542             if (!element->firstChild()) {
543                 insertedNodes.willRemoveNodePreservingChildren(element);
544                 removeNodePreservingChildren(element);
545                 continue;
546             }
547             // There are other styles that style rules can give to style spans,
548             // but these are the two important ones because they'll prevent
549             // inserted content from appearing in the right paragraph.
550             // FIXME: Hyatt is concerned that selectively using display:inline will give inconsistent
551             // results. We already know one issue because td elements ignore their display property
552             // in quirks mode (which Mail.app is always in). We should look for an alternative.
553
554             // Mutate using the CSSOM wrapper so we get the same event behavior as a script.
555             if (isBlock(element))
556                 element->style()->setPropertyInternal(CSSPropertyDisplay, "inline", false, IGNORE_EXCEPTION);
557             if (element->renderer() && element->renderer()->style().isFloating())
558                 element->style()->setPropertyInternal(CSSPropertyFloat, "none", false, IGNORE_EXCEPTION);
559         }
560     }
561 }
562
563 static bool isProhibitedParagraphChild(const AtomicString& name)
564 {
565     // https://dvcs.w3.org/hg/editing/raw-file/57abe6d3cb60/editing.html#prohibited-paragraph-child
566     DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<AtomicString>, elements, ());
567     if (elements.isEmpty()) {
568         elements.add(addressTag.localName());
569         elements.add(articleTag.localName());
570         elements.add(asideTag.localName());
571         elements.add(blockquoteTag.localName());
572         elements.add(captionTag.localName());
573         elements.add(centerTag.localName());
574         elements.add(colTag.localName());
575         elements.add(colgroupTag.localName());
576         elements.add(ddTag.localName());
577         elements.add(detailsTag.localName());
578         elements.add(dirTag.localName());
579         elements.add(divTag.localName());
580         elements.add(dlTag.localName());
581         elements.add(dtTag.localName());
582         elements.add(fieldsetTag.localName());
583         elements.add(figcaptionTag.localName());
584         elements.add(figureTag.localName());
585         elements.add(footerTag.localName());
586         elements.add(formTag.localName());
587         elements.add(h1Tag.localName());
588         elements.add(h2Tag.localName());
589         elements.add(h3Tag.localName());
590         elements.add(h4Tag.localName());
591         elements.add(h5Tag.localName());
592         elements.add(h6Tag.localName());
593         elements.add(headerTag.localName());
594         elements.add(hgroupTag.localName());
595         elements.add(hrTag.localName());
596         elements.add(liTag.localName());
597         elements.add(listingTag.localName());
598         elements.add(mainTag.localName()); // Missing in the specification.
599         elements.add(menuTag.localName());
600         elements.add(navTag.localName());
601         elements.add(olTag.localName());
602         elements.add(pTag.localName());
603         elements.add(plaintextTag.localName());
604         elements.add(preTag.localName());
605         elements.add(sectionTag.localName());
606         elements.add(summaryTag.localName());
607         elements.add(tableTag.localName());
608         elements.add(tbodyTag.localName());
609         elements.add(tdTag.localName());
610         elements.add(tfootTag.localName());
611         elements.add(thTag.localName());
612         elements.add(theadTag.localName());
613         elements.add(trTag.localName());
614         elements.add(ulTag.localName());
615         elements.add(xmpTag.localName());
616     }
617     return elements.contains(name);
618 }
619
620 void ReplaceSelectionCommand::makeInsertedContentRoundTrippableWithHTMLTreeBuilder(const InsertedNodes& insertedNodes)
621 {
622     RefPtr<Node> pastEndNode = insertedNodes.pastLastLeaf();
623     RefPtr<Node> next;
624     for (RefPtr<Node> node = insertedNodes.firstNodeInserted(); node && node != pastEndNode; node = next) {
625         next = NodeTraversal::next(node.get());
626
627         if (!node->isHTMLElement())
628             continue;
629
630         if (isProhibitedParagraphChild(toHTMLElement(node.get())->localName())) {
631             if (HTMLElement* paragraphElement = toHTMLElement(enclosingNodeWithTag(positionInParentBeforeNode(node.get()), pTag)))
632                 moveNodeOutOfAncestor(node, paragraphElement);
633         }
634
635         if (isHeaderElement(node.get())) {
636             if (HTMLElement* headerElement = toHTMLElement(highestEnclosingNodeOfType(positionInParentBeforeNode(node.get()), isHeaderElement)))
637                 moveNodeOutOfAncestor(node, headerElement);
638         }
639     }
640 }
641
642 void ReplaceSelectionCommand::moveNodeOutOfAncestor(PassRefPtr<Node> prpNode, PassRefPtr<Node> prpAncestor)
643 {
644     RefPtr<Node> node = prpNode;
645     RefPtr<Node> ancestor = prpAncestor;
646
647     VisiblePosition positionAtEndOfNode = lastPositionInOrAfterNode(node.get());
648     VisiblePosition lastPositionInParagraph = lastPositionInNode(ancestor.get());
649     if (positionAtEndOfNode == lastPositionInParagraph) {
650         removeNode(node);
651         if (ancestor->nextSibling())
652             insertNodeBefore(node, ancestor->nextSibling());
653         else
654             appendNode(node, ancestor->parentNode());
655     } else {
656         RefPtr<Node> nodeToSplitTo = splitTreeToNode(node.get(), ancestor.get(), true);
657         removeNode(node);
658         insertNodeBefore(node, nodeToSplitTo);
659     }
660     if (!ancestor->firstChild())
661         removeNode(ancestor.release());
662 }
663
664 static inline bool hasRenderedText(const Text& text)
665 {
666     return text.renderer() && text.renderer()->hasRenderedText();
667 }
668
669 void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds(InsertedNodes& insertedNodes)
670 {
671     document().updateLayoutIgnorePendingStylesheets();
672
673     Node* lastLeafInserted = insertedNodes.lastLeafInserted();
674     if (lastLeafInserted && lastLeafInserted->isTextNode() && !hasRenderedText(toText(*lastLeafInserted))
675         && !enclosingNodeWithTag(firstPositionInOrBeforeNode(lastLeafInserted), selectTag)
676         && !enclosingNodeWithTag(firstPositionInOrBeforeNode(lastLeafInserted), scriptTag)) {
677         insertedNodes.willRemoveNode(lastLeafInserted);
678         removeNode(lastLeafInserted);
679     }
680
681     // We don't have to make sure that firstNodeInserted isn't inside a select or script element
682     // because it is a top level node in the fragment and the user can't insert into those elements.
683     Node* firstNodeInserted = insertedNodes.firstNodeInserted();
684     if (firstNodeInserted && firstNodeInserted->isTextNode() && !hasRenderedText(toText(*firstNodeInserted))) {
685         insertedNodes.willRemoveNode(firstNodeInserted);
686         removeNode(firstNodeInserted);
687     }
688 }
689
690 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() const
691 {
692     // FIXME: Why is this hack here?  What's special about <select> tags?
693     Node* enclosingSelect = enclosingNodeWithTag(m_endOfInsertedContent, selectTag);
694     return enclosingSelect ? lastPositionInOrAfterNode(enclosingSelect) : m_endOfInsertedContent;
695 }
696
697 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() const
698 {
699     return m_startOfInsertedContent;
700 }
701
702 static void removeHeadContents(ReplacementFragment& fragment)
703 {
704     if (fragment.isEmpty())
705         return;
706
707     Vector<Element*> toRemove;
708
709     auto it = descendantsOfType<Element>(*fragment.fragment()).begin();
710     auto end = descendantsOfType<Element>(*fragment.fragment()).end();
711     while (it != end) {
712         if (it->hasTagName(baseTag) || it->hasTagName(linkTag) || it->hasTagName(metaTag) || it->hasTagName(styleTag) || isHTMLTitleElement(*it)) {
713             toRemove.append(&*it);
714             it.traverseNextSkippingChildren();
715             continue;
716         }
717         ++it;
718     }
719
720     for (unsigned i = 0; i < toRemove.size(); ++i)
721         fragment.removeNode(toRemove[i]);
722 }
723
724 // Remove style spans before insertion if they are unnecessary.  It's faster because we'll 
725 // avoid doing a layout.
726 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos)
727 {
728     Node* topNode = fragment.firstChild();
729
730     // Handling the case where we are doing Paste as Quotation or pasting into quoted content is more complicated (see handleStyleSpans)
731     // and doesn't receive the optimization.
732     if (isMailPasteAsQuotationNode(topNode) || enclosingNodeOfType(firstPositionInOrBeforeNode(topNode), isMailBlockquote, CanCrossEditingBoundary))
733         return false;
734
735     // Either there are no style spans in the fragment or a WebKit client has added content to the fragment
736     // before inserting it.  Look for and handle style spans after insertion.
737     if (!isLegacyAppleStyleSpan(topNode))
738         return false;
739
740     Node* wrappingStyleSpan = topNode;
741     RefPtr<EditingStyle> styleAtInsertionPos = EditingStyle::create(insertionPos.parentAnchoredEquivalent());
742     String styleText = styleAtInsertionPos->style()->asText();
743
744     // FIXME: This string comparison is a naive way of comparing two styles.
745     // We should be taking the diff and check that the diff is empty.
746     if (styleText != toElement(wrappingStyleSpan)->getAttribute(styleAttr))
747         return false;
748
749     fragment.removeNodePreservingChildren(wrappingStyleSpan);
750     return true;
751 }
752
753 // At copy time, WebKit wraps copied content in a span that contains the source document's 
754 // default styles.  If the copied Range inherits any other styles from its ancestors, we put 
755 // those styles on a second span.
756 // This function removes redundant styles from those spans, and removes the spans if all their 
757 // styles are redundant. 
758 // We should remove the Apple-style-span class when we're done, see <rdar://problem/5685600>.
759 // We should remove styles from spans that are overridden by all of their children, either here
760 // or at copy time.
761 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes)
762 {
763     HTMLElement* wrappingStyleSpan = 0;
764     // The style span that contains the source document's default style should be at
765     // the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Quotation),
766     // so search for the top level style span instead of assuming it's at the top.
767     for (Node* node = insertedNodes.firstNodeInserted(); node; node = NodeTraversal::next(node)) {
768         if (isLegacyAppleStyleSpan(node)) {
769             wrappingStyleSpan = toHTMLElement(node);
770             break;
771         }
772     }
773     
774     // There might not be any style spans if we're pasting from another application or if 
775     // we are here because of a document.execCommand("InsertHTML", ...) call.
776     if (!wrappingStyleSpan)
777         return;
778
779     RefPtr<EditingStyle> style = EditingStyle::create(wrappingStyleSpan->inlineStyle());
780     ContainerNode* context = wrappingStyleSpan->parentNode();
781
782     // If Mail wraps the fragment with a Paste as Quotation blockquote, or if you're pasting into a quoted region,
783     // styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>.
784     Node* blockquoteNode = isMailPasteAsQuotationNode(context) ? context : enclosingNodeOfType(firstPositionInNode(context), isMailBlockquote, CanCrossEditingBoundary);
785     if (blockquoteNode)
786         context = document().documentElement();
787
788     // This operation requires that only editing styles to be removed from sourceDocumentStyle.
789     style->prepareToApplyAt(firstPositionInNode(context));
790
791     // Remove block properties in the span's style. This prevents properties that probably have no effect 
792     // currently from affecting blocks later if the style is cloned for a new block element during a future 
793     // editing operation.
794     // FIXME: They *can* have an effect currently if blocks beneath the style span aren't individually marked
795     // with block styles by the editing engine used to style them.  WebKit doesn't do this, but others might.
796     style->removeBlockProperties();
797
798     if (style->isEmpty() || !wrappingStyleSpan->firstChild()) {
799         insertedNodes.willRemoveNodePreservingChildren(wrappingStyleSpan);
800         removeNodePreservingChildren(wrappingStyleSpan);
801     } else
802         setNodeAttribute(wrappingStyleSpan, styleAttr, style->style()->asText());
803 }
804
805 void ReplaceSelectionCommand::mergeEndIfNeeded()
806 {
807     if (!m_shouldMergeEnd)
808         return;
809
810     VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent());
811     VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent());
812     
813     // Bail to avoid infinite recursion.
814     if (m_movingParagraph) {
815         ASSERT_NOT_REACHED();
816         return;
817     }
818     
819     // Merging two paragraphs will destroy the moved one's block styles.  Always move the end of inserted forward 
820     // to preserve the block style of the paragraph already in the document, unless the paragraph to move would 
821     // include the what was the start of the selection that was pasted into, so that we preserve that paragraph's
822     // block styles.
823     bool mergeForward = !(inSameParagraph(startOfInsertedContent, endOfInsertedContent) && !isStartOfParagraph(startOfInsertedContent));
824     
825     VisiblePosition destination = mergeForward ? endOfInsertedContent.next() : endOfInsertedContent;
826     VisiblePosition startOfParagraphToMove = mergeForward ? startOfParagraph(endOfInsertedContent) : endOfInsertedContent.next();
827    
828     // Merging forward could result in deleting the destination anchor node.
829     // To avoid this, we add a placeholder node before the start of the paragraph.
830     if (endOfParagraph(startOfParagraphToMove) == destination) {
831         RefPtr<Node> placeholder = createBreakElement(document());
832         insertNodeBefore(placeholder, startOfParagraphToMove.deepEquivalent().deprecatedNode());
833         destination = VisiblePosition(positionBeforeNode(placeholder.get()));
834     }
835
836     moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
837     
838     // Merging forward will remove m_endOfInsertedContent from the document.
839     if (mergeForward) {
840         if (m_startOfInsertedContent.isOrphan())
841             m_startOfInsertedContent = endingSelection().visibleStart().deepEquivalent();
842          m_endOfInsertedContent = endingSelection().visibleEnd().deepEquivalent();
843         // If we merged text nodes, m_endOfInsertedContent could be null. If this is the case, we use m_startOfInsertedContent.
844         if (m_endOfInsertedContent.isNull())
845             m_endOfInsertedContent = m_startOfInsertedContent;
846     }
847 }
848
849 static Node* enclosingInline(Node* node)
850 {
851     while (ContainerNode* parent = node->parentNode()) {
852         if (isBlockFlowElement(parent) || parent->hasTagName(bodyTag))
853             return node;
854         // Stop if any previous sibling is a block.
855         for (Node* sibling = node->previousSibling(); sibling; sibling = sibling->previousSibling()) {
856             if (isBlockFlowElement(sibling))
857                 return node;
858         }
859         node = parent;
860     }
861     return node;
862 }
863
864 static bool isInlineNodeWithStyle(const Node* node)
865 {
866     // We don't want to skip over any block elements.
867     if (isBlock(node))
868         return false;
869
870     if (!node->isHTMLElement())
871         return false;
872
873     // We can skip over elements whose class attribute is
874     // one of our internal classes.
875     const HTMLElement* element = static_cast<const HTMLElement*>(node);
876     const AtomicString& classAttributeValue = element->getAttribute(classAttr);
877     if (classAttributeValue == AppleTabSpanClass
878         || classAttributeValue == AppleConvertedSpace
879         || classAttributeValue == ApplePasteAsQuotation)
880         return true;
881
882     return EditingStyle::elementIsStyledSpanOrHTMLEquivalent(element);
883 }
884
885 inline Node* nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(const Position& insertionPos)
886 {
887     Node* containgBlock = enclosingBlock(insertionPos.containerNode());
888     return highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle, CannotCrossEditingBoundary, containgBlock);
889 }
890
891 void ReplaceSelectionCommand::doApply()
892 {
893     VisibleSelection selection = endingSelection();
894     ASSERT(selection.isCaretOrRange());
895     ASSERT(selection.start().deprecatedNode());
896     if (!selection.isNonOrphanedCaretOrRange() || !selection.start().deprecatedNode())
897         return;
898
899     if (!selection.rootEditableElement())
900         return;
901
902 #if PLATFORM(IOS)
903     // In plain text only regions, we create style-less fragments, so the inserted content will automatically
904     // match the style of the surrounding area and so we can avoid unnecessary work below for m_matchStyle.
905     if (!selection.isContentRichlyEditable())
906         m_matchStyle = false;
907 #endif
908
909     ReplacementFragment fragment(document(), m_documentFragment.get(), selection);
910     if (performTrivialReplace(fragment))
911         return;
912     
913     // We can skip matching the style if the selection is plain text.
914     if ((selection.start().deprecatedNode()->renderer() && selection.start().deprecatedNode()->renderer()->style().userModify() == READ_WRITE_PLAINTEXT_ONLY)
915         && (selection.end().deprecatedNode()->renderer() && selection.end().deprecatedNode()->renderer()->style().userModify() == READ_WRITE_PLAINTEXT_ONLY))
916         m_matchStyle = false;
917     
918     if (m_matchStyle) {
919         m_insertionStyle = EditingStyle::create(selection.start());
920         m_insertionStyle->mergeTypingStyle(document());
921     }
922
923     VisiblePosition visibleStart = selection.visibleStart();
924     VisiblePosition visibleEnd = selection.visibleEnd();
925     
926     bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd);
927     bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart);
928     
929     Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().deprecatedNode());
930     
931     Position insertionPos = selection.start();
932     bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailBlockquote, CanCrossEditingBoundary);
933     bool selectionIsPlainText = !selection.isContentRichlyEditable();
934     Element* currentRoot = selection.rootEditableElement();
935
936     if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !startIsInsideMailBlockquote) ||
937         startBlock == currentRoot || isListItem(startBlock) || selectionIsPlainText)
938         m_preventNesting = false;
939     
940     if (selection.isRange()) {
941         // When the end of the selection being pasted into is at the end of a paragraph, and that selection
942         // spans multiple blocks, not merging may leave an empty line.
943         // When the start of the selection being pasted into is at the start of a block, not merging 
944         // will leave hanging block(s).
945         // Merge blocks if the start of the selection was in a Mail blockquote, since we handle  
946         // that case specially to prevent nesting. 
947         bool mergeBlocksAfterDelete = startIsInsideMailBlockquote || isEndOfParagraph(visibleEnd) || isStartOfBlock(visibleStart);
948         // FIXME: We should only expand to include fully selected special elements if we are copying a 
949         // selection and pasting it on top of itself.
950         deleteSelection(false, mergeBlocksAfterDelete, true, false);
951         visibleStart = endingSelection().visibleStart();
952         if (fragment.hasInterchangeNewlineAtStart()) {
953             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
954                 if (!isEndOfEditableOrNonEditableContent(visibleStart))
955                     setEndingSelection(visibleStart.next());
956             } else
957                 insertParagraphSeparator();
958         }
959         insertionPos = endingSelection().start();
960     } else {
961         ASSERT(selection.isCaret());
962         if (fragment.hasInterchangeNewlineAtStart()) {
963             VisiblePosition next = visibleStart.next(CannotCrossEditingBoundary);
964             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart) && next.isNotNull())
965                 setEndingSelection(next);
966             else  {
967                 insertParagraphSeparator();
968                 visibleStart = endingSelection().visibleStart();
969             }
970         }
971         // We split the current paragraph in two to avoid nesting the blocks from the fragment inside the current block.
972         // For example paste <div>foo</div><div>bar</div><div>baz</div> into <div>x^x</div>, where ^ is the caret.  
973         // As long as the  div styles are the same, visually you'd expect: <div>xbar</div><div>bar</div><div>bazx</div>, 
974         // not <div>xbar<div>bar</div><div>bazx</div></div>.
975         // Don't do this if the selection started in a Mail blockquote.
976         if (m_preventNesting && !startIsInsideMailBlockquote && !isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
977             insertParagraphSeparator();
978             setEndingSelection(endingSelection().visibleStart().previous());
979         }
980         insertionPos = endingSelection().start();
981     }
982     
983     // We don't want any of the pasted content to end up nested in a Mail blockquote, so first break 
984     // out of any surrounding Mail blockquotes. Unless we're inserting in a table, in which case
985     // breaking the blockquote will prevent the content from actually being inserted in the table.
986     if (startIsInsideMailBlockquote && m_preventNesting && !(enclosingNodeOfType(insertionPos, &isTableStructureNode))) { 
987         applyCommandToComposite(BreakBlockquoteCommand::create(document())); 
988         // This will leave a br between the split. 
989         Node* br = endingSelection().start().deprecatedNode(); 
990         ASSERT(br->hasTagName(brTag)); 
991         // Insert content between the two blockquotes, but remove the br (since it was just a placeholder). 
992         insertionPos = positionInParentBeforeNode(br);
993         removeNode(br);
994     }
995     
996     // Inserting content could cause whitespace to collapse, e.g. inserting <div>foo</div> into hello^ world.
997     prepareWhitespaceAtPositionForSplit(insertionPos);
998
999     // If the downstream node has been removed there's no point in continuing.
1000     if (!insertionPos.downstream().deprecatedNode())
1001       return;
1002     
1003     // NOTE: This would be an incorrect usage of downstream() if downstream() were changed to mean the last position after 
1004     // p that maps to the same visible position as p (since in the case where a br is at the end of a block and collapsed 
1005     // away, there are positions after the br which map to the same visible position as [br, 0]).  
1006     RefPtr<Node> endBR = insertionPos.downstream().deprecatedNode()->hasTagName(brTag) ? insertionPos.downstream().deprecatedNode() : nullptr;
1007     VisiblePosition originalVisPosBeforeEndBR;
1008     if (endBR)
1009         originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR.get()), DOWNSTREAM).previous();
1010     
1011     RefPtr<Node> insertionBlock = enclosingBlock(insertionPos.deprecatedNode());
1012     
1013     // Adjust insertionPos to prevent nesting.
1014     // If the start was in a Mail blockquote, we will have already handled adjusting insertionPos above.
1015     if (m_preventNesting && insertionBlock && !isTableCell(insertionBlock.get()) && !startIsInsideMailBlockquote) {
1016         ASSERT(insertionBlock != currentRoot);
1017         VisiblePosition visibleInsertionPos(insertionPos);
1018         if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInsertionPos) && fragment.hasInterchangeNewlineAtEnd()))
1019             insertionPos = positionInParentAfterNode(insertionBlock.get());
1020         else if (isStartOfBlock(visibleInsertionPos))
1021             insertionPos = positionInParentBeforeNode(insertionBlock.get());
1022     }
1023     
1024     // Paste at start or end of link goes outside of link.
1025     insertionPos = positionAvoidingSpecialElementBoundary(insertionPos);
1026     
1027     // FIXME: Can this wait until after the operation has been performed?  There doesn't seem to be
1028     // any work performed after this that queries or uses the typing style.
1029     frame().selection().clearTypingStyle();
1030
1031     removeHeadContents(fragment);
1032
1033     // We don't want the destination to end up inside nodes that weren't selected.  To avoid that, we move the
1034     // position forward without changing the visible position so we're still at the same visible location, but
1035     // outside of preceding tags.
1036     insertionPos = positionAvoidingPrecedingNodes(insertionPos);
1037
1038     // Paste into run of tabs splits the tab span.
1039     insertionPos = positionOutsideTabSpan(insertionPos);
1040
1041     bool handledStyleSpans = handleStyleSpansBeforeInsertion(fragment, insertionPos);
1042
1043     // We're finished if there is nothing to add.
1044     if (fragment.isEmpty() || !fragment.firstChild())
1045         return;
1046
1047     // If we are not trying to match the destination style we prefer a position
1048     // that is outside inline elements that provide style.
1049     // This way we can produce a less verbose markup.
1050     // We can skip this optimization for fragments not wrapped in one of
1051     // our style spans and for positions inside list items
1052     // since insertAsListItems already does the right thing.
1053     if (!m_matchStyle && !enclosingList(insertionPos.containerNode())) {
1054         if (insertionPos.containerNode()->isTextNode() && insertionPos.offsetInContainerNode() && !insertionPos.atLastEditingPositionForNode()) {
1055             splitTextNode(insertionPos.containerText(), insertionPos.offsetInContainerNode());
1056             insertionPos = firstPositionInNode(insertionPos.containerNode());
1057         }
1058
1059         if (RefPtr<Node> nodeToSplitTo = nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(insertionPos)) {
1060             if (insertionPos.containerNode() != nodeToSplitTo->parentNode()) {
1061                 Node* splitStart = insertionPos.computeNodeAfterPosition();
1062                 if (!splitStart)
1063                     splitStart = insertionPos.containerNode();
1064                 nodeToSplitTo = splitTreeToNode(splitStart, nodeToSplitTo->parentNode()).get();
1065                 insertionPos = positionInParentBeforeNode(nodeToSplitTo.get());
1066             }
1067         }
1068     }
1069
1070     // FIXME: When pasting rich content we're often prevented from heading down the fast path by style spans.  Try
1071     // again here if they've been removed.
1072
1073     // 1) Insert the content.
1074     // 2) Remove redundant styles and style tags, this inner <b> for example: <b>foo <b>bar</b> baz</b>.
1075     // 3) Merge the start of the added content with the content before the position being pasted into.
1076     // 4) Do one of the following: a) expand the last br if the fragment ends with one and it collapsed,
1077     // b) merge the last paragraph of the incoming fragment with the paragraph that contained the 
1078     // end of the selection that was pasted into, or c) handle an interchange newline at the end of the 
1079     // incoming fragment.
1080     // 5) Add spaces for smart replace.
1081     // 6) Select the replacement if requested, and match style if requested.
1082
1083     InsertedNodes insertedNodes;
1084     RefPtr<Node> refNode = fragment.firstChild();
1085     RefPtr<Node> node = refNode->nextSibling();
1086     
1087     fragment.removeNode(refNode);
1088
1089     Node* blockStart = enclosingBlock(insertionPos.deprecatedNode());
1090     if ((isListElement(refNode.get()) || (isLegacyAppleStyleSpan(refNode.get()) && isListElement(refNode->firstChild())))
1091         && blockStart && blockStart->renderer()->isListItem())
1092         refNode = insertAsListItems(toHTMLElement(refNode.get()), blockStart, insertionPos, insertedNodes);
1093     else {
1094         insertNodeAt(refNode, insertionPos);
1095         insertedNodes.respondToNodeInsertion(refNode.get());
1096     }
1097
1098     // Mutation events (bug 22634) may have already removed the inserted content
1099     if (!refNode->inDocument())
1100         return;
1101
1102     bool plainTextFragment = isPlainTextMarkup(refNode.get());
1103
1104     while (node) {
1105         RefPtr<Node> next = node->nextSibling();
1106         fragment.removeNode(node.get());
1107         insertNodeAfter(node, refNode.get());
1108         insertedNodes.respondToNodeInsertion(node.get());
1109
1110         // Mutation events (bug 22634) may have already removed the inserted content
1111         if (!node->inDocument())
1112             return;
1113
1114         refNode = node;
1115         if (node && plainTextFragment)
1116             plainTextFragment = isPlainTextMarkup(node.get());
1117         node = next;
1118     }
1119
1120     removeUnrenderedTextNodesAtEnds(insertedNodes);
1121
1122     if (!handledStyleSpans)
1123         handleStyleSpans(insertedNodes);
1124
1125     // Mutation events (bug 20161) may have already removed the inserted content
1126     if (!insertedNodes.firstNodeInserted() || !insertedNodes.firstNodeInserted()->inDocument())
1127         return;
1128
1129     VisiblePosition startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted());
1130
1131     // We inserted before the insertionBlock to prevent nesting, and the content before the insertionBlock wasn't in its own block and
1132     // didn't have a br after it, so the inserted content ended up in the same paragraph.
1133     if (insertionBlock && insertionPos.deprecatedNode() == insertionBlock->parentNode() && (unsigned)insertionPos.deprecatedEditingOffset() < insertionBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent))
1134         insertNodeAt(createBreakElement(document()), startOfInsertedContent.deepEquivalent());
1135
1136     if (endBR && (plainTextFragment || shouldRemoveEndBR(endBR.get(), originalVisPosBeforeEndBR))) {
1137         RefPtr<Node> parent = endBR->parentNode();
1138         insertedNodes.willRemoveNode(endBR.get());
1139         removeNode(endBR);
1140         if (Node* nodeToRemove = highestNodeToRemoveInPruning(parent.get())) {
1141             insertedNodes.willRemoveNode(nodeToRemove);
1142             removeNode(nodeToRemove);
1143         }
1144     }
1145     
1146     makeInsertedContentRoundTrippableWithHTMLTreeBuilder(insertedNodes);
1147
1148     removeRedundantStylesAndKeepStyleSpanInline(insertedNodes);
1149
1150     if (m_sanitizeFragment)
1151         applyCommandToComposite(SimplifyMarkupCommand::create(document(), insertedNodes.firstNodeInserted(), insertedNodes.pastLastLeaf()));
1152
1153     // Setup m_startOfInsertedContent and m_endOfInsertedContent. This should be the last two lines of code that access insertedNodes.
1154     m_startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted());
1155     m_endOfInsertedContent = lastPositionInOrAfterNode(insertedNodes.lastLeafInserted());
1156
1157     // Determine whether or not we should merge the end of inserted content with what's after it before we do
1158     // the start merge so that the start merge doesn't effect our decision.
1159     m_shouldMergeEnd = shouldMergeEnd(selectionEndWasEndOfParagraph);
1160     
1161     if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasInterchangeNewlineAtStart(), startIsInsideMailBlockquote)) {
1162         VisiblePosition startOfParagraphToMove = positionAtStartOfInsertedContent();
1163         VisiblePosition destination = startOfParagraphToMove.previous();
1164         // We need to handle the case where we need to merge the end
1165         // but our destination node is inside an inline that is the last in the block.
1166         // We insert a placeholder before the newly inserted content to avoid being merged into the inline.
1167         Node* destinationNode = destination.deepEquivalent().deprecatedNode();
1168         if (m_shouldMergeEnd && destinationNode != enclosingInline(destinationNode) && enclosingInline(destinationNode)->nextSibling())
1169             insertNodeBefore(createBreakElement(document()), refNode.get());
1170         
1171         // Merging the the first paragraph of inserted content with the content that came
1172         // before the selection that was pasted into would also move content after 
1173         // the selection that was pasted into if: only one paragraph was being pasted, 
1174         // and it was not wrapped in a block, the selection that was pasted into ended 
1175         // at the end of a block and the next paragraph didn't start at the start of a block.
1176         // Insert a line break just after the inserted content to separate it from what 
1177         // comes after and prevent that from happening.
1178         VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent();
1179         if (startOfParagraph(endOfInsertedContent) == startOfParagraphToMove) {
1180             insertNodeAt(createBreakElement(document()), endOfInsertedContent.deepEquivalent());
1181             // Mutation events (bug 22634) triggered by inserting the <br> might have removed the content we're about to move
1182             if (!startOfParagraphToMove.deepEquivalent().anchorNode()->inDocument())
1183                 return;
1184         }
1185
1186         // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes.  The nodes are
1187         // only ever used to create positions where inserted content starts/ends.
1188         moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
1189         m_startOfInsertedContent = endingSelection().visibleStart().deepEquivalent().downstream();
1190         if (m_endOfInsertedContent.isOrphan())
1191             m_endOfInsertedContent = endingSelection().visibleEnd().deepEquivalent().upstream();
1192     }
1193
1194     Position lastPositionToSelect;
1195     if (fragment.hasInterchangeNewlineAtEnd()) {
1196         VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent();
1197         VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary);
1198
1199         if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedContent) || next.isNull()) {
1200             if (!isStartOfParagraph(endOfInsertedContent)) {
1201                 setEndingSelection(endOfInsertedContent);
1202                 Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEquivalent().deprecatedNode());
1203                 if (isListItem(enclosingNode)) {
1204                     RefPtr<Node> newListItem = createListItemElement(document());
1205                     insertNodeAfter(newListItem, enclosingNode);
1206                     setEndingSelection(VisiblePosition(firstPositionInNode(newListItem.get())));
1207                 } else {
1208                     // Use a default paragraph element (a plain div) for the empty paragraph, using the last paragraph
1209                     // block's style seems to annoy users.
1210                     insertParagraphSeparator(true, !startIsInsideMailBlockquote && highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(),
1211                         isMailBlockquote, CannotCrossEditingBoundary, insertedNodes.firstNodeInserted()->parentNode()));
1212                 }
1213
1214                 // Select up to the paragraph separator that was added.
1215                 lastPositionToSelect = endingSelection().visibleStart().deepEquivalent();
1216                 updateNodesInserted(lastPositionToSelect.deprecatedNode());
1217             }
1218         } else {
1219             // Select up to the beginning of the next paragraph.
1220             lastPositionToSelect = next.deepEquivalent().downstream();
1221         }
1222         
1223     } else
1224         mergeEndIfNeeded();
1225
1226     if (Node* mailBlockquote = enclosingNodeOfType(positionAtStartOfInsertedContent().deepEquivalent(), isMailPasteAsQuotationNode))
1227         removeNodeAttribute(toElement(mailBlockquote), classAttr);
1228
1229     if (shouldPerformSmartReplace())
1230         addSpacesForSmartReplace();
1231
1232     // If we are dealing with a fragment created from plain text
1233     // no style matching is necessary.
1234     if (plainTextFragment)
1235         m_matchStyle = false;
1236         
1237     completeHTMLReplacement(lastPositionToSelect);
1238 }
1239
1240 bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR, const VisiblePosition& originalVisPosBeforeEndBR)
1241 {
1242     if (!endBR || !endBR->inDocument())
1243         return false;
1244         
1245     VisiblePosition visiblePos(positionBeforeNode(endBR));
1246     
1247     // Don't remove the br if nothing was inserted.
1248     if (visiblePos.previous() == originalVisPosBeforeEndBR)
1249         return false;
1250     
1251     // Remove the br if it is collapsed away and so is unnecessary.
1252     if (!document().inNoQuirksMode() && isEndOfBlock(visiblePos) && !isStartOfParagraph(visiblePos))
1253         return true;
1254         
1255     // A br that was originally holding a line open should be displaced by inserted content or turned into a line break.
1256     // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder.
1257     return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos);
1258 }
1259
1260 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const
1261 {
1262     if (!m_smartReplace)
1263         return false;
1264
1265     Element* textControl = enclosingTextFormControl(positionAtStartOfInsertedContent().deepEquivalent());
1266     if (textControl && isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPasswordField())
1267         return false; // Disable smart replace for password fields.
1268
1269     return true;
1270 }
1271
1272 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 character, bool previousCharacter)
1273 {
1274     return isCharacterSmartReplaceExempt(character == noBreakSpace ? ' ' : character, previousCharacter);
1275 }
1276
1277 void ReplaceSelectionCommand::addSpacesForSmartReplace()
1278 {
1279     VisiblePosition startOfInsertedContent = positionAtStartOfInsertedContent();
1280     VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent();
1281
1282     Position endUpstream = endOfInsertedContent.deepEquivalent().upstream();
1283     Node* endNode = endUpstream.computeNodeBeforePosition();
1284     int endOffset = endNode && endNode->isTextNode() ? toText(endNode)->length() : 0;
1285     if (endUpstream.anchorType() == Position::PositionIsOffsetInAnchor) {
1286         endNode = endUpstream.containerNode();
1287         endOffset = endUpstream.offsetInContainerNode();
1288     }
1289
1290     bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isCharacterSmartReplaceExemptConsideringNonBreakingSpace(endOfInsertedContent.characterAfter(), false);
1291     if (needsTrailingSpace && endNode) {
1292         bool collapseWhiteSpace = !endNode->renderer() || endNode->renderer()->style().collapseWhiteSpace();
1293         if (endNode->isTextNode()) {
1294             insertTextIntoNode(toText(endNode), endOffset, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
1295             if (m_endOfInsertedContent.containerNode() == endNode)
1296                 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offsetInContainerNode() + 1);
1297         } else {
1298             RefPtr<Node> node = document().createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " ");
1299             insertNodeAfter(node, endNode);
1300             updateNodesInserted(node.get());
1301         }
1302     }
1303
1304     document().updateLayout();
1305
1306     Position startDownstream = startOfInsertedContent.deepEquivalent().downstream();
1307     Node* startNode = startDownstream.computeNodeAfterPosition();
1308     unsigned startOffset = 0;
1309     if (startDownstream.anchorType() == Position::PositionIsOffsetInAnchor) {
1310         startNode = startDownstream.containerNode();
1311         startOffset = startDownstream.offsetInContainerNode();
1312     }
1313
1314     bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isCharacterSmartReplaceExemptConsideringNonBreakingSpace(startOfInsertedContent.previous().characterAfter(), true);
1315     if (needsLeadingSpace && startNode) {
1316         bool collapseWhiteSpace = !startNode->renderer() || startNode->renderer()->style().collapseWhiteSpace();
1317         if (startNode->isTextNode()) {
1318             insertTextIntoNode(toText(startNode), startOffset, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
1319             if (m_endOfInsertedContent.containerNode() == startNode && m_endOfInsertedContent.offsetInContainerNode())
1320                 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offsetInContainerNode() + 1);
1321         } else {
1322             RefPtr<Node> node = document().createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " ");
1323             // Don't updateNodesInserted. Doing so would set m_endOfInsertedContent to be the node containing the leading space,
1324             // but m_endOfInsertedContent is supposed to mark the end of pasted content.
1325             insertNodeBefore(node, startNode);
1326             m_startOfInsertedContent = firstPositionInNode(node.get());
1327         }
1328     }
1329 }
1330
1331 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositionToSelect)
1332 {
1333     Position start = positionAtStartOfInsertedContent().deepEquivalent();
1334     Position end = positionAtEndOfInsertedContent().deepEquivalent();
1335
1336     // Mutation events may have deleted start or end
1337     if (start.isNotNull() && !start.isOrphan() && end.isNotNull() && !end.isOrphan()) {
1338         // FIXME (11475): Remove this and require that the creator of the fragment to use nbsps.
1339         rebalanceWhitespaceAt(start);
1340         rebalanceWhitespaceAt(end);
1341
1342         if (m_matchStyle) {
1343             ASSERT(m_insertionStyle);
1344             applyStyle(m_insertionStyle.get(), start, end);
1345         }
1346
1347         if (lastPositionToSelect.isNotNull())
1348             end = lastPositionToSelect;
1349
1350         mergeTextNodesAroundPosition(start, end);
1351         mergeTextNodesAroundPosition(end, start);
1352     } else if (lastPositionToSelect.isNotNull())
1353         start = end = lastPositionToSelect;
1354     else
1355         return;
1356
1357     if (m_selectReplacement)
1358         setEndingSelection(VisibleSelection(start, end, SEL_DEFAULT_AFFINITY, endingSelection().isDirectional()));
1359     else
1360         setEndingSelection(VisibleSelection(end, SEL_DEFAULT_AFFINITY, endingSelection().isDirectional()));
1361 }
1362
1363 void ReplaceSelectionCommand::mergeTextNodesAroundPosition(Position& position, Position& positionOnlyToBeUpdated)
1364 {
1365     bool positionIsOffsetInAnchor = position.anchorType() == Position::PositionIsOffsetInAnchor;
1366     bool positionOnlyToBeUpdatedIsOffsetInAnchor = positionOnlyToBeUpdated.anchorType() == Position::PositionIsOffsetInAnchor;
1367     RefPtr<Text> text = 0;
1368     if (positionIsOffsetInAnchor && position.containerNode() && position.containerNode()->isTextNode())
1369         text = toText(position.containerNode());
1370     else {
1371         Node* before = position.computeNodeBeforePosition();
1372         if (before && before->isTextNode())
1373             text = toText(before);
1374         else {
1375             Node* after = position.computeNodeAfterPosition();
1376             if (after && after->isTextNode())
1377                 text = toText(after);
1378         }
1379     }
1380     if (!text)
1381         return;
1382
1383     if (text->previousSibling() && text->previousSibling()->isTextNode()) {
1384         RefPtr<Text> previous = toText(text->previousSibling());
1385         insertTextIntoNode(text, 0, previous->data());
1386
1387         if (positionIsOffsetInAnchor)
1388             position.moveToOffset(previous->length() + position.offsetInContainerNode());
1389         else
1390             updatePositionForNodeRemoval(position, previous.get());
1391
1392         if (positionOnlyToBeUpdatedIsOffsetInAnchor) {
1393             if (positionOnlyToBeUpdated.containerNode() == text)
1394                 positionOnlyToBeUpdated.moveToOffset(previous->length() + positionOnlyToBeUpdated.offsetInContainerNode());
1395             else if (positionOnlyToBeUpdated.containerNode() == previous)
1396                 positionOnlyToBeUpdated.moveToPosition(text, positionOnlyToBeUpdated.offsetInContainerNode());
1397         } else
1398             updatePositionForNodeRemoval(positionOnlyToBeUpdated, previous.get());
1399
1400         removeNode(previous);
1401     }
1402     if (text->nextSibling() && text->nextSibling()->isTextNode()) {
1403         RefPtr<Text> next = toText(text->nextSibling());
1404         unsigned originalLength = text->length();
1405         insertTextIntoNode(text, originalLength, next->data());
1406
1407         if (!positionIsOffsetInAnchor)
1408             updatePositionForNodeRemoval(position, next.get());
1409
1410         if (positionOnlyToBeUpdatedIsOffsetInAnchor && positionOnlyToBeUpdated.containerNode() == next)
1411             positionOnlyToBeUpdated.moveToPosition(text, originalLength + positionOnlyToBeUpdated.offsetInContainerNode());
1412         else
1413             updatePositionForNodeRemoval(positionOnlyToBeUpdated, next.get());
1414
1415         removeNode(next);
1416     }
1417 }
1418
1419 EditAction ReplaceSelectionCommand::editingAction() const
1420 {
1421     return m_editAction;
1422 }
1423
1424 // If the user is inserting a list into an existing list, instead of nesting the list,
1425 // we put the list items into the existing list.
1426 Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtr<HTMLElement> prpListElement, Node* insertionBlock, const Position& insertPos, InsertedNodes& insertedNodes)
1427 {
1428     RefPtr<HTMLElement> listElement = prpListElement;
1429
1430     while (listElement->hasChildNodes() && isListElement(listElement->firstChild()) && listElement->childNodeCount() == 1)
1431         listElement = toHTMLElement(listElement->firstChild());
1432
1433     bool isStart = isStartOfParagraph(insertPos);
1434     bool isEnd = isEndOfParagraph(insertPos);
1435     bool isMiddle = !isStart && !isEnd;
1436     Node* lastNode = insertionBlock;
1437
1438     // If we're in the middle of a list item, we should split it into two separate
1439     // list items and insert these nodes between them.
1440     if (isMiddle) {
1441         int textNodeOffset = insertPos.offsetInContainerNode();
1442         if (insertPos.deprecatedNode()->isTextNode() && textNodeOffset > 0)
1443             splitTextNode(toText(insertPos.deprecatedNode()), textNodeOffset);
1444         splitTreeToNode(insertPos.deprecatedNode(), lastNode, true);
1445     }
1446
1447     while (RefPtr<Node> listItem = listElement->firstChild()) {
1448         listElement->removeChild(listItem.get(), ASSERT_NO_EXCEPTION);
1449         if (isStart || isMiddle) {
1450             insertNodeBefore(listItem, lastNode);
1451             insertedNodes.respondToNodeInsertion(listItem.get());
1452         } else if (isEnd) {
1453             insertNodeAfter(listItem, lastNode);
1454             insertedNodes.respondToNodeInsertion(listItem.get());
1455             lastNode = listItem.get();
1456         } else
1457             ASSERT_NOT_REACHED();
1458     }
1459     if (isStart || isMiddle)
1460         lastNode = lastNode->previousSibling();
1461     return lastNode;
1462 }
1463
1464 void ReplaceSelectionCommand::updateNodesInserted(Node *node)
1465 {
1466     if (!node)
1467         return;
1468
1469     if (m_startOfInsertedContent.isNull())
1470         m_startOfInsertedContent = firstPositionInOrBeforeNode(node);
1471
1472     m_endOfInsertedContent = lastPositionInOrAfterNode(node->lastDescendant());
1473 }
1474
1475 // During simple pastes, where we're just pasting a text node into a run of text, we insert the text node
1476 // directly into the text node that holds the selection.  This is much faster than the generalized code in
1477 // ReplaceSelectionCommand, and works around <https://bugs.webkit.org/show_bug.cgi?id=6148> since we don't 
1478 // split text nodes.
1479 bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& fragment)
1480 {
1481     if (!fragment.firstChild() || fragment.firstChild() != fragment.lastChild() || !fragment.firstChild()->isTextNode())
1482         return false;
1483
1484     // FIXME: Would be nice to handle smart replace in the fast path.
1485     if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || fragment.hasInterchangeNewlineAtEnd())
1486         return false;
1487
1488     // e.g. when "bar" is inserted after "foo" in <div><u>foo</u></div>, "bar" should not be underlined.
1489     if (nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(endingSelection().start()))
1490         return false;
1491
1492     RefPtr<Node> nodeAfterInsertionPos = endingSelection().end().downstream().anchorNode();
1493     Text* textNode = toText(fragment.firstChild());
1494     // Our fragment creation code handles tabs, spaces, and newlines, so we don't have to worry about those here.
1495
1496     Position start = endingSelection().start();
1497     Position end = replaceSelectedTextInNode(textNode->data());
1498     if (end.isNull())
1499         return false;
1500
1501     if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && nodeAfterInsertionPos->hasTagName(brTag)
1502         && shouldRemoveEndBR(nodeAfterInsertionPos.get(), positionBeforeNode(nodeAfterInsertionPos.get())))
1503         removeNodeAndPruneAncestors(nodeAfterInsertionPos.get());
1504
1505     VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, end);
1506
1507     setEndingSelection(selectionAfterReplace);
1508
1509     return true;
1510 }
1511
1512 } // namespace WebCore