WebCore:
[WebKit-https.git] / WebCore / editing / ReplaceSelectionCommand.cpp
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "ReplaceSelectionCommand.h"
28
29 #include "ApplyStyleCommand.h"
30 #include "BeforeTextInsertedEvent.h"
31 #include "CSSComputedStyleDeclaration.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSValueKeywords.h"
34 #include "Document.h"
35 #include "DocumentFragment.h"
36 #include "EditingText.h"
37 #include "EventNames.h"
38 #include "Element.h"
39 #include "Frame.h"
40 #include "HTMLElement.h"
41 #include "HTMLInterchange.h"
42 #include "HTMLInputElement.h"
43 #include "HTMLNames.h"
44 #include "SelectionController.h"
45 #include "SmartReplace.h"
46 #include "TextIterator.h"
47 #include "htmlediting.h"
48 #include "markup.h"
49 #include "visible_units.h"
50
51 namespace WebCore {
52
53 using namespace EventNames;
54 using namespace HTMLNames;
55
56 static bool isInterchangeNewlineNode(const Node *node)
57 {
58     static String interchangeNewlineClassString(AppleInterchangeNewline);
59     return node && node->hasTagName(brTag) && 
60            static_cast<const Element *>(node)->getAttribute(classAttr) == interchangeNewlineClassString;
61 }
62
63 static bool isInterchangeConvertedSpaceSpan(const Node *node)
64 {
65     static String convertedSpaceSpanClassString(AppleConvertedSpace);
66     return node->isHTMLElement() && 
67            static_cast<const HTMLElement *>(node)->getAttribute(classAttr) == convertedSpaceSpanClassString;
68 }
69
70 ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* fragment, bool matchStyle, const Selection& selection)
71     : m_document(document),
72       m_fragment(fragment),
73       m_matchStyle(matchStyle), 
74       m_hasInterchangeNewlineAtStart(false), 
75       m_hasInterchangeNewlineAtEnd(false)
76 {
77     if (!m_document)
78         return;
79     if (!m_fragment)
80         return;
81     if (!m_fragment->firstChild())
82         return;
83     
84     Element* editableRoot = selection.rootEditableElement();
85     ASSERT(editableRoot);
86     if (!editableRoot)
87         return;
88     
89     Node* shadowAncestorNode = editableRoot->shadowAncestorNode();
90     
91     if (!editableRoot->getHTMLEventListener(webkitBeforeTextInsertedEvent) &&
92         // FIXME: Remove these checks once textareas and textfields actually register an event handler.
93         !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextField()) &&
94         !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextArea()) &&
95         editableRoot->isContentRichlyEditable()) {
96         removeInterchangeNodes(m_fragment->firstChild());
97         return;
98     }
99
100     Node* styleNode = selection.base().node();
101     RefPtr<Node> holder = insertFragmentForTestRendering(styleNode);
102     
103     RefPtr<Range> range = Selection::selectionFromContentsOfNode(holder.get()).toRange();
104     String text = plainText(range.get());
105     // Give the root a chance to change the text.
106     RefPtr<BeforeTextInsertedEvent> evt = new BeforeTextInsertedEvent(text);
107     ExceptionCode ec = 0;
108     editableRoot->dispatchEvent(evt, ec, true);
109     ASSERT(ec == 0);
110     if (text != evt->text() || !editableRoot->isContentRichlyEditable()) {
111         restoreTestRenderingNodesToFragment(holder.get());
112         removeNode(holder);
113
114         m_fragment = createFragmentFromText(selection.toRange().get(), evt->text());
115         if (!m_fragment->firstChild())
116             return;
117         holder = insertFragmentForTestRendering(styleNode);
118     }
119     
120     removeInterchangeNodes(holder->firstChild());
121     
122     removeUnrenderedNodes(holder.get());
123     restoreTestRenderingNodesToFragment(holder.get());
124     removeNode(holder);
125 }
126
127 bool ReplacementFragment::isEmpty() const
128 {
129     return (!m_fragment || !m_fragment->firstChild()) && !m_hasInterchangeNewlineAtStart && !m_hasInterchangeNewlineAtEnd;
130 }
131
132 Node *ReplacementFragment::firstChild() const 
133
134     return m_fragment ? m_fragment->firstChild() : 0; 
135 }
136
137 Node *ReplacementFragment::lastChild() const 
138
139     return m_fragment ? m_fragment->lastChild() : 0; 
140 }
141
142 void ReplacementFragment::removeNodePreservingChildren(Node *node)
143 {
144     if (!node)
145         return;
146
147     while (RefPtr<Node> n = node->firstChild()) {
148         removeNode(n);
149         insertNodeBefore(n.get(), node);
150     }
151     removeNode(node);
152 }
153
154 void ReplacementFragment::removeNode(PassRefPtr<Node> node)
155 {
156     if (!node)
157         return;
158     
159     Node *parent = node->parentNode();
160     if (!parent)
161         return;
162     
163     ExceptionCode ec = 0;
164     parent->removeChild(node.get(), ec);
165     ASSERT(ec == 0);
166 }
167
168 void ReplacementFragment::insertNodeBefore(Node *node, Node *refNode)
169 {
170     if (!node || !refNode)
171         return;
172         
173     Node *parent = refNode->parentNode();
174     if (!parent)
175         return;
176         
177     ExceptionCode ec = 0;
178     parent->insertBefore(node, refNode, ec);
179     ASSERT(ec == 0);
180 }
181
182 PassRefPtr<Node> ReplacementFragment::insertFragmentForTestRendering(Node* context)
183 {
184     Node* body = m_document->body();
185     if (!body)
186         return 0;
187
188     RefPtr<StyledElement> holder = static_pointer_cast<StyledElement>(createDefaultParagraphElement(m_document.get()));
189     
190     ExceptionCode ec = 0;
191
192     // Copy the whitespace and user-select style from the context onto this element.
193     // FIXME: We should examine other style properties to see if they would be appropriate to consider during the test rendering.
194     Node* n = context;
195     while (n && !n->isElementNode())
196         n = n->parentNode();
197     if (n) {
198         RefPtr<CSSComputedStyleDeclaration> contextStyle = new CSSComputedStyleDeclaration(static_cast<Element*>(n));
199         CSSStyleDeclaration* style = holder->style();
200         style->setProperty(CSS_PROP_WHITE_SPACE, contextStyle->getPropertyValue(CSS_PROP_WHITE_SPACE), false, ec);
201         ASSERT(ec == 0);
202         style->setProperty(CSS_PROP__WEBKIT_USER_SELECT, contextStyle->getPropertyValue(CSS_PROP__WEBKIT_USER_SELECT), false, ec);
203         ASSERT(ec == 0);
204     }
205     
206     holder->appendChild(m_fragment, ec);
207     ASSERT(ec == 0);
208     
209     body->appendChild(holder.get(), ec);
210     ASSERT(ec == 0);
211     
212     m_document->updateLayoutIgnorePendingStylesheets();
213     
214     return holder.release();
215 }
216
217 void ReplacementFragment::restoreTestRenderingNodesToFragment(Node *holder)
218 {
219     if (!holder)
220         return;
221     
222     ExceptionCode ec = 0;
223     while (RefPtr<Node> node = holder->firstChild()) {
224         holder->removeChild(node.get(), ec);
225         ASSERT(ec == 0);
226         m_fragment->appendChild(node.get(), ec);
227         ASSERT(ec == 0);
228     }
229 }
230
231 void ReplacementFragment::removeUnrenderedNodes(Node* holder)
232 {
233     Vector<Node*> unrendered;
234
235     for (Node* node = holder->firstChild(); node; node = node->traverseNextNode(holder))
236         if (!isNodeRendered(node) && !isTableStructureNode(node))
237             unrendered.append(node);
238
239     size_t n = unrendered.size();
240     for (size_t i = 0; i < n; ++i)
241         removeNode(unrendered[i]);
242 }
243
244 void ReplacementFragment::removeInterchangeNodes(Node* startNode)
245 {
246     Node* node = startNode;
247     Node* newlineAtStartNode = 0;
248     Node* newlineAtEndNode = 0;
249     while (node) {
250         Node *next = node->traverseNextNode();
251         if (isInterchangeNewlineNode(node)) {
252             if (next || node == startNode) {
253                 m_hasInterchangeNewlineAtStart = true;
254                 newlineAtStartNode = node;
255             }
256             else {
257                 m_hasInterchangeNewlineAtEnd = true;
258                 newlineAtEndNode = node;
259             }
260         }
261         else if (isInterchangeConvertedSpaceSpan(node)) {
262             RefPtr<Node> n = 0;
263             while ((n = node->firstChild())) {
264                 removeNode(n);
265                 insertNodeBefore(n.get(), node);
266             }
267             removeNode(node);
268             if (n)
269                 next = n->traverseNextNode();
270         }
271         node = next;
272     }
273
274     if (newlineAtStartNode)
275         removeNode(newlineAtStartNode);
276     if (newlineAtEndNode)
277         removeNode(newlineAtEndNode);
278 }
279
280 ReplaceSelectionCommand::ReplaceSelectionCommand(Document* document, PassRefPtr<DocumentFragment> fragment,
281         bool selectReplacement, bool smartReplace, bool matchStyle, bool preventNesting, bool movingParagraph,
282         EditAction editAction) 
283     : CompositeEditCommand(document),
284       m_selectReplacement(selectReplacement), 
285       m_smartReplace(smartReplace),
286       m_matchStyle(matchStyle),
287       m_documentFragment(fragment),
288       m_preventNesting(preventNesting),
289       m_movingParagraph(movingParagraph),
290       m_editAction(editAction)
291 {
292 }
293
294 bool ReplaceSelectionCommand::shouldMergeStart(bool selectionStartWasStartOfParagraph, bool fragmentHasInterchangeNewlineAtStart)
295 {
296     VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent());
297     VisiblePosition prev = startOfInsertedContent.previous(true);
298     if (prev.isNull())
299         return false;
300         
301     return !selectionStartWasStartOfParagraph && 
302            !fragmentHasInterchangeNewlineAtStart &&
303            isStartOfParagraph(startOfInsertedContent) && 
304            !startOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) &&
305            shouldMerge(startOfInsertedContent, prev);
306 }
307
308 bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph)
309 {
310     VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent());
311     VisiblePosition next = endOfInsertedContent.next(true);
312     if (next.isNull())
313         return false;
314
315     return !selectionEndWasEndOfParagraph &&
316            isEndOfParagraph(endOfInsertedContent) && 
317            !endOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) &&
318            shouldMerge(endOfInsertedContent, next);
319 }
320
321 static bool isMailPasteAsQuotationNode(Node* node)
322 {
323     return node && node->hasTagName(blockquoteTag) && node->isElementNode() && static_cast<Element*>(node)->getAttribute(classAttr) == ApplePasteAsQuotation;
324 }
325
326 // Wrap CompositeEditCommand::removeNodePreservingChildren() so we can update the nodes we track
327 void ReplaceSelectionCommand::removeNodePreservingChildren(Node* node)
328 {
329     if (m_firstNodeInserted == node)
330         m_firstNodeInserted = node->traverseNextNode();
331     if (m_lastLeafInserted == node)
332         m_lastLeafInserted = node->lastChild() ? node->lastChild() : node->traverseNextSibling();
333     CompositeEditCommand::removeNodePreservingChildren(node);
334 }
335
336 // Wrap CompositeEditCommand::removeNodeAndPruneAncestors() so we can update the nodes we track
337 void ReplaceSelectionCommand::removeNodeAndPruneAncestors(Node* node)
338 {
339     // prepare in case m_firstNodeInserted and/or m_lastLeafInserted get removed
340     // FIXME: shouldn't m_lastLeafInserted be adjusted using traversePreviousNode()?
341     Node* afterFirst = m_firstNodeInserted ? m_firstNodeInserted->traverseNextSibling() : 0;
342     Node* afterLast = m_lastLeafInserted ? m_lastLeafInserted->traverseNextSibling() : 0;
343     
344     CompositeEditCommand::removeNodeAndPruneAncestors(node);
345     
346     // adjust m_firstNodeInserted and m_lastLeafInserted since either or both may have been removed
347     if (m_lastLeafInserted && !m_lastLeafInserted->inDocument())
348         m_lastLeafInserted = afterLast;
349     if (m_firstNodeInserted && !m_firstNodeInserted->inDocument())
350         m_firstNodeInserted = m_lastLeafInserted && m_lastLeafInserted->inDocument() ? afterFirst : 0;
351 }
352
353 bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& from, const VisiblePosition& to)
354 {
355     if (from.isNull() || to.isNull())
356         return false;
357         
358     Node* fromNode = from.deepEquivalent().node();
359     Node* toNode = to.deepEquivalent().node();
360     Node* fromNodeBlock = enclosingBlock(fromNode);
361     return !enclosingNodeOfType(fromNode, &isMailPasteAsQuotationNode) &&
362            fromNodeBlock && (!fromNodeBlock->hasTagName(blockquoteTag) || isMailBlockquote(fromNodeBlock))  &&
363            enclosingListChild(fromNode) == enclosingListChild(toNode) &&
364            enclosingTableCell(from.deepEquivalent()) == enclosingTableCell(from.deepEquivalent()) &&
365            // Don't merge to or from a position before or after a block because it would
366            // be a no-op and cause infinite recursion.
367            !isBlock(fromNode) && !isBlock(toNode);
368 }
369
370 // Style rules that match just inserted elements could change their appearance, like
371 // a div inserted into a document with div { display:inline; }.
372 void ReplaceSelectionCommand::negateStyleRulesThatAffectAppearance()
373 {
374     for (RefPtr<Node> node = m_firstNodeInserted.get(); node; node = node->traverseNextNode()) {
375         // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
376         if (isStyleSpan(node.get())) {
377             HTMLElement* e = static_cast<HTMLElement*>(node.get());
378             // There are other styles that style rules can give to style spans,
379             // but these are the two important ones because they'll prevent
380             // inserted content from appearing in the right paragraph.
381             // FIXME: Hyatt is concerned that selectively using display:inline will give inconsistent
382             // results. We already know one issue because td elements ignore their display property
383             // in quirks mode (which Mail.app is always in). We should look for an alternative.
384             if (isBlock(e))
385                 e->getInlineStyleDecl()->setProperty(CSS_PROP_DISPLAY, CSS_VAL_INLINE);
386             if (e->renderer() && e->renderer()->style()->floating() != FNONE)
387                 e->getInlineStyleDecl()->setProperty(CSS_PROP_FLOAT, CSS_VAL_NONE);
388         }
389         if (node == m_lastLeafInserted)
390             break;
391     }
392 }
393
394 void ReplaceSelectionCommand::removeRedundantStyles(Node* mailBlockquoteEnclosingSelectionStart)
395 {
396     // There's usually a top level style span that holds the document's default style, push it down.
397     Node* node = m_firstNodeInserted.get();
398     if (isStyleSpan(node) && mailBlockquoteEnclosingSelectionStart) {
399         // Calculate the document default style.
400         RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = Position(mailBlockquoteEnclosingSelectionStart, 0).computedStyle()->copyInheritableProperties();
401         RefPtr<CSSMutableStyleDeclaration> spanStyle = static_cast<HTMLElement*>(node)->inlineStyleDecl();
402         spanStyle->merge(blockquoteStyle.get());  
403     }
404     
405     // Compute and save the non-redundant styles for all HTML elements.
406     // Don't do any mutation here, because that would cause the diffs to trigger layouts.
407     Vector<RefPtr<CSSMutableStyleDeclaration> > styles;
408     Vector<RefPtr<HTMLElement> > elements;
409     for (node = m_firstNodeInserted.get(); node; node = node->traverseNextNode()) {
410         if (node->isHTMLElement() && isStyleSpan(node)) {
411             elements.append(static_cast<HTMLElement*>(node));
412             
413             RefPtr<CSSMutableStyleDeclaration> parentStyle = computedStyle(node->parentNode())->copyInheritableProperties();
414             RefPtr<CSSMutableStyleDeclaration> style = computedStyle(node)->copyInheritableProperties();
415             parentStyle->diff(style.get());
416             
417             styles.append(style.release());
418         }
419         if (node == m_lastLeafInserted)
420             break;
421     }
422     
423     size_t count = styles.size();
424     for (size_t i = 0; i < count; ++i) {
425         HTMLElement* element = elements[i].get();
426
427         // Handle case where the element was already removed by earlier processing.
428         // It's possible this no longer occurs, but it did happen in an earlier version
429         // that processed elements in a less-determistic order, and I can't prove it
430         // does not occur.
431         if (!element->inDocument())
432             continue;
433
434         // Remove empty style spans.
435         if (isStyleSpan(element) && !element->hasChildNodes()) {
436             removeNodeAndPruneAncestors(element);
437             continue;
438         }
439
440         // Remove redundant style tags and style spans.
441         CSSMutableStyleDeclaration* style = styles[i].get();
442         if (style->length() == 0
443                 && (isStyleSpan(element)
444                     || element->hasTagName(bTag)
445                     || element->hasTagName(fontTag)
446                     || element->hasTagName(iTag)
447                     || element->hasTagName(uTag))) {
448             removeNodePreservingChildren(element);
449             continue;
450         }
451
452         // Clear redundant styles from elements.
453         CSSMutableStyleDeclaration* inlineStyleDecl = element->inlineStyleDecl();
454         if (inlineStyleDecl) {
455             CSSComputedStyleDeclaration::removeComputedInheritablePropertiesFrom(inlineStyleDecl);
456             inlineStyleDecl->merge(style, true);
457             setNodeAttribute(element, styleAttr, inlineStyleDecl->cssText());
458         }
459     }
460 }
461
462 void ReplaceSelectionCommand::handlePasteAsQuotationNode()
463 {
464     Node* node = m_firstNodeInserted.get();
465     if (isMailPasteAsQuotationNode(node))
466         static_cast<Element*>(node)->setAttribute(classAttr, "");
467 }
468
469 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent()
470 {
471     Node* lastNode = m_lastLeafInserted.get();
472     Node* enclosingSelect = enclosingNodeWithTag(lastNode, selectTag);
473     if (enclosingSelect)
474         lastNode = enclosingSelect;
475     return VisiblePosition(Position(lastNode, maxDeepOffset(lastNode)));
476 }
477
478 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent()
479 {
480     // Return the inserted content's first VisiblePosition.
481     return VisiblePosition(nextCandidate(positionBeforeNode(m_firstNodeInserted.get())));
482 }
483
484 void ReplaceSelectionCommand::doApply()
485 {
486     Selection selection = endingSelection();
487     ASSERT(selection.isCaretOrRange());
488     ASSERT(selection.start().node());
489     if (selection.isNone() || !selection.start().node())
490         return;
491     
492     bool selectionIsPlainText = !selection.isContentRichlyEditable();
493     if (selectionIsPlainText)
494         m_matchStyle = true;
495     
496     Element* currentRoot = selection.rootEditableElement();
497     ReplacementFragment fragment(document(), m_documentFragment.get(), m_matchStyle, selection);
498     
499     if (m_matchStyle)
500         m_insertionStyle = styleAtPosition(selection.start());
501     
502     VisiblePosition visibleStart = selection.visibleStart();
503     VisiblePosition visibleEnd = selection.visibleEnd();
504     
505     bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd);
506     bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart);
507     Node* mailBlockquoteEnclosingSelectionStart = nearestMailBlockquote(visibleStart.deepEquivalent().node());
508     
509     Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().node());
510     
511     if (selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph ||
512         startBlock == currentRoot ||
513         startBlock && startBlock->renderer() && startBlock->renderer()->isListItem() ||
514         selectionIsPlainText)
515         m_preventNesting = false;
516     
517     Position insertionPos = selection.start();
518     
519     if (selection.isRange()) {
520         // When the end of the selection being pasted into is at the end of a paragraph, and that selection
521         // spans multiple blocks, not merging may leave an empty line.
522         // When the start of the selection being pasted into is at the start of a block, not merging 
523         // will leave hanging block(s).
524         bool mergeBlocksAfterDelete = isEndOfParagraph(visibleEnd) || isStartOfBlock(visibleStart);
525         // FIXME: We should only expand to include fully selected special elements if we are copying a 
526         // selection and pasting it on top of itself.
527         deleteSelection(false, mergeBlocksAfterDelete, true, false);
528         visibleStart = endingSelection().visibleStart();
529         if (fragment.hasInterchangeNewlineAtStart()) {
530             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
531                 if (!isEndOfDocument(visibleStart))
532                     setEndingSelection(visibleStart.next());
533             } else
534                 insertParagraphSeparator();
535         }
536         insertionPos = endingSelection().start();
537     } else {
538         ASSERT(selection.isCaret());
539         if (fragment.hasInterchangeNewlineAtStart()) {
540             VisiblePosition next = visibleStart.next(true);
541             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart) && next.isNotNull())
542                 setEndingSelection(next);
543             else 
544                 insertParagraphSeparator();
545         }
546         // We split the current paragraph in two to avoid nesting the blocks from the fragment inside the current block.
547         // For example paste <div>foo</div><div>bar</div><div>baz</div> into <div>x^x</div>, where ^ is the caret.  
548         // As long as the  div styles are the same, visually you'd expect: <div>xbar</div><div>bar</div><div>bazx</div>, 
549         // not <div>xbar<div>bar</div><div>bazx</div></div>
550         if (m_preventNesting && !isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
551             insertParagraphSeparator();
552             setEndingSelection(endingSelection().visibleStart().previous());
553         }
554         insertionPos = endingSelection().start();
555     }
556     
557     // Inserting content could cause whitespace to collapse, e.g. inserting <div>foo</div> into hello^ world.
558     prepareWhitespaceAtPositionForSplit(insertionPos);
559     
560     // NOTE: This would be an incorrect usage of downstream() if downstream() were changed to mean the last position after 
561     // 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 
562     // away, there are positions after the br which map to the same visible position as [br, 0]).  
563     Node* endBR = insertionPos.downstream().node()->hasTagName(brTag) ? insertionPos.downstream().node() : 0;
564     VisiblePosition originalVisPosBeforeEndBR;
565     if (endBR)
566         originalVisPosBeforeEndBR = VisiblePosition(endBR, 0, DOWNSTREAM).previous();
567     
568     startBlock = enclosingBlock(insertionPos.node());
569     
570     // Adjust insertionPos to prevent nesting.
571     if (m_preventNesting && startBlock) {
572         ASSERT(startBlock != currentRoot);
573         VisiblePosition visibleInsertionPos(insertionPos);
574         if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInsertionPos) && fragment.hasInterchangeNewlineAtEnd()))
575             insertionPos = positionAfterNode(startBlock);
576         else if (isStartOfBlock(visibleInsertionPos))
577             insertionPos = positionBeforeNode(startBlock);
578     }
579
580     // Paste into run of tabs splits the tab span.
581     insertionPos = positionOutsideTabSpan(insertionPos);
582     
583     // Paste at start or end of link goes outside of link.
584     insertionPos = positionAvoidingSpecialElementBoundary(insertionPos);
585
586     Frame *frame = document()->frame();
587     
588     // FIXME: Improve typing style.
589     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
590     frame->clearTypingStyle();
591     setTypingStyle(0);    
592     
593     // Remove the top level style span if its unnecessary before inserting it into the document, its faster.
594     RefPtr<CSSMutableStyleDeclaration> styleAtInsertionPos = insertionPos.computedStyle()->copyInheritableProperties();
595     if (isStyleSpan(fragment.firstChild())) {
596         Node* styleSpan = fragment.firstChild();
597         String styleText = static_cast<Element*>(styleSpan)->getAttribute(styleAttr);
598         if (styleText == styleAtInsertionPos->cssText())
599             fragment.removeNodePreservingChildren(styleSpan);
600     }
601     
602     // We're finished if there is nothing to add.
603     if (fragment.isEmpty() || !fragment.firstChild())
604         return;
605     
606     // 1) Insert the content.
607     // 2) Remove redundant styles and style tags, this inner <b> for example: <b>foo <b>bar</b> baz</b>.
608     // 3) Merge the start of the added content with the content before the position being pasted into.
609     // 4) Do one of the following: a) expand the last br if the fragment ends with one and it collapsed,
610     // b) merge the last paragraph of the incoming fragment with the paragraph that contained the 
611     // end of the selection that was pasted into, or c) handle an interchange newline at the end of the 
612     // incoming fragment.
613     // 5) Add spaces for smart replace.
614     // 6) Select the replacement if requested, and match style if requested.
615     
616     VisiblePosition startOfInsertedContent, endOfInsertedContent;
617     
618     RefPtr<Node> refNode = fragment.firstChild();
619     RefPtr<Node> node = refNode->nextSibling();
620     
621     fragment.removeNode(refNode);
622     insertNodeAtAndUpdateNodesInserted(refNode.get(), insertionPos);
623     
624     while (node) {
625         Node* next = node->nextSibling();
626         fragment.removeNode(node);
627         insertNodeAfterAndUpdateNodesInserted(node.get(), refNode.get());
628         refNode = node;
629         node = next;
630     }
631     
632     negateStyleRulesThatAffectAppearance();
633     
634     removeRedundantStyles(mailBlockquoteEnclosingSelectionStart);
635     
636     if (!m_firstNodeInserted)
637         return;
638     
639     endOfInsertedContent = positionAtEndOfInsertedContent();
640     startOfInsertedContent = positionAtStartOfInsertedContent();
641     
642     // We inserted before the startBlock to prevent nesting, and the content before the startBlock wasn't in its own block and
643     // didn't have a br after it, so the inserted content ended up in the same paragraph.
644     if (startBlock && insertionPos.node() == startBlock->parentNode() && (unsigned)insertionPos.offset() < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent))
645         insertNodeAt(createBreakElement(document()).get(), startOfInsertedContent.deepEquivalent());
646     
647     Position lastPositionToSelect;
648     
649     bool interchangeNewlineAtEnd = fragment.hasInterchangeNewlineAtEnd();
650
651     if (shouldRemoveEndBR(endBR, originalVisPosBeforeEndBR))
652         removeNodeAndPruneAncestors(endBR);
653     
654     if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasInterchangeNewlineAtStart())) {
655         // Bail to avoid infinite recursion.
656         if (m_movingParagraph) {
657             // setting display:inline does not work for td elements in quirks mode
658             ASSERT(m_firstNodeInserted->hasTagName(tdTag));
659             return;
660         }
661         VisiblePosition destination = startOfInsertedContent.previous();
662         VisiblePosition startOfParagraphToMove = startOfInsertedContent;
663         
664         // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes.  The nodes are
665         // only ever used to create positions where inserted content starts/ends.
666         moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
667         m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().downstream().node();
668         if (!m_lastLeafInserted->inDocument())
669             m_lastLeafInserted = endingSelection().visibleEnd().deepEquivalent().upstream().node();
670     }
671             
672     endOfInsertedContent = positionAtEndOfInsertedContent();
673     startOfInsertedContent = positionAtStartOfInsertedContent();
674     
675     if (interchangeNewlineAtEnd) {
676         VisiblePosition next = endOfInsertedContent.next(true);
677
678         if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedContent) || next.isNull()) {
679             if (!isStartOfParagraph(endOfInsertedContent)) {
680                 setEndingSelection(endOfInsertedContent);
681                 // Use a default paragraph element (a plain div) for the empty paragraph, using the last paragraph
682                 // block's style seems to annoy users.
683                 insertParagraphSeparator(true);
684
685                 // Select up to the paragraph separator that was added.
686                 lastPositionToSelect = endingSelection().visibleStart().deepEquivalent();
687                 updateNodesInserted(lastPositionToSelect.node());
688             }
689         } else {
690             // Select up to the beginning of the next paragraph.
691             lastPositionToSelect = next.deepEquivalent().downstream();
692         }
693             
694     } else if (shouldMergeEnd(selectionEndWasEndOfParagraph)) {
695         // Bail to avoid infinite recursion.
696         if (m_movingParagraph) {
697             ASSERT_NOT_REACHED();
698             return;
699         }
700         // Merging two paragraphs will destroy the moved one's block styles.  Always move forward to preserve
701         // the block style of the paragraph already in the document, unless the paragraph to move would include the
702         // what was the start of the selection that was pasted into.
703         bool mergeForward = !inSameParagraph(startOfInsertedContent, endOfInsertedContent) || isStartOfParagraph(startOfInsertedContent);
704         
705         VisiblePosition destination = mergeForward ? endOfInsertedContent.next() : endOfInsertedContent;
706         VisiblePosition startOfParagraphToMove = mergeForward ? startOfParagraph(endOfInsertedContent) : endOfInsertedContent.next();
707
708         moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
709         // Merging forward will remove m_lastLeafInserted from the document.
710         // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes.  The nodes are
711         // only ever used to create positions where inserted content starts/ends.
712         if (mergeForward) {
713             m_lastLeafInserted = destination.previous().deepEquivalent().node();
714             if (!m_firstNodeInserted->inDocument())
715                 m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().node();
716         }
717     }
718     
719     handlePasteAsQuotationNode();
720     
721     endOfInsertedContent = positionAtEndOfInsertedContent();
722     startOfInsertedContent = positionAtStartOfInsertedContent();
723     
724     // Add spaces for smart replace.
725     if (m_smartReplace && currentRoot) {
726         // Disable smart replace for password fields.
727         Node* start = currentRoot->shadowAncestorNode();
728         if (start->hasTagName(inputTag) && static_cast<HTMLInputElement*>(start)->inputType() == HTMLInputElement::PASSWORD)
729             m_smartReplace = false;
730     }
731     if (m_smartReplace) {
732         bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) &&
733                                   !isCharacterSmartReplaceExempt(endOfInsertedContent.characterAfter(), false);
734         if (needsTrailingSpace) {
735             RenderObject* renderer = m_lastLeafInserted->renderer();
736             bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace();
737             Node* endNode = positionAtEndOfInsertedContent().deepEquivalent().upstream().node();
738             if (endNode->isTextNode()) {
739                 Text* text = static_cast<Text*>(endNode);
740                 insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " ");
741             } else {
742                 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " ");
743                 insertNodeAfterAndUpdateNodesInserted(node.get(), endNode);
744             }
745         }
746     
747         bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) &&
748                                  !isCharacterSmartReplaceExempt(startOfInsertedContent.previous().characterAfter(), true);
749         if (needsLeadingSpace) {
750             RenderObject* renderer = m_lastLeafInserted->renderer();
751             bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace();
752             Node* startNode = positionAtStartOfInsertedContent().deepEquivalent().downstream().node();
753             if (startNode->isTextNode()) {
754                 Text* text = static_cast<Text*>(startNode);
755                 insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
756             } else {
757                 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " ");
758                 // Don't updateNodesInserted.  Doing so would set m_lastLeafInserted to be the node containing the 
759                 // leading space, but m_lastLeafInserted is supposed to mark the end of pasted content.
760                 insertNodeBefore(node.get(), startNode);
761                 // FIXME: Use positions to track the start/end of inserted content.
762                 m_firstNodeInserted = node;
763             }
764         }
765     }
766     
767     completeHTMLReplacement(lastPositionToSelect);
768 }
769
770 bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR, const VisiblePosition& originalVisPosBeforeEndBR)
771 {
772     if (!endBR || !endBR->inDocument())
773         return false;
774         
775     VisiblePosition visiblePos(Position(endBR, 0));
776     
777     // Don't remove the br if nothing was inserted.
778     if (visiblePos.previous() == originalVisPosBeforeEndBR)
779         return false;
780     
781     // Remove the br if it is collapsed away and so is unnecessary.
782     if (!document()->inStrictMode() && isEndOfBlock(visiblePos) && !isStartOfParagraph(visiblePos))
783         return true;
784         
785     // A br that was originally holding a line open should be displaced by inserted content or turned into a line break.
786     // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder.
787     return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos);
788 }
789
790 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositionToSelect)
791 {
792     Position start;
793     Position end;
794
795     // FIXME: This should never not be the case.
796     if (m_firstNodeInserted && m_firstNodeInserted->inDocument() && m_lastLeafInserted && m_lastLeafInserted->inDocument()) {
797         
798         start = positionAtStartOfInsertedContent().deepEquivalent();
799         end = positionAtEndOfInsertedContent().deepEquivalent();
800         
801         // FIXME (11475): Remove this and require that the creator of the fragment to use nbsps.
802         rebalanceWhitespaceAt(start);
803         rebalanceWhitespaceAt(end);
804
805         if (m_matchStyle) {
806             ASSERT(m_insertionStyle);
807             applyStyle(m_insertionStyle.get(), start, end);
808         }    
809         
810         if (lastPositionToSelect.isNotNull())
811             end = lastPositionToSelect;
812     } else if (lastPositionToSelect.isNotNull())
813         start = end = lastPositionToSelect;
814     else
815         return;
816     
817     if (m_selectReplacement)
818         setEndingSelection(Selection(start, end, SEL_DEFAULT_AFFINITY));
819     else
820         setEndingSelection(Selection(end, SEL_DEFAULT_AFFINITY));
821 }
822
823 EditAction ReplaceSelectionCommand::editingAction() const
824 {
825     return m_editAction;
826 }
827
828 void ReplaceSelectionCommand::insertNodeAfterAndUpdateNodesInserted(Node *insertChild, Node *refChild)
829 {
830     insertNodeAfter(insertChild, refChild);
831     updateNodesInserted(insertChild);
832 }
833
834 void ReplaceSelectionCommand::insertNodeAtAndUpdateNodesInserted(Node *insertChild, const Position& p)
835 {
836     insertNodeAt(insertChild, p);
837     updateNodesInserted(insertChild);
838 }
839
840 void ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted(Node *insertChild, Node *refChild)
841 {
842     insertNodeBefore(insertChild, refChild);
843     updateNodesInserted(insertChild);
844 }
845
846 void ReplaceSelectionCommand::updateNodesInserted(Node *node)
847 {
848     if (!node)
849         return;
850
851     if (!m_firstNodeInserted)
852         m_firstNodeInserted = node;
853     
854     if (node == m_lastLeafInserted)
855         return;
856     
857     m_lastLeafInserted = node->lastDescendant();
858 }
859
860 } // namespace WebCore