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