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