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