71ad0b4096bb47379b60ab148ce60a4e182c57d2
[WebKit-https.git] / WebCore / khtml / editing / htmlediting.cpp
1 /*
2  * Copyright (C) 2004 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 "htmlediting.h"
27
28 #include "css_computedstyle.h"
29 #include "css_value.h"
30 #include "css_valueimpl.h"
31 #include "cssproperties.h"
32 #include "dom_doc.h"
33 #include "dom_docimpl.h"
34 #include "dom_docimpl.h"
35 #include "dom_elementimpl.h"
36 #include "dom_nodeimpl.h"
37 #include "dom_position.h"
38 #include "dom_positioniterator.h"
39 #include "dom_stringimpl.h"
40 #include "dom_textimpl.h"
41 #include "dom2_rangeimpl.h"
42 #include "html_elementimpl.h"
43 #include "html_imageimpl.h"
44 #include "html_interchange.h"
45 #include "htmlattrs.h"
46 #include "htmltags.h"
47 #include "khtml_part.h"
48 #include "khtml_part.h"
49 #include "khtmlview.h"
50 #include "qptrlist.h"
51 #include "render_object.h"
52 #include "render_style.h"
53 #include "render_text.h"
54 #include "visible_position.h"
55 #include "visible_units.h"
56
57 using DOM::AttrImpl;
58 using DOM::CSSComputedStyleDeclarationImpl;
59 using DOM::CSSMutableStyleDeclarationImpl;
60 using DOM::CSSPrimitiveValue;
61 using DOM::CSSPrimitiveValueImpl;
62 using DOM::CSSProperty;
63 using DOM::CSSStyleDeclarationImpl;
64 using DOM::CSSValue;
65 using DOM::CSSValueImpl;
66 using DOM::DocumentFragmentImpl;
67 using DOM::DocumentImpl;
68 using DOM::DOMString;
69 using DOM::DOMStringImpl;
70 using DOM::DoNotStayInBlock;
71 using DOM::DoNotUpdateLayout;
72 using DOM::EditingTextImpl;
73 using DOM::ElementImpl;
74 using DOM::EStayInBlock;
75 using DOM::HTMLElementImpl;
76 using DOM::HTMLImageElementImpl;
77 using DOM::NamedAttrMapImpl;
78 using DOM::Node;
79 using DOM::NodeImpl;
80 using DOM::NodeListImpl;
81 using DOM::Position;
82 using DOM::PositionIterator;
83 using DOM::Range;
84 using DOM::RangeImpl;
85 using DOM::StayInBlock;
86 using DOM::TextImpl;
87 using DOM::TreeWalkerImpl;
88
89 #if APPLE_CHANGES
90 #include "KWQAssertions.h"
91 #include "KWQLogging.h"
92 #include "KWQKHTMLPart.h"
93 #endif
94
95 #if !APPLE_CHANGES
96 #define ASSERT(assertion) ((void)0)
97 #define ASSERT_WITH_MESSAGE(assertion, formatAndArgs...) ((void)0)
98 #define ASSERT_NOT_REACHED() ((void)0)
99 #define LOG(channel, formatAndArgs...) ((void)0)
100 #define ERROR(formatAndArgs...) ((void)0)
101 #define ASSERT(assertion) assert(assertion)
102 #if LOG_DISABLED
103 #define debugPosition(a,b) ((void)0)
104 #define debugNode(a,b) ((void)0)
105 #endif
106 #endif
107
108 #define IF_IMPL_NULL_RETURN_ARG(arg) do { \
109         if (isNull()) { return arg; } \
110     } while (0)
111         
112 #define IF_IMPL_NULL_RETURN do { \
113         if (isNull()) { return; } \
114     } while (0)
115
116 namespace khtml {
117
118 static inline bool isNBSP(const QChar &c)
119 {
120     return c == QChar(0xa0);
121 }
122
123 static inline bool isWS(const QChar &c)
124 {
125     return c.isSpace() && c != QChar(0xa0);
126 }
127
128 static inline bool isWS(const DOMString &text)
129 {
130     if (text.length() != 1)
131         return false;
132     
133     return isWS(text[0]);
134 }
135
136 static inline bool isWS(const Position &pos)
137 {
138     if (!pos.node())
139         return false;
140         
141     if (!pos.node()->isTextNode())
142         return false;
143
144     const DOMString &string = static_cast<TextImpl *>(pos.node())->data();
145     return isWS(string[pos.offset()]);
146 }
147
148 static const int spacesPerTab = 4;
149
150 static inline bool isTab(const DOMString &text)
151 {
152     static QChar tabCharacter = QChar(0x9);
153     if (text.length() != 1)
154         return false;
155     
156     return text[0] == tabCharacter;
157 }
158
159 static inline bool isTableStructureNode(const NodeImpl *node)
160 {
161     RenderObject *r = node->renderer();
162     return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
163 }
164
165 static DOMString &nonBreakingSpaceString()
166 {
167     static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
168     return nonBreakingSpaceString;
169 }
170
171 static DOMString &styleSpanClassString()
172 {
173     static DOMString styleSpanClassString = "khtml-style-span";
174     return styleSpanClassString;
175 }
176
177 static bool isEmptyStyleSpan(const NodeImpl *node)
178 {
179     if (!node || !node->isHTMLElement())
180         return false;
181
182     const HTMLElementImpl *elem = static_cast<const HTMLElementImpl *>(node);
183     CSSMutableStyleDeclarationImpl *inlineStyleDecl = elem->inlineStyleDecl();
184     if (!inlineStyleDecl || inlineStyleDecl->length() == 0) {
185         NamedAttrMapImpl *map = elem->attributes();
186         if (map && map->length() == 1 && elem->getAttribute(ATTR_CLASS) == styleSpanClassString())
187             return true;
188     }
189     
190     return false;
191 }
192
193 static DOMString &blockPlaceholderClassString()
194 {
195     static DOMString blockPlaceholderClassString = "khtml-block-placeholder";
196     return blockPlaceholderClassString;
197 }
198
199 static void derefNodesInList(QPtrList<NodeImpl> &list)
200 {
201     for (QPtrListIterator<NodeImpl> it(list); it.current(); ++it)
202         it.current()->deref();
203 }
204
205 static void debugPosition(const char *prefix, const Position &pos)
206 {
207     if (!prefix)
208         prefix = "";
209     if (pos.isNull())
210         LOG(Editing, "%s <null>", prefix);
211     else
212         LOG(Editing, "%s%s %p : %d", prefix, pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
213 }
214
215 static void debugNode(const char *prefix, const NodeImpl *node)
216 {
217     if (!prefix)
218         prefix = "";
219     if (!node)
220         LOG(Editing, "%s <null>", prefix);
221     else
222         LOG(Editing, "%s%s %p", prefix, node->nodeName().string().latin1(), node);
223 }
224
225 //------------------------------------------------------------------------------------------
226 // EditCommandPtr
227
228 EditCommandPtr::EditCommandPtr()
229 {
230 }
231
232 EditCommandPtr::EditCommandPtr(EditCommand *impl) : SharedPtr<EditCommand>(impl)
233 {
234 }
235
236 EditCommandPtr::EditCommandPtr(const EditCommandPtr &o) : SharedPtr<EditCommand>(o)
237 {
238 }
239
240 EditCommandPtr::~EditCommandPtr()
241 {
242 }
243
244 EditCommandPtr &EditCommandPtr::operator=(const EditCommandPtr &c)
245 {
246     static_cast<SharedPtr<EditCommand> &>(*this) = c;
247     return *this;
248 }
249
250 bool EditCommandPtr::isCompositeStep() const
251 {
252     IF_IMPL_NULL_RETURN_ARG(false);        
253     return get()->isCompositeStep();
254 }
255
256 bool EditCommandPtr::isInsertTextCommand() const
257 {
258     IF_IMPL_NULL_RETURN_ARG(false);        
259     return get()->isInsertTextCommand();
260 }
261
262 bool EditCommandPtr::isTypingCommand() const
263 {
264     IF_IMPL_NULL_RETURN_ARG(false);        
265     return get()->isTypingCommand();
266 }
267
268 void EditCommandPtr::apply() const
269 {
270     IF_IMPL_NULL_RETURN;
271     get()->apply();
272 }
273
274 void EditCommandPtr::unapply() const
275 {
276     IF_IMPL_NULL_RETURN;
277     get()->unapply();
278 }
279
280 void EditCommandPtr::reapply() const
281 {
282     IF_IMPL_NULL_RETURN;
283     get()->reapply();
284 }
285
286 EditAction EditCommandPtr::editingAction() const
287 {
288     IF_IMPL_NULL_RETURN_ARG(EditActionUnspecified);
289     return get()->editingAction();
290 }
291
292 DocumentImpl * const EditCommandPtr::document() const
293 {
294     IF_IMPL_NULL_RETURN_ARG(0);
295     return get()->document();
296 }
297
298 Selection EditCommandPtr::startingSelection() const
299 {
300     IF_IMPL_NULL_RETURN_ARG(Selection());
301     return get()->startingSelection();
302 }
303
304 Selection EditCommandPtr::endingSelection() const
305 {
306     IF_IMPL_NULL_RETURN_ARG(Selection());
307     return get()->endingSelection();
308 }
309
310 void EditCommandPtr::setStartingSelection(const Selection &s) const
311 {
312     IF_IMPL_NULL_RETURN;
313     get()->setStartingSelection(s);
314 }
315
316 void EditCommandPtr::setEndingSelection(const Selection &s) const
317 {
318     IF_IMPL_NULL_RETURN;
319     get()->setEndingSelection(s);
320 }
321
322 CSSMutableStyleDeclarationImpl *EditCommandPtr::typingStyle() const
323 {
324     IF_IMPL_NULL_RETURN_ARG(0);
325     return get()->typingStyle();
326 }
327
328 void EditCommandPtr::setTypingStyle(CSSMutableStyleDeclarationImpl *style) const
329 {
330     IF_IMPL_NULL_RETURN;
331     get()->setTypingStyle(style);
332 }
333
334 EditCommandPtr EditCommandPtr::parent() const
335 {
336     IF_IMPL_NULL_RETURN_ARG(0);
337     return get()->parent();
338 }
339
340 void EditCommandPtr::setParent(const EditCommandPtr &cmd) const
341 {
342     IF_IMPL_NULL_RETURN;
343     get()->setParent(cmd.get());
344 }
345
346 EditCommandPtr &EditCommandPtr::emptyCommand()
347 {
348     static EditCommandPtr m_emptyCommand;
349     return m_emptyCommand;
350 }
351
352 //------------------------------------------------------------------------------------------
353 // StyleChange
354
355 StyleChange::StyleChange(CSSStyleDeclarationImpl *style, ELegacyHTMLStyles usesLegacyStyles)
356     : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles)
357 {
358     init(style, Position());
359 }
360
361 StyleChange::StyleChange(CSSStyleDeclarationImpl *style, const Position &position, ELegacyHTMLStyles usesLegacyStyles)
362     : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles)
363 {
364     init(style, position);
365 }
366
367 void StyleChange::init(CSSStyleDeclarationImpl *style, const Position &position)
368 {
369     style->ref();
370     CSSMutableStyleDeclarationImpl *mutableStyle = style->makeMutable();
371     mutableStyle->ref();
372     style->deref();
373     
374     QString styleText("");
375
376     QValueListConstIterator<CSSProperty> end;
377     for (QValueListConstIterator<CSSProperty> it = mutableStyle->valuesIterator(); it != end; ++it) {
378         const CSSProperty *property = &*it;
379
380         // If position is empty or the position passed in already has the 
381         // style, just move on.
382         if (position.isNotNull() && currentlyHasStyle(position, property))
383             continue;
384         
385         // If needed, figure out if this change is a legacy HTML style change.
386         if (m_usesLegacyStyles && checkForLegacyHTMLStyleChange(property))
387             continue;
388
389         // Add this property
390
391         if (property->id() == CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT) {
392             // we have to special-case text decorations
393             CSSProperty alteredProperty = CSSProperty(CSS_PROP_TEXT_DECORATION, property->value(), property->isImportant());
394             styleText += alteredProperty.cssText().string();
395         } else {
396             styleText += property->cssText().string();
397         }
398     }
399
400     mutableStyle->deref();
401
402     // Save the result for later
403     m_cssStyle = styleText.stripWhiteSpace();
404 }
405
406 bool StyleChange::checkForLegacyHTMLStyleChange(const DOM::CSSProperty *property)
407 {
408     DOMString valueText(property->value()->cssText());
409     switch (property->id()) {
410         case CSS_PROP_FONT_WEIGHT:
411             if (strcasecmp(valueText, "bold") == 0) {
412                 m_applyBold = true;
413                 return true;
414             }
415             break;
416         case CSS_PROP_FONT_STYLE:
417             if (strcasecmp(valueText, "italic") == 0 || strcasecmp(valueText, "oblique") == 0) {
418                 m_applyItalic = true;
419                 return true;
420             }
421             break;
422     }
423     return false;
424 }
425
426 bool StyleChange::currentlyHasStyle(const Position &pos, const CSSProperty *property)
427 {
428     ASSERT(pos.isNotNull());
429     CSSComputedStyleDeclarationImpl *style = pos.computedStyle();
430     ASSERT(style);
431     style->ref();
432     CSSValueImpl *value = style->getPropertyCSSValue(property->id(), DoNotUpdateLayout);
433     style->deref();
434     if (!value)
435         return false;
436     value->ref();
437     bool result = strcasecmp(value->cssText(), property->value()->cssText()) == 0;
438     value->deref();
439     return result;
440 }
441
442 //------------------------------------------------------------------------------------------
443 // EditCommand
444
445 EditCommand::EditCommand(DocumentImpl *document) 
446     : m_document(document), m_state(NotApplied), m_typingStyle(0), m_parent(0)
447 {
448     ASSERT(m_document);
449     ASSERT(m_document->part());
450     m_document->ref();
451     m_startingSelection = m_document->part()->selection();
452     m_endingSelection = m_startingSelection;
453
454     m_document->part()->setSelection(Selection(), false, true);
455 }
456
457 EditCommand::~EditCommand()
458 {
459     ASSERT(m_document);
460     m_document->deref();
461     if (m_typingStyle)
462         m_typingStyle->deref();
463 }
464
465 void EditCommand::apply()
466 {
467     ASSERT(m_document);
468     ASSERT(m_document->part());
469     ASSERT(state() == NotApplied);
470  
471     KHTMLPart *part = m_document->part();
472
473     ASSERT(part->selection().isNone());
474
475     doApply();
476     
477     m_state = Applied;
478
479     // FIXME: Improve typing style.
480     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
481     if (!preservesTypingStyle())
482         setTypingStyle(0);
483
484     if (!isCompositeStep()) {
485         document()->updateLayout();
486         EditCommandPtr cmd(this);
487         part->appliedEditing(cmd);
488     }
489 }
490
491 void EditCommand::unapply()
492 {
493     ASSERT(m_document);
494     ASSERT(m_document->part());
495     ASSERT(state() == Applied);
496
497     bool topLevel = !isCompositeStep();
498  
499     KHTMLPart *part = m_document->part();
500
501     if (topLevel) {
502         part->setSelection(Selection(), false, true);
503     }
504     ASSERT(part->selection().isNone());
505     
506     doUnapply();
507     
508     m_state = NotApplied;
509
510     if (topLevel) {
511         document()->updateLayout();
512         EditCommandPtr cmd(this);
513         part->unappliedEditing(cmd);
514     }
515 }
516
517 void EditCommand::reapply()
518 {
519     ASSERT(m_document);
520     ASSERT(m_document->part());
521     ASSERT(state() == NotApplied);
522     
523     bool topLevel = !isCompositeStep();
524  
525     KHTMLPart *part = m_document->part();
526
527     if (topLevel) {
528         part->setSelection(Selection(), false, true);
529     }
530     ASSERT(part->selection().isNone());
531     
532     doReapply();
533     
534     m_state = Applied;
535
536     if (topLevel) {
537         document()->updateLayout();
538         EditCommandPtr cmd(this);
539         part->reappliedEditing(cmd);
540     }
541 }
542
543 void EditCommand::doReapply()
544 {
545     doApply();
546 }
547
548 EditAction EditCommand::editingAction() const
549 {
550     return EditActionUnspecified;
551 }
552
553 void EditCommand::setStartingSelection(const Selection &s)
554 {
555     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
556         cmd->m_startingSelection = s;
557 }
558
559 void EditCommand::setEndingSelection(const Selection &s)
560 {
561     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
562         cmd->m_endingSelection = s;
563 }
564
565 void EditCommand::assignTypingStyle(CSSMutableStyleDeclarationImpl *style)
566 {
567     if (m_typingStyle == style)
568         return;
569         
570     CSSMutableStyleDeclarationImpl *old = m_typingStyle;
571     m_typingStyle = style;
572     if (m_typingStyle)
573         m_typingStyle->ref();
574     if (old)
575         old->deref();
576 }
577
578 void EditCommand::setTypingStyle(CSSMutableStyleDeclarationImpl *style)
579 {
580     // FIXME: Improve typing style.
581     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
582     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
583         cmd->assignTypingStyle(style);
584 }
585
586 bool EditCommand::preservesTypingStyle() const
587 {
588     return false;
589 }
590
591 bool EditCommand::isInsertTextCommand() const
592 {
593     return false;
594 }
595
596 bool EditCommand::isTypingCommand() const
597 {
598     return false;
599 }
600
601 //------------------------------------------------------------------------------------------
602 // CompositeEditCommand
603
604 CompositeEditCommand::CompositeEditCommand(DocumentImpl *document) 
605     : EditCommand(document)
606 {
607 }
608
609 void CompositeEditCommand::doUnapply()
610 {
611     if (m_cmds.count() == 0) {
612         return;
613     }
614     
615     for (int i = m_cmds.count() - 1; i >= 0; --i)
616         m_cmds[i]->unapply();
617
618     setState(NotApplied);
619 }
620
621 void CompositeEditCommand::doReapply()
622 {
623     if (m_cmds.count() == 0) {
624         return;
625     }
626
627     for (QValueList<EditCommandPtr>::ConstIterator it = m_cmds.begin(); it != m_cmds.end(); ++it)
628         (*it)->reapply();
629
630     setState(Applied);
631 }
632
633 //
634 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
635 //
636 void CompositeEditCommand::applyCommandToComposite(EditCommandPtr &cmd)
637 {
638     cmd.setStartingSelection(endingSelection());
639     cmd.setEndingSelection(endingSelection());
640     cmd.setParent(this);
641     cmd.apply();
642     m_cmds.append(cmd);
643 }
644
645 void CompositeEditCommand::applyStyle(CSSStyleDeclarationImpl *style, EditAction editingAction)
646 {
647     EditCommandPtr cmd(new ApplyStyleCommand(document(), style, editingAction));
648     applyCommandToComposite(cmd);
649 }
650
651 void CompositeEditCommand::insertParagraphSeparator()
652 {
653     EditCommandPtr cmd(new InsertParagraphSeparatorCommand(document()));
654     applyCommandToComposite(cmd);
655 }
656
657 void CompositeEditCommand::insertNodeBefore(NodeImpl *insertChild, NodeImpl *refChild)
658 {
659     EditCommandPtr cmd(new InsertNodeBeforeCommand(document(), insertChild, refChild));
660     applyCommandToComposite(cmd);
661 }
662
663 void CompositeEditCommand::insertNodeAfter(NodeImpl *insertChild, NodeImpl *refChild)
664 {
665     if (refChild->parentNode()->lastChild() == refChild) {
666         appendNode(insertChild, refChild->parentNode());
667     }
668     else {
669         ASSERT(refChild->nextSibling());
670         insertNodeBefore(insertChild, refChild->nextSibling());
671     }
672 }
673
674 void CompositeEditCommand::insertNodeAt(NodeImpl *insertChild, NodeImpl *refChild, long offset)
675 {
676     if (refChild->hasChildNodes() || (refChild->renderer() && refChild->renderer()->isBlockFlow())) {
677         NodeImpl *child = refChild->firstChild();
678         for (long i = 0; child && i < offset; i++)
679             child = child->nextSibling();
680         if (child)
681             insertNodeBefore(insertChild, child);
682         else
683             appendNode(insertChild, refChild);
684     } 
685     else if (refChild->caretMinOffset() >= offset) {
686         insertNodeBefore(insertChild, refChild);
687     } 
688     else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
689         splitTextNode(static_cast<TextImpl *>(refChild), offset);
690         insertNodeBefore(insertChild, refChild);
691     } 
692     else {
693         insertNodeAfter(insertChild, refChild);
694     }
695 }
696
697 void CompositeEditCommand::appendNode(NodeImpl *appendChild, NodeImpl *parent)
698 {
699     EditCommandPtr cmd(new AppendNodeCommand(document(), appendChild, parent));
700     applyCommandToComposite(cmd);
701 }
702
703 void CompositeEditCommand::removeFullySelectedNode(NodeImpl *node)
704 {
705     if (isTableStructureNode(node)) {
706         // Do not remove an element of table structure; remove its contents.
707         NodeImpl *child = node->firstChild();
708         while (child) {
709             NodeImpl *remove = child;
710             child = child->nextSibling();
711             removeFullySelectedNode(remove);
712         }
713     }
714     else {
715         EditCommandPtr cmd(new RemoveNodeCommand(document(), node));
716         applyCommandToComposite(cmd);
717     }
718 }
719
720 void CompositeEditCommand::removeNode(NodeImpl *removeChild)
721 {
722     EditCommandPtr cmd(new RemoveNodeCommand(document(), removeChild));
723     applyCommandToComposite(cmd);
724 }
725
726 void CompositeEditCommand::removeNodePreservingChildren(NodeImpl *removeChild)
727 {
728     EditCommandPtr cmd(new RemoveNodePreservingChildrenCommand(document(), removeChild));
729     applyCommandToComposite(cmd);
730 }
731
732 void CompositeEditCommand::splitTextNode(TextImpl *text, long offset)
733 {
734     EditCommandPtr cmd(new SplitTextNodeCommand(document(), text, offset));
735     applyCommandToComposite(cmd);
736 }
737
738 void CompositeEditCommand::splitElement(ElementImpl *element, NodeImpl *atChild)
739 {
740     EditCommandPtr cmd(new SplitElementCommand(document(), element, atChild));
741     applyCommandToComposite(cmd);
742 }
743
744 void CompositeEditCommand::mergeIdenticalElements(DOM::ElementImpl *first, DOM::ElementImpl *second)
745 {
746     EditCommandPtr cmd(new MergeIdenticalElementsCommand(document(), first, second));
747     applyCommandToComposite(cmd);
748 }
749
750 void CompositeEditCommand::wrapContentsInDummySpan(DOM::ElementImpl *element)
751 {
752     EditCommandPtr cmd(new WrapContentsInDummySpanCommand(document(), element));
753     applyCommandToComposite(cmd);
754 }
755
756 void CompositeEditCommand::splitTextNodeContainingElement(DOM::TextImpl *text, long offset)
757 {
758     EditCommandPtr cmd(new SplitTextNodeContainingElementCommand(document(), text, offset));
759     applyCommandToComposite(cmd);
760 }
761
762 void CompositeEditCommand::joinTextNodes(TextImpl *text1, TextImpl *text2)
763 {
764     EditCommandPtr cmd(new JoinTextNodesCommand(document(), text1, text2));
765     applyCommandToComposite(cmd);
766 }
767
768 void CompositeEditCommand::inputText(const DOMString &text, bool selectInsertedText)
769 {
770     InsertTextCommand *impl = new InsertTextCommand(document());
771     EditCommandPtr cmd(impl);
772     applyCommandToComposite(cmd);
773     impl->input(text, selectInsertedText);
774 }
775
776 void CompositeEditCommand::insertTextIntoNode(TextImpl *node, long offset, const DOMString &text)
777 {
778     EditCommandPtr cmd(new InsertIntoTextNode(document(), node, offset, text));
779     applyCommandToComposite(cmd);
780 }
781
782 void CompositeEditCommand::deleteTextFromNode(TextImpl *node, long offset, long count)
783 {
784     EditCommandPtr cmd(new DeleteFromTextNodeCommand(document(), node, offset, count));
785     applyCommandToComposite(cmd);
786 }
787
788 void CompositeEditCommand::replaceTextInNode(TextImpl *node, long offset, long count, const DOMString &replacementText)
789 {
790     EditCommandPtr deleteCommand(new DeleteFromTextNodeCommand(document(), node, offset, count));
791     applyCommandToComposite(deleteCommand);
792     EditCommandPtr insertCommand(new InsertIntoTextNode(document(), node, offset, replacementText));
793     applyCommandToComposite(insertCommand);
794 }
795
796 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete)
797 {
798     if (endingSelection().isRange()) {
799         EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete, mergeBlocksAfterDelete));
800         applyCommandToComposite(cmd);
801     }
802 }
803
804 void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
805 {
806     if (selection.isRange()) {
807         EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete, mergeBlocksAfterDelete));
808         applyCommandToComposite(cmd);
809     }
810 }
811
812 void CompositeEditCommand::removeCSSProperty(CSSStyleDeclarationImpl *decl, int property)
813 {
814     EditCommandPtr cmd(new RemoveCSSPropertyCommand(document(), decl, property));
815     applyCommandToComposite(cmd);
816 }
817
818 void CompositeEditCommand::removeNodeAttribute(ElementImpl *element, int attribute)
819 {
820     DOMString value = element->getAttribute(attribute);
821     if (value.isEmpty())
822         return;
823     EditCommandPtr cmd(new RemoveNodeAttributeCommand(document(), element, attribute));
824     applyCommandToComposite(cmd);
825 }
826
827 void CompositeEditCommand::setNodeAttribute(ElementImpl *element, int attribute, const DOMString &value)
828 {
829     EditCommandPtr cmd(new SetNodeAttributeCommand(document(), element, attribute, value));
830     applyCommandToComposite(cmd);
831 }
832
833 void CompositeEditCommand::rebalanceWhitespace()
834 {
835     Selection selection = endingSelection();
836     if (selection.isCaretOrRange()) {
837         EditCommandPtr startCmd(new RebalanceWhitespaceCommand(document(), endingSelection().start()));
838         applyCommandToComposite(startCmd);
839         if (selection.isRange()) {
840             EditCommandPtr endCmd(new RebalanceWhitespaceCommand(document(), endingSelection().end()));
841             applyCommandToComposite(endCmd);
842         }
843     }
844 }
845
846 void CompositeEditCommand::deleteInsignificantText(TextImpl *textNode, int start, int end)
847 {
848     if (!textNode || !textNode->renderer() || start >= end)
849         return;
850
851     RenderText *textRenderer = static_cast<RenderText *>(textNode->renderer());
852     InlineTextBox *box = textRenderer->firstTextBox();
853     if (!box) {
854         // whole text node is empty
855         removeNode(textNode);
856         return;    
857     }
858     
859     long length = textNode->length();
860     if (start >= length || end > length)
861         return;
862
863     int removed = 0;
864     InlineTextBox *prevBox = 0;
865     DOMStringImpl *str = 0;
866
867     // This loop structure works to process all gaps preceding a box,
868     // and also will look at the gap after the last box.
869     while (prevBox || box) {
870         int gapStart = prevBox ? prevBox->m_start + prevBox->m_len : 0;
871         if (end < gapStart)
872             // No more chance for any intersections
873             break;
874
875         int gapEnd = box ? box->m_start : length;
876         bool indicesIntersect = start <= gapEnd && end >= gapStart;
877         int gapLen = gapEnd - gapStart;
878         if (indicesIntersect && gapLen > 0) {
879             gapStart = kMax(gapStart, start);
880             gapEnd = kMin(gapEnd, end);
881             if (!str) {
882                 str = textNode->string()->substring(start, end - start);
883                 str->ref();
884             }    
885             // remove text in the gap
886             str->remove(gapStart - start - removed, gapLen);
887             removed += gapLen;
888         }
889         
890         prevBox = box;
891         if (box)
892             box = box->nextTextBox();
893     }
894
895     if (str) {
896         // Replace the text between start and end with our pruned version.
897         if (str->l > 0) {
898             replaceTextInNode(textNode, start, end - start, str);
899         }
900         else {
901             // Assert that we are not going to delete all of the text in the node.
902             // If we were, that should have been done above with the call to 
903             // removeNode and return.
904             ASSERT(start > 0 || (unsigned long)end - start < textNode->length());
905             deleteTextFromNode(textNode, start, end - start);
906         }
907         str->deref();
908     }
909 }
910
911 void CompositeEditCommand::deleteInsignificantText(const Position &start, const Position &end)
912 {
913     if (start.isNull() || end.isNull())
914         return;
915
916     if (RangeImpl::compareBoundaryPoints(start, end) >= 0)
917         return;
918
919     NodeImpl *node = start.node();
920     while (node) {
921         NodeImpl *next = node->traverseNextNode();
922     
923         if (node->isTextNode()) {
924             TextImpl *textNode = static_cast<TextImpl *>(node);
925             bool isStartNode = node == start.node();
926             bool isEndNode = node == end.node();
927             int startOffset = isStartNode ? start.offset() : 0;
928             int endOffset = isEndNode ? end.offset() : textNode->length();
929             deleteInsignificantText(textNode, startOffset, endOffset);
930         }
931             
932         if (node == end.node())
933             break;
934         node = next;
935     }
936 }
937
938 void CompositeEditCommand::deleteInsignificantTextDownstream(const DOM::Position &pos)
939 {
940     Position end = VisiblePosition(pos).next().deepEquivalent().downstream(StayInBlock);
941     deleteInsignificantText(pos, end);
942 }
943
944 void CompositeEditCommand::insertBlockPlaceholder(NodeImpl *node)
945 {
946     if (!node)
947         return;
948
949     ASSERT(node->renderer() && node->renderer()->isBlockFlow());
950
951     appendNode(createBlockPlaceholderElement(document()), node);
952 }
953
954 bool CompositeEditCommand::insertBlockPlaceholderIfNeeded(NodeImpl *node)
955 {
956     if (!node)
957         return false;
958
959     document()->updateLayout();
960
961     RenderObject *renderer = node->renderer();
962     if (!renderer || !renderer->isBlockFlow())
963         return false;
964     
965     if (renderer->height() > 0)
966         return false;
967
968     insertBlockPlaceholder(node);
969     return true;
970 }
971
972 bool CompositeEditCommand::removeBlockPlaceholderIfNeeded(NodeImpl *node)
973 {
974     if (!node)
975         return false;
976
977     document()->updateLayout();
978
979     RenderObject *renderer = node->renderer();
980     if (!renderer || !renderer->isBlockFlow())
981         return false;
982
983     for (NodeImpl *checkMe = node; checkMe; checkMe = checkMe->traverseNextNode(node)) {
984         if (checkMe->isElementNode()) {
985             ElementImpl *element = static_cast<ElementImpl *>(checkMe);
986             if (element->enclosingBlockFlowElement() == node && 
987                 element->getAttribute(ATTR_CLASS) == blockPlaceholderClassString()) {
988                 removeNode(element);
989                 return true;
990             }
991         }
992     }
993     
994     return false;
995 }
996
997 void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
998 {
999     if (pos.isNull())
1000         return;
1001         
1002     VisiblePosition visiblePos(pos);
1003     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
1004     VisiblePosition visibleParagraphEnd(endOfParagraph(visiblePos, IncludeLineBreak));
1005     Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream(StayInBlock);
1006     Position paragraphEnd = visibleParagraphEnd.deepEquivalent().upstream(StayInBlock);
1007     Position beforeParagraphStart = paragraphStart.upstream(DoNotStayInBlock);
1008     
1009     // Perform some checks to see if we need to perform work in this function.
1010     if (paragraphStart.node()->isBlockFlow()) {
1011         if (paragraphEnd.node()->isBlockFlow()) {
1012             if (!paragraphEnd.node()->isAncestor(paragraphStart.node())) {
1013                 // If the paragraph end is a descendant of paragraph start, then we need to run
1014                 // the rest of this function. If not, we can bail here.
1015                 return;
1016             }
1017         }
1018         else if (paragraphEnd.node()->enclosingBlockFlowElement() != paragraphStart.node()) {
1019             // The paragraph end is in another block that is an ancestor of the paragraph start.
1020             // We can bail as we have a full block to work with.
1021             ASSERT(paragraphStart.node()->isAncestor(paragraphEnd.node()->enclosingBlockFlowElement()));
1022             return;
1023         }
1024         else if (isEndOfDocument(visibleParagraphEnd)) {
1025             // At the end of the document. We can bail here as well.
1026             return;
1027         }
1028     }
1029     
1030     // Create the block to insert. Most times, this will be a shallow clone of the block containing
1031     // the start of the selection (the start block), except for two cases:
1032     //    1) When the start block is a body element.
1033     //    2) When the start block is a mail blockquote and we are not in a position to insert
1034     //       the new block as a peer of the start block. This prevents creating an unwanted 
1035     //       additional level of quoting.
1036     NodeImpl *startBlock = paragraphStart.node()->enclosingBlockFlowElement();
1037     NodeImpl *newBlock = 0;
1038     if (startBlock->id() == ID_BODY || (isMailBlockquote(startBlock) && paragraphStart.node() != startBlock))
1039         newBlock = createDefaultParagraphElement(document());
1040     else
1041         newBlock = startBlock->cloneNode(false);
1042
1043     NodeImpl *moveNode = paragraphStart.node();
1044     if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
1045         moveNode = moveNode->traverseNextNode();
1046     NodeImpl *endNode = paragraphEnd.node();
1047     while (moveNode && !moveNode->isBlockFlow()) {
1048         NodeImpl *next = moveNode->traverseNextNode();
1049         removeNode(moveNode);
1050         appendNode(moveNode, newBlock);
1051         if (moveNode == endNode)
1052             break;
1053         moveNode = next;
1054     }
1055
1056     if (paragraphStart.node()->id() == ID_BODY) {
1057         insertNodeAt(newBlock, paragraphStart.node(), 0);
1058     }
1059     else if (paragraphStart.node()->id() == ID_BR) {
1060         insertNodeAfter(newBlock, paragraphStart.node());
1061     }
1062     else if (paragraphStart.node()->isBlockFlow()) {
1063         insertNodeBefore(newBlock, paragraphStart.node());
1064     }
1065     else if (beforeParagraphStart.node()->enclosingBlockFlowElement()->id() != ID_BODY) {
1066         insertNodeAfter(newBlock, beforeParagraphStart.node()->enclosingBlockFlowElement());
1067     }
1068     else {
1069         insertNodeAfter(newBlock, beforeParagraphStart.node());
1070     }
1071 }
1072
1073 bool CompositeEditCommand::isMailBlockquote(const NodeImpl *node) const
1074 {
1075     if (!node || !node->renderer() || !node->isElementNode() && node->id() != ID_BLOCKQUOTE)
1076         return false;
1077         
1078     return static_cast<const ElementImpl *>(node)->getAttribute("type") == "cite";
1079 }
1080
1081 //==========================================================================================
1082 // Concrete commands
1083 //------------------------------------------------------------------------------------------
1084 // AppendNodeCommand
1085
1086 AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *appendChild, NodeImpl *parentNode)
1087     : EditCommand(document), m_appendChild(appendChild), m_parentNode(parentNode)
1088 {
1089     ASSERT(m_appendChild);
1090     m_appendChild->ref();
1091
1092     ASSERT(m_parentNode);
1093     m_parentNode->ref();
1094 }
1095
1096 AppendNodeCommand::~AppendNodeCommand()
1097 {
1098     ASSERT(m_appendChild);
1099     m_appendChild->deref();
1100
1101     ASSERT(m_parentNode);
1102     m_parentNode->deref();
1103 }
1104
1105 void AppendNodeCommand::doApply()
1106 {
1107     ASSERT(m_appendChild);
1108     ASSERT(m_parentNode);
1109
1110     int exceptionCode = 0;
1111     m_parentNode->appendChild(m_appendChild, exceptionCode);
1112     ASSERT(exceptionCode == 0);
1113 }
1114
1115 void AppendNodeCommand::doUnapply()
1116 {
1117     ASSERT(m_appendChild);
1118     ASSERT(m_parentNode);
1119     ASSERT(state() == Applied);
1120
1121     int exceptionCode = 0;
1122     m_parentNode->removeChild(m_appendChild, exceptionCode);
1123     ASSERT(exceptionCode == 0);
1124 }
1125
1126 //------------------------------------------------------------------------------------------
1127 // ApplyStyleCommand
1128
1129 ApplyStyleCommand::ApplyStyleCommand(DocumentImpl *document, CSSStyleDeclarationImpl *style, EditAction editingAction)
1130     : CompositeEditCommand(document), m_style(style->makeMutable()), m_editingAction(editingAction)
1131 {   
1132     ASSERT(m_style);
1133     m_style->ref();
1134 }
1135
1136 ApplyStyleCommand::~ApplyStyleCommand()
1137 {
1138     ASSERT(m_style);
1139     m_style->deref();
1140 }
1141
1142 void ApplyStyleCommand::doApply()
1143 {
1144     // apply the block-centric properties of the style
1145     CSSMutableStyleDeclarationImpl *blockStyle = m_style->copyBlockProperties();
1146     blockStyle->ref();
1147     applyBlockStyle(blockStyle);
1148
1149     // apply any remaining styles to the inline elements
1150     // NOTE: hopefully, this string comparison is the same as checking for a non-null diff
1151     if (blockStyle->length() < m_style->length()) {
1152         CSSMutableStyleDeclarationImpl *inlineStyle = m_style->copy();
1153         inlineStyle->ref();
1154         applyRelativeFontStyleChange(inlineStyle);
1155         blockStyle->diff(inlineStyle);
1156         applyInlineStyle(inlineStyle);
1157         inlineStyle->deref();
1158     }
1159
1160     blockStyle->deref();
1161     
1162     setEndingSelectionNeedsLayout();
1163 }
1164
1165 EditAction ApplyStyleCommand::editingAction() const
1166 {
1167     return m_editingAction;
1168 }
1169
1170 void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclarationImpl *style)
1171 {
1172     // update document layout once before removing styles
1173     // so that we avoid the expense of updating before each and every call
1174     // to check a computed style
1175     document()->updateLayout();
1176
1177     // get positions we want to use for applying style
1178     Position start(endingSelection().start());
1179     Position end(endingSelection().end());
1180     
1181     // remove current values, if any, of the specified styles from the blocks
1182     // NOTE: tracks the previous block to avoid repeated processing
1183     NodeImpl *beyondEnd = end.node()->traverseNextNode();
1184     NodeImpl *prevBlock = 0;
1185     for (NodeImpl *node = start.node(); node != beyondEnd; node = node->traverseNextNode()) {
1186         NodeImpl *block = node->enclosingBlockFlowElement();
1187         if (block != prevBlock && block->isHTMLElement()) {
1188             removeCSSStyle(style, static_cast<HTMLElementImpl *>(block));
1189             prevBlock = block;
1190         }
1191     }
1192     
1193     // apply specified styles to the block flow elements in the selected range
1194     prevBlock = 0;
1195     for (NodeImpl *node = start.node(); node != beyondEnd; node = node->traverseNextNode()) {
1196         if (node->renderer()) {
1197             NodeImpl *block = node->enclosingBlockFlowElement();
1198             if (block != prevBlock) {
1199                 addBlockStyleIfNeeded(style, node);
1200                 prevBlock = block;
1201             }
1202         }
1203     }
1204 }
1205
1206 #define NoFontDelta (0.0f)
1207 #define MinimumFontSize (0.1f)
1208
1209 void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclarationImpl *style)
1210 {
1211     if (style->getPropertyCSSValue(CSS_PROP_FONT_SIZE)) {
1212         // Explicit font size overrides any delta.
1213         style->removeProperty(CSS_PROP__KHTML_FONT_SIZE_DELTA);
1214         return;
1215     }
1216
1217     // Get the adjustment amount out of the style.
1218     CSSValueImpl *value = style->getPropertyCSSValue(CSS_PROP__KHTML_FONT_SIZE_DELTA);
1219     if (!value)
1220         return;
1221     value->ref();
1222     float adjustment = NoFontDelta;
1223     if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
1224         CSSPrimitiveValueImpl *primitiveValue = static_cast<CSSPrimitiveValueImpl *>(value);
1225         if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_PX) {
1226             // Only PX handled now. If we handle more types in the future, perhaps
1227             // a switch statement here would be more appropriate.
1228             adjustment = primitiveValue->getFloatValue(CSSPrimitiveValue::CSS_PX);
1229         }
1230     }
1231     style->removeProperty(CSS_PROP__KHTML_FONT_SIZE_DELTA);
1232     value->deref();
1233     if (adjustment == NoFontDelta)
1234         return;
1235     
1236     // Adjust to the positions we want to use for applying style.
1237     Selection selection = endingSelection();
1238     Position start(selection.start().downstream(StayInBlock));
1239     Position end(selection.end().upstream(StayInBlock));
1240     if (RangeImpl::compareBoundaryPoints(end, start) < 0) {
1241         Position swap = start;
1242         start = end;
1243         end = swap;
1244     }
1245
1246     // Join up any adjacent text nodes.
1247     if (start.node()->isTextNode()) {
1248         joinChildTextNodes(start.node()->parentNode(), start, end);
1249         selection = endingSelection();
1250         start = selection.start();
1251         end = selection.end();
1252     }
1253     if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) {
1254         joinChildTextNodes(end.node()->parentNode(), start, end);
1255         selection = endingSelection();
1256         start = selection.start();
1257         end = selection.end();
1258     }
1259
1260     // Split the start text nodes if needed to apply style.
1261     bool splitStart = splitTextAtStartIfNeeded(start, end); 
1262     if (splitStart) {
1263         start = endingSelection().start();
1264         end = endingSelection().end();
1265     }
1266     bool splitEnd = splitTextAtEndIfNeeded(start, end);
1267     if (splitEnd) {
1268         start = endingSelection().start();
1269         end = endingSelection().end();
1270     }
1271
1272     NodeImpl *beyondEnd = end.node()->traverseNextNode(); // Calculate loop end point.
1273     start = start.upstream(StayInBlock); // Move upstream to ensure we do not add redundant spans.
1274
1275     // Store away font size before making any changes to the document.
1276     // This ensures that changes to one node won't effect another.
1277     QMap<const NodeImpl *,float> startingFontSizes;
1278     for (const NodeImpl *node = start.node(); node != beyondEnd; node = node->traverseNextNode())
1279         startingFontSizes.insert(node, computedFontSize(node));
1280
1281     // These spans were added by us. If empty after font size changes, they can be removed.
1282     QPtrList<NodeImpl> emptySpans;
1283     
1284     NodeImpl *lastStyledNode = 0;
1285     for (NodeImpl *node = start.node(); node != beyondEnd; node = node->traverseNextNode()) {
1286         // Only work on fully selected nodes.
1287         if (!nodeFullySelected(node, start, end))
1288             continue;
1289
1290         HTMLElementImpl *elem = 0;
1291         if (node->isHTMLElement()) {
1292             elem = static_cast<HTMLElementImpl *>(node);
1293         }
1294         else if (node->isTextNode() && node->parentNode() != lastStyledNode) {
1295             // Last styled node was not parent node of this text node, but we wish to style this
1296             // text node. To make this possible, add a style span to surround this text node.
1297             elem = static_cast<HTMLElementImpl *>(createStyleSpanElement(document()));
1298             insertNodeBefore(elem, node);
1299             surroundNodeRangeWithElement(node, node, elem);
1300         }
1301         else {
1302             // Only handle HTML elements and text nodes.
1303             continue;
1304         }
1305         lastStyledNode = node;
1306         
1307         CSSMutableStyleDeclarationImpl *inlineStyleDecl = elem->getInlineStyleDecl();
1308         float currentFontSize = computedFontSize(node);
1309         float desiredFontSize = kMax(MinimumFontSize, startingFontSizes[node] + adjustment);
1310         if (inlineStyleDecl->getPropertyCSSValue(CSS_PROP_FONT_SIZE)) {
1311             inlineStyleDecl->removeProperty(CSS_PROP_FONT_SIZE, true);
1312             currentFontSize = computedFontSize(node);
1313         }
1314         if (currentFontSize != desiredFontSize) {
1315             QString desiredFontSizeString = QString::number(desiredFontSize);
1316             desiredFontSizeString += "px";
1317             inlineStyleDecl->setProperty(CSS_PROP_FONT_SIZE, desiredFontSizeString, false, false);
1318             setNodeAttribute(elem, ATTR_STYLE, inlineStyleDecl->cssText());
1319         }
1320         if (inlineStyleDecl->length() == 0) {
1321             removeNodeAttribute(elem, ATTR_STYLE);
1322             if (isEmptyStyleSpan(elem))
1323                 emptySpans.append(elem);
1324         }
1325     }
1326
1327     for (QPtrListIterator<NodeImpl> it(emptySpans); it.current(); ++it)
1328         removeNodePreservingChildren(it.current());
1329 }
1330
1331 #undef NoFontDelta
1332 #undef MinimumFontSize
1333
1334 void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclarationImpl *style)
1335 {
1336     // adjust to the positions we want to use for applying style
1337     Position start(endingSelection().start().downstream(StayInBlock).equivalentRangeCompliantPosition());
1338     Position end(endingSelection().end().upstream(StayInBlock));
1339     if (RangeImpl::compareBoundaryPoints(end, start) < 0) {
1340         Position swap = start;
1341         start = end;
1342         end = swap;
1343     }
1344
1345     // update document layout once before removing styles
1346     // so that we avoid the expense of updating before each and every call
1347     // to check a computed style
1348     document()->updateLayout();
1349
1350     // split the start node and containing element if the selection starts inside of it
1351     bool splitStart = splitTextElementAtStartIfNeeded(start, end); 
1352     if (splitStart) {
1353         start = endingSelection().start();
1354         end = endingSelection().end();
1355     }
1356
1357     // split the end node and containing element if the selection ends inside of it
1358     bool splitEnd = splitTextElementAtEndIfNeeded(start, end);
1359     start = endingSelection().start();
1360     end = endingSelection().end();
1361
1362     // Remove style from the selection.
1363     // Use the upstream position of the start for removing style.
1364     // This will ensure we remove all traces of the relevant styles from the selection
1365     // and prevent us from adding redundant ones, as described in:
1366     // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
1367     removeInlineStyle(style, start.upstream(StayInBlock), end);
1368
1369     if (splitStart || splitEnd) {
1370         cleanUpEmptyStyleSpans(start, end);
1371     }
1372
1373     if (splitStart) {
1374         bool mergedStart = mergeStartWithPreviousIfIdentical(start, end);
1375         if (mergedStart) {
1376             start = endingSelection().start();
1377             end = endingSelection().end();
1378         }
1379     }
1380
1381     if (splitEnd) {
1382         mergeEndWithNextIfIdentical(start, end);
1383         start = endingSelection().start();
1384         end = endingSelection().end();
1385     }
1386
1387     // update document layout once before running the rest of the function
1388     // so that we avoid the expense of updating before each and every call
1389     // to check a computed style
1390     document()->updateLayout();
1391     
1392     if (start.node() == end.node()) {
1393         // simple case...start and end are the same node
1394         addInlineStyleIfNeeded(style, start.node(), end.node());
1395     }
1396     else {
1397         NodeImpl *node = start.node();
1398         while (1) {
1399             if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
1400                 NodeImpl *runStart = node;
1401                 while (1) {
1402                     NodeImpl *next = node->traverseNextNode();
1403                     // Break if node is the end node, or if the next node does not fit in with
1404                     // the current group.
1405                     if (node == end.node() || 
1406                         runStart->parentNode() != next->parentNode() || 
1407                         (next->isHTMLElement() && next->id() != ID_BR) || 
1408                         (next->renderer() && !next->renderer()->isInline()))
1409                         break;
1410                     node = next;
1411                 }
1412                 // Now apply style to the run we found.
1413                 addInlineStyleIfNeeded(style, runStart, node);
1414             }
1415             if (node == end.node())
1416                 break;
1417             node = node->traverseNextNode();
1418         }
1419     }
1420 }
1421
1422 //------------------------------------------------------------------------------------------
1423 // ApplyStyleCommand: style-removal helpers
1424
1425 bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclarationImpl *style, HTMLElementImpl *elem)
1426 {
1427     QValueListConstIterator<CSSProperty> end;
1428     for (QValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
1429         switch ((*it).id()) {
1430             case CSS_PROP_FONT_WEIGHT:
1431                 if (elem->id() == ID_B)
1432                     return true;
1433                 break;
1434             case CSS_PROP_FONT_STYLE:
1435                 if (elem->id() == ID_I)
1436                     return true;
1437                 break;
1438         }
1439     }
1440
1441     return false;
1442 }
1443
1444 void ApplyStyleCommand::removeHTMLStyleNode(HTMLElementImpl *elem)
1445 {
1446     // This node can be removed.
1447     // EDIT FIXME: This does not handle the case where the node
1448     // has attributes. But how often do people add attributes to <B> tags? 
1449     // Not so often I think.
1450     ASSERT(elem);
1451     removeNodePreservingChildren(elem);
1452 }
1453
1454 void ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclarationImpl *style, HTMLElementImpl *elem)
1455 {
1456     ASSERT(style);
1457     ASSERT(elem);
1458
1459     CSSMutableStyleDeclarationImpl *decl = elem->inlineStyleDecl();
1460     if (!decl)
1461         return;
1462
1463     QValueListConstIterator<CSSProperty> end;
1464     for (QValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
1465         int propertyID = (*it).id();
1466         CSSValueImpl *value = decl->getPropertyCSSValue(propertyID);
1467         if (value) {
1468             value->ref();
1469             removeCSSProperty(decl, propertyID);
1470             value->deref();
1471         }
1472     }
1473
1474     if (elem->id() == ID_SPAN && elem->renderer() && elem->renderer()->isInline()) {
1475         // Check to see if the span is one we added to apply style.
1476         // If it is, and there are no more attributes on the span other than our
1477         // class marker, remove the span.
1478         if (decl->length() == 0) {
1479             removeNodeAttribute(elem, ATTR_STYLE);
1480             NamedAttrMapImpl *map = elem->attributes();
1481             if (map && map->length() == 1 && elem->getAttribute(ATTR_CLASS) == styleSpanClassString())
1482                 removeNodePreservingChildren(elem);
1483         }
1484     }
1485 }
1486
1487 void ApplyStyleCommand::removeBlockStyle(CSSMutableStyleDeclarationImpl *style, const Position &start, const Position &end)
1488 {
1489     ASSERT(start.isNotNull());
1490     ASSERT(end.isNotNull());
1491     ASSERT(start.node()->inDocument());
1492     ASSERT(end.node()->inDocument());
1493     ASSERT(RangeImpl::compareBoundaryPoints(start, end) <= 0);
1494     
1495 }
1496
1497 static bool hasTextDecorationProperty(NodeImpl *node)
1498 {
1499     if (!node->isElementNode())
1500         return false;
1501
1502     ElementImpl *element = static_cast<ElementImpl *>(node);
1503     CSSComputedStyleDeclarationImpl style(element);
1504
1505     CSSValueImpl *value = style.getPropertyCSSValue(CSS_PROP_TEXT_DECORATION, DoNotUpdateLayout);
1506
1507     if (value) {
1508         value->ref();
1509         DOMString valueText(value->cssText());
1510         value->deref();
1511         if (strcasecmp(valueText,"none") != 0)
1512             return true;
1513     }
1514
1515     return false;
1516 }
1517
1518 static NodeImpl* highestAncestorWithTextDecoration(NodeImpl *node)
1519 {
1520     NodeImpl *result = NULL;
1521
1522     for (NodeImpl *n = node; n; n = n->parentNode()) {
1523         if (hasTextDecorationProperty(n))
1524             result = n;
1525     }
1526
1527     return result;
1528 }
1529
1530 CSSMutableStyleDeclarationImpl *ApplyStyleCommand::extractTextDecorationStyle(NodeImpl *node)
1531 {
1532     ASSERT(node);
1533     ASSERT(node->isElementNode());
1534     
1535     // non-html elements not handled yet
1536     if (!node->isHTMLElement())
1537         return 0;
1538
1539     HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
1540     CSSMutableStyleDeclarationImpl *style = element->inlineStyleDecl();
1541     if (!style)
1542         return 0;
1543
1544     style->ref();
1545     int properties[1] = { CSS_PROP_TEXT_DECORATION };
1546     CSSMutableStyleDeclarationImpl *textDecorationStyle = style->copyPropertiesInSet(properties, 1);
1547
1548     CSSValueImpl *property = style->getPropertyCSSValue(CSS_PROP_TEXT_DECORATION);
1549     if (property && strcasecmp(property->cssText(), "none") != 0) {
1550         removeCSSProperty(style, CSS_PROP_TEXT_DECORATION);
1551     }
1552
1553     style->deref();
1554
1555     return textDecorationStyle;
1556 }
1557
1558 CSSMutableStyleDeclarationImpl *ApplyStyleCommand::extractAndNegateTextDecorationStyle(NodeImpl *node)
1559 {
1560     ASSERT(node);
1561     ASSERT(node->isElementNode());
1562     
1563     // non-html elements not handled yet
1564     if (!node->isHTMLElement())
1565         return 0;
1566
1567     HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
1568     CSSComputedStyleDeclarationImpl *computedStyle = new CSSComputedStyleDeclarationImpl(element);
1569     ASSERT(computedStyle);
1570
1571     computedStyle->ref();
1572
1573     int properties[1] = { CSS_PROP_TEXT_DECORATION };
1574     CSSMutableStyleDeclarationImpl *textDecorationStyle = computedStyle->copyPropertiesInSet(properties, 1);
1575     
1576
1577     CSSValueImpl *property = computedStyle->getPropertyCSSValue(CSS_PROP_TEXT_DECORATION);
1578     if (property && strcasecmp(property->cssText(), "none") != 0) {
1579         property->ref();
1580         CSSMutableStyleDeclarationImpl *newStyle = textDecorationStyle->copy();
1581
1582         newStyle->ref();
1583         newStyle->setProperty(CSS_PROP_TEXT_DECORATION, "none");
1584         applyTextDecorationStyle(node, newStyle);
1585         newStyle->deref();
1586
1587         property->deref();
1588     }
1589
1590     computedStyle->deref();
1591
1592     return textDecorationStyle;
1593 }
1594
1595 void ApplyStyleCommand::applyTextDecorationStyle(NodeImpl *node, CSSMutableStyleDeclarationImpl *style)
1596 {
1597     ASSERT(node);
1598
1599     if (!style || !style->cssText().length())
1600         return;
1601
1602     if (node->isTextNode()) {
1603         HTMLElementImpl *styleSpan = static_cast<HTMLElementImpl *>(createStyleSpanElement(document()));
1604         insertNodeBefore(styleSpan, node);
1605         surroundNodeRangeWithElement(node, node, styleSpan);
1606         node = styleSpan;
1607     }
1608
1609     if (!node->isElementNode())
1610         return;
1611
1612     HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
1613         
1614     StyleChange styleChange(style, Position(element, 0), StyleChange::DoNotUseLegacyHTMLStyles);
1615     if (styleChange.cssStyle().length() > 0) {
1616         DOMString cssText = styleChange.cssStyle();
1617         CSSMutableStyleDeclarationImpl *decl = element->inlineStyleDecl();
1618         if (decl)
1619             cssText += decl->cssText();
1620         setNodeAttribute(element, ATTR_STYLE, cssText);
1621     }
1622 }
1623
1624 void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(NodeImpl *node, const Position &start, const Position &end, bool force)
1625 {
1626     NodeImpl *highestAncestor = highestAncestorWithTextDecoration(node);
1627     
1628     if (highestAncestor) {
1629         NodeImpl *nextCurrent;
1630         NodeImpl *nextChild;
1631         for (NodeImpl *current = highestAncestor; current != node; current = nextCurrent) {
1632             ASSERT(current);
1633             
1634             nextCurrent = NULL;
1635             
1636             CSSMutableStyleDeclarationImpl *decoration = force ? extractAndNegateTextDecorationStyle(current) : extractTextDecorationStyle(current);
1637             if (decoration)
1638                 decoration->ref();
1639
1640             for (NodeImpl *child = current->firstChild(); child; child = nextChild) {
1641                 nextChild = child->nextSibling();
1642
1643                 if (node == child) {
1644                     nextCurrent = child;
1645                 } else if (node->isAncestor(child)) {
1646                     applyTextDecorationStyle(child, decoration);
1647                     nextCurrent = child;
1648                 } else {
1649                     applyTextDecorationStyle(child, decoration);
1650                 }
1651             }
1652
1653             if (decoration)
1654                 decoration->deref();
1655         }
1656     }
1657 }
1658
1659 void ApplyStyleCommand::pushDownTextDecorationStyleAtBoundaries(const Position &start, const Position &end)
1660 {
1661     // We need to work in two passes. First we push down any inline
1662     // styles that set text decoration. Then we look for any remaining
1663     // styles (caused by stylesheets) and explicitly negate text
1664     // decoration while pushing down.
1665
1666     pushDownTextDecorationStyleAroundNode(start.node(), start, end, false);
1667     document()->updateLayout();
1668     pushDownTextDecorationStyleAroundNode(start.node(), start, end, true);
1669
1670     pushDownTextDecorationStyleAroundNode(end.node(), start, end, false);
1671     document()->updateLayout();
1672     pushDownTextDecorationStyleAroundNode(end.node(), start, end, true);
1673 }
1674
1675 void ApplyStyleCommand::removeInlineStyle(CSSMutableStyleDeclarationImpl *style, const Position &start, const Position &end)
1676 {
1677     ASSERT(start.isNotNull());
1678     ASSERT(end.isNotNull());
1679     ASSERT(start.node()->inDocument());
1680     ASSERT(end.node()->inDocument());
1681     ASSERT(RangeImpl::compareBoundaryPoints(start, end) < 0);
1682     
1683     CSSValueImpl *textDecorationSpecialProperty = style->getPropertyCSSValue(CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT);
1684
1685     if (textDecorationSpecialProperty) {
1686         pushDownTextDecorationStyleAtBoundaries(start.downstream(StayInBlock), end.upstream(StayInBlock));
1687         style = style->copy();
1688         style->setProperty(CSS_PROP_TEXT_DECORATION, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT));
1689     }
1690
1691     NodeImpl *node = start.node();
1692     while (node) {
1693         NodeImpl *next = node->traverseNextNode();
1694         if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
1695             HTMLElementImpl *elem = static_cast<HTMLElementImpl *>(node);
1696             if (isHTMLStyleNode(style, elem))
1697                 removeHTMLStyleNode(elem);
1698             else
1699                 removeCSSStyle(style, elem);
1700         }
1701         if (node == end.node())
1702             break;
1703         node = next;
1704     }
1705
1706
1707     if (textDecorationSpecialProperty) {
1708         style->deref();
1709     }
1710 }
1711
1712 bool ApplyStyleCommand::nodeFullySelected(NodeImpl *node, const Position &start, const Position &end) const
1713 {
1714     ASSERT(node);
1715
1716     Position pos = Position(node, node->childNodeCount()).upstream();
1717     return RangeImpl::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 &&
1718         RangeImpl::compareBoundaryPoints(pos, end) <= 0;
1719 }
1720
1721 bool ApplyStyleCommand::nodeFullyUnselected(NodeImpl *node, const Position &start, const Position &end) const
1722 {
1723     ASSERT(node);
1724
1725     Position pos = Position(node, node->childNodeCount()).upstream();
1726     bool isFullyBeforeStart = RangeImpl::compareBoundaryPoints(pos, start) < 0;
1727     bool isFullyAfterEnd = RangeImpl::compareBoundaryPoints(node, 0, end.node(), end.offset()) > 0;
1728
1729     return isFullyBeforeStart || isFullyAfterEnd;
1730 }
1731
1732
1733 //------------------------------------------------------------------------------------------
1734 // ApplyStyleCommand: style-application helpers
1735
1736 bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
1737 {
1738     if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
1739         long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
1740         TextImpl *text = static_cast<TextImpl *>(start.node());
1741         splitTextNode(text, start.offset());
1742         setEndingSelection(Selection(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)));
1743         return true;
1744     }
1745     return false;
1746 }
1747
1748 bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end)
1749 {
1750     if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
1751         TextImpl *text = static_cast<TextImpl *>(end.node());
1752         splitTextNode(text, end.offset());
1753         
1754         NodeImpl *prevNode = text->previousSibling();
1755         ASSERT(prevNode);
1756         NodeImpl *startNode = start.node() == end.node() ? prevNode : start.node();
1757         ASSERT(startNode);
1758         setEndingSelection(Selection(Position(startNode, start.offset()), Position(prevNode, prevNode->caretMaxOffset())));
1759         return true;
1760     }
1761     return false;
1762 }
1763
1764 bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end)
1765 {
1766     if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
1767         long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
1768         TextImpl *text = static_cast<TextImpl *>(start.node());
1769         splitTextNodeContainingElement(text, start.offset());
1770
1771         setEndingSelection(Selection(Position(start.node()->parentNode(), start.node()->nodeIndex()), Position(end.node(), end.offset() - endOffsetAdjustment)));
1772         return true;
1773     }
1774     return false;
1775 }
1776
1777 bool ApplyStyleCommand::splitTextElementAtEndIfNeeded(const Position &start, const Position &end)
1778 {
1779     if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
1780         TextImpl *text = static_cast<TextImpl *>(end.node());
1781         splitTextNodeContainingElement(text, end.offset());
1782
1783         NodeImpl *prevNode = text->parent()->previousSibling()->lastChild();
1784         ASSERT(prevNode);
1785         NodeImpl *startNode = start.node() == end.node() ? prevNode : start.node();
1786         ASSERT(startNode);
1787         setEndingSelection(Selection(Position(startNode, start.offset()), Position(prevNode->parent(), prevNode->nodeIndex() + 1)));
1788         return true;
1789     }
1790     return false;
1791 }
1792
1793 static bool areIdenticalElements(NodeImpl *first, NodeImpl *second)
1794 {
1795     // check that tag name and all attribute names and values are identical
1796
1797     if (!first->isElementNode())
1798         return false;
1799     
1800     if (!second->isElementNode())
1801         return false;
1802
1803     ElementImpl *firstElement = static_cast<ElementImpl *>(first);
1804     ElementImpl *secondElement = static_cast<ElementImpl *>(second);
1805     
1806     if (firstElement->id() != secondElement->id())
1807         return false;
1808
1809     NamedAttrMapImpl *firstMap = firstElement->attributes();
1810     NamedAttrMapImpl *secondMap = secondElement->attributes();
1811
1812     unsigned firstLength = firstMap->length();
1813
1814     if (firstLength != secondMap->length())
1815         return false;
1816
1817     for (unsigned i = 0; i < firstLength; i++) {
1818         DOM::AttributeImpl *attribute = firstMap->attributeItem(i);
1819         DOM::AttributeImpl *secondAttribute = secondMap->getAttributeItem(attribute->id());
1820
1821         if (!secondAttribute || attribute->value() != secondAttribute->value())
1822             return false;
1823     }
1824     
1825     return true;
1826 }
1827
1828 bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end)
1829 {
1830     NodeImpl *startNode = start.node();
1831     long startOffset = start.offset();
1832
1833     if (start.node()->isAtomicNode()) {
1834         if (start.offset() != 0)
1835             return false;
1836
1837         if (start.node()->previousSibling())
1838             return false;
1839
1840         startNode = start.node()->parent();
1841         startOffset = 0;
1842     }
1843
1844     if (!startNode->isElementNode())
1845         return false;
1846
1847     if (startOffset != 0)
1848         return false;
1849
1850     NodeImpl *previousSibling = startNode->previousSibling();
1851
1852     if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
1853         ElementImpl *previousElement = static_cast<ElementImpl *>(previousSibling);
1854         ElementImpl *element = static_cast<ElementImpl *>(startNode);
1855         NodeImpl *startChild = element->firstChild();
1856         ASSERT(startChild);
1857         mergeIdenticalElements(previousElement, element);
1858
1859         long startOffsetAdjustment = startChild->nodeIndex();
1860         long endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0;
1861
1862         setEndingSelection(Selection(Position(startNode, startOffsetAdjustment),
1863                                      Position(end.node(), end.offset() + endOffsetAdjustment))); 
1864
1865         return true;
1866     }
1867
1868     return false;
1869 }
1870
1871 bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end)
1872 {
1873     NodeImpl *endNode = end.node();
1874     int endOffset = end.offset();
1875
1876     if (endNode->isAtomicNode()) {
1877         if (endOffset < endNode->caretMaxOffset())
1878             return false;
1879
1880         unsigned parentLastOffset = end.node()->parent()->childNodes()->length() - 1;
1881         if (end.node()->nextSibling())
1882             return false;
1883
1884         endNode = end.node()->parent();
1885         endOffset = parentLastOffset;
1886     }
1887
1888     if (!endNode->isElementNode() || endNode->id() == ID_BR)
1889         return false;
1890
1891     NodeImpl *nextSibling = endNode->nextSibling();
1892
1893     if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
1894         ElementImpl *nextElement = static_cast<ElementImpl *>(nextSibling);
1895         ElementImpl *element = static_cast<ElementImpl *>(endNode);
1896         NodeImpl *nextChild = nextElement->firstChild();
1897
1898         mergeIdenticalElements(element, nextElement);
1899
1900         NodeImpl *startNode = start.node() == endNode ? nextElement : start.node();
1901         ASSERT(startNode);
1902
1903         int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
1904
1905         setEndingSelection(Selection(Position(startNode, start.offset()), 
1906                                      Position(nextElement, endOffset)));
1907         return true;
1908     }
1909
1910     return false;
1911 }
1912
1913 void ApplyStyleCommand::cleanUpEmptyStyleSpans(const Position &start, const Position &end)
1914 {
1915     NodeImpl *node;
1916     for (node = start.node(); node && !node->previousSibling(); node = node->parentNode()) {
1917     }
1918
1919     if (node && isEmptyStyleSpan(node->previousSibling())) {
1920         removeNodePreservingChildren(node->previousSibling());
1921     }
1922
1923     if (start.node() == end.node()) {
1924         if (start.node()->isTextNode()) {
1925             for (NodeImpl *last = start.node(), *cur = last->parentNode(); cur && !last->previousSibling() && !last->nextSibling(); last = cur, cur = cur->parentNode()) {
1926                 if (isEmptyStyleSpan(cur)) {
1927                     removeNodePreservingChildren(cur);
1928                     break;
1929                 }
1930             }
1931
1932         }
1933     } else {
1934         if (start.node()->isTextNode()) {
1935             for (NodeImpl *last = start.node(), *cur = last->parentNode(); cur && !last->previousSibling(); last = cur, cur = cur->parentNode()) {
1936                 if (isEmptyStyleSpan(cur)) {
1937                     removeNodePreservingChildren(cur);
1938                     break;
1939                 }
1940             }
1941         }
1942
1943         if (end.node()->isTextNode()) {
1944             for (NodeImpl *last = end.node(), *cur = last->parentNode(); cur && !last->nextSibling(); last = cur, cur = cur->parentNode()) {
1945                 if (isEmptyStyleSpan(cur)) {
1946                     removeNodePreservingChildren(cur);
1947                     break;
1948                 }
1949             }
1950         }
1951     }
1952     
1953     for (node = end.node(); node && !node->nextSibling(); node = node->parentNode()) {
1954     }
1955     if (node && isEmptyStyleSpan(node->nextSibling())) {
1956         removeNodePreservingChildren(node->nextSibling());
1957     }
1958 }
1959
1960 void ApplyStyleCommand::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
1961 {
1962     ASSERT(startNode);
1963     ASSERT(endNode);
1964     ASSERT(element);
1965     
1966     NodeImpl *node = startNode;
1967     while (1) {
1968         NodeImpl *next = node->traverseNextNode();
1969         if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
1970             removeNode(node);
1971             appendNode(node, element);
1972         }
1973         if (node == endNode)
1974             break;
1975         node = next;
1976     }
1977 }
1978
1979 void ApplyStyleCommand::addBlockStyleIfNeeded(CSSMutableStyleDeclarationImpl *style, NodeImpl *node)
1980 {
1981     // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
1982     // inline content.
1983     if (!node)
1984         return;
1985     
1986     HTMLElementImpl *block = static_cast<HTMLElementImpl *>(node->enclosingBlockFlowElement());
1987     if (!block)
1988         return;
1989         
1990     StyleChange styleChange(style, Position(block, 0), StyleChange::DoNotUseLegacyHTMLStyles);
1991     if (styleChange.cssStyle().length() > 0) {
1992         moveParagraphContentsToNewBlockIfNecessary(Position(node, 0));
1993         block = static_cast<HTMLElementImpl *>(node->enclosingBlockFlowElement());
1994         DOMString cssText = styleChange.cssStyle();
1995         CSSMutableStyleDeclarationImpl *decl = block->inlineStyleDecl();
1996         if (decl)
1997             cssText += decl->cssText();
1998         setNodeAttribute(block, ATTR_STYLE, cssText);
1999     }
2000 }
2001
2002 void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclarationImpl *style, NodeImpl *startNode, NodeImpl *endNode)
2003 {
2004     StyleChange styleChange(style, Position(startNode, 0));
2005     int exceptionCode = 0;
2006     
2007     if (styleChange.cssStyle().length() > 0) {
2008         ElementImpl *styleElement = createStyleSpanElement(document());
2009         styleElement->setAttribute(ATTR_STYLE, styleChange.cssStyle());
2010         styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
2011         insertNodeBefore(styleElement, startNode);
2012         surroundNodeRangeWithElement(startNode, endNode, styleElement);
2013     }
2014
2015     if (styleChange.applyBold()) {
2016         ElementImpl *boldElement = document()->createHTMLElement("B", exceptionCode);
2017         ASSERT(exceptionCode == 0);
2018         insertNodeBefore(boldElement, startNode);
2019         surroundNodeRangeWithElement(startNode, endNode, boldElement);
2020     }
2021
2022     if (styleChange.applyItalic()) {
2023         ElementImpl *italicElement = document()->createHTMLElement("I", exceptionCode);
2024         ASSERT(exceptionCode == 0);
2025         insertNodeBefore(italicElement, startNode);
2026         surroundNodeRangeWithElement(startNode, endNode, italicElement);
2027     }
2028 }
2029
2030 float ApplyStyleCommand::computedFontSize(const NodeImpl *node)
2031 {
2032     float size = 0.0f;
2033     
2034     if (!node)
2035         return size;
2036     
2037     Position pos(const_cast<NodeImpl *>(node), 0);
2038     CSSComputedStyleDeclarationImpl *computedStyle = pos.computedStyle();
2039     if (!computedStyle)
2040         return size;
2041     computedStyle->ref();
2042
2043     CSSPrimitiveValueImpl *value = static_cast<CSSPrimitiveValueImpl *>(computedStyle->getPropertyCSSValue(CSS_PROP_FONT_SIZE));
2044     if (value) {
2045         value->ref();
2046         size = value->getFloatValue(CSSPrimitiveValue::CSS_PX);
2047         value->deref();
2048     }
2049
2050     computedStyle->deref();
2051     return size;
2052 }
2053
2054 void ApplyStyleCommand::joinChildTextNodes(NodeImpl *node, const Position &start, const Position &end)
2055 {
2056     if (!node)
2057         return;
2058
2059     Position newStart = start;
2060     Position newEnd = end;
2061     
2062     NodeImpl *child = node->firstChild();
2063     while (child) {
2064         NodeImpl *next = child->nextSibling();
2065         if (child->isTextNode() && next && next->isTextNode()) {
2066             TextImpl *childText = static_cast<TextImpl *>(child);
2067             TextImpl *nextText = static_cast<TextImpl *>(next);
2068             if (next == start.node())
2069                 newStart = Position(childText, childText->length() + start.offset());
2070             if (next == end.node())
2071                 newEnd = Position(childText, childText->length() + end.offset());
2072             DOMString textToMove = nextText->data();
2073             insertTextIntoNode(childText, childText->length(), textToMove);
2074             removeNode(next);
2075             // don't move child node pointer. it may want to merge with more text nodes.
2076         }
2077         else {
2078             child = child->nextSibling();
2079         }
2080     }
2081
2082     setEndingSelection(Selection(newStart, newEnd));
2083 }
2084
2085 //------------------------------------------------------------------------------------------
2086 // DeleteFromTextNodeCommand
2087
2088 DeleteFromTextNodeCommand::DeleteFromTextNodeCommand(DocumentImpl *document, TextImpl *node, long offset, long count)
2089     : EditCommand(document), m_node(node), m_offset(offset), m_count(count)
2090 {
2091     ASSERT(m_node);
2092     ASSERT(m_offset >= 0);
2093     ASSERT(m_offset < (long)m_node->length());
2094     ASSERT(m_count >= 0);
2095     
2096     m_node->ref();
2097 }
2098
2099 DeleteFromTextNodeCommand::~DeleteFromTextNodeCommand()
2100 {
2101     ASSERT(m_node);
2102     m_node->deref();
2103 }
2104
2105 void DeleteFromTextNodeCommand::doApply()
2106 {
2107     ASSERT(m_node);
2108
2109     int exceptionCode = 0;
2110     m_text = m_node->substringData(m_offset, m_count, exceptionCode);
2111     ASSERT(exceptionCode == 0);
2112     
2113     m_node->deleteData(m_offset, m_count, exceptionCode);
2114     ASSERT(exceptionCode == 0);
2115 }
2116
2117 void DeleteFromTextNodeCommand::doUnapply()
2118 {
2119     ASSERT(m_node);
2120     ASSERT(!m_text.isEmpty());
2121
2122     int exceptionCode = 0;
2123     m_node->insertData(m_offset, m_text, exceptionCode);
2124     ASSERT(exceptionCode == 0);
2125 }
2126
2127 //------------------------------------------------------------------------------------------
2128 // DeleteSelectionCommand
2129
2130 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, bool smartDelete, bool mergeBlocksAfterDelete)
2131     : CompositeEditCommand(document), 
2132       m_hasSelectionToDelete(false), 
2133       m_smartDelete(smartDelete), 
2134       m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
2135       m_startBlock(0),
2136       m_endBlock(0),
2137       m_startNode(0),
2138       m_typingStyle(0)
2139 {
2140 }
2141
2142 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
2143     : CompositeEditCommand(document), 
2144       m_hasSelectionToDelete(true), 
2145       m_smartDelete(smartDelete), 
2146       m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
2147       m_selectionToDelete(selection),
2148       m_startBlock(0),
2149       m_endBlock(0),
2150       m_startNode(0),
2151       m_typingStyle(0)
2152 {
2153 }
2154
2155 void DeleteSelectionCommand::initializePositionData()
2156 {
2157     //
2158     // Handle setting some basic positions
2159     //
2160     Position start = m_selectionToDelete.start();
2161     Position end = m_selectionToDelete.end();
2162
2163     m_upstreamStart = start.upstream(StayInBlock);
2164     m_downstreamStart = start.downstream(StayInBlock);
2165     m_upstreamEnd = end.upstream(StayInBlock);
2166     m_downstreamEnd = end.downstream(StayInBlock);
2167
2168     //
2169     // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection
2170     //
2171     m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition();
2172     bool hasLeadingWhitespaceBeforeAdjustment = m_leadingWhitespace.isNotNull();
2173     if (m_smartDelete && hasLeadingWhitespaceBeforeAdjustment) {
2174         Position pos = VisiblePosition(start).previous().deepEquivalent();
2175         // Expand out one character upstream for smart delete and recalculate
2176         // positions based on this change.
2177         m_upstreamStart = pos.upstream(StayInBlock);
2178         m_downstreamStart = pos.downstream(StayInBlock);
2179         m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition();
2180     }
2181     m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition();
2182     // Note: trailing whitespace is only considered for smart delete if there is no leading
2183     // whitespace, as in the case where you double-click the first word of a paragraph.
2184     if (m_smartDelete && !hasLeadingWhitespaceBeforeAdjustment && m_trailingWhitespace.isNotNull()) {
2185         // Expand out one character downstream for smart delete and recalculate
2186         // positions based on this change.
2187         Position pos = VisiblePosition(end).next().deepEquivalent();
2188         m_upstreamEnd = pos.upstream(StayInBlock);
2189         m_downstreamEnd = pos.downstream(StayInBlock);
2190         m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition();
2191     }
2192     m_trailingWhitespaceValid = true;
2193     
2194     //
2195     // Handle setting start and end blocks and the start node.
2196     //
2197     m_startBlock = m_downstreamStart.node()->enclosingBlockFlowElement();
2198     m_startBlock->ref();
2199     m_endBlock = m_upstreamEnd.node()->enclosingBlockFlowElement();
2200     m_endBlock->ref();
2201     m_startNode = m_upstreamStart.node();
2202     m_startNode->ref();
2203
2204     //
2205     // Handle detecting if the line containing the selection end is itself fully selected.
2206     // This is one of the tests that determines if block merging of content needs to be done.
2207     //
2208     VisiblePosition visibleEnd(end);
2209     if (isFirstVisiblePositionInParagraph(visibleEnd) || isLastVisiblePositionInParagraph(visibleEnd)) {
2210         Position previousLineStart = previousLinePosition(visibleEnd, DOWNSTREAM, 0).deepEquivalent();
2211         if (previousLineStart.isNull() || RangeImpl::compareBoundaryPoints(previousLineStart, m_downstreamStart) >= 0)
2212             m_mergeBlocksAfterDelete = false;
2213     }
2214
2215     debugPosition("m_upstreamStart      ", m_upstreamStart);
2216     debugPosition("m_downstreamStart    ", m_downstreamStart);
2217     debugPosition("m_upstreamEnd        ", m_upstreamEnd);
2218     debugPosition("m_downstreamEnd      ", m_downstreamEnd);
2219     debugPosition("m_leadingWhitespace  ", m_leadingWhitespace);
2220     debugPosition("m_trailingWhitespace ", m_trailingWhitespace);
2221     debugNode(    "m_startBlock         ", m_startBlock);
2222     debugNode(    "m_endBlock           ", m_endBlock);    
2223     debugNode(    "m_startNode          ", m_startNode);    
2224 }
2225
2226 void DeleteSelectionCommand::insertPlaceholderForAncestorBlockContent()
2227 {
2228     // This code makes sure a line does not disappear when deleting in this case:
2229     // <p>foo</p>bar<p>baz</p>
2230     // Select "bar" and hit delete. If nothing is done, the line containing bar will disappear.
2231     // It needs to be held open by inserting a placeholder.
2232     // Also see:
2233     // <rdar://problem/3928305> selecting an entire line and typing over causes new inserted text at top of document
2234     //
2235     // The checks below detect the case where the selection contains content in an ancestor block 
2236     // surrounded by child blocks.
2237     //
2238     NodeImpl *upstreamBlock = m_upstreamStart.node()->enclosingBlockFlowElement();
2239     NodeImpl *beforeUpstreamBlock = m_upstreamStart.upstream().node()->enclosingBlockFlowElement();
2240     
2241     if (upstreamBlock != beforeUpstreamBlock && beforeUpstreamBlock->isAncestor(upstreamBlock)) {
2242         NodeImpl *downstreamBlock = m_downstreamEnd.node()->enclosingBlockFlowElement();
2243         NodeImpl *afterDownstreamBlock = m_downstreamEnd.downstream().node()->enclosingBlockFlowElement();
2244         
2245         if (afterDownstreamBlock != downstreamBlock && afterDownstreamBlock != upstreamBlock) {
2246             NodeImpl *block = createDefaultParagraphElement(document());
2247             insertNodeBefore(block, m_upstreamStart.node());
2248             insertBlockPlaceholderIfNeeded(block);
2249             m_endingPosition = Position(block, 0);
2250         }
2251     }
2252 }
2253
2254 void DeleteSelectionCommand::saveTypingStyleState()
2255 {
2256     // Figure out the typing style in effect before the delete is done.
2257     // FIXME: Improve typing style.
2258     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
2259     CSSComputedStyleDeclarationImpl *computedStyle = m_selectionToDelete.start().computedStyle();
2260     computedStyle->ref();
2261     m_typingStyle = computedStyle->copyInheritableProperties();
2262     m_typingStyle->ref();
2263     computedStyle->deref();
2264 }
2265
2266 bool DeleteSelectionCommand::handleSpecialCaseAllContentDelete()
2267 {
2268     Position start = m_downstreamStart;
2269     Position end = m_upstreamEnd;
2270
2271     ElementImpl *rootElement = start.node()->rootEditableElement();
2272     Position rootStart = Position(rootElement, 0);
2273     Position rootEnd = Position(rootElement, rootElement ? rootElement->childNodeCount() : 0).equivalentDeepPosition();
2274     if (start == VisiblePosition(rootStart).downstreamDeepEquivalent() && end == VisiblePosition(rootEnd).deepEquivalent()) {
2275         // Delete every child of the root editable element
2276         NodeImpl *node = rootElement->firstChild();
2277         while (node) {
2278             NodeImpl *next = node->traverseNextSibling();
2279             removeNode(node);
2280             node = next;
2281         }
2282         return true;
2283     }
2284     return false;
2285 }
2286
2287 bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
2288 {
2289     // Check for special-case where the selection contains only a BR on a line by itself after another BR.
2290     bool upstreamStartIsBR = m_startNode->id() == ID_BR;
2291     bool downstreamStartIsBR = m_downstreamStart.node()->id() == ID_BR;
2292     bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.node() == m_upstreamEnd.node();
2293     if (isBROnLineByItself) {
2294         removeNode(m_downstreamStart.node());
2295         m_endingPosition = m_upstreamStart;
2296         m_mergeBlocksAfterDelete = false;
2297         return true;
2298     }
2299
2300     // Check for special-case where the selection contains only a BR right after a block ended.
2301     bool downstreamEndIsBR = m_downstreamEnd.node()->id() == ID_BR;
2302     Position upstreamFromBR = m_downstreamEnd.upstream();
2303     Position downstreamFromStart = m_downstreamStart.downstream();
2304     bool startIsBRAfterBlock = downstreamEndIsBR && downstreamFromStart.node() == m_downstreamEnd.node() &&
2305         m_downstreamEnd.node()->enclosingBlockFlowElement() != upstreamFromBR.node()->enclosingBlockFlowElement();
2306     if (startIsBRAfterBlock) {
2307         removeNode(m_downstreamEnd.node());
2308         m_endingPosition = upstreamFromBR;
2309         m_mergeBlocksAfterDelete = false;
2310         return true;
2311     }
2312
2313     // Not a special-case delete per se, but we can detect that the merging of content between blocks
2314     // should not be done.
2315     if (upstreamStartIsBR && downstreamStartIsBR)
2316         m_mergeBlocksAfterDelete = false;
2317
2318     return false;
2319 }
2320
2321 void DeleteSelectionCommand::handleGeneralDelete()
2322 {
2323     int startOffset = m_upstreamStart.offset();
2324
2325     if (startOffset == 0 && m_startNode->isBlockFlow() && m_startBlock != m_endBlock && !m_endBlock->isAncestor(m_startBlock)) {
2326         // The block containing the start of the selection is completely selected. 
2327         // See if it can be deleted in one step right here.
2328         ASSERT(!m_downstreamEnd.node()->isAncestor(m_startNode));
2329
2330         // The next few lines help us deal with a bit of a quirk.
2331         //     1. Open a new Blot or Mail document
2332         //     2. hit Return ten times or so
2333         //     3. Type a letter (do not hit Return after it)
2334         //     4. Type shift-up-arrow to select the line containing the letter and the previous blank line
2335         //     5. Hit Delete
2336         // You expect the insertion point to wind up at the start of the line where your selection began.
2337         // Because of the nature of HTML, the editing code needs to perform a special check to get
2338         // this behavior. So:
2339         // If the entire start block is selected, and
2340         //     a) the selection does not extend to the end of the document, then delete the start block, otherwise
2341         //     b) the selection extends to the end of the document, then do not delete the start block.
2342         //
2343         NodeImpl *old = m_startNode;
2344         VisiblePosition visibleEnd = VisiblePosition(m_downstreamEnd);
2345         if (isEndOfDocument(visibleEnd) && !isFirstVisiblePositionOnLine(visibleEnd)) {
2346             m_startNode = m_startBlock->firstChild();
2347         }
2348         else {
2349             // shift the start node to the start of the next block.
2350             m_startNode = m_startBlock->traverseNextSibling();
2351             removeFullySelectedNode(m_startBlock);
2352         }
2353
2354         if (m_startNode)
2355             m_startNode->ref();
2356         old->deref();
2357         startOffset = 0;
2358     }
2359     else if (startOffset >= m_startNode->caretMaxOffset()) {
2360         // Move the start node to the next node in the tree since the startOffset is equal to
2361         // or beyond the start node's caretMaxOffset This means there is nothing visible to delete. 
2362         // However, before moving on, delete any insignificant text that may be present in a text node.
2363         if (m_startNode->isTextNode()) {
2364             // Delete any insignificant text from this node.
2365             TextImpl *text = static_cast<TextImpl *>(m_startNode);
2366             if (text->length() > (unsigned)m_startNode->caretMaxOffset())
2367                 deleteTextFromNode(text, m_startNode->caretMaxOffset(), text->length() - m_startNode->caretMaxOffset());
2368         }
2369         
2370         // shift the start node to the next
2371         NodeImpl *old = m_startNode;
2372         m_startNode = old->traverseNextNode();
2373         m_startNode->ref();
2374         old->deref();
2375         startOffset = 0;
2376     }
2377
2378     if (m_startNode == m_downstreamEnd.node()) {
2379         // The selection to delete is all in one node.
2380         if (!m_startNode->renderer() || 
2381             (startOffset <= m_startNode->caretMinOffset() && m_downstreamEnd.offset() >= m_startNode->caretMaxOffset())) {
2382             // just delete
2383             removeFullySelectedNode(m_startNode);
2384         }
2385         else if (m_downstreamEnd.offset() - startOffset > 0) {
2386             // in a text node that needs to be trimmed
2387             TextImpl *text = static_cast<TextImpl *>(m_startNode);
2388             deleteTextFromNode(text, startOffset, m_downstreamEnd.offset() - startOffset);
2389             m_trailingWhitespaceValid = false;
2390         }
2391     }
2392     else {
2393         // The selection to delete spans more than one node.
2394         NodeImpl *node = m_startNode;
2395         
2396         if (startOffset > 0) {
2397             // in a text node that needs to be trimmed
2398             TextImpl *text = static_cast<TextImpl *>(node);
2399             deleteTextFromNode(text, startOffset, text->length() - startOffset);
2400             node = node->traverseNextNode();
2401         }
2402         
2403         // handle deleting all nodes that are completely selected
2404         while (node && node != m_downstreamEnd.node()) {
2405             if (!m_downstreamEnd.node()->isAncestor(node)) {
2406                 NodeImpl *nextNode = node->traverseNextSibling();
2407                 removeFullySelectedNode(node);
2408                 node = nextNode;
2409             }
2410             else {
2411                 NodeImpl *n = node->lastChild();
2412                 while (n && n->lastChild())
2413                     n = n->lastChild();
2414                 if (n == m_downstreamEnd.node() && m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMaxOffset()) {
2415                     // remove an ancestor of m_downstreamEnd.node(), and thus m_downstreamEnd.node() itself
2416                     removeFullySelectedNode(node);
2417                     m_trailingWhitespaceValid = false;
2418                     node = 0;
2419                 } 
2420                 else {
2421                     node = node->traverseNextNode();
2422                 }
2423             }
2424         }
2425
2426         if (m_downstreamEnd.node() != m_startNode && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMinOffset()) {
2427             if (m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMaxOffset()) {
2428                 // need to delete whole node
2429                 // we can get here if this is the last node in the block
2430                 removeFullySelectedNode(m_downstreamEnd.node());
2431                 m_trailingWhitespaceValid = false;
2432             }
2433             else {
2434                 // in a text node that needs to be trimmed
2435                 TextImpl *text = static_cast<TextImpl *>(m_downstreamEnd.node());
2436                 if (m_downstreamEnd.offset() > 0) {
2437                     deleteTextFromNode(text, 0, m_downstreamEnd.offset());
2438                     m_downstreamEnd = Position(text, 0);
2439                     m_trailingWhitespaceValid = false;
2440                 }
2441             }
2442         }
2443     }
2444 }
2445
2446 void DeleteSelectionCommand::fixupWhitespace()
2447 {
2448     document()->updateLayout();
2449     if (m_leadingWhitespace.isNotNull() && (m_trailingWhitespace.isNotNull() || !m_leadingWhitespace.isRenderedCharacter())) {
2450         LOG(Editing, "replace leading");
2451         TextImpl *textNode = static_cast<TextImpl *>(m_leadingWhitespace.node());
2452         replaceTextInNode(textNode, m_leadingWhitespace.offset(), 1, nonBreakingSpaceString());
2453     }
2454     else if (m_trailingWhitespace.isNotNull()) {
2455         if (m_trailingWhitespaceValid) {
2456             if (!m_trailingWhitespace.isRenderedCharacter()) {
2457                 LOG(Editing, "replace trailing [valid]");
2458                 TextImpl *textNode = static_cast<TextImpl *>(m_trailingWhitespace.node());
2459                 replaceTextInNode(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString());
2460             }
2461         }
2462         else {
2463             Position pos = m_endingPosition.downstream(StayInBlock);
2464             pos = Position(pos.node(), pos.offset() - 1);
2465             if (isWS(pos) && !pos.isRenderedCharacter()) {
2466                 LOG(Editing, "replace trailing [invalid]");
2467                 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
2468                 replaceTextInNode(textNode, pos.offset(), 1, nonBreakingSpaceString());
2469                 // need to adjust ending position since the trailing position is not valid.
2470                 m_endingPosition = pos;
2471             }
2472         }
2473     }
2474 }
2475
2476 // This function moves nodes in the block containing startNode to dstBlock, starting
2477 // from startNode and proceeding to the end of the paragraph. Nodes in the block containing
2478 // startNode that appear in document order before startNode are not moved.
2479 // This function is an important helper for deleting selections that cross paragraph
2480 // boundaries.
2481 void DeleteSelectionCommand::moveNodesAfterNode()
2482 {
2483     if (!m_mergeBlocksAfterDelete)
2484         return;
2485
2486     if (m_endBlock == m_startBlock)
2487         return;
2488
2489     NodeImpl *startNode = m_downstreamEnd.node();
2490     NodeImpl *dstNode = m_upstreamStart.node();
2491
2492     if (!startNode->inDocument() || !dstNode->inDocument())
2493         return;
2494
2495     NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
2496     if (isTableStructureNode(startBlock))
2497         // Do not move content between parts of a table
2498         return;
2499
2500     // Now that we are about to add content, check to see if a placeholder element
2501     // can be removed.
2502     removeBlockPlaceholderIfNeeded(startBlock);
2503
2504     // Move the subtree containing node
2505     NodeImpl *node = startNode->enclosingInlineElement();
2506
2507     // Insert after the subtree containing destNode
2508     NodeImpl *refNode = dstNode->enclosingInlineElement();
2509
2510     // Nothing to do if start is already at the beginning of dstBlock
2511     NodeImpl *dstBlock = refNode->enclosingBlockFlowElement();
2512     if (startBlock == dstBlock->firstChild())
2513         return;
2514
2515     // Do the move.
2516     NodeImpl *rootNode = refNode->rootEditableElement();
2517     while (node && node->isAncestor(startBlock)) {
2518         NodeImpl *moveNode = node;
2519         node = node->nextSibling();
2520         removeNode(moveNode);
2521         if (moveNode->id() == ID_BR && !moveNode->renderer()) {
2522             // Just remove this node, and don't put it back.
2523             // If the BR was not rendered (since it was at the end of a block, for instance), 
2524             // putting it back in the document might make it appear, and that is not desirable.
2525             break;
2526         }
2527         if (refNode == rootNode)
2528             insertNodeAt(moveNode, refNode, 0);
2529         else
2530             insertNodeAfter(moveNode, refNode);
2531         refNode = moveNode;
2532         if (moveNode->id() == ID_BR)
2533             break;
2534     }
2535
2536     // If the startBlock no longer has any kids, we may need to deal with adding a BR
2537     // to make the layout come out right. Consider this document:
2538     //
2539     // One
2540     // <div>Two</div>
2541     // Three
2542     // 
2543     // Placing the insertion before before the 'T' of 'Two' and hitting delete will
2544     // move the contents of the div to the block containing 'One' and delete the div.
2545     // This will have the side effect of moving 'Three' on to the same line as 'One'
2546     // and 'Two'. This is undesirable. We fix this up by adding a BR before the 'Three'.
2547     // This may not be ideal, but it is better than nothing.
2548     document()->updateLayout();
2549     if (!startBlock->renderer() || !startBlock->renderer()->firstChild()) {
2550         removeNode(startBlock);
2551         document()->updateLayout();
2552         if (refNode->renderer() && refNode->renderer()->inlineBox() && refNode->renderer()->inlineBox()->nextOnLineExists()) {
2553             insertNodeAfter(createBreakElement(document()), refNode);
2554         }
2555     }
2556 }
2557
2558 void DeleteSelectionCommand::calculateEndingPosition()
2559 {
2560     if (m_endingPosition.isNotNull() && m_endingPosition.node()->inDocument())
2561         return;
2562
2563     m_endingPosition = m_upstreamStart;
2564     if (m_endingPosition.node()->inDocument())
2565         return;
2566     
2567     m_endingPosition = m_downstreamEnd;
2568     if (m_endingPosition.node()->inDocument())
2569         return;
2570
2571     m_endingPosition = Position(m_startBlock, 0);
2572     if (m_endingPosition.node()->inDocument())
2573         return;
2574
2575     m_endingPosition = Position(m_endBlock, 0);
2576     if (m_endingPosition.node()->inDocument())
2577         return;
2578
2579     m_endingPosition = Position(document()->documentElement(), 0);
2580 }
2581
2582 void DeleteSelectionCommand::calculateTypingStyleAfterDelete(bool insertedPlaceholder)
2583 {
2584     // Compute the difference between the style before the delete and the style now
2585     // after the delete has been done. Set this style on the part, so other editing
2586     // commands being composed with this one will work, and also cache it on the command,
2587     // so the KHTMLPart::appliedEditing can set it after the whole composite command 
2588     // has completed.
2589     // FIXME: Improve typing style.
2590     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
2591     CSSComputedStyleDeclarationImpl endingStyle(m_endingPosition.node());
2592     endingStyle.diff(m_typingStyle);
2593     if (!m_typingStyle->length()) {
2594         m_typingStyle->deref();
2595         m_typingStyle = 0;
2596     }
2597     if (insertedPlaceholder && m_typingStyle) {
2598         // Apply style to the placeholder. This makes sure that the single line in the
2599         // paragraph has the right height, and that the paragraph takes on the style
2600         // of the preceding line and retains it even if you click away, click back, and
2601         // then start typing. In this case, the typing style is applied right now, and
2602         // is not retained until the next typing action.
2603         Position pastPlaceholder = endOfParagraph(VisiblePosition(m_endingPosition)).deepEquivalent();
2604         setEndingSelection(Selection(m_endingPosition, pastPlaceholder));
2605         applyStyle(m_typingStyle, EditActionUnspecified);
2606         m_typingStyle->deref();
2607         m_typingStyle = 0;
2608     }
2609     // Set m_typingStyle as the typing style.
2610     // It's perfectly OK for m_typingStyle to be null.
2611     document()->part()->setTypingStyle(m_typingStyle);
2612     setTypingStyle(m_typingStyle);
2613 }
2614
2615 void DeleteSelectionCommand::clearTransientState()
2616 {
2617     m_selectionToDelete.clear();
2618     m_upstreamStart.clear();
2619     m_downstreamStart.clear();
2620     m_upstreamEnd.clear();
2621     m_downstreamEnd.clear();
2622     m_endingPosition.clear();
2623     m_leadingWhitespace.clear();
2624     m_trailingWhitespace.clear();
2625
2626     if (m_startBlock) {
2627         m_startBlock->deref();
2628         m_startBlock = 0;
2629     }
2630     if (m_endBlock) {
2631         m_endBlock->deref();
2632         m_endBlock = 0;
2633     }
2634     if (m_startNode) {
2635         m_startNode->deref();
2636         m_startNode = 0;
2637     }
2638     if (m_typingStyle) {
2639         m_typingStyle->deref();
2640         m_typingStyle = 0;
2641     }
2642 }
2643
2644 void DeleteSelectionCommand::doApply()
2645 {
2646     // If selection has not been set to a custom selection when the command was created,
2647     // use the current ending selection.
2648     if (!m_hasSelectionToDelete)
2649         m_selectionToDelete = endingSelection();
2650         
2651     if (!m_selectionToDelete.isRange())
2652         return;
2653
2654     initializePositionData();
2655
2656     if (!m_startBlock || !m_endBlock) {
2657         // Can't figure out what blocks we're in. This can happen if
2658         // the document structure is not what we are expecting, like if
2659         // the document has no body element, or if the editable block
2660         // has been changed to display: inline. Some day it might
2661         // be nice to be able to deal with this, but for now, bail.
2662         clearTransientState();
2663         return;
2664     }
2665
2666     // Delete any text that may hinder our ability to fixup whitespace after the detele
2667     deleteInsignificantTextDownstream(m_trailingWhitespace);    
2668
2669     saveTypingStyleState();
2670     insertPlaceholderForAncestorBlockContent();
2671     
2672     if (!handleSpecialCaseAllContentDelete())
2673         if (!handleSpecialCaseBRDelete())
2674             handleGeneralDelete();
2675     
2676     // Do block merge if start and end of selection are in different blocks.
2677     moveNodesAfterNode();
2678     
2679     calculateEndingPosition();
2680     fixupWhitespace();
2681
2682     // If the delete emptied a block, add in a placeholder so the block does not
2683     // seem to disappear.
2684     bool insertedPlaceholder = insertBlockPlaceholderIfNeeded(m_endingPosition.node());
2685     calculateTypingStyleAfterDelete(insertedPlaceholder);
2686     debugPosition("endingPosition   ", m_endingPosition);
2687     setEndingSelection(m_endingPosition);
2688     clearTransientState();
2689     rebalanceWhitespace();
2690 }
2691
2692 EditAction DeleteSelectionCommand::editingAction() const
2693 {
2694     // Note that DeleteSelectionCommand is also used when the user presses the Delete key,
2695     // but in that case there's a TypingCommand that supplies the editingAction(), so
2696     // the Undo menu correctly shows "Undo Typing"
2697     return EditActionCut;
2698 }
2699
2700 bool DeleteSelectionCommand::preservesTypingStyle() const
2701 {
2702     return true;
2703 }
2704
2705 //------------------------------------------------------------------------------------------
2706 // InsertIntoTextNode
2707
2708 InsertIntoTextNode::InsertIntoTextNode(DocumentImpl *document, TextImpl *node, long offset, const DOMString &text)
2709     : EditCommand(document), m_node(node), m_offset(offset)
2710 {
2711     ASSERT(m_node);
2712     ASSERT(m_offset >= 0);
2713     ASSERT(!text.isEmpty());
2714     
2715     m_node->ref();
2716     m_text = text.copy(); // make a copy to ensure that the string never changes
2717 }
2718
2719 InsertIntoTextNode::~InsertIntoTextNode()
2720 {
2721     if (m_node)
2722         m_node->deref();
2723 }
2724
2725 void InsertIntoTextNode::doApply()
2726 {
2727     ASSERT(m_node);
2728     ASSERT(m_offset >= 0);
2729     ASSERT(!m_text.isEmpty());
2730
2731     int exceptionCode = 0;
2732     m_node->insertData(m_offset, m_text, exceptionCode);
2733     ASSERT(exceptionCode == 0);
2734 }
2735
2736 void InsertIntoTextNode::doUnapply()
2737 {
2738     ASSERT(m_node);
2739     ASSERT(m_offset >= 0);
2740     ASSERT(!m_text.isEmpty());
2741
2742     int exceptionCode = 0;
2743     m_node->deleteData(m_offset, m_text.length(), exceptionCode);
2744     ASSERT(exceptionCode == 0);
2745 }
2746
2747 //------------------------------------------------------------------------------------------
2748 // InsertLineBreakCommand
2749
2750 InsertLineBreakCommand::InsertLineBreakCommand(DocumentImpl *document) 
2751     : CompositeEditCommand(document)
2752 {
2753 }
2754
2755 bool InsertLineBreakCommand::preservesTypingStyle() const
2756 {
2757     return true;
2758 }
2759
2760 void InsertLineBreakCommand::insertNodeAfterPosition(NodeImpl *node, const Position &pos)
2761 {
2762     // Insert the BR after the caret position. In the case the
2763     // position is a block, do an append. We don't want to insert
2764     // the BR *after* the block.
2765     Position upstream(pos.upstream(StayInBlock));
2766     NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
2767     if (cb == pos.node())
2768         appendNode(node, cb);
2769     else
2770         insertNodeAfter(node, pos.node());
2771 }
2772
2773 void InsertLineBreakCommand::insertNodeBeforePosition(NodeImpl *node, const Position &pos)
2774 {
2775     // Insert the BR after the caret position. In the case the
2776     // position is a block, do an append. We don't want to insert
2777     // the BR *before* the block.
2778     Position upstream(pos.upstream(StayInBlock));
2779     NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
2780     if (cb == pos.node())
2781         appendNode(node, cb);
2782     else
2783         insertNodeBefore(node, pos.node());
2784 }
2785
2786 void InsertLineBreakCommand::doApply()
2787 {
2788     deleteSelection();
2789     Selection selection = endingSelection();
2790
2791     ElementImpl *breakNode = createBreakElement(document());
2792     NodeImpl *nodeToInsert = breakNode;
2793     
2794     Position pos(selection.start().upstream(StayInBlock));
2795     bool atStart = pos.offset() <= pos.node()->caretMinOffset();
2796     bool atEnd = pos.offset() >= pos.node()->caretMaxOffset();
2797     bool atEndOfBlock = isLastVisiblePositionInBlock(VisiblePosition(pos));
2798     
2799     if (atEndOfBlock) {
2800         LOG(Editing, "input newline case 1");
2801         // Check for a trailing BR. If there isn't one, we'll need to insert an "extra" one.
2802         // This makes the "real" BR we want to insert appear in the rendering without any 
2803         // significant side effects (and no real worries either since you can't arrow past 
2804         // this extra one.
2805         if (pos.node()->id() == ID_BR && pos.offset() == 0) {
2806             // Already placed in a trailing BR. Insert "real" BR before it and leave the selection alone.
2807             insertNodeBefore(nodeToInsert, pos.node());
2808         }
2809         else {
2810             NodeImpl *next = pos.node()->traverseNextNode();
2811             bool hasTrailingBR = next && next->id() == ID_BR && pos.node()->enclosingBlockFlowElement() == next->enclosingBlockFlowElement();
2812             insertNodeAfterPosition(nodeToInsert, pos);
2813             if (hasTrailingBR) {
2814                 setEndingSelection(Position(next, 0));
2815             }
2816             else if (!document()->inStrictMode()) {
2817                 // Insert an "extra" BR at the end of the block. 
2818                 ElementImpl *extraBreakNode = createBreakElement(document());
2819                 insertNodeAfter(extraBreakNode, nodeToInsert);
2820                 setEndingSelection(Position(extraBreakNode, 0));
2821             }
2822         }
2823     }
2824     else if (atStart) {
2825         LOG(Editing, "input newline case 2");
2826         // Insert node before downstream position, and place caret there as well. 
2827         Position endingPosition = pos.downstream(StayInBlock);
2828         insertNodeBeforePosition(nodeToInsert, endingPosition);
2829         setEndingSelection(endingPosition);
2830     }
2831     else if (atEnd) {
2832         LOG(Editing, "input newline case 3");
2833         // Insert BR after this node. Place caret in the position that is downstream
2834         // of the current position, reckoned before inserting the BR in between.
2835         Position endingPosition = pos.downstream(StayInBlock);
2836         insertNodeAfterPosition(nodeToInsert, pos);
2837         setEndingSelection(endingPosition);
2838     }
2839     else {
2840         // Split a text node
2841         LOG(Editing, "input newline case 4");
2842         ASSERT(pos.node()->isTextNode());
2843         
2844         // Do the split
2845         int exceptionCode = 0;
2846         TextImpl *textNode = static_cast<TextImpl *>(pos.node());
2847         TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
2848         deleteTextFromNode(textNode, 0, pos.offset());
2849         insertNodeBefore(textBeforeNode, textNode);
2850         insertNodeBefore(nodeToInsert, textNode);
2851         Position endingPosition = Position(textNode, 0);
2852         
2853         // Handle whitespace that occurs after the split
2854         document()->updateLayout();
2855         if (!endingPosition.isRenderedCharacter()) {
2856             // Clear out all whitespace and insert one non-breaking space
2857             deleteInsignificantTextDownstream(endingPosition);
2858             insertTextIntoNode(textNode, 0, nonBreakingSpaceString());
2859         }
2860         
2861         setEndingSelection(endingPosition);
2862     }
2863
2864     // Handle the case where there is a typing style.
2865     // FIXME: Improve typing style.
2866     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
2867     
2868     CSSMutableStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
2869     
2870     if (typingStyle && typingStyle->length() > 0) {
2871         Selection selectionBeforeStyle = endingSelection();
2872
2873         DOM::RangeImpl *rangeAroundNode = document()->createRange();
2874         int exception;
2875         rangeAroundNode->selectNode(nodeToInsert, exception);
2876
2877         setEndingSelection(Selection(rangeAroundNode));
2878         applyStyle(typingStyle);
2879
2880         setEndingSelection(selectionBeforeStyle);
2881     }
2882
2883     rebalanceWhitespace();
2884 }
2885
2886 //------------------------------------------------------------------------------------------
2887 // InsertNodeBeforeCommand
2888
2889 InsertNodeBeforeCommand::InsertNodeBeforeCommand(DocumentImpl *document, NodeImpl *insertChild, NodeImpl *refChild)
2890     : EditCommand(document), m_insertChild(insertChild), m_refChild(refChild)
2891 {
2892     ASSERT(m_insertChild);
2893     m_insertChild->ref();
2894
2895     ASSERT(m_refChild);
2896     m_refChild->ref();
2897 }
2898
2899 InsertNodeBeforeCommand::~InsertNodeBeforeCommand()
2900 {
2901     ASSERT(m_insertChild);
2902     m_insertChild->deref();
2903
2904     ASSERT(m_refChild);
2905     m_refChild->deref();
2906 }
2907
2908 void InsertNodeBeforeCommand::doApply()
2909 {
2910     ASSERT(m_insertChild);
2911     ASSERT(m_refChild);
2912     ASSERT(m_refChild->parentNode());
2913
2914     int exceptionCode = 0;
2915     m_refChild->parentNode()->insertBefore(m_insertChild, m_refChild, exceptionCode);
2916     ASSERT(exceptionCode == 0);
2917 }
2918
2919 void InsertNodeBeforeCommand::doUnapply()
2920 {
2921     ASSERT(m_insertChild);
2922     ASSERT(m_refChild);
2923     ASSERT(m_refChild->parentNode());
2924
2925     int exceptionCode = 0;
2926     m_refChild->parentNode()->removeChild(m_insertChild, exceptionCode);
2927     ASSERT(exceptionCode == 0);
2928 }
2929
2930 //------------------------------------------------------------------------------------------
2931 // InsertParagraphSeparatorCommand
2932
2933 InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(DocumentImpl *document) 
2934     : CompositeEditCommand(document), m_style(0)
2935 {
2936 }
2937
2938 InsertParagraphSeparatorCommand::~InsertParagraphSeparatorCommand() 
2939 {
2940     derefNodesInList(clonedNodes);
2941     if (m_style)
2942         m_style->deref();
2943 }
2944
2945 bool InsertParagraphSeparatorCommand::preservesTypingStyle() const
2946 {
2947     return true;
2948 }
2949
2950 ElementImpl *InsertParagraphSeparatorCommand::createParagraphElement()
2951 {
2952     ElementImpl *element = createDefaultParagraphElement(document());
2953     element->ref();
2954     clonedNodes.append(element);
2955     return element;
2956 }
2957
2958 void InsertParagraphSeparatorCommand::calculateStyleBeforeInsertion(const Position &pos)
2959 {
2960     // FIXME: Improve typing style.
2961     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
2962     CSSComputedStyleDeclarationImpl *computedStyle = pos.computedStyle();
2963     computedStyle->ref();
2964     if (m_style)
2965         m_style->deref();
2966     m_style = computedStyle->copyInheritableProperties();
2967     m_style->ref();
2968     computedStyle->deref();
2969     
2970     CSSMutableStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
2971     if (typingStyle)
2972         m_style->merge(typingStyle);
2973 }
2974
2975 void InsertParagraphSeparatorCommand::applyStyleAfterInsertion()
2976 {
2977     // FIXME: Improve typing style.
2978     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
2979     if (!m_style)
2980         return;
2981
2982     CSSComputedStyleDeclarationImpl endingStyle(endingSelection().start().node());
2983     endingStyle.diff(m_style);
2984     if (m_style->length() > 0) {
2985         applyStyle(m_style);
2986     }
2987 }
2988
2989 void InsertParagraphSeparatorCommand::doApply()
2990 {
2991     bool splitText = false;
2992     Selection selection = endingSelection();
2993     if (selection.isNone())
2994         return;
2995     
2996     // Delete the current selection.
2997     // If the selection is a range and the start and end nodes are in different blocks, 
2998     // then this command bails after the delete, but takes the one additional step of
2999     // moving the selection downstream so it is in the ending block (if that block is
3000     // still around, that is).
3001     Position pos = selection.start();
3002         
3003     if (selection.isRange()) {
3004         NodeImpl *startBlockBeforeDelete = selection.start().node()->enclosingBlockFlowElement();
3005         NodeImpl *endBlockBeforeDelete = selection.end().node()->enclosingBlockFlowElement();
3006         bool doneAfterDelete = startBlockBeforeDelete != endBlockBeforeDelete;
3007         calculateStyleBeforeInsertion(pos);
3008         deleteSelection(false, false);
3009         if (doneAfterDelete) {
3010             document()->updateLayout();
3011             setEndingSelection(endingSelection().start().downstream());
3012             rebalanceWhitespace();
3013             applyStyleAfterInsertion();
3014             return;
3015         }
3016         pos = endingSelection().start();
3017     }
3018
3019     calculateStyleBeforeInsertion(pos);
3020
3021     // Find the start block.
3022     NodeImpl *startNode = pos.node();
3023     NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
3024     if (!startBlock || !startBlock->parentNode())
3025         return;
3026
3027     VisiblePosition visiblePos(pos);
3028     bool isFirstInBlock = isFirstVisiblePositionInBlock(visiblePos);
3029     bool isLastInBlock = isLastVisiblePositionInBlock(visiblePos);
3030     bool startBlockIsRoot = startBlock == startBlock->rootEditableElement();
3031
3032     // This is the block that is going to be inserted.
3033     NodeImpl *blockToInsert = startBlockIsRoot ? createParagraphElement() : startBlock->cloneNode(false);
3034
3035     //---------------------------------------------------------------------
3036     // Handle empty block case.
3037     if (isFirstInBlock && isLastInBlock) {
3038         LOG(Editing, "insert paragraph separator: empty block case");
3039         if (startBlockIsRoot) {
3040             NodeImpl *extraBlock = createParagraphElement();
3041             appendNode(extraBlock, startBlock);
3042             insertBlockPlaceholder(extraBlock);
3043             appendNode(blockToInsert, startBlock);
3044         }
3045         else {
3046             insertNodeAfter(blockToInsert, startBlock);
3047         }
3048         insertBlockPlaceholder(blockToInsert);
3049         setEndingSelection(Position(blockToInsert, 0));
3050         applyStyleAfterInsertion();
3051         return;
3052     }
3053
3054     //---------------------------------------------------------------------
3055     // Handle case when position is in the first visible position in its block.
3056     // and similar case where upstream position is in another block.
3057     bool upstreamInDifferentBlock = startBlock != pos.upstream(DoNotStayInBlock).node()->enclosingBlockFlowElement();
3058     if (upstreamInDifferentBlock || isFirstInBlock) {
3059         LOG(Editing, "insert paragraph separator: first in block case");
3060         pos = pos.downstream(StayInBlock);
3061         NodeImpl *refNode = isFirstInBlock && !startBlockIsRoot ? startBlock : pos.node();
3062         insertNodeBefore(blockToInsert, refNode);
3063         insertBlockPlaceholder(blockToInsert);
3064         setEndingSelection(Position(blockToInsert, 0));
3065         applyStyleAfterInsertion();
3066         setEndingSelection(pos);
3067         return;
3068     }
3069
3070     //---------------------------------------------------------------------
3071     // Handle case when position is in the last visible position in its block, 
3072     // and similar case where downstream position is in another block.
3073     bool downstreamInDifferentBlock = startBlock != pos.downstream(DoNotStayInBlock).node()->enclosingBlockFlowElement();
3074     if (downstreamInDifferentBlock || isLastInBlock) {
3075         LOG(Editing, "insert paragraph separator: last in block case");
3076         NodeImpl *refNode = isLastInBlock && !startBlockIsRoot ? startBlock : pos.node();
3077         insertNodeAfter(blockToInsert, refNode);
3078         insertBlockPlaceholder(blockToInsert);
3079         setEndingSelection(Position(blockToInsert, 0));
3080         applyStyleAfterInsertion();
3081         return;
3082     }
3083
3084     //---------------------------------------------------------------------
3085     // Handle the (more complicated) general case,
3086
3087     LOG(Editing, "insert paragraph separator: general case");
3088
3089     // Check if pos.node() is a <br>. If it is, and the document is in quirks mode, 
3090     // then this <br> will collapse away when we add a block after it. Add an extra <br>.
3091     if (!document()->inStrictMode()) {
3092         Position upstreamPos = pos.upstream(StayInBlock);
3093         if (upstreamPos.node()->id() == ID_BR)
3094             insertNodeAfter(createBreakElement(document()), upstreamPos.node());
3095     }
3096     
3097     // Move downstream. Typing style code will take care of carrying along the 
3098     // style of the upstream position.
3099     pos = pos.downstream(StayInBlock);
3100     startNode = pos.node();
3101
3102     // Build up list of ancestors in between the start node and the start block.
3103     if (startNode != startBlock) {
3104         for (NodeImpl *n = startNode->parentNode(); n && n != startBlock; n = n->parentNode())
3105             ancestors.prepend(n);
3106     }
3107
3108     // Make sure we do not cause a rendered space to become unrendered.
3109     Position leadingWhitespace = pos.leadingWhitespacePosition();
3110     if (leadingWhitespace.isNotNull()) {
3111         TextImpl *textNode = static_cast<TextImpl *>(leadingWhitespace.node());
3112         replaceTextInNode(textNode, leadingWhitespace.offset(), 1, nonBreakingSpaceString());
3113     }
3114     
3115     // Split at pos if in the middle of a text node.
3116     if (startNode->isTextNode()) {
3117         TextImpl *textNode = static_cast<TextImpl *>(startNode);
3118         bool atEnd = (unsigned long)pos.offset() >= textNode->length();
3119         if (pos.offset() > 0 && !atEnd) {
3120             splitTextNode(textNode, pos.offset());
3121             pos = Position(startNode, 0);
3122             splitText = true;
3123         }
3124     }
3125
3126     // Put the added block in the tree.
3127     if (startBlockIsRoot) {
3128         NodeImpl *lastSibling = pos.node();
3129         while (lastSibling->nextSibling())
3130             lastSibling = lastSibling->nextSibling();
3131         insertNodeAfter(blockToInsert, lastSibling);
3132     }
3133     else {
3134         insertNodeAfter(blockToInsert, startBlock);
3135     }
3136
3137     // Make clones of ancestors in between the start node and the start block.
3138     NodeImpl *parent = blockToInsert;
3139     for (QPtrListIterator<NodeImpl> it(ancestors); it.current(); ++it) {
3140         NodeImpl *child = it.current()->cloneNode(false); // shallow clone
3141         child->ref();
3142         clonedNodes.append(child);
3143         appendNode(child, parent);
3144         parent = child;
3145     }
3146
3147     // Move the start node and the siblings of the start node.
3148     if (startNode != startBlock) {
3149         NodeImpl *n = startNode;
3150         while (n && n != blockToInsert) {
3151             NodeImpl *next = n->nextSibling();
3152             removeNode(n);
3153             appendNode(n, parent);
3154             n = next;
3155         }
3156     }            
3157
3158     // Move everything after the start node.
3159     NodeImpl *leftParent = ancestors.last();
3160     while (leftParent && leftParent != startBlock) {
3161         parent = parent->parentNode();
3162         NodeImpl *n = leftParent->nextSibling();
3163         while (n) {
3164             NodeImpl *next = n->nextSibling();
3165             removeNode(n);
3166             appendNode(n, parent);
3167             n = next;
3168         }
3169         leftParent = leftParent->parentNode();
3170     }
3171
3172     // Handle whitespace that occurs after the split
3173     if (splitText) {
3174         document()->updateLayout();
3175         pos = Position(startNode, 0);
3176         if (!pos.isRenderedCharacter()) {
3177             // Clear out all whitespace and insert one non-breaking space
3178             ASSERT(startNode && startNode->isTextNode());
3179             deleteInsignificantTextDownstream(pos);
3180             insertTextIntoNode(static_cast<TextImpl *>(startNode), 0, nonBreakingSpaceString());
3181         }
3182     }
3183
3184     setEndingSelection(Position(blockToInsert, 0));
3185     rebalanceWhitespace();
3186     applyStyleAfterInsertion();
3187 }
3188
3189 //------------------------------------------------------------------------------------------
3190 // InsertParagraphSeparatorInQuotedContentCommand
3191
3192 InsertParagraphSeparatorInQuotedContentCommand::InsertParagraphSeparatorInQuotedContentCommand(DocumentImpl *document)
3193     : CompositeEditCommand(document), m_breakNode(0)
3194 {
3195 }
3196
3197 InsertParagraphSeparatorInQuotedContentCommand::~InsertParagraphSeparatorInQuotedContentCommand()
3198 {
3199     derefNodesInList(clonedNodes);
3200     if (m_breakNode)
3201         m_breakNode->deref();
3202 }
3203
3204 void InsertParagraphSeparatorInQuotedContentCommand::doApply()
3205 {
3206     Selection selection = endingSelection();
3207     if (selection.isNone())
3208         return;
3209     
3210     // Delete the current selection.
3211     Position pos = selection.start();
3212     if (selection.isRange()) {
3213         deleteSelection(false, false);
3214         pos = endingSelection().start().upstream();
3215     }
3216     
3217     // Find the top-most blockquote from the start.
3218     NodeImpl *startNode = pos.node();
3219     NodeImpl *topBlockquote = 0;
3220     for (NodeImpl *n = startNode->parentNode(); n; n = n->parentNode()) {
3221         if (isMailBlockquote(n))
3222             topBlockquote = n;
3223     }
3224     if (!topBlockquote || !topBlockquote->parentNode())
3225         return;
3226
3227     // Build up list of ancestors in between the start node and the top blockquote.
3228     if (startNode != topBlockquote) {
3229         for (NodeImpl *n = startNode->parentNode(); n && n != topBlockquote; n = n->parentNode())
3230             ancestors.prepend(n);
3231     }
3232
3233     // Insert a break after the top blockquote.
3234     m_breakNode = createBreakElement(document());
3235     m_breakNode->ref();
3236     insertNodeAfter(m_breakNode, topBlockquote);
3237
3238     if (!isLastVisiblePositionInNode(VisiblePosition(pos), topBlockquote)) {
3239         // Split at pos if in the middle of a text node.
3240         if (startNode->isTextNode()) {
3241             TextImpl *textNode = static_cast<TextImpl *>(startNode);
3242             bool atEnd = (unsigned long)pos.offset() >= textNode->length();
3243             if (pos.offset() > 0 && !atEnd) {
3244                 splitTextNode(textNode, pos.offset());
3245                 pos = Position(startNode, 0);
3246             }
3247             else if (atEnd) {
3248                 startNode = startNode->traverseNextNode();
3249                 ASSERT(startNode);
3250             }
3251         }
3252         else if (pos.offset() > 0) {
3253             startNode = startNode->traverseNextNode();
3254             ASSERT(startNode);
3255         }
3256
3257         // Insert a clone of the top blockquote after the break.
3258         NodeImpl *clonedBlockquote = topBlockquote->cloneNode(false);
3259         clonedBlockquote->ref();
3260         clonedNodes.append(clonedBlockquote);
3261         insertNodeAfter(clonedBlockquote, m_breakNode);
3262         
3263         // Make clones of ancestors in between the start node and the top blockquote.
3264         NodeImpl *parent = clonedBlockquote;
3265         for (QPtrListIterator<NodeImpl> it(ancestors); it.current(); ++it) {
3266             NodeImpl *child = it.current()->cloneNode(false); // shallow clone
3267             child->ref();
3268             clonedNodes.append(child);
3269             appendNode(child, parent);
3270             parent = child;
3271         }
3272
3273         // Move the start node and the siblings of the start node.
3274         bool startIsBR = false;
3275         if (startNode != topBlockquote) {
3276             NodeImpl *n = startNode;
3277             startIsBR = n->id() == ID_BR;
3278             if (startIsBR)
3279                 n = n->nextSibling();
3280             while (n) {
3281                 NodeImpl *next = n->nextSibling();
3282                 removeNode(n);
3283                 appendNode(n, parent);
3284                 n = next;
3285             }
3286         }
3287         
3288         // Move everything after the start node.
3289         NodeImpl *leftParent = ancestors.last();
3290
3291         if (!startIsBR) {
3292             if (!leftParent)
3293                 leftParent = topBlockquote;
3294             ElementImpl *b = createBreakElement(document());
3295             b->ref();
3296             clonedNodes.append(b);
3297             appendNode(b, leftParent);
3298         }
3299         
3300         leftParent = ancestors.last();
3301         while (leftParent && leftParent != topBlockquote) {
3302             parent = parent->parentNode();
3303             NodeImpl *n = leftParent->nextSibling();
3304             while (n) {
3305                 NodeImpl *next = n->nextSibling();
3306                 removeNode(n);
3307                 appendNode(n, parent);
3308                 n = next;
3309             }
3310             leftParent = leftParent->parentNode();
3311         }
3312         
3313         // Make sure the cloned block quote renders.
3314         insertBlockPlaceholderIfNeeded(clonedBlockquote);
3315     }
3316     
3317     // Put the selection right before the break.
3318     setEndingSelection(Position(m_breakNode, 0));
3319     rebalanceWhitespace();
3320 }
3321
3322 //------------------------------------------------------------------------------------------
3323 // InsertTextCommand
3324
3325 InsertTextCommand::InsertTextCommand(DocumentImpl *document) 
3326     : CompositeEditCommand(document), m_charactersAdded(0)
3327 {
3328 }
3329
3330 void InsertTextCommand::doApply()
3331 {
3332 }
3333
3334 Position InsertTextCommand::prepareForTextInsertion(bool adjustDownstream)
3335 {
3336     // Prepare for text input by looking at the current position.
3337     // It may be necessary to insert a text node to receive characters.
3338     Selection selection = endingSelection();
3339     ASSERT(selection.isCaret());
3340     
3341     Position pos = selection.start();
3342     if (adjustDownstream)
3343         pos = pos.downstream(StayInBlock);
3344     else
3345         pos = pos.upstream(StayInBlock);
3346     
3347     Selection typingStyleRange;
3348
3349     if (!pos.node()->isTextNode()) {
3350         NodeImpl *textNode = document()->createEditingTextNode("");
3351         NodeImpl *nodeToInsert = textNode;
3352
3353         // Now insert the node in the right place
3354         if (pos.node()->isEditableBlock()) {
3355             LOG(Editing, "prepareForTextInsertion case 1");
3356             appendNode(nodeToInsert, pos.node());
3357         }
3358         else if (pos.node()->caretMinOffset() == pos.offset()) {
3359             LOG(Editing, "prepareForTextInsertion case 2");
3360             insertNodeBefore(nodeToInsert, pos.node());
3361         }
3362         else if (pos.node()->caretMaxOffset() == pos.offset()) {
3363             LOG(Editing, "prepareForTextInsertion case 3");
3364             insertNodeAfter(nodeToInsert, pos.node());
3365         }
3366         else
3367             ASSERT_NOT_REACHED();
3368         
3369         pos = Position(textNode, 0);
3370     }
3371
3372     return pos;
3373 }
3374
3375 void InsertTextCommand::input(const DOMString &text, bool selectInsertedText)
3376 {
3377     Selection selection = endingSelection();
3378     bool adjustDownstream = isFirstVisiblePositionOnLine(VisiblePosition(selection.start().downstream(StayInBlock)));
3379
3380     // Delete the current selection, or collapse whitespace, as needed
3381     if (selection.isRange())
3382         deleteSelection();
3383     
3384     // Delete any insignificant text that could get in the way of whitespace turning
3385     // out correctly after the insertion.
3386     deleteInsignificantTextDownstream(endingSelection().end().trailingWhitespacePosition());
3387     
3388     // Make sure the document is set up to receive text
3389     Position startPosition = prepareForTextInsertion(adjustDownstream);
3390     
3391     Position endPosition;
3392
3393     TextImpl *textNode = static_cast<TextImpl *>(startPosition.node());
3394     long offset = startPosition.offset();
3395
3396     // Now that we are about to add content, check to see if a placeholder element
3397     // can be removed.
3398     removeBlockPlaceholderIfNeeded(textNode->enclosingBlockFlowElement());
3399     
3400     // These are temporary implementations for inserting adjoining spaces
3401     // into a document. We are working on a CSS-related whitespace solution
3402     // that will replace this some day. We hope.
3403     if (isTab(text)) {
3404         // Treat a tab like a number of spaces. This seems to be the HTML editing convention,
3405         // although the number of spaces varies (we choose four spaces). 
3406         // Note that there is no attempt to make this work like a real tab stop, it is merely 
3407         // a set number of spaces. This also seems to be the HTML editing convention.
3408         for (int i = 0; i < spacesPerTab; i++) {
3409             insertSpace(textNode, offset);
3410             rebalanceWhitespace();
3411             document()->updateLayout();
3412         }
3413         
3414         endPosition = Position(textNode, offset + spacesPerTab);
3415
3416         m_charactersAdded += spacesPerTab;
3417     }
3418     else if (isWS(text)) {
3419         insertSpace(textNode, offset);
3420         endPosition = Position(textNode, offset + 1);
3421
3422         m_charactersAdded++;
3423         rebalanceWhitespace();
3424     }
3425     else {
3426         const DOMString &existingText = textNode->data();
3427         if (textNode->length() >= 2 && offset >= 2 && isNBSP(existingText[offset - 1]) && !isWS(existingText[offset - 2])) {
3428             // DOM looks like this:
3429             // character nbsp caret
3430             // As we are about to insert a non-whitespace character at the caret
3431             // convert the nbsp to a regular space.
3432             // EDIT FIXME: This needs to be improved some day to convert back only
3433             // those nbsp's added by the editor to make rendering come out right.
3434             replaceTextInNode(textNode, offset - 1, 1, " ");
3435         }
3436         insertTextIntoNode(textNode, offset, text);
3437         endPosition = Position(textNode, offset + text.length());
3438
3439         m_charactersAdded += text.length();
3440     }
3441
3442     setEndingSelection(Selection(startPosition, endPosition));
3443
3444     // Handle the case where there is a typing style.
3445     // FIXME: Improve typing style.
3446     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
3447     CSSMutableStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
3448     if (typingStyle && typingStyle->length() > 0)
3449         applyStyle(typingStyle);
3450
3451     if (!selectInsertedText)
3452         setEndingSelection(endingSelection().end());
3453 }
3454
3455 void InsertTextCommand::insertSpace(TextImpl *textNode, unsigned long offset)
3456 {
3457     ASSERT(textNode);
3458
3459     DOMString text(textNode->data());
3460
3461     // count up all spaces and newlines in front of the caret
3462     // delete all collapsed ones
3463     // this will work out OK since the offset we have been passed has been upstream-ized 
3464     int count = 0;
3465     for (unsigned int i = offset; i < text.length(); i++) {
3466         if (isWS(text[i]))
3467             count++;
3468         else 
3469             break;
3470     }
3471     if (count > 0) {
3472         // By checking the character at the downstream position, we can
3473         // check if there is a rendered WS at the caret
3474         Position pos(textNode, offset);
3475         Position downstream = pos.downstream();
3476         if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
3477             count--; // leave this WS in
3478         if (count > 0)
3479             deleteTextFromNode(textNode, offset, count);
3480     }
3481
3482     if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
3483         // insert a "regular" space
3484         insertTextIntoNode(textNode, offset, " ");
3485         return;
3486     }
3487
3488     if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
3489         // DOM looks like this:
3490         // nbsp nbsp caret
3491         // insert a space between the two nbsps
3492         insertTextIntoNode(textNode, offset - 1, " ");
3493         return;
3494     }
3495
3496     // insert an nbsp
3497     insertTextIntoNode(textNode, offset, nonBreakingSpaceString());
3498 }
3499
3500 bool InsertTextCommand::isInsertTextCommand() const
3501 {
3502     return true;
3503 }
3504
3505 //------------------------------------------------------------------------------------------
3506 // JoinTextNodesCommand
3507
3508 JoinTextNodesCommand::JoinTextNodesCommand(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
3509     : EditCommand(document), m_text1(text1), m_text2(text2)
3510 {
3511     ASSERT(m_text1);
3512     ASSERT(m_text2);
3513     ASSERT(m_text1->nextSibling() == m_text2);
3514     ASSERT(m_text1->length() > 0);
3515     ASSERT(m_text2->length() > 0);
3516
3517     m_text1->ref();
3518     m_text2->ref();
3519 }
3520
3521 JoinTextNodesCommand::~JoinTextNodesCommand()
3522 {
3523     ASSERT(m_text1);
3524     m_text1->deref();
3525     ASSERT(m_text2);
3526     m_text2->deref();
3527 }
3528
3529 void JoinTextNodesCommand::doApply()
3530 {
3531     ASSERT(m_text1);
3532     ASSERT(m_text2);
3533     ASSERT(m_text1->nextSibling() == m_text2);
3534
3535     int exceptionCode = 0;
3536     m_text2->insertData(0, m_text1->data(), exceptionCode);
3537     ASSERT(exceptionCode == 0);
3538
3539     m_text2->parentNode()->removeChild(m_text1, exceptionCode);
3540     ASSERT(exceptionCode == 0);
3541
3542     m_offset = m_text1->length();
3543 }
3544
3545 void JoinTextNodesCommand::doUnapply()
3546 {
3547     ASSERT(m_text2);
3548     ASSERT(m_offset > 0);
3549
3550     int exceptionCode = 0;
3551
3552     m_text2->deleteData(0, m_offset, exceptionCode);
3553     ASSERT(exceptionCode == 0);
3554
3555     m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
3556     ASSERT(exceptionCode == 0);
3557         
3558     ASSERT(m_text2->previousSibling()->isTextNode());
3559     ASSERT(m_text2->previousSibling() == m_text1);
3560 }
3561
3562 //------------------------------------------------------------------------------------------
3563 // MoveSelectionCommand
3564
3565 MoveSelectionCommand::MoveSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, Position &position, bool smartMove) 
3566     : CompositeEditCommand(document), m_fragment(fragment), m_position(position), m_smartMove(smartMove)
3567 {
3568     ASSERT(m_fragment);
3569     m_fragment->ref();
3570 }
3571
3572 MoveSelectionCommand::~MoveSelectionCommand()
3573 {
3574     ASSERT(m_fragment);
3575     m_fragment->deref();
3576 }
3577
3578 void MoveSelectionCommand::doApply()
3579 {
3580     Selection selection = endingSelection();
3581     ASSERT(selection.isRange());
3582
3583     Position pos = m_position;
3584
3585     // Update the position otherwise it may become invalid after the selection is deleted.
3586     NodeImpl *positionNode = m_position.node();
3587     long positionOffset = m_position.offset();
3588     Position selectionEnd = selection.end();
3589     long selectionEndOffset = selectionEnd.offset();    
3590     if (selectionEnd.node() == positionNode && selectionEndOffset < positionOffset) {
3591         positionOffset -= selectionEndOffset;
3592         Position selectionStart = selection.start();
3593         if (selectionStart.node() == positionNode) {
3594             positionOffset += selectionStart.offset();
3595         }
3596         pos = Position(positionNode, positionOffset);
3597     }
3598
3599     deleteSelection(m_smartMove);
3600
3601     // If the node for the destination has been removed as a result of the deletion,
3602     // set the destination to the ending point after the deletion.
3603     // Fixes: <rdar://problem/3910425> REGRESSION (Mail): Crash in ReplaceSelectionCommand; 
3604     //        selection is empty, leading to null deref
3605     if (!pos.node()->inDocument())
3606         pos = endingSelection().start();
3607
3608     setEndingSelection(pos);
3609     EditCommandPtr cmd(new ReplaceSelectionCommand(document(), m_fragment, true, m_smartMove));
3610     applyCommandToComposite(cmd);
3611 }
3612
3613 EditAction MoveSelectionCommand::editingAction() const
3614 {
3615     return EditActionDrag;
3616 }
3617
3618 //------------------------------------------------------------------------------------------
3619 // RebalanceWhitespaceCommand
3620
3621 RebalanceWhitespaceCommand::RebalanceWhitespaceCommand(DocumentImpl *document, const Position &pos)
3622     : EditCommand(document), m_position(pos), m_upstreamOffset(InvalidOffset), m_downstreamOffset(InvalidOffset)
3623 {
3624 }
3625
3626 RebalanceWhitespaceCommand::~RebalanceWhitespaceCommand()
3627 {
3628 }
3629
3630 void RebalanceWhitespaceCommand::doApply()
3631 {
3632     static DOMString space(" ");
3633
3634     if (m_position.isNull() || !m_position.node()->isTextNode())
3635         return;
3636         
3637     TextImpl *textNode = static_cast<TextImpl *>(m_position.node());
3638     DOMString text = textNode->data();
3639     if (text.length() == 0)
3640         return;
3641     
3642     // find upstream offset
3643     long upstream = m_position.offset();
3644     while (upstream > 0 && isWS(text[upstream - 1]) || isNBSP(text[upstream - 1])) {
3645         upstream--;
3646         m_upstreamOffset = upstream;
3647     }
3648
3649     // find downstream offset
3650     long downstream = m_position.offset();
3651     while ((unsigned)downstream < text.length() && isWS(text[downstream]) || isNBSP(text[downstream])) {
3652         downstream++;
3653         m_downstreamOffset = downstream;
3654     }
3655
3656     if (m_upstreamOffset == InvalidOffset && m_downstreamOffset == InvalidOffset)
3657         return;
3658         
3659     m_upstreamOffset = upstream;
3660     m_downstreamOffset = downstream;
3661     long length = m_downstreamOffset - m_upstreamOffset;
3662     
3663     m_beforeString = text.substring(m_upstreamOffset, length);
3664     
3665     // The following loop figures out a "rebalanced" whitespace string for any length
3666     // string, and takes into account the special cases that need to handled for the
3667     // start and end of strings (i.e. first and last character must be an nbsp.
3668     long i = m_upstreamOffset;
3669     while (i < m_downstreamOffset) {
3670         long add = (m_downstreamOffset - i) % 3;
3671         switch (add) {
3672             case 0:
3673                 m_afterString += nonBreakingSpaceString();
3674                 m_afterString += space;
3675                 m_afterString += nonBreakingSpaceString();
3676                 add = 3;
3677                 break;
3678             case 1:
3679                 if (i == 0 || (unsigned)i + 1 == text.length()) // at start or end of string
3680                     m_afterString += nonBreakingSpaceString();
3681                 else
3682                     m_afterString += space;
3683                 break;
3684             case 2:
3685                 if ((unsigned)i + 2 == text.length()) {
3686                      // at end of string
3687                     m_afterString += nonBreakingSpaceString();
3688                     m_afterString += nonBreakingSpaceString();
3689                 }
3690                 else {
3691                     m_afterString += nonBreakingSpaceString();
3692                     m_afterString += space;
3693                 }
3694                 break;
3695         }
3696         i += add;
3697     }
3698     
3699     text.remove(m_upstreamOffset, length);
3700     text.insert(m_afterString, m_upstreamOffset);
3701 }
3702
3703 void RebalanceWhitespaceCommand::doUnapply()
3704 {
3705     if (m_upstreamOffset == InvalidOffset && m_downstreamOffset == InvalidOffset)
3706         return;
3707     
3708     ASSERT(m_position.node()->isTextNode());
3709     TextImpl *textNode = static_cast<TextImpl *>(m_position.node());
3710     DOMString text = textNode->data();
3711     text.remove(m_upstreamOffset, m_afterString.length());
3712     text.insert(m_beforeString, m_upstreamOffset);
3713 }
3714
3715 bool RebalanceWhitespaceCommand::preservesTypingStyle() const
3716 {
3717     return true;
3718 }
3719
3720 //------------------------------------------------------------------------------------------
3721 // RemoveCSSPropertyCommand
3722
3723 RemoveCSSPropertyCommand::RemoveCSSPropertyCommand(DocumentImpl *document, CSSStyleDeclarationImpl *decl, int property)
3724     : EditCommand(document), m_decl(decl->makeMutable()), m_property(property), m_important(false)
3725 {
3726     ASSERT(m_decl);
3727     m_decl->ref();
3728 }
3729
3730 RemoveCSSPropertyCommand::~RemoveCSSPropertyCommand()
3731 {
3732     ASSERT(m_decl);
3733     m_decl->deref();
3734 }
3735
3736 void RemoveCSSPropertyCommand::doApply()
3737 {
3738     ASSERT(m_decl);
3739
3740     m_oldValue = m_decl->getPropertyValue(m_property);
3741     ASSERT(!m_oldValue.isNull());
3742
3743     m_important = m_decl->getPropertyPriority(m_property);
3744     m_decl->removeProperty(m_property);
3745 }
3746
3747 void RemoveCSSPropertyCommand::doUnapply()
3748 {
3749     ASSERT(m_decl);
3750     ASSERT(!m_oldValue.isNull());
3751
3752     m_decl->setProperty(m_property, m_oldValue, m_important);
3753 }
3754
3755 //------------------------------------------------------------------------------------------
3756 // RemoveNodeAttributeCommand
3757
3758 RemoveNodeAttributeCommand::RemoveNodeAttributeCommand(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute)
3759     : EditCommand(document), m_element(element), m_attribute(attribute)
3760 {
3761     ASSERT(m_element);
3762     m_element->ref();
3763 }
3764
3765 RemoveNodeAttributeCommand::~RemoveNodeAttributeCommand()
3766 {
3767     ASSERT(m_element);
3768     m_element->deref();
3769 }
3770
3771 void RemoveNodeAttributeCommand::doApply()
3772 {
3773     ASSERT(m_element);
3774
3775     m_oldValue = m_element->getAttribute(m_attribute);
3776     ASSERT(!m_oldValue.isNull());
3777
3778     int exceptionCode = 0;
3779     m_element->removeAttribute(m_attribute, exceptionCode);
3780     ASSERT(exceptionCode == 0);
3781 }
3782
3783 void RemoveNodeAttributeCommand::doUnapply()
3784 {
3785     ASSERT(m_element);
3786     ASSERT(!m_oldValue.isNull());
3787
3788     int exceptionCode = 0;
3789     m_element->setAttribute(m_attribute, m_oldValue.implementation(), exceptionCode);
3790     ASSERT(exceptionCode == 0);
3791 }
3792
3793 //------------------------------------------------------------------------------------------
3794 // RemoveNodeCommand
3795
3796 RemoveNodeCommand::RemoveNodeCommand(DocumentImpl *document, NodeImpl *removeChild)
3797     : EditCommand(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
3798 {
3799     ASSERT(m_removeChild);
3800     m_removeChild->ref();
3801
3802     m_parent = m_removeChild->parentNode();
3803     ASSERT(m_parent);
3804     m_parent->ref();
3805     
3806     m_refChild = m_removeChild->nextSibling();
3807     if (m_refChild)
3808         m_refChild->ref();
3809 }
3810
3811 RemoveNodeCommand::~RemoveNodeCommand()
3812 {
3813     ASSERT(m_parent);
3814     m_parent->deref();
3815
3816     ASSERT(m_removeChild);
3817     m_removeChild->deref();
3818
3819     if (m_refChild)
3820         m_refChild->deref();
3821 }
3822
3823 void RemoveNodeCommand::doApply()
3824 {
3825     ASSERT(m_parent);
3826     ASSERT(m_removeChild);
3827
3828     int exceptionCode = 0;
3829     m_parent->removeChild(m_removeChild, exceptionCode);
3830     ASSERT(exceptionCode == 0);
3831 }
3832
3833 void RemoveNodeCommand::doUnapply()
3834 {
3835     ASSERT(m_parent);
3836     ASSERT(m_removeChild);
3837
3838     int exceptionCode = 0;
3839     m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
3840     ASSERT(exceptionCode == 0);
3841 }
3842
3843 //------------------------------------------------------------------------------------------
3844 // RemoveNodePreservingChildrenCommand
3845
3846 RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand(DocumentImpl *document, NodeImpl *node)
3847     : CompositeEditCommand(document), m_node(node)
3848 {
3849     ASSERT(m_node);
3850     m_node->ref();
3851 }
3852
3853 RemoveNodePreservingChildrenCommand::~RemoveNodePreservingChildrenCommand()
3854 {
3855     ASSERT(m_node);
3856     m_node->deref();
3857 }
3858
3859 void RemoveNodePreservingChildrenCommand::doApply()
3860 {
3861     while (NodeImpl* curr = node()->firstChild()) {
3862         removeNode(curr);
3863         insertNodeBefore(curr, node());
3864     }
3865     removeNode(node());
3866 }
3867
3868 //------------------------------------------------------------------------------------------
3869 // ReplaceSelectionCommand
3870
3871 ReplacementFragment::ReplacementFragment(DocumentFragmentImpl *fragment)
3872     : m_fragment(fragment), m_hasInterchangeNewline(false), m_hasMoreThanOneBlock(false)
3873 {
3874     if (!m_fragment) {
3875         m_type = EmptyFragment;
3876         return;
3877     }
3878
3879     m_fragment->ref();
3880
3881     NodeImpl *firstChild = m_fragment->firstChild();
3882     NodeImpl *lastChild = m_fragment->lastChild();
3883
3884     if (!firstChild) {
3885         m_type = EmptyFragment;
3886         return;
3887     }
3888
3889     if (firstChild == lastChild && firstChild->isTextNode()) {
3890         m_type = SingleTextNodeFragment;
3891         return;
3892     }
3893     
3894     m_type = TreeFragment;
3895
3896     NodeImpl *node = firstChild;
3897     int realBlockCount = 0;
3898     NodeImpl *nodeToDelete = 0;
3899     while (node) {
3900         NodeImpl *next = node->traverseNextNode();
3901         if (isInterchangeNewlineNode(node)) {
3902             m_hasInterchangeNewline = true;
3903             nodeToDelete = node;
3904         }
3905         else if (isInterchangeConvertedSpaceSpan(node)) {
3906             NodeImpl *n = 0;
3907             while ((n = node->firstChild())) {
3908                 n->ref();
3909                 removeNode(n);
3910                 insertNodeBefore(n, node);
3911                 n->deref();
3912             }
3913             removeNode(node);
3914             if (n)
3915                 next = n->traverseNextNode();
3916         }
3917         else if (isProbablyBlock(node))
3918             realBlockCount++;    
3919         node = next;
3920     }
3921
3922     if (nodeToDelete)
3923         removeNode(nodeToDelete);
3924
3925     int blockCount = realBlockCount;
3926     firstChild = m_fragment->firstChild();
3927     lastChild = m_fragment->lastChild();
3928     if (!isProbablyBlock(firstChild))
3929         blockCount++;
3930     if (!isProbablyBlock(lastChild) && realBlockCount > 0)
3931         blockCount++;
3932
3933      if (blockCount > 1)
3934         m_hasMoreThanOneBlock = true;
3935 }
3936
3937 ReplacementFragment::~ReplacementFragment()
3938 {
3939     if (m_fragment)
3940         m_fragment->deref();
3941 }
3942
3943 NodeImpl *ReplacementFragment::firstChild() const 
3944
3945     return m_fragment->firstChild(); 
3946 }
3947
3948 NodeImpl *ReplacementFragment::lastChild() const 
3949
3950     return  m_fragment->lastChild(); 
3951 }
3952
3953 NodeImpl *ReplacementFragment::mergeStartNode() const
3954 {
3955     NodeImpl *node = m_fragment->firstChild();
3956     if (!node)
3957         return 0;
3958     if (!isProbablyBlock(node))
3959         return node;
3960     return node->firstChild();
3961 }
3962
3963 NodeImpl *ReplacementFragment::mergeEndNode() const
3964 {
3965     NodeImpl *node = m_fragment->lastChild();
3966     while (node && node->lastChild())
3967         node = node->lastChild();
3968     
3969     if (isProbablyBlock(node))
3970         return 0;
3971         
3972     NodeImpl *startingBlock = enclosingBlock(node);
3973     ASSERT(startingBlock != node);
3974     while (node) {
3975         NodeImpl *prev = node->traversePreviousNode();
3976         if (prev == m_fragment || prev == startingBlock || enclosingBlock(prev) != startingBlock)
3977             return node;
3978         node = prev;
3979     }
3980     
3981     return 0;
3982 }
3983
3984 void ReplacementFragment::pruneEmptyNodes()
3985 {
3986     bool run = true;
3987     while (run) {
3988         run = false;
3989         NodeImpl *node = m_fragment->firstChild();
3990         while (node) {
3991             if ((node->isTextNode() && static_cast<TextImpl *>(node)->length() == 0) ||
3992                 (isProbablyBlock(node) && node->childNodeCount() == 0)) {
3993                 NodeImpl *next = node->traverseNextSibling();
3994                 removeNode(node);
3995                 node = next;
3996                 run = true;
3997             }
3998             else {
3999                 node = node->traverseNextNode();
4000             }
4001          }
4002     }
4003 }
4004
4005 bool ReplacementFragment::isInterchangeNewlineNode(const NodeImpl *node)
4006 {
4007     static DOMString interchangeNewlineClassString(AppleInterchangeNewline);
4008     return node && node->id() == ID_BR && static_cast<const ElementImpl *>(node)->getAttribute(ATTR_CLASS) == interchangeNewlineClassString;
4009 }
4010
4011 bool ReplacementFragment::isInterchangeConvertedSpaceSpan(const NodeImpl *node)
4012 {
4013     static DOMString convertedSpaceSpanClassString(AppleConvertedSpace);
4014     return node->isHTMLElement() && static_cast<const HTMLElementImpl *>(node)->getAttribute(ATTR_CLASS) == convertedSpaceSpanClassString;
4015 }
4016
4017 NodeImpl *ReplacementFragment::enclosingBlock(NodeImpl *node) const
4018 {
4019     while (node && !isProbablyBlock(node))
4020         node = node->parentNode();    
4021     return node ? node : m_fragment;
4022 }
4023
4024 void ReplacementFragment::removeNode(NodeImpl *node)
4025 {
4026     if (!node)
4027         return;
4028         
4029     NodeImpl *parent = node->parentNode();
4030     if (!parent)
4031         return;
4032         
4033     int exceptionCode = 0;
4034     parent->removeChild(node, exceptionCode);
4035     ASSERT(exceptionCode == 0);
4036 }
4037
4038 void ReplacementFragment::insertNodeBefore(NodeImpl *node, NodeImpl *refNode)
4039 {
4040     if (!node || !refNode)
4041         return;
4042         
4043     NodeImpl *parent = refNode->parentNode();
4044     if (!parent)
4045         return;
4046         
4047     int exceptionCode = 0;
4048     parent->insertBefore(node, refNode, exceptionCode);
4049     ASSERT(exceptionCode == 0);
4050  }
4051
4052
4053 bool isProbablyBlock(const NodeImpl *node)
4054 {
4055     if (!node)
4056         return false;
4057     
4058     switch (node->id()) {
4059         case ID_BLOCKQUOTE:
4060         case ID_DD:
4061         case ID_DIV:
4062         case ID_DL:
4063         case ID_DT:
4064         case ID_H1:
4065         case ID_H2:
4066         case ID_H3:
4067         case ID_H4:
4068         case ID_H5:
4069         case ID_H6:
4070         case ID_HR:
4071         case ID_LI:
4072         case ID_OL:
4073         case ID_P:
4074         case ID_PRE:
4075         case ID_TD:
4076         case ID_TH:
4077         case ID_UL:
4078             return true;
4079     }
4080     
4081     return false;
4082 }
4083
4084 ReplaceSelectionCommand::ReplaceSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, bool selectReplacement, bool smartReplace) 
4085     : CompositeEditCommand(document), 
4086       m_fragment(fragment),
4087       m_selectReplacement(selectReplacement), 
4088       m_smartReplace(smartReplace)
4089 {
4090 }
4091
4092 ReplaceSelectionCommand::~ReplaceSelectionCommand()
4093 {
4094 }
4095
4096 void ReplaceSelectionCommand::doApply()
4097 {
4098     Selection selection = endingSelection();
4099     ASSERT(selection.isCaretOrRange());
4100     VisiblePosition visibleStart(selection.start());
4101     VisiblePosition visibleEnd(selection.end());
4102     bool startAtStartOfBlock = isFirstVisiblePositionInBlock(visibleStart);
4103     bool startAtEndOfBlock = isLastVisiblePositionInBlock(visibleStart);
4104     bool startAtBlockBoundary = startAtStartOfBlock || startAtEndOfBlock;
4105     NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
4106     NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
4107     bool mergeStart = false;
4108     bool mergeEnd = false;
4109     if (startBlock == startBlock->rootEditableElement() && startAtStartOfBlock && startAtEndOfBlock) {
4110         // Empty document. Merge neither start nor end.
4111         mergeStart = mergeEnd = false;
4112     }
4113     else {
4114         mergeStart = !isStartOfParagraph(visibleStart) || (!m_fragment.hasInterchangeNewline() && !m_fragment.hasMoreThanOneBlock());
4115         mergeEnd = !m_fragment.hasInterchangeNewline() && m_fragment.hasMoreThanOneBlock() && !isEndOfParagraph(visibleEnd);
4116     }
4117     
4118     Position startPos = Position(selection.start().node()->enclosingBlockFlowElement(), 0);
4119     Position endPos; 
4120     EStayInBlock upstreamStayInBlock = StayInBlock;
4121
4122     // Delete the current selection, or collapse whitespace, as needed
4123     if (selection.isRange()) {
4124         deleteSelection(false, !(m_fragment.hasInterchangeNewline() || m_fragment.hasMoreThanOneBlock()));
4125     }
4126     else if (selection.isCaret() && mergeEnd && !startAtBlockBoundary) {
4127         // The start and the end need&nb