WebCore:
[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 "CSSComputedStyleDeclaration.h"
32 #include "CSSProperty.h"
33 #include "CSSPropertyNames.h"
34 #include "CSSValueKeywords.h"
35 #include "Document.h"
36 #include "DocumentFragment.h"
37 #include "EditingText.h"
38 #include "EventNames.h"
39 #include "Element.h"
40 #include "Frame.h"
41 #include "HTMLElement.h"
42 #include "HTMLInterchange.h"
43 #include "HTMLInputElement.h"
44 #include "HTMLNames.h"
45 #include "SelectionController.h"
46 #include "SmartReplace.h"
47 #include "TextIterator.h"
48 #include "htmlediting.h"
49 #include "markup.h"
50 #include "visible_units.h"
51
52 namespace WebCore {
53
54 using namespace EventNames;
55 using namespace HTMLNames;
56
57 static bool isInterchangeNewlineNode(const Node *node)
58 {
59     static String interchangeNewlineClassString(AppleInterchangeNewline);
60     return node && node->hasTagName(brTag) && 
61            static_cast<const Element *>(node)->getAttribute(classAttr) == interchangeNewlineClassString;
62 }
63
64 static bool isInterchangeConvertedSpaceSpan(const Node *node)
65 {
66     static String convertedSpaceSpanClassString(AppleConvertedSpace);
67     return node->isHTMLElement() && 
68            static_cast<const HTMLElement *>(node)->getAttribute(classAttr) == convertedSpaceSpanClassString;
69 }
70
71 ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* fragment, bool matchStyle, const Selection& selection)
72     : m_document(document),
73       m_fragment(fragment),
74       m_matchStyle(matchStyle), 
75       m_hasInterchangeNewlineAtStart(false), 
76       m_hasInterchangeNewlineAtEnd(false)
77 {
78     if (!m_document)
79         return;
80     if (!m_fragment)
81         return;
82     if (!m_fragment->firstChild())
83         return;
84     
85     Element* editableRoot = selection.rootEditableElement();
86     ASSERT(editableRoot);
87     if (!editableRoot)
88         return;
89     
90     Node* shadowAncestorNode = editableRoot->shadowAncestorNode();
91     
92     if (!editableRoot->getHTMLEventListener(webkitBeforeTextInsertedEvent) &&
93         // FIXME: Remove these checks once textareas and textfields actually register an event handler.
94         !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextField()) &&
95         !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextArea()) &&
96         editableRoot->isContentRichlyEditable()) {
97         removeInterchangeNodes(m_fragment->firstChild());
98         return;
99     }
100
101     Node* styleNode = selection.base().node();
102     RefPtr<Node> holder = insertFragmentForTestRendering(styleNode);
103     
104     RefPtr<Range> range = Selection::selectionFromContentsOfNode(holder.get()).toRange();
105     String text = plainText(range.get());
106     // Give the root a chance to change the text.
107     RefPtr<BeforeTextInsertedEvent> evt = new BeforeTextInsertedEvent(text);
108     ExceptionCode ec = 0;
109     editableRoot->dispatchEvent(evt, ec, true);
110     ASSERT(ec == 0);
111     if (text != evt->text() || !editableRoot->isContentRichlyEditable()) {
112         restoreTestRenderingNodesToFragment(holder.get());
113         removeNode(holder);
114
115         m_fragment = createFragmentFromText(selection.toRange().get(), evt->text());
116         if (!m_fragment->firstChild())
117             return;
118         holder = insertFragmentForTestRendering(styleNode);
119     }
120     
121     removeInterchangeNodes(holder->firstChild());
122     
123     removeUnrenderedNodes(holder.get());
124     restoreTestRenderingNodesToFragment(holder.get());
125     removeNode(holder);
126 }
127
128 bool ReplacementFragment::isEmpty() const
129 {
130     return (!m_fragment || !m_fragment->firstChild()) && !m_hasInterchangeNewlineAtStart && !m_hasInterchangeNewlineAtEnd;
131 }
132
133 Node *ReplacementFragment::firstChild() const 
134
135     return m_fragment ? m_fragment->firstChild() : 0; 
136 }
137
138 Node *ReplacementFragment::lastChild() const 
139
140     return m_fragment ? m_fragment->lastChild() : 0; 
141 }
142
143 void ReplacementFragment::removeNodePreservingChildren(Node *node)
144 {
145     if (!node)
146         return;
147
148     while (RefPtr<Node> n = node->firstChild()) {
149         removeNode(n);
150         insertNodeBefore(n.get(), node);
151     }
152     removeNode(node);
153 }
154
155 void ReplacementFragment::removeNode(PassRefPtr<Node> node)
156 {
157     if (!node)
158         return;
159     
160     Node *parent = node->parentNode();
161     if (!parent)
162         return;
163     
164     ExceptionCode ec = 0;
165     parent->removeChild(node.get(), ec);
166     ASSERT(ec == 0);
167 }
168
169 void ReplacementFragment::insertNodeBefore(Node *node, Node *refNode)
170 {
171     if (!node || !refNode)
172         return;
173         
174     Node *parent = refNode->parentNode();
175     if (!parent)
176         return;
177         
178     ExceptionCode ec = 0;
179     parent->insertBefore(node, refNode, ec);
180     ASSERT(ec == 0);
181 }
182
183 PassRefPtr<Node> ReplacementFragment::insertFragmentForTestRendering(Node* context)
184 {
185     Node* body = m_document->body();
186     if (!body)
187         return 0;
188
189     RefPtr<StyledElement> holder = static_pointer_cast<StyledElement>(createDefaultParagraphElement(m_document.get()));
190     
191     ExceptionCode ec = 0;
192
193     // Copy the whitespace and user-select style from the context onto this element.
194     // FIXME: We should examine other style properties to see if they would be appropriate to consider during the test rendering.
195     Node* n = context;
196     while (n && !n->isElementNode())
197         n = n->parentNode();
198     if (n) {
199         RefPtr<CSSComputedStyleDeclaration> conFontStyle = new CSSComputedStyleDeclaration(static_cast<Element*>(n));
200         CSSStyleDeclaration* style = holder->style();
201         style->setProperty(CSS_PROP_WHITE_SPACE, conFontStyle->getPropertyValue(CSS_PROP_WHITE_SPACE), false, ec);
202         ASSERT(ec == 0);
203         style->setProperty(CSS_PROP__WEBKIT_USER_SELECT, conFontStyle->getPropertyValue(CSS_PROP__WEBKIT_USER_SELECT), false, ec);
204         ASSERT(ec == 0);
205     }
206     
207     holder->appendChild(m_fragment, ec);
208     ASSERT(ec == 0);
209     
210     body->appendChild(holder.get(), ec);
211     ASSERT(ec == 0);
212     
213     m_document->updateLayoutIgnorePendingStylesheets();
214     
215     return holder.release();
216 }
217
218 void ReplacementFragment::restoreTestRenderingNodesToFragment(Node *holder)
219 {
220     if (!holder)
221         return;
222     
223     ExceptionCode ec = 0;
224     while (RefPtr<Node> node = holder->firstChild()) {
225         holder->removeChild(node.get(), ec);
226         ASSERT(ec == 0);
227         m_fragment->appendChild(node.get(), ec);
228         ASSERT(ec == 0);
229     }
230 }
231
232 void ReplacementFragment::removeUnrenderedNodes(Node* holder)
233 {
234     Vector<Node*> unrendered;
235
236     for (Node* node = holder->firstChild(); node; node = node->traverseNextNode(holder))
237         if (!isNodeRendered(node) && !isTableStructureNode(node))
238             unrendered.append(node);
239
240     size_t n = unrendered.size();
241     for (size_t i = 0; i < n; ++i)
242         removeNode(unrendered[i]);
243 }
244
245 void ReplacementFragment::removeInterchangeNodes(Node* startNode)
246 {
247     Node* node = startNode;
248     Node* newlineAtStartNode = 0;
249     Node* newlineAtEndNode = 0;
250     while (node) {
251         Node *next = node->traverseNextNode();
252         if (isInterchangeNewlineNode(node)) {
253             if (next || node == startNode) {
254                 m_hasInterchangeNewlineAtStart = true;
255                 newlineAtStartNode = node;
256             }
257             else {
258                 m_hasInterchangeNewlineAtEnd = true;
259                 newlineAtEndNode = node;
260             }
261         }
262         else if (isInterchangeConvertedSpaceSpan(node)) {
263             RefPtr<Node> n = 0;
264             while ((n = node->firstChild())) {
265                 removeNode(n);
266                 insertNodeBefore(n.get(), node);
267             }
268             removeNode(node);
269             if (n)
270                 next = n->traverseNextNode();
271         }
272         node = next;
273     }
274
275     if (newlineAtStartNode)
276         removeNode(newlineAtStartNode);
277     if (newlineAtEndNode)
278         removeNode(newlineAtEndNode);
279 }
280
281 ReplaceSelectionCommand::ReplaceSelectionCommand(Document* document, PassRefPtr<DocumentFragment> fragment,
282         bool selectReplacement, bool smartReplace, bool matchStyle, bool preventNesting, bool movingParagraph,
283         EditAction editAction) 
284     : CompositeEditCommand(document),
285       m_selectReplacement(selectReplacement), 
286       m_smartReplace(smartReplace),
287       m_matchStyle(matchStyle),
288       m_documentFragment(fragment),
289       m_preventNesting(preventNesting),
290       m_movingParagraph(movingParagraph),
291       m_editAction(editAction)
292 {
293 }
294
295 bool ReplaceSelectionCommand::shouldMergeStart(bool selectionStartWasStartOfParagraph, bool fragmentHasInterchangeNewlineAtStart)
296 {
297     VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent());
298     VisiblePosition prev = startOfInsertedContent.previous(true);
299     if (prev.isNull())
300         return false;
301         
302     return !selectionStartWasStartOfParagraph && 
303            !fragmentHasInterchangeNewlineAtStart &&
304            isStartOfParagraph(startOfInsertedContent) && 
305            !startOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) &&
306            shouldMerge(startOfInsertedContent, prev);
307 }
308
309 bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph)
310 {
311     VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent());
312     VisiblePosition next = endOfInsertedContent.next(true);
313     if (next.isNull())
314         return false;
315
316     return !selectionEndWasEndOfParagraph &&
317            isEndOfParagraph(endOfInsertedContent) && 
318            !endOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) &&
319            shouldMerge(endOfInsertedContent, next);
320 }
321
322 static bool isMailPasteAsQuotationNode(const Node* node)
323 {
324     return node && node->hasTagName(blockquoteTag) && node->isElementNode() && static_cast<const Element*>(node)->getAttribute(classAttr) == ApplePasteAsQuotation;
325 }
326
327 // Wrap CompositeEditCommand::removeNodePreservingChildren() so we can update the nodes we track
328 void ReplaceSelectionCommand::removeNodePreservingChildren(Node* node)
329 {
330     if (m_firstNodeInserted == node)
331         m_firstNodeInserted = node->traverseNextNode();
332     if (m_lastLeafInserted == node)
333         m_lastLeafInserted = node->lastChild() ? node->lastChild() : node->traverseNextSibling();
334     CompositeEditCommand::removeNodePreservingChildren(node);
335 }
336
337 // Wrap CompositeEditCommand::removeNodeAndPruneAncestors() so we can update the nodes we track
338 void ReplaceSelectionCommand::removeNodeAndPruneAncestors(Node* node)
339 {
340     // prepare in case m_firstNodeInserted and/or m_lastLeafInserted get removed
341     // FIXME: shouldn't m_lastLeafInserted be adjusted using traversePreviousNode()?
342     Node* afterFirst = m_firstNodeInserted ? m_firstNodeInserted->traverseNextSibling() : 0;
343     Node* afterLast = m_lastLeafInserted ? m_lastLeafInserted->traverseNextSibling() : 0;
344     
345     CompositeEditCommand::removeNodeAndPruneAncestors(node);
346     
347     // adjust m_firstNodeInserted and m_lastLeafInserted since either or both may have been removed
348     if (m_lastLeafInserted && !m_lastLeafInserted->inDocument())
349         m_lastLeafInserted = afterLast;
350     if (m_firstNodeInserted && !m_firstNodeInserted->inDocument())
351         m_firstNodeInserted = m_lastLeafInserted && m_lastLeafInserted->inDocument() ? afterFirst : 0;
352 }
353
354 bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& from, const VisiblePosition& to)
355 {
356     if (from.isNull() || to.isNull())
357         return false;
358         
359     Node* fromNode = from.deepEquivalent().node();
360     Node* toNode = to.deepEquivalent().node();
361     Node* fromNodeBlock = enclosingBlock(fromNode);
362     return !enclosingNodeOfType(from.deepEquivalent(), &isMailPasteAsQuotationNode) &&
363            fromNodeBlock && (!fromNodeBlock->hasTagName(blockquoteTag) || isMailBlockquote(fromNodeBlock))  &&
364            enclosingListChild(fromNode) == enclosingListChild(toNode) &&
365            enclosingTableCell(from.deepEquivalent()) == enclosingTableCell(from.deepEquivalent()) &&
366            // Don't merge to or from a position before or after a block because it would
367            // be a no-op and cause infinite recursion.
368            !isBlock(fromNode) && !isBlock(toNode);
369 }
370
371 // Style rules that match just inserted elements could change their appearance, like
372 // a div inserted into a document with div { display:inline; }.
373 void ReplaceSelectionCommand::negateStyleRulesThatAffectAppearance()
374 {
375     for (RefPtr<Node> node = m_firstNodeInserted.get(); node; node = node->traverseNextNode()) {
376         // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
377         if (isStyleSpan(node.get())) {
378             HTMLElement* e = static_cast<HTMLElement*>(node.get());
379             // There are other styles that style rules can give to style spans,
380             // but these are the two important ones because they'll prevent
381             // inserted content from appearing in the right paragraph.
382             // FIXME: Hyatt is concerned that selectively using display:inline will give inconsistent
383             // results. We already know one issue because td elements ignore their display property
384             // in quirks mode (which Mail.app is always in). We should look for an alternative.
385             if (isBlock(e))
386                 e->getInlineStyleDecl()->setProperty(CSS_PROP_DISPLAY, CSS_VAL_INLINE);
387             if (e->renderer() && e->renderer()->style()->floating() != FNONE)
388                 e->getInlineStyleDecl()->setProperty(CSS_PROP_FLOAT, CSS_VAL_NONE);
389         }
390         if (node == m_lastLeafInserted)
391             break;
392     }
393 }
394
395 void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds()
396 {
397     document()->updateLayoutIgnorePendingStylesheets();
398     if (!m_lastLeafInserted->renderer() && 
399         m_lastLeafInserted->isTextNode() && 
400         !enclosingNodeWithTag(Position(m_lastLeafInserted.get(), 0), selectTag) && 
401         !enclosingNodeWithTag(Position(m_lastLeafInserted.get(), 0), scriptTag)) {
402         RefPtr<Node> previous = m_firstNodeInserted == m_lastLeafInserted ? 0 : m_lastLeafInserted->traversePreviousNode();
403         removeNode(m_lastLeafInserted.get());
404         m_lastLeafInserted = previous;
405     }
406     
407     // We don't have to make sure that m_firstNodeInserted isn't inside a select or script element, because
408     // it is a top level node in the fragment and the user can't insert into those elements.
409     if (!m_firstNodeInserted->renderer() && 
410         m_firstNodeInserted->isTextNode()) {
411         RefPtr<Node> next = m_firstNodeInserted == m_lastLeafInserted ? 0 : m_firstNodeInserted->traverseNextSibling();
412         removeNode(m_firstNodeInserted.get());
413         m_firstNodeInserted = next;
414     }
415 }
416
417 void ReplaceSelectionCommand::handlePasteAsQuotationNode()
418 {
419     Node* node = m_firstNodeInserted.get();
420     if (isMailPasteAsQuotationNode(node))
421         removeNodeAttribute(static_cast<Element*>(node), classAttr);
422 }
423
424 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent()
425 {
426     Node* lastNode = m_lastLeafInserted.get();
427     Node* enclosingSelect = enclosingNodeWithTag(Position(lastNode, 0), selectTag);
428     if (enclosingSelect)
429         lastNode = enclosingSelect;
430     return VisiblePosition(Position(lastNode, maxDeepOffset(lastNode)));
431 }
432
433 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent()
434 {
435     // Return the inserted content's first VisiblePosition.
436     return VisiblePosition(nextCandidate(positionBeforeNode(m_firstNodeInserted.get())));
437 }
438
439 // Remove style spans before insertion if they are unnecessary.  It's faster because we'll 
440 // avoid doing a layout.
441 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos)
442 {
443     Node* topNode = fragment.firstChild();
444     
445     // Handling this case is more complicated (see handleStyleSpans) and doesn't receive the optimization.
446     if (isMailPasteAsQuotationNode(topNode))
447         return false;
448     
449     // Either there are no style spans in the fragment or a WebKit client has added content to the fragment
450     // before inserting it.  Look for and handle style spans after insertion.
451     if (!isStyleSpan(topNode))
452         return false;
453     
454     Node* sourceDocumentStyleSpan = topNode;
455     RefPtr<Node> copiedRangeStyleSpan = sourceDocumentStyleSpan->firstChild();
456     
457     RefPtr<CSSMutableStyleDeclaration> styleAtInsertionPos = rangeCompliantEquivalent(insertionPos).computedStyle()->copyInheritableProperties();
458     String styleText = styleAtInsertionPos->cssText();
459     
460     if (styleText == static_cast<Element*>(sourceDocumentStyleSpan)->getAttribute(styleAttr)) {
461         fragment.removeNodePreservingChildren(sourceDocumentStyleSpan);
462         if (!isStyleSpan(copiedRangeStyleSpan.get()))
463             return true;
464     }
465         
466     if (isStyleSpan(copiedRangeStyleSpan.get()) && styleText == static_cast<Element*>(copiedRangeStyleSpan.get())->getAttribute(styleAttr)) {
467         fragment.removeNodePreservingChildren(copiedRangeStyleSpan.get());
468         return true;
469     }
470     
471     return false;
472 }
473
474 // At copy time, WebKit wraps copied content in a span that contains the source document's 
475 // default styles.  If the copied Range inherits any other styles from its ancestors, we put 
476 // those styles on a second span.
477 // This function removes redundant styles from those spans, and removes the spans if all their 
478 // styles are redundant. 
479 // We should remove the Apple-style-span class when we're done, see <rdar://problem/5685600>.
480 // We should remove styles from spans that are overridden by all of their children, either here
481 // or at copy time.
482 void ReplaceSelectionCommand::handleStyleSpans()
483 {
484     Node* sourceDocumentStyleSpan = 0;
485     Node* copiedRangeStyleSpan = 0;
486     // The style span that contains the source document's default style should be at
487     // the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Quotation),
488     // so search for the top level style span instead of assuming it's at the top.
489     for (Node* node = m_firstNodeInserted.get(); node; node = node->traverseNextNode()) {
490         if (isStyleSpan(node)) {
491             sourceDocumentStyleSpan = node;
492             // If the copied Range's common ancestor had user applied inheritable styles
493             // on it, they'll be on a second style span, just below the one that holds the 
494             // document defaults.
495             if (isStyleSpan(node->firstChild()))
496                 copiedRangeStyleSpan = node->firstChild();
497             break;
498         }
499     }
500     
501     // There might not be any style spans if we're pasting from another application or if 
502     // we are here because of a document.execCommand("InsertHTML", ...) call.
503     if (!sourceDocumentStyleSpan)
504         return;
505         
506     RefPtr<CSSMutableStyleDeclaration> sourceDocumentStyle = static_cast<HTMLElement*>(sourceDocumentStyleSpan)->getInlineStyleDecl()->copy();
507     Node* context = sourceDocumentStyleSpan->parentNode();
508     
509     // If Mail wraps the fragment with a Paste as Quotation blockquote, styles from that element are
510     // allowed to override those from the source document, see <rdar://problem/4930986>.
511     if (isMailPasteAsQuotationNode(context)) {
512         RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = computedStyle(context)->copyInheritableProperties();
513         RefPtr<CSSMutableStyleDeclaration> parentStyle = computedStyle(context->parentNode())->copyInheritableProperties();
514         parentStyle->diff(blockquoteStyle.get());
515
516         DeprecatedValueListConstIterator<CSSProperty> end;
517         for (DeprecatedValueListConstIterator<CSSProperty> it = blockquoteStyle->valuesIterator(); it != end; ++it) {
518             const CSSProperty& property = *it;
519             sourceDocumentStyle->removeProperty(property.id());
520         }        
521
522         context = context->parentNode();
523     }
524     
525     RefPtr<CSSMutableStyleDeclaration> contextStyle = computedStyle(context)->copyInheritableProperties();
526     String contextStyleText = contextStyle->cssText();
527     String sourceDocumentStyleText = sourceDocumentStyle->cssText();
528     contextStyle->diff(sourceDocumentStyle.get());
529     
530     // Remove block properties in the span's style. This prevents properties that probably have no effect 
531     // currently from affecting blocks later if the style is cloned for a new block element during a future 
532     // editing operation.
533     // FIXME: They *can* have an effect currently if blocks beneath the style span aren't individually marked
534     // with block styles by the editing engine used to style them.  WebKit doesn't do this, but others might.
535     sourceDocumentStyle->removeBlockProperties();
536     
537     // The styles on sourceDocumentStyleSpan are all redundant, and there is no copiedRangeStyleSpan
538     // to consider.  We're finished.
539     if (sourceDocumentStyle->length() == 0 && !copiedRangeStyleSpan) {
540         removeNodePreservingChildren(sourceDocumentStyleSpan);
541         return;
542     }
543     
544     // There are non-redundant styles on sourceDocumentStyleSpan, but there is no
545     // copiedRangeStyleSpan.  Clear the redundant styles from sourceDocumentStyleSpan
546     // and return.
547     if (sourceDocumentStyle->length() > 0 && !copiedRangeStyleSpan) {
548         setNodeAttribute(static_cast<Element*>(sourceDocumentStyleSpan), styleAttr, sourceDocumentStyle->cssText());
549         return;
550     }
551     
552     RefPtr<CSSMutableStyleDeclaration> copiedRangeStyle = static_cast<HTMLElement*>(copiedRangeStyleSpan)->getInlineStyleDecl()->copy();
553     
554     // We're going to put sourceDocumentStyleSpan's non-redundant styles onto copiedRangeStyleSpan,
555     // as long as they aren't overridden by ones on copiedRangeStyleSpan.
556     sourceDocumentStyle->merge(copiedRangeStyle.get(), true);
557     copiedRangeStyle = sourceDocumentStyle;
558     
559     removeNodePreservingChildren(sourceDocumentStyleSpan);
560     
561     // Remove redundant styles.
562     context = copiedRangeStyleSpan->parentNode();
563     contextStyle = computedStyle(context)->copyInheritableProperties();
564     contextStyle->diff(copiedRangeStyle.get());
565     
566     // See the comments above about removing block properties.
567     copiedRangeStyle->removeBlockProperties();
568
569     // All the styles on copiedRangeStyleSpan are redundant, remove it.
570     if (copiedRangeStyle->length() == 0) {
571         removeNodePreservingChildren(copiedRangeStyleSpan);
572         return;
573     }
574     
575     // Clear the redundant styles from the span's style attribute.
576     setNodeAttribute(static_cast<Element*>(copiedRangeStyleSpan), styleAttr, copiedRangeStyle->cssText());
577 }
578
579 void ReplaceSelectionCommand::doApply()
580 {
581     Selection selection = endingSelection();
582     ASSERT(selection.isCaretOrRange());
583     ASSERT(selection.start().node());
584     if (selection.isNone() || !selection.start().node())
585         return;
586     
587     bool selectionIsPlainText = !selection.isContentRichlyEditable();
588     
589     Element* currentRoot = selection.rootEditableElement();
590     ReplacementFragment fragment(document(), m_documentFragment.get(), m_matchStyle, selection);
591     
592     if (m_matchStyle)
593         m_insertionStyle = styleAtPosition(selection.start());
594     
595     VisiblePosition visibleStart = selection.visibleStart();
596     VisiblePosition visibleEnd = selection.visibleEnd();
597     
598     bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd);
599     bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart);
600     
601     Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().node());
602     
603     if (selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph ||
604         startBlock == currentRoot ||
605         startBlock && startBlock->renderer() && startBlock->renderer()->isListItem() ||
606         selectionIsPlainText)
607         m_preventNesting = false;
608     
609     Position insertionPos = selection.start();
610     
611     if (selection.isRange()) {
612         // When the end of the selection being pasted into is at the end of a paragraph, and that selection
613         // spans multiple blocks, not merging may leave an empty line.
614         // When the start of the selection being pasted into is at the start of a block, not merging 
615         // will leave hanging block(s).
616         bool mergeBlocksAfterDelete = isEndOfParagraph(visibleEnd) || isStartOfBlock(visibleStart);
617         // FIXME: We should only expand to include fully selected special elements if we are copying a 
618         // selection and pasting it on top of itself.
619         deleteSelection(false, mergeBlocksAfterDelete, true, false);
620         visibleStart = endingSelection().visibleStart();
621         if (fragment.hasInterchangeNewlineAtStart()) {
622             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
623                 if (!isEndOfDocument(visibleStart))
624                     setEndingSelection(visibleStart.next());
625             } else
626                 insertParagraphSeparator();
627         }
628         insertionPos = endingSelection().start();
629     } else {
630         ASSERT(selection.isCaret());
631         if (fragment.hasInterchangeNewlineAtStart()) {
632             VisiblePosition next = visibleStart.next(true);
633             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart) && next.isNotNull())
634                 setEndingSelection(next);
635             else 
636                 insertParagraphSeparator();
637         }
638         // We split the current paragraph in two to avoid nesting the blocks from the fragment inside the current block.
639         // For example paste <div>foo</div><div>bar</div><div>baz</div> into <div>x^x</div>, where ^ is the caret.  
640         // As long as the  div styles are the same, visually you'd expect: <div>xbar</div><div>bar</div><div>bazx</div>, 
641         // not <div>xbar<div>bar</div><div>bazx</div></div>
642         if (m_preventNesting && !isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
643             insertParagraphSeparator();
644             setEndingSelection(endingSelection().visibleStart().previous());
645         }
646         insertionPos = endingSelection().start();
647     }
648     
649     // Inserting content could cause whitespace to collapse, e.g. inserting <div>foo</div> into hello^ world.
650     prepareWhitespaceAtPositionForSplit(insertionPos);
651     
652     // NOTE: This would be an incorrect usage of downstream() if downstream() were changed to mean the last position after 
653     // p that maps to the same visible position as p (since in the case where a br is at the end of a block and collapsed 
654     // away, there are positions after the br which map to the same visible position as [br, 0]).  
655     Node* endBR = insertionPos.downstream().node()->hasTagName(brTag) ? insertionPos.downstream().node() : 0;
656     VisiblePosition originalVisPosBeforeEndBR;
657     if (endBR)
658         originalVisPosBeforeEndBR = VisiblePosition(endBR, 0, DOWNSTREAM).previous();
659     
660     startBlock = enclosingBlock(insertionPos.node());
661     
662     // Adjust insertionPos to prevent nesting.
663     if (m_preventNesting && startBlock) {
664         ASSERT(startBlock != currentRoot);
665         VisiblePosition visibleInsertionPos(insertionPos);
666         if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInsertionPos) && fragment.hasInterchangeNewlineAtEnd()))
667             insertionPos = positionAfterNode(startBlock);
668         else if (isStartOfBlock(visibleInsertionPos))
669             insertionPos = positionBeforeNode(startBlock);
670     }
671
672     // Paste into run of tabs splits the tab span.
673     insertionPos = positionOutsideTabSpan(insertionPos);
674     
675     // Paste at start or end of link goes outside of link.
676     insertionPos = positionAvoidingSpecialElementBoundary(insertionPos);
677
678     Frame *frame = document()->frame();
679     
680     // FIXME: Improve typing style.
681     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
682     frame->clearTypingStyle();
683     setTypingStyle(0);
684     
685     bool handledStyleSpans = handleStyleSpansBeforeInsertion(fragment, insertionPos);
686     
687     // We're finished if there is nothing to add.
688     if (fragment.isEmpty() || !fragment.firstChild())
689         return;
690     
691     // 1) Insert the content.
692     // 2) Remove redundant styles and style tags, this inner <b> for example: <b>foo <b>bar</b> baz</b>.
693     // 3) Merge the start of the added content with the content before the position being pasted into.
694     // 4) Do one of the following: a) expand the last br if the fragment ends with one and it collapsed,
695     // b) merge the last paragraph of the incoming fragment with the paragraph that contained the 
696     // end of the selection that was pasted into, or c) handle an interchange newline at the end of the 
697     // incoming fragment.
698     // 5) Add spaces for smart replace.
699     // 6) Select the replacement if requested, and match style if requested.
700     
701     VisiblePosition startOfInsertedContent, endOfInsertedContent;
702     
703     RefPtr<Node> refNode = fragment.firstChild();
704     RefPtr<Node> node = refNode->nextSibling();
705     
706     fragment.removeNode(refNode);
707     insertNodeAtAndUpdateNodesInserted(refNode.get(), insertionPos);
708     
709     while (node) {
710         Node* next = node->nextSibling();
711         fragment.removeNode(node);
712         insertNodeAfterAndUpdateNodesInserted(node.get(), refNode.get());
713         refNode = node;
714         node = next;
715     }
716     
717     removeUnrenderedTextNodesAtEnds();
718     
719     negateStyleRulesThatAffectAppearance();
720     
721     if (!handledStyleSpans)
722         handleStyleSpans();
723     
724     if (!m_firstNodeInserted)
725         return;
726     
727     endOfInsertedContent = positionAtEndOfInsertedContent();
728     startOfInsertedContent = positionAtStartOfInsertedContent();
729     
730     // We inserted before the startBlock to prevent nesting, and the content before the startBlock wasn't in its own block and
731     // didn't have a br after it, so the inserted content ended up in the same paragraph.
732     if (startBlock && insertionPos.node() == startBlock->parentNode() && (unsigned)insertionPos.offset() < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent))
733         insertNodeAt(createBreakElement(document()).get(), startOfInsertedContent.deepEquivalent());
734     
735     Position lastPositionToSelect;
736     
737     bool interchangeNewlineAtEnd = fragment.hasInterchangeNewlineAtEnd();
738
739     if (shouldRemoveEndBR(endBR, originalVisPosBeforeEndBR))
740         removeNodeAndPruneAncestors(endBR);
741     
742     if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasInterchangeNewlineAtStart())) {
743         // Bail to avoid infinite recursion.
744         if (m_movingParagraph) {
745             // setting display:inline does not work for td elements in quirks mode
746             ASSERT(m_firstNodeInserted->hasTagName(tdTag));
747             return;
748         }
749         VisiblePosition destination = startOfInsertedContent.previous();
750         VisiblePosition startOfParagraphToMove = startOfInsertedContent;
751         
752         // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes.  The nodes are
753         // only ever used to create positions where inserted content starts/ends.
754         moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
755         m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().downstream().node();
756         if (!m_lastLeafInserted->inDocument())
757             m_lastLeafInserted = endingSelection().visibleEnd().deepEquivalent().upstream().node();
758     }
759             
760     endOfInsertedContent = positionAtEndOfInsertedContent();
761     startOfInsertedContent = positionAtStartOfInsertedContent();
762     
763     if (interchangeNewlineAtEnd) {
764         VisiblePosition next = endOfInsertedContent.next(true);
765
766         if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedContent) || next.isNull()) {
767             if (!isStartOfParagraph(endOfInsertedContent)) {
768                 setEndingSelection(endOfInsertedContent);
769                 // Use a default paragraph element (a plain div) for the empty paragraph, using the last paragraph
770                 // block's style seems to annoy users.
771                 insertParagraphSeparator(true);
772
773                 // Select up to the paragraph separator that was added.
774                 lastPositionToSelect = endingSelection().visibleStart().deepEquivalent();
775                 updateNodesInserted(lastPositionToSelect.node());
776             }
777         } else {
778             // Select up to the beginning of the next paragraph.
779             lastPositionToSelect = next.deepEquivalent().downstream();
780         }
781             
782     } else if (shouldMergeEnd(selectionEndWasEndOfParagraph)) {
783         // Bail to avoid infinite recursion.
784         if (m_movingParagraph) {
785             ASSERT_NOT_REACHED();
786             return;
787         }
788         // Merging two paragraphs will destroy the moved one's block styles.  Always move forward to preserve
789         // the block style of the paragraph already in the document, unless the paragraph to move would include the
790         // what was the start of the selection that was pasted into.
791         bool mergeForward = !inSameParagraph(startOfInsertedContent, endOfInsertedContent) || isStartOfParagraph(startOfInsertedContent);
792         
793         VisiblePosition destination = mergeForward ? endOfInsertedContent.next() : endOfInsertedContent;
794         VisiblePosition startOfParagraphToMove = mergeForward ? startOfParagraph(endOfInsertedContent) : endOfInsertedContent.next();
795
796         moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
797         // Merging forward will remove m_lastLeafInserted from the document.
798         // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes.  The nodes are
799         // only ever used to create positions where inserted content starts/ends.
800         if (mergeForward) {
801             m_lastLeafInserted = destination.previous().deepEquivalent().node();
802             if (!m_firstNodeInserted->inDocument())
803                 m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().node();
804         }
805     }
806     
807     handlePasteAsQuotationNode();
808     
809     endOfInsertedContent = positionAtEndOfInsertedContent();
810     startOfInsertedContent = positionAtStartOfInsertedContent();
811     
812     // Add spaces for smart replace.
813     if (m_smartReplace && currentRoot) {
814         // Disable smart replace for password fields.
815         Node* start = currentRoot->shadowAncestorNode();
816         if (start->hasTagName(inputTag) && static_cast<HTMLInputElement*>(start)->inputType() == HTMLInputElement::PASSWORD)
817             m_smartReplace = false;
818     }
819     if (m_smartReplace) {
820         bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) &&
821                                   !isCharacterSmartReplaceExempt(endOfInsertedContent.characterAfter(), false);
822         if (needsTrailingSpace) {
823             RenderObject* renderer = m_lastLeafInserted->renderer();
824             bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace();
825             Node* endNode = positionAtEndOfInsertedContent().deepEquivalent().upstream().node();
826             if (endNode->isTextNode()) {
827                 Text* text = static_cast<Text*>(endNode);
828                 insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " ");
829             } else {
830                 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " ");
831                 insertNodeAfterAndUpdateNodesInserted(node.get(), endNode);
832             }
833         }
834     
835         bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) &&
836                                  !isCharacterSmartReplaceExempt(startOfInsertedContent.previous().characterAfter(), true);
837         if (needsLeadingSpace) {
838             RenderObject* renderer = m_lastLeafInserted->renderer();
839             bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace();
840             Node* startNode = positionAtStartOfInsertedContent().deepEquivalent().downstream().node();
841             if (startNode->isTextNode()) {
842                 Text* text = static_cast<Text*>(startNode);
843                 insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
844             } else {
845                 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " ");
846                 // Don't updateNodesInserted.  Doing so would set m_lastLeafInserted to be the node containing the 
847                 // leading space, but m_lastLeafInserted is supposed to mark the end of pasted content.
848                 insertNodeBefore(node.get(), startNode);
849                 // FIXME: Use positions to track the start/end of inserted content.
850                 m_firstNodeInserted = node;
851             }
852         }
853     }
854     
855     completeHTMLReplacement(lastPositionToSelect);
856 }
857
858 bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR, const VisiblePosition& originalVisPosBeforeEndBR)
859 {
860     if (!endBR || !endBR->inDocument())
861         return false;
862         
863     VisiblePosition visiblePos(Position(endBR, 0));
864     
865     // Don't remove the br if nothing was inserted.
866     if (visiblePos.previous() == originalVisPosBeforeEndBR)
867         return false;
868     
869     // Remove the br if it is collapsed away and so is unnecessary.
870     if (!document()->inStrictMode() && isEndOfBlock(visiblePos) && !isStartOfParagraph(visiblePos))
871         return true;
872         
873     // A br that was originally holding a line open should be displaced by inserted content or turned into a line break.
874     // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder.
875     return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos);
876 }
877
878 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositionToSelect)
879 {
880     Position start;
881     Position end;
882
883     // FIXME: This should never not be the case.
884     if (m_firstNodeInserted && m_firstNodeInserted->inDocument() && m_lastLeafInserted && m_lastLeafInserted->inDocument()) {
885         
886         start = positionAtStartOfInsertedContent().deepEquivalent();
887         end = positionAtEndOfInsertedContent().deepEquivalent();
888         
889         // FIXME (11475): Remove this and require that the creator of the fragment to use nbsps.
890         rebalanceWhitespaceAt(start);
891         rebalanceWhitespaceAt(end);
892
893         if (m_matchStyle) {
894             ASSERT(m_insertionStyle);
895             applyStyle(m_insertionStyle.get(), start, end);
896         }    
897         
898         if (lastPositionToSelect.isNotNull())
899             end = lastPositionToSelect;
900     } else if (lastPositionToSelect.isNotNull())
901         start = end = lastPositionToSelect;
902     else
903         return;
904     
905     if (m_selectReplacement)
906         setEndingSelection(Selection(start, end, SEL_DEFAULT_AFFINITY));
907     else
908         setEndingSelection(Selection(end, SEL_DEFAULT_AFFINITY));
909 }
910
911 EditAction ReplaceSelectionCommand::editingAction() const
912 {
913     return m_editAction;
914 }
915
916 void ReplaceSelectionCommand::insertNodeAfterAndUpdateNodesInserted(Node *insertChild, Node *refChild)
917 {
918     insertNodeAfter(insertChild, refChild);
919     updateNodesInserted(insertChild);
920 }
921
922 void ReplaceSelectionCommand::insertNodeAtAndUpdateNodesInserted(Node *insertChild, const Position& p)
923 {
924     insertNodeAt(insertChild, p);
925     updateNodesInserted(insertChild);
926 }
927
928 void ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted(Node *insertChild, Node *refChild)
929 {
930     insertNodeBefore(insertChild, refChild);
931     updateNodesInserted(insertChild);
932 }
933
934 void ReplaceSelectionCommand::updateNodesInserted(Node *node)
935 {
936     if (!node)
937         return;
938
939     if (!m_firstNodeInserted)
940         m_firstNodeInserted = node;
941     
942     if (node == m_lastLeafInserted)
943         return;
944     
945     m_lastLeafInserted = node->lastDescendant();
946 }
947
948 } // namespace WebCore