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