LayoutTests:
[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 "Document.h"
34 #include "DocumentFragment.h"
35 #include "EditingText.h"
36 #include "Element.h"
37 #include "Frame.h"
38 #include "HTMLElement.h"
39 #include "HTMLInterchange.h"
40 #include "HTMLNames.h"
41 #include "SelectionController.h"
42 #include "TextIterator.h"
43 #include "htmlediting.h"
44 #include "markup.h"
45 #include "visible_units.h"
46
47 namespace WebCore {
48
49 using namespace HTMLNames;
50
51 ReplacementFragment::ReplacementFragment(Document *document, DocumentFragment *fragment, bool matchStyle, Element* editableRoot)
52     : m_document(document),
53       m_fragment(fragment),
54       m_matchStyle(matchStyle), 
55       m_hasInterchangeNewlineAtStart(false), 
56       m_hasInterchangeNewlineAtEnd(false), 
57       m_hasMoreThanOneBlock(false)
58 {
59     if (!m_document)
60         return;
61
62     if (!m_fragment) {
63         m_type = EmptyFragment;
64         return;
65     }
66
67     Node* firstChild = m_fragment->firstChild();
68     Node* lastChild = m_fragment->lastChild();
69
70     if (!firstChild) {
71         m_type = EmptyFragment;
72         return;
73     }
74     
75     m_type = firstChild == lastChild && firstChild->isTextNode() ? SingleTextNodeFragment : TreeFragment;
76     
77     ASSERT(editableRoot);
78     if (!editableRoot)
79         return;
80             
81     RefPtr<Node> holder = insertFragmentForTestRendering();
82     
83     RefPtr<Range> range = Selection::selectionFromContentsOfNode(holder.get()).toRange();
84     String text = plainText(range.get());
85     // Give the root a chance to change the text.
86     RefPtr<BeforeTextInsertedEvent> evt = new BeforeTextInsertedEvent(text);
87     ExceptionCode ec = 0;
88     editableRoot->dispatchEvent(evt, ec, true);
89     ASSERT(ec == 0);
90     if (text != evt->text() || !editableRoot->isContentRichlyEditable()) {
91         m_fragment = createFragmentFromText(document, evt->text().deprecatedString());
92         firstChild = m_fragment->firstChild();
93         lastChild = m_fragment->firstChild();
94         
95         removeNode(holder);
96         holder = insertFragmentForTestRendering();
97     }
98     
99     Node *node = firstChild;
100     Node *newlineAtStartNode = 0;
101     Node *newlineAtEndNode = 0;
102     while (node) {
103         Node *next = node->traverseNextNode();
104         if (isInterchangeNewlineNode(node)) {
105             if (next || node == firstChild) {
106                 m_hasInterchangeNewlineAtStart = true;
107                 newlineAtStartNode = node;
108             }
109             else {
110                 m_hasInterchangeNewlineAtEnd = true;
111                 newlineAtEndNode = node;
112             }
113         }
114         else if (isInterchangeConvertedSpaceSpan(node)) {
115             RefPtr<Node> n = 0;
116             while ((n = node->firstChild())) {
117                 removeNode(n);
118                 insertNodeBefore(n.get(), node);
119             }
120             removeNode(node);
121             if (n)
122                 next = n->traverseNextNode();
123         }
124         node = next;
125     }
126
127     if (newlineAtStartNode)
128         removeNode(newlineAtStartNode);
129     if (newlineAtEndNode)
130         removeNode(newlineAtEndNode);
131     
132     saveRenderingInfo(holder.get());
133     removeUnrenderedNodes(holder.get());
134     m_hasMoreThanOneBlock = renderedBlocks(holder.get()) > 1;
135     restoreTestRenderingNodesToFragment(holder.get());
136     removeNode(holder);
137     removeStyleNodes();
138 }
139
140 ReplacementFragment::~ReplacementFragment()
141 {
142 }
143
144 Node *ReplacementFragment::firstChild() const 
145
146     return m_fragment->firstChild(); 
147 }
148
149 Node *ReplacementFragment::lastChild() const 
150
151     return m_fragment->lastChild(); 
152 }
153
154 static bool isMailPasteAsQuotationNode(const Node *node)
155 {
156     return node && static_cast<const Element *>(node)->getAttribute("class") == ApplePasteAsQuotation;
157 }
158
159 Node *ReplacementFragment::mergeStartNode() const
160 {
161     Node *node = m_fragment->firstChild();
162     while (node && isBlockFlow(node) && !isMailPasteAsQuotationNode(node))
163         node = node->traverseNextNode();
164     return node;
165 }
166
167 bool ReplacementFragment::isInterchangeNewlineNode(const Node *node)
168 {
169     static String interchangeNewlineClassString(AppleInterchangeNewline);
170     return node && node->hasTagName(brTag) && 
171            static_cast<const Element *>(node)->getAttribute(classAttr) == interchangeNewlineClassString;
172 }
173
174 bool ReplacementFragment::isInterchangeConvertedSpaceSpan(const Node *node)
175 {
176     static String convertedSpaceSpanClassString(AppleConvertedSpace);
177     return node->isHTMLElement() && 
178            static_cast<const HTMLElement *>(node)->getAttribute(classAttr) == convertedSpaceSpanClassString;
179 }
180
181 Node *ReplacementFragment::enclosingBlock(Node *node) const
182 {
183     while (node && !isBlockFlow(node))
184         node = node->parentNode();    
185     return node ? node : m_fragment.get();
186 }
187
188 void ReplacementFragment::removeNodePreservingChildren(Node *node)
189 {
190     if (!node)
191         return;
192
193     while (RefPtr<Node> n = node->firstChild()) {
194         removeNode(n);
195         insertNodeBefore(n.get(), node);
196     }
197     removeNode(node);
198 }
199
200 void ReplacementFragment::removeNode(PassRefPtr<Node> node)
201 {
202     if (!node)
203         return;
204     
205     Node *parent = node->parentNode();
206     if (!parent)
207         return;
208     
209     ExceptionCode ec = 0;
210     parent->removeChild(node.get(), ec);
211     ASSERT(ec == 0);
212 }
213
214 void ReplacementFragment::insertNodeBefore(Node *node, Node *refNode)
215 {
216     if (!node || !refNode)
217         return;
218         
219     Node *parent = refNode->parentNode();
220     if (!parent)
221         return;
222         
223     ExceptionCode ec = 0;
224     parent->insertBefore(node, refNode, ec);
225     ASSERT(ec == 0);
226 }
227
228 PassRefPtr<Node> ReplacementFragment::insertFragmentForTestRendering()
229 {
230     Node *body = m_document->body();
231     if (!body)
232         return 0;
233
234     RefPtr<Node> holder = createDefaultParagraphElement(m_document.get());
235     
236     ExceptionCode ec = 0;
237     holder->appendChild(m_fragment, ec);
238     ASSERT(ec == 0);
239     
240     body->appendChild(holder.get(), ec);
241     ASSERT(ec == 0);
242     
243     m_document->updateLayoutIgnorePendingStylesheets();
244     
245     return holder.release();
246 }
247
248 void ReplacementFragment::restoreTestRenderingNodesToFragment(Node *holder)
249 {
250     if (!holder)
251         return;
252     
253     ExceptionCode ec = 0;
254     while (RefPtr<Node> node = holder->firstChild()) {
255         holder->removeChild(node.get(), ec);
256         ASSERT(ec == 0);
257         m_fragment->appendChild(node.get(), ec);
258         ASSERT(ec == 0);
259     }
260 }
261
262 bool ReplacementFragment::isBlockFlow(Node* node) const
263 {
264     RefPtr<RenderingInfo> info = m_renderingInfo.get(node);
265     ASSERT(info);
266     if (!info)
267         return false;
268     
269     return info->isBlockFlow();
270 }
271
272 static String &matchNearestBlockquoteColorString()
273 {
274     static String matchNearestBlockquoteColorString = "match";
275     return matchNearestBlockquoteColorString;
276 }
277
278 void ReplaceSelectionCommand::fixupNodeStyles(const NodeVector& nodes, const RenderingInfoMap& renderingInfo)
279 {
280     // This function uses the mapped "desired style" to apply the additional style needed, if any,
281     // to make the node have the desired style.
282
283     updateLayout();
284     
285     NodeVector::const_iterator e = nodes.end();
286     for (NodeVector::const_iterator it = nodes.begin(); it != e; ++it) {
287         Node *node = (*it).get();
288         RefPtr<RenderingInfo> info = renderingInfo.get(node);
289         ASSERT(info);
290         if (!info)
291             continue;
292         CSSMutableStyleDeclaration *desiredStyle = info->style();
293         ASSERT(desiredStyle);
294
295         if (!node->inDocument())
296             continue;
297
298         // The desiredStyle declaration tells what style this node wants to be.
299         // Compare that to the style that it is right now in the document.
300         Position pos(node, 0);
301         RefPtr<CSSComputedStyleDeclaration> currentStyle = pos.computedStyle();
302
303         // Check for the special "match nearest blockquote color" property and resolve to the correct
304         // color if necessary.
305         String matchColorCheck = desiredStyle->getPropertyValue(CSS_PROP__WEBKIT_MATCH_NEAREST_MAIL_BLOCKQUOTE_COLOR);
306         if (matchColorCheck == matchNearestBlockquoteColorString()) {
307             Node *blockquote = nearestMailBlockquote(node);
308             Position pos(blockquote ? blockquote : node->document()->documentElement(), 0);
309             RefPtr<CSSComputedStyleDeclaration> style = pos.computedStyle();
310             String desiredColor = desiredStyle->getPropertyValue(CSS_PROP_COLOR);
311             String nearestColor = style->getPropertyValue(CSS_PROP_COLOR);
312             if (desiredColor != nearestColor)
313                 desiredStyle->setProperty(CSS_PROP_COLOR, nearestColor);
314         }
315         desiredStyle->removeProperty(CSS_PROP__WEBKIT_MATCH_NEAREST_MAIL_BLOCKQUOTE_COLOR);
316
317         currentStyle->diff(desiredStyle);
318         
319         // Only add in block properties if the node is at the start of a 
320         // paragraph. This matches AppKit.
321         if (!isStartOfParagraph(VisiblePosition(pos, DOWNSTREAM)))
322             desiredStyle->removeBlockProperties();
323         
324         // If the desiredStyle is non-zero length, that means the current style differs
325         // from the desired by the styles remaining in the desiredStyle declaration.
326         if (desiredStyle->length() > 0)
327             applyStyle(desiredStyle, Position(node, 0), Position(node, maxDeepOffset(node)));
328     }
329 }
330
331 static PassRefPtr<CSSMutableStyleDeclaration> styleForNode(Node *node)
332 {
333     if (!node || !node->inDocument())
334         return 0;
335         
336     RefPtr<CSSComputedStyleDeclaration> computedStyle = Position(node, 0).computedStyle();
337     RefPtr<CSSMutableStyleDeclaration> style = computedStyle->copyInheritableProperties();
338
339     // In either of the color-matching tests below, set the color to a pseudo-color that will
340     // make the content take on the color of the nearest-enclosing blockquote (if any) after
341     // being pasted in.
342     if (Node *blockquote = nearestMailBlockquote(node)) {
343         RefPtr<CSSComputedStyleDeclaration> blockquoteStyle = Position(blockquote, 0).computedStyle();
344         bool match = (blockquoteStyle->getPropertyValue(CSS_PROP_COLOR) == style->getPropertyValue(CSS_PROP_COLOR));
345         if (match) {
346             style->setProperty(CSS_PROP__WEBKIT_MATCH_NEAREST_MAIL_BLOCKQUOTE_COLOR, matchNearestBlockquoteColorString());
347             return style.release();
348         }
349     }
350     Node *documentElement = node->document()->documentElement();
351     RefPtr<CSSComputedStyleDeclaration> documentStyle = Position(documentElement, 0).computedStyle();
352     bool match = (documentStyle->getPropertyValue(CSS_PROP_COLOR) == style->getPropertyValue(CSS_PROP_COLOR));
353     if (match)
354         style->setProperty(CSS_PROP__WEBKIT_MATCH_NEAREST_MAIL_BLOCKQUOTE_COLOR, matchNearestBlockquoteColorString());
355         
356     return style.release();
357 }
358
359 void ReplacementFragment::saveRenderingInfo(Node *holder)
360 {
361     m_document->updateLayoutIgnorePendingStylesheets();
362     
363     if (m_matchStyle) {
364         // No style restoration will be done, so we don't need to save styles or keep a node vector.
365         for (Node *node = holder->firstChild(); node; node = node->traverseNextNode(holder))
366             m_renderingInfo.add(node, new RenderingInfo(0, node->isBlockFlow()));
367     } else {
368         for (Node *node = holder->firstChild(); node; node = node->traverseNextNode(holder)) {
369             m_renderingInfo.add(node, new RenderingInfo(styleForNode(node), node->isBlockFlow()));
370             m_nodes.append(node);
371         }
372     }
373 }
374
375 void ReplacementFragment::removeUnrenderedNodes(Node *holder)
376 {
377     DeprecatedPtrList<Node> unrendered;
378
379     for (Node *node = holder->firstChild(); node; node = node->traverseNextNode(holder)) {
380         if (!isNodeRendered(node) && !isTableStructureNode(node))
381             unrendered.append(node);
382     }
383
384     for (DeprecatedPtrListIterator<Node> it(unrendered); it.current(); ++it)
385         removeNode(it.current());
386 }
387
388 // FIXME: This counts two blocks for <span><div>foo</div></span>.  Get rid of uses of hasMoreThanOneBlock so that we can get rid of this function.
389 int ReplacementFragment::renderedBlocks(Node *holder)
390 {
391     int count = 0;
392     Node *prev = 0;
393     for (Node *node = holder->firstChild(); node; node = node->traverseNextNode(holder)) {
394         if (node->isBlockFlow()) {
395             if (!prev) {
396                 count++;
397                 prev = node;
398             }
399         } else {
400             Node *block = node->enclosingBlockFlowElement();
401             if (block != prev) {
402                 count++;
403                 prev = block;
404             }
405         }
406     }
407     
408     return count;
409 }
410
411 void ReplacementFragment::removeStyleNodes()
412 {
413     // Since style information has been computed and cached away in
414     // computeStylesUsingTestRendering(), these style nodes can be removed, since
415     // the correct styles will be added back in fixupNodeStyles().
416     Node *node = m_fragment->firstChild();
417     while (node) {
418         Node *next = node->traverseNextNode();
419         // This list of tags change the appearance of content
420         // in ways we can add back on later with CSS, if necessary.
421         //  FIXME: This list is incomplete
422         if (node->hasTagName(bTag) || 
423             node->hasTagName(bigTag) || 
424             node->hasTagName(centerTag) || 
425             node->hasTagName(fontTag) || 
426             node->hasTagName(iTag) || 
427             node->hasTagName(sTag) || 
428             node->hasTagName(smallTag) || 
429             node->hasTagName(strikeTag) || 
430             node->hasTagName(subTag) || 
431             node->hasTagName(supTag) || 
432             node->hasTagName(ttTag) || 
433             node->hasTagName(uTag) || 
434             isStyleSpan(node)) {
435             removeNodePreservingChildren(node);
436         }
437         // need to skip tab span because fixupNodeStyles() is not called
438         // when replace is matching style
439         else if (node->isHTMLElement() && !isTabSpanNode(node)) {
440             HTMLElement *elem = static_cast<HTMLElement *>(node);
441             CSSMutableStyleDeclaration *inlineStyleDecl = elem->inlineStyleDecl();
442             if (inlineStyleDecl) {
443                 inlineStyleDecl->removeBlockProperties();
444                 inlineStyleDecl->removeInheritableProperties();
445             }
446         }
447         node = next;
448     }
449 }
450
451 RenderingInfo::RenderingInfo(PassRefPtr<CSSMutableStyleDeclaration> style, bool isBlockFlow = false)
452     : m_style(style), m_isBlockFlow(isBlockFlow)
453 {
454 }
455
456 ReplaceSelectionCommand::ReplaceSelectionCommand(Document *document, DocumentFragment *fragment, bool selectReplacement, bool smartReplace, bool matchStyle, bool forceMergeStart) 
457     : CompositeEditCommand(document),
458       m_selectReplacement(selectReplacement), 
459       m_smartReplace(smartReplace),
460       m_matchStyle(matchStyle),
461       m_documentFragment(fragment),
462       m_forceMergeStart(forceMergeStart)
463 {
464 }
465
466 ReplaceSelectionCommand::~ReplaceSelectionCommand()
467 {
468 }
469
470 // FIXME: This will soon operate on the fragment after it's been inserted so that it can check renderers and create visible positions.
471 bool ReplaceSelectionCommand::shouldMergeStart(const ReplacementFragment& incomingFragment, const Selection& destinationSelection)
472 {
473     if (m_forceMergeStart)
474         return true;
475         
476     VisiblePosition visibleStart = destinationSelection.visibleStart();
477     Node* startBlock = destinationSelection.start().node()->enclosingBlockFlowElement();
478
479     // <rdar://problem/4013642> Copied quoted word does not paste as a quote if pasted at the start of a line    
480     if (isStartOfParagraph(visibleStart) && isMailBlockquote(incomingFragment.firstChild()))
481         return false;
482     
483     // Don't pull content out of a list item.
484     // FIXMEs: Don't pull content out of a table cell either.
485     if (enclosingList(incomingFragment.mergeStartNode()))
486         return false;
487     
488     // Merge if this is an empty editable subtree, to prevent an extra level of block nesting.
489     if (startBlock == startBlock->rootEditableElement() && isStartOfBlock(visibleStart) && isEndOfBlock(visibleStart))
490         return true;
491     
492     if (!incomingFragment.hasInterchangeNewlineAtStart() && 
493         (!isStartOfParagraph(visibleStart) || !incomingFragment.hasInterchangeNewlineAtEnd() && !incomingFragment.hasMoreThanOneBlock()))
494        return true;
495     
496     return false;
497 }
498
499 bool ReplaceSelectionCommand::shouldMergeEnd(const ReplacementFragment& incomingFragment, const VisiblePosition& endOfInsertedContent, const VisiblePosition& destination)
500 {
501     Node* endNode = endOfInsertedContent.deepEquivalent().node();
502     Node* destinationNode = destination.deepEquivalent().node();
503     // FIXME: Unify the naming scheme for these enclosing element getters.
504     return !incomingFragment.hasInterchangeNewlineAtEnd() && 
505            isEndOfParagraph(endOfInsertedContent) && 
506            nearestMailBlockquote(endNode) == nearestMailBlockquote(destinationNode) &&
507            enclosingListChild(endNode) == enclosingListChild(destinationNode) &&
508            enclosingTableCell(endNode) == enclosingTableCell(destinationNode) &&
509            !endNode->hasTagName(hrTag);
510 }
511
512 void ReplaceSelectionCommand::doApply()
513 {
514     // collect information about the current selection, prior to deleting the selection
515     Selection selection = endingSelection();
516     ASSERT(selection.isCaretOrRange());
517     ASSERT(selection.start().node());
518     if (selection.isNone() || !selection.start().node())
519         return;
520     
521     if (!selection.isContentRichlyEditable())
522         m_matchStyle = true;
523     
524     Element* currentRoot = selection.rootEditableElement();
525     ReplacementFragment fragment(document(), m_documentFragment.get(), m_matchStyle, currentRoot);
526     
527     if (fragment.type() == EmptyFragment)
528         return;
529     
530     if (m_matchStyle)
531         m_insertionStyle = styleAtPosition(selection.start());
532     
533     VisiblePosition visibleStart(selection.start(), selection.affinity());
534     VisiblePosition visibleEnd(selection.end(), selection.affinity());
535     bool startAtStartOfBlock = isStartOfBlock(visibleStart);
536     Node* startBlock = selection.start().node()->enclosingBlockFlowElement();
537
538     // Whether the first paragraph of the incoming fragment should be merged with content from visibleStart to startOfParagraph(visibleStart).
539     bool mergeStart = shouldMergeStart(fragment, selection);
540     
541     bool endWasEndOfParagraph = isEndOfParagraph(visibleEnd);
542
543     Position startPos = selection.start();
544     
545     // delete the current range selection, or insert paragraph for caret selection, as needed
546     if (selection.isRange()) {
547         // When the end of the selection to delete is at the end of a paragraph, and the selection
548         // to delete spans multiple blocks, not merging will leave an empty line containing the
549         // end of the selection to delete.
550         bool mergeBlocksAfterDelete = !fragment.hasInterchangeNewlineAtEnd() && !fragment.hasInterchangeNewlineAtStart() && isEndOfParagraph(visibleEnd);
551         deleteSelection(false, mergeBlocksAfterDelete);
552         updateLayout();
553         visibleStart = endingSelection().visibleStart();
554         if (fragment.hasInterchangeNewlineAtStart()) {
555             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
556                 if (!isEndOfDocument(visibleStart))
557                     setEndingSelection(visibleStart.next());
558             } else
559                 insertParagraphSeparator();
560         }
561         startPos = endingSelection().start();
562     } 
563     else {
564         ASSERT(selection.isCaret());
565         if (fragment.hasInterchangeNewlineAtStart()) {
566             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
567                 if (!isEndOfDocument(visibleStart))
568                     setEndingSelection(visibleStart.next());
569             } else 
570                 insertParagraphSeparator();
571         }
572         // We split the current paragraph in two to avoid nesting the blocks from the fragment inside the current block.
573         // For example paste <div>foo</div><div>bar</div><div>baz</div> into <div>x^x</div>, where ^ is the caret.  
574         // As long as the  div styles are the same, visually you'd expect: <div>xbar</div><div>bar</div><div>bazx</div>, 
575         // not <div>xbar<div>bar</div><div>bazx</div></div>
576         // FIXME: If this code is really about preventing block nesting, then the check should be !isEndOfBlock(visibleStart) and we 
577         // should split the block in two, instead of inserting a paragraph separator. In the meantime, it appears that code below 
578         // depends on this split happening when the paste position is not the start or end of a paragraph.
579         if (fragment.hasMoreThanOneBlock() && !isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
580             insertParagraphSeparator();
581             setEndingSelection(VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY).previous());
582         }
583         startPos = endingSelection().start();
584     }
585     
586     // NOTE: This would be an incorrect usage of downstream() if downstream() were changed to mean the last position after 
587     // 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 
588     // away, there are positions after the br which map to the same visible position as [br, 0]).  
589     Node* endBR = startPos.downstream().node()->hasTagName(brTag) ? startPos.downstream().node() : 0;
590     
591     if (startAtStartOfBlock && startBlock->inDocument())
592         startPos = Position(startBlock, 0);
593
594     // paste into run of tabs splits the tab span
595     startPos = positionOutsideTabSpan(startPos);
596     
597     // paste at start or end of link goes outside of link
598     startPos = positionAvoidingSpecialElementBoundary(startPos);
599
600     Frame *frame = document()->frame();
601     
602     // FIXME: Improve typing style.
603     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
604     frame->clearTypingStyle();
605     setTypingStyle(0);    
606     
607     // done if there is nothing to add
608     if (!fragment.firstChild())
609         return;
610     
611     // check whether to "smart replace" needs to add leading and/or trailing space
612     bool addLeadingSpace = false;
613     bool addTrailingSpace = false;
614     // FIXME: We need the affinity for startPos and endPos, but Position::downstream
615     // and Position::upstream do not give it
616     if (m_smartReplace) {
617         VisiblePosition visiblePos = VisiblePosition(startPos, VP_DEFAULT_AFFINITY);
618         assert(visiblePos.isNotNull());
619         addLeadingSpace = startPos.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isStartOfParagraph(visiblePos);
620         if (addLeadingSpace) {
621             QChar previousChar = visiblePos.previous().characterAfter();
622             if (!previousChar.isNull()) {
623                 addLeadingSpace = !frame->isCharacterSmartReplaceExempt(previousChar, true);
624             }
625         }
626         addTrailingSpace = startPos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isEndOfParagraph(visiblePos);
627         if (addTrailingSpace) {
628             QChar thisChar = visiblePos.characterAfter();
629             if (!thisChar.isNull()) {
630                 addTrailingSpace = !frame->isCharacterSmartReplaceExempt(thisChar, false);
631             }
632         }
633     }
634     
635     // There are five steps to adding the content: merge blocks at start, add remaining blocks,
636     // add "smart replace" space, handle trailing newline, clean up.
637     
638     // initially, we say the insertion point is the start of selection
639     updateLayout();
640     Position insertionPos = startPos;
641
642     // step 1: merge content into the start block
643     if (mergeStart) {
644         RefPtr<Node> refNode = fragment.mergeStartNode();
645         if (refNode) {
646             Node *parent = refNode->parentNode();
647             RefPtr<Node> node = refNode->nextSibling();
648             fragment.removeNode(refNode);
649             insertNodeAtAndUpdateNodesInserted(refNode.get(), startPos.node(), startPos.offset());
650             while (node && !fragment.isBlockFlow(node.get())) {
651                 Node *next = node->nextSibling();
652                 fragment.removeNode(node);
653                 insertNodeAfterAndUpdateNodesInserted(node.get(), refNode.get());
654                 refNode = node;
655                 node = next;
656             }
657
658             // remove any ancestors we emptied, except the root itself which cannot be removed
659             while (parent && parent->parentNode() && parent->childNodeCount() == 0) {
660                 Node *nextParent = parent->parentNode();
661                 fragment.removeNode(parent);
662                 parent = nextParent;
663             }
664         }
665         
666         // update insertion point to be at the end of the last block inserted
667         if (m_lastNodeInserted) {
668             updateLayout();
669             insertionPos = Position(m_lastNodeInserted.get(), m_lastNodeInserted->caretMaxOffset());
670         }
671     }
672     
673     // step 2 : merge everything remaining in the fragment
674     if (fragment.firstChild()) {
675         RefPtr<Node> refNode = fragment.firstChild();
676         RefPtr<Node> node = refNode ? refNode->nextSibling() : 0;
677         Node *insertionBlock = insertionPos.node()->enclosingBlockFlowElement();
678         Node* insertionRoot = insertionPos.node()->rootEditableElement();
679         bool insertionBlockIsRoot = insertionBlock == insertionRoot;
680         VisiblePosition visibleInsertionPos(insertionPos);
681         fragment.removeNode(refNode);
682         // FIXME: The first two cases need to be rethought.  They're about preventing the nesting of 
683         // incoming blocks in the block where the paste is being performed.  But, avoiding nesting doesn't 
684         // always produce the desired visual result, and the decisions are based on isBlockFlow, which 
685         // we're getting rid of.
686         if (!insertionBlockIsRoot && fragment.isBlockFlow(refNode.get()) && isStartOfBlock(visibleInsertionPos) && !m_lastNodeInserted)
687             insertNodeBeforeAndUpdateNodesInserted(refNode.get(), insertionBlock);
688         else if (!insertionBlockIsRoot && fragment.isBlockFlow(refNode.get()) && isEndOfBlock(visibleInsertionPos))
689             insertNodeAfterAndUpdateNodesInserted(refNode.get(), insertionBlock);
690         else if (m_lastNodeInserted && !fragment.isBlockFlow(refNode.get())) {
691             // A non-null m_lastNodeInserted means we've done merging above.  That means everything in the first paragraph 
692             // of the fragment has been merged with everything up to the start of the paragraph where the paste was performed.  
693             // refNode is the first node in the second paragraph of the fragment to paste.  Since it's inline, we can't 
694             // insert it at insertionPos, because it wouldn't end up in its own paragraph.
695
696             // FIXME: Code above does paragraph splitting and so we are assured that visibleInsertionPos is the end of
697             // a paragraph, but the above splitting should eventually be only about preventing nesting.
698             ASSERT(isEndOfParagraph(visibleInsertionPos));
699             VisiblePosition next = visibleInsertionPos.next();
700             if (next.isNull() || next.rootEditableElement() != insertionRoot) {
701                 setEndingSelection(visibleInsertionPos);
702                 insertParagraphSeparator();
703                 next = visibleInsertionPos.next();
704             }
705             
706             Position pos = next.deepEquivalent().downstream();
707             insertNodeAtAndUpdateNodesInserted(refNode.get(), pos.node(), pos.offset());
708         } else {
709             insertNodeAtAndUpdateNodesInserted(refNode.get(), insertionPos.node(), insertionPos.offset());
710         }
711         
712         while (node) {
713             Node *next = node->nextSibling();
714             fragment.removeNode(node);
715             insertNodeAfterAndUpdateNodesInserted(node.get(), refNode.get());
716             refNode = node;
717             node = next;
718         }
719         updateLayout();
720         insertionPos = Position(m_lastNodeInserted.get(), m_lastNodeInserted->caretMaxOffset());
721     }
722
723     // step 3 : handle "smart replace" whitespace
724     if (addTrailingSpace && m_lastNodeInserted) {
725         updateLayout();
726         Position pos(m_lastNodeInserted.get(), m_lastNodeInserted->caretMaxOffset());
727         bool needsTrailingSpace = pos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull();
728         if (needsTrailingSpace) {
729             if (m_lastNodeInserted->isTextNode()) {
730                 Text *text = static_cast<Text *>(m_lastNodeInserted.get());
731                 insertTextIntoNode(text, text->length(), nonBreakingSpaceString());
732                 insertionPos = Position(text, text->length());
733             }
734             else {
735                 RefPtr<Node> node = document()->createEditingTextNode(nonBreakingSpaceString());
736                 insertNodeAfterAndUpdateNodesInserted(node.get(), m_lastNodeInserted.get());
737                 insertionPos = Position(node.get(), 1);
738             }
739         }
740     }
741
742     if (addLeadingSpace && m_firstNodeInserted) {
743         updateLayout();
744         Position pos(m_firstNodeInserted.get(), 0);
745         bool needsLeadingSpace = pos.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull();
746         if (needsLeadingSpace) {
747             if (m_firstNodeInserted->isTextNode()) {
748                 Text *text = static_cast<Text *>(m_firstNodeInserted.get());
749                 insertTextIntoNode(text, 0, nonBreakingSpaceString());
750             } else {
751                 RefPtr<Node> node = document()->createEditingTextNode(nonBreakingSpaceString());
752                 insertNodeBeforeAndUpdateNodesInserted(node.get(), m_firstNodeInserted.get());
753             }
754         }
755     }
756     
757     Position lastPositionToSelect;
758     
759     removeEndBRIfNeeded(endBR);
760
761     // step 4 : handle trailing newline
762     if (fragment.hasInterchangeNewlineAtEnd()) {
763
764         if (!m_lastNodeInserted) {
765             lastPositionToSelect = endingSelection().end().downstream();
766         }
767         else {
768             VisiblePosition pos(insertionPos);
769             VisiblePosition next = pos.next();
770             
771             if (!isEndOfParagraph(pos) || next.isNull() || next.rootEditableElement() != currentRoot) {
772                 setEndingSelection(insertionPos, DOWNSTREAM);
773                 insertParagraphSeparator();
774                 next = pos.next();
775
776                 // Select up to the paragraph separator that was added.
777                 lastPositionToSelect = next.deepEquivalent().downstream();
778                 updateNodesInserted(lastPositionToSelect.node());
779             } else {
780                 // Select up to the preexising paragraph separator.
781                 VisiblePosition next = pos.next();
782                 lastPositionToSelect = next.deepEquivalent().downstream();
783             }
784         }
785     } else {
786         // We want to honor the last incoming line break, so, if it will collapse away because of quirks mode, 
787         // add an extra one.
788         // FIXME: If <div><br></div> is pasted, the br will be expanded.  That's fine, if this code is about 
789         // interpreting incoming brs strictly, but if that's true then we should expand all incoming brs, not 
790         // just the last one. 
791         if (m_lastNodeInserted && m_lastNodeInserted->hasTagName(brTag) && 
792             !document()->inStrictMode() && isEndOfBlock(VisiblePosition(Position(m_lastNodeInserted.get(), 0)))) { 
793             insertNodeBeforeAndUpdateNodesInserted(createBreakElement(document()).get(), m_lastNodeInserted.get());
794         }
795     }
796     
797     if (!m_matchStyle)
798         fixupNodeStyles(fragment.nodes(), fragment.renderingInfo());
799     
800     // Make sure that content after the end of the selection being pasted into is in the same paragraph as the 
801     // last bit of content that was inserted.
802     VisiblePosition endOfInsertedContent(Position(m_lastNodeInserted.get(), maxDeepOffset(m_lastNodeInserted.get())));
803     VisiblePosition destination = endOfInsertedContent.next();
804     if (!endWasEndOfParagraph && shouldMergeEnd(fragment, endOfInsertedContent, destination)) {
805         VisiblePosition startOfParagraphToMove = startOfParagraph(endOfInsertedContent);
806         moveParagraph(startOfParagraphToMove, endOfInsertedContent, destination);
807     }
808     
809     completeHTMLReplacement(lastPositionToSelect);
810 }
811
812 void ReplaceSelectionCommand::removeEndBRIfNeeded(Node* endBR)
813 {
814     if (!endBR || !endBR->inDocument())
815         return;
816         
817     VisiblePosition visiblePos(Position(endBR, 0));
818     
819     if (// The br is collapsed away and so is unnecessary.
820         !document()->inStrictMode() && isEndOfBlock(visiblePos) ||
821         // A br that was originally holding a line open should be displaced by inserted content.
822         // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder.
823         isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos))
824         removeNodeAndPruneAncestors(endBR);
825 }
826
827 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositionToSelect)
828 {
829     Position start;
830     Position end;
831
832     if (m_firstNodeInserted && m_firstNodeInserted->inDocument() && m_lastNodeInserted && m_lastNodeInserted->inDocument()) {
833         // Find the last leaf.
834         Node *lastLeaf = m_lastNodeInserted.get();
835         while (1) {
836             Node *nextChild = lastLeaf->lastChild();
837             if (!nextChild)
838                 break;
839             lastLeaf = nextChild;
840         }
841     
842         // Find the first leaf.
843         Node *firstLeaf = m_firstNodeInserted.get();
844         while (1) {
845             Node *nextChild = firstLeaf->firstChild();
846             if (!nextChild)
847                 break;
848             firstLeaf = nextChild;
849         }
850         
851         // Call updateLayout so caretMinOffset and caretMaxOffset return correct values.
852         updateLayout();
853         start = Position(firstLeaf, firstLeaf->caretMinOffset());
854         end = Position(lastLeaf, lastLeaf->caretMaxOffset());
855
856         if (m_matchStyle) {
857             assert(m_insertionStyle);
858             applyStyle(m_insertionStyle.get(), start, end);
859         }    
860         
861         if (lastPositionToSelect.isNotNull())
862             end = lastPositionToSelect;
863     } else if (lastPositionToSelect.isNotNull())
864         start = end = lastPositionToSelect;
865     else
866         return;
867     
868     if (m_selectReplacement)
869         setEndingSelection(Selection(start, end, SEL_DEFAULT_AFFINITY));
870     else
871         setEndingSelection(end, SEL_DEFAULT_AFFINITY);
872     
873     rebalanceWhitespace();
874 }
875
876 EditAction ReplaceSelectionCommand::editingAction() const
877 {
878     return EditActionPaste;
879 }
880
881 void ReplaceSelectionCommand::insertNodeAfterAndUpdateNodesInserted(Node *insertChild, Node *refChild)
882 {
883     insertNodeAfter(insertChild, refChild);
884     updateNodesInserted(insertChild);
885 }
886
887 void ReplaceSelectionCommand::insertNodeAtAndUpdateNodesInserted(Node *insertChild, Node *refChild, int offset)
888 {
889     insertNodeAt(insertChild, refChild, offset);
890     updateNodesInserted(insertChild);
891 }
892
893 void ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted(Node *insertChild, Node *refChild)
894 {
895     insertNodeBefore(insertChild, refChild);
896     updateNodesInserted(insertChild);
897 }
898
899 void ReplaceSelectionCommand::updateNodesInserted(Node *node)
900 {
901     if (!node)
902         return;
903
904     m_lastTopNodeInserted = node;
905     if (!m_firstNodeInserted)
906         m_firstNodeInserted = node;
907     
908     if (node == m_lastNodeInserted)
909         return;
910     
911     m_lastNodeInserted = node->lastDescendant();
912 }
913
914 } // namespace WebCore