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