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