9a00c3e78db56d125a6e7864d8cb30365962bc52
[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 "htmlattrs.h"
45 #include "htmltags.h"
46 #include "khtml_part.h"
47 #include "khtml_part.h"
48 #include "khtmlview.h"
49 #include "qptrlist.h"
50 #include "render_object.h"
51 #include "render_style.h"
52 #include "render_text.h"
53 #include "visible_position.h"
54 #include "visible_units.h"
55
56 using DOM::AttrImpl;
57 using DOM::CSSComputedStyleDeclarationImpl;
58 using DOM::CSSPrimitiveValue;
59 using DOM::CSSPrimitiveValueImpl;
60 using DOM::CSSProperty;
61 using DOM::CSSStyleDeclarationImpl;
62 using DOM::CSSValueImpl;
63 using DOM::DocumentFragmentImpl;
64 using DOM::DocumentImpl;
65 using DOM::DOMString;
66 using DOM::DOMStringImpl;
67 using DOM::DoNotUpdateLayout;
68 using DOM::EditingTextImpl;
69 using DOM::ElementImpl;
70 using DOM::HTMLElementImpl;
71 using DOM::HTMLImageElementImpl;
72 using DOM::NamedAttrMapImpl;
73 using DOM::Node;
74 using DOM::NodeImpl;
75 using DOM::NodeListImpl;
76 using DOM::Position;
77 using DOM::PositionIterator;
78 using DOM::Range;
79 using DOM::RangeImpl;
80 using DOM::StayInBlock;
81 using DOM::TextImpl;
82 using DOM::TreeWalkerImpl;
83
84 #if APPLE_CHANGES
85 #include "KWQAssertions.h"
86 #include "KWQLogging.h"
87 #include "KWQKHTMLPart.h"
88 #endif
89
90 #if !APPLE_CHANGES
91 #define ASSERT(assertion) ((void)0)
92 #define ASSERT_WITH_MESSAGE(assertion, formatAndArgs...) ((void)0)
93 #define ASSERT_NOT_REACHED() ((void)0)
94 #define LOG(channel, formatAndArgs...) ((void)0)
95 #define ERROR(formatAndArgs...) ((void)0)
96 #define ASSERT(assertion) assert(assertion)
97 #if LOG_DISABLED
98 #define debugPosition(a,b) ((void)0)
99 #endif
100 #endif
101
102 #define IF_IMPL_NULL_RETURN_ARG(arg) do { \
103         if (isNull()) { return arg; } \
104     } while (0)
105         
106 #define IF_IMPL_NULL_RETURN do { \
107         if (isNull()) { return; } \
108     } while (0)
109
110 namespace khtml {
111
112 static inline bool isNBSP(const QChar &c)
113 {
114     return c == QChar(0xa0);
115 }
116
117 static inline bool isWS(const QChar &c)
118 {
119     return c.isSpace() && c != QChar(0xa0);
120 }
121
122 static inline bool isWS(const DOMString &text)
123 {
124     if (text.length() != 1)
125         return false;
126     
127     return isWS(text[0]);
128 }
129
130 static inline bool isWS(const Position &pos)
131 {
132     if (!pos.node())
133         return false;
134         
135     if (!pos.node()->isTextNode())
136         return false;
137
138     const DOMString &string = static_cast<TextImpl *>(pos.node())->data();
139     return isWS(string[pos.offset()]);
140 }
141
142 static const int spacesPerTab = 4;
143
144 static inline bool isTab(const DOMString &text)
145 {
146     static QChar tabCharacter = QChar(0x9);
147     if (text.length() != 1)
148         return false;
149     
150     return text[0] == tabCharacter;
151 }
152
153 static inline bool isTableStructureNode(const NodeImpl *node)
154 {
155     RenderObject *r = node->renderer();
156     return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
157 }
158
159 static DOMString &nonBreakingSpaceString()
160 {
161     static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
162     return nonBreakingSpaceString;
163 }
164
165 static DOMString &styleSpanClassString()
166 {
167     static DOMString styleSpanClassString = "khtml-style-span";
168     return styleSpanClassString;
169 }
170
171 static DOMString &blockPlaceholderClassString()
172 {
173     static DOMString blockPlaceholderClassString = "khtml-block-placeholder";
174     return blockPlaceholderClassString;
175 }
176
177 static void debugPosition(const char *prefix, const Position &pos)
178 {
179     if (!prefix)
180         prefix = "";
181     if (pos.isNull())
182         LOG(Editing, "%s <null>", prefix);
183     else
184         LOG(Editing, "%s%s %p : %d", prefix, getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
185 }
186
187 //------------------------------------------------------------------------------------------
188 // EditCommandPtr
189
190 EditCommandPtr::EditCommandPtr()
191 {
192 }
193
194 EditCommandPtr::EditCommandPtr(EditCommand *impl) : SharedPtr<EditCommand>(impl)
195 {
196 }
197
198 EditCommandPtr::EditCommandPtr(const EditCommandPtr &o) : SharedPtr<EditCommand>(o)
199 {
200 }
201
202 EditCommandPtr::~EditCommandPtr()
203 {
204 }
205
206 EditCommandPtr &EditCommandPtr::operator=(const EditCommandPtr &c)
207 {
208     static_cast<SharedPtr<EditCommand> &>(*this) = c;
209     return *this;
210 }
211
212 bool EditCommandPtr::isCompositeStep() const
213 {
214     IF_IMPL_NULL_RETURN_ARG(false);        
215     return get()->isCompositeStep();
216 }
217
218 bool EditCommandPtr::isInputTextCommand() const
219 {
220     IF_IMPL_NULL_RETURN_ARG(false);        
221     return get()->isInputTextCommand();
222 }
223
224 bool EditCommandPtr::isTypingCommand() const
225 {
226     IF_IMPL_NULL_RETURN_ARG(false);        
227     return get()->isTypingCommand();
228 }
229
230 void EditCommandPtr::apply() const
231 {
232     IF_IMPL_NULL_RETURN;
233     get()->apply();
234 }
235
236 void EditCommandPtr::unapply() const
237 {
238     IF_IMPL_NULL_RETURN;
239     get()->unapply();
240 }
241
242 void EditCommandPtr::reapply() const
243 {
244     IF_IMPL_NULL_RETURN;
245     get()->reapply();
246 }
247
248 DocumentImpl * const EditCommandPtr::document() const
249 {
250     IF_IMPL_NULL_RETURN_ARG(0);
251     return get()->document();
252 }
253
254 Selection EditCommandPtr::startingSelection() const
255 {
256     IF_IMPL_NULL_RETURN_ARG(Selection());
257     return get()->startingSelection();
258 }
259
260 Selection EditCommandPtr::endingSelection() const
261 {
262     IF_IMPL_NULL_RETURN_ARG(Selection());
263     return get()->endingSelection();
264 }
265
266 void EditCommandPtr::setStartingSelection(const Selection &s) const
267 {
268     IF_IMPL_NULL_RETURN;
269     get()->setStartingSelection(s);
270 }
271
272 void EditCommandPtr::setEndingSelection(const Selection &s) const
273 {
274     IF_IMPL_NULL_RETURN;
275     get()->setEndingSelection(s);
276 }
277
278 CSSStyleDeclarationImpl *EditCommandPtr::typingStyle() const
279 {
280     IF_IMPL_NULL_RETURN_ARG(0);
281     return get()->typingStyle();
282 }
283
284 void EditCommandPtr::setTypingStyle(CSSStyleDeclarationImpl *style) const
285 {
286     IF_IMPL_NULL_RETURN;
287     get()->setTypingStyle(style);
288 }
289
290 EditCommandPtr EditCommandPtr::parent() const
291 {
292     IF_IMPL_NULL_RETURN_ARG(0);
293     return get()->parent();
294 }
295
296 void EditCommandPtr::setParent(const EditCommandPtr &cmd) const
297 {
298     IF_IMPL_NULL_RETURN;
299     get()->setParent(cmd.get());
300 }
301
302 EditCommandPtr &EditCommandPtr::emptyCommand()
303 {
304     static EditCommandPtr m_emptyCommand;
305     return m_emptyCommand;
306 }
307
308 //------------------------------------------------------------------------------------------
309 // StyleChange
310
311 StyleChange::StyleChange(CSSStyleDeclarationImpl *style) 
312 {
313     init(style, Position());
314 }
315
316 StyleChange::StyleChange(CSSStyleDeclarationImpl *style, const Position &position)
317 {
318     init(style, position);
319 }
320
321 void StyleChange::init(CSSStyleDeclarationImpl *style, const Position &position)
322 {
323     m_applyBold = false;
324     m_applyItalic = false;
325
326     QString styleText;
327
328     for (QPtrListIterator<CSSProperty> it(*(style->values())); it.current(); ++it) {
329         CSSProperty *property = it.current();
330
331         // If position is empty or the position passed in already has the 
332         // style, just move on.
333         if (position.isNotNull() && currentlyHasStyle(position, property))
334             continue;
335
336         // Figure out the manner of change that is needed.
337         DOMString valueText(property->value()->cssText());
338         switch (property->id()) {
339             case CSS_PROP_FONT_WEIGHT:
340                 if (strcasecmp(valueText, "bold") == 0) {
341                     m_applyBold = true;
342                     continue;
343                 }
344                 break;
345             case CSS_PROP_FONT_STYLE:
346                 if (strcasecmp(valueText, "italic") == 0 || strcasecmp(valueText, "oblique") == 0) {
347                     m_applyItalic = true;
348                     continue;
349                 }
350                 break;
351         }
352
353         styleText += property->cssText().string();
354     }
355
356     m_cssStyle = styleText.stripWhiteSpace();
357 }
358
359 bool StyleChange::currentlyHasStyle(const Position &pos, const CSSProperty *property)
360 {
361     ASSERT(pos.isNotNull());
362     CSSComputedStyleDeclarationImpl *style = pos.computedStyle();
363     ASSERT(style);
364     style->ref();
365     CSSValueImpl *value = style->getPropertyCSSValue(property->id(), DoNotUpdateLayout);
366     style->deref();
367     return value && strcasecmp(value->cssText(), property->value()->cssText()) == 0;
368 }
369
370 //------------------------------------------------------------------------------------------
371 // EditCommand
372
373 EditCommand::EditCommand(DocumentImpl *document) 
374     : m_document(document), m_state(NotApplied), m_typingStyle(0), m_parent(0)
375 {
376     ASSERT(m_document);
377     ASSERT(m_document->part());
378     m_document->ref();
379     m_startingSelection = m_document->part()->selection();
380     m_endingSelection = m_startingSelection;
381
382     m_document->part()->setSelection(Selection(), false, true);
383 }
384
385 EditCommand::~EditCommand()
386 {
387     ASSERT(m_document);
388     m_document->deref();
389     if (m_typingStyle)
390         m_typingStyle->deref();
391 }
392
393 void EditCommand::apply()
394 {
395     ASSERT(m_document);
396     ASSERT(m_document->part());
397     ASSERT(state() == NotApplied);
398  
399     KHTMLPart *part = m_document->part();
400
401     ASSERT(part->selection().isNone());
402
403     doApply();
404     
405     m_state = Applied;
406
407     // FIXME: Improve typing style.
408     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
409     if (!preservesTypingStyle())
410         setTypingStyle(0);
411
412     if (!isCompositeStep()) {
413         document()->updateLayout();
414         EditCommandPtr cmd(this);
415         part->appliedEditing(cmd);
416     }
417 }
418
419 void EditCommand::unapply()
420 {
421     ASSERT(m_document);
422     ASSERT(m_document->part());
423     ASSERT(state() == Applied);
424
425     bool topLevel = !isCompositeStep();
426  
427     KHTMLPart *part = m_document->part();
428
429     if (topLevel) {
430         part->setSelection(Selection(), false, true);
431     }
432     ASSERT(part->selection().isNone());
433     
434     doUnapply();
435     
436     m_state = NotApplied;
437
438     if (topLevel) {
439         document()->updateLayout();
440         EditCommandPtr cmd(this);
441         part->unappliedEditing(cmd);
442     }
443 }
444
445 void EditCommand::reapply()
446 {
447     ASSERT(m_document);
448     ASSERT(m_document->part());
449     ASSERT(state() == NotApplied);
450     
451     bool topLevel = !isCompositeStep();
452  
453     KHTMLPart *part = m_document->part();
454
455     if (topLevel) {
456         part->setSelection(Selection(), false, true);
457     }
458     ASSERT(part->selection().isNone());
459     
460     doReapply();
461     
462     m_state = Applied;
463
464     if (topLevel) {
465         document()->updateLayout();
466         EditCommandPtr cmd(this);
467         part->reappliedEditing(cmd);
468     }
469 }
470
471 void EditCommand::doReapply()
472 {
473     doApply();
474 }
475
476 void EditCommand::setStartingSelection(const Selection &s)
477 {
478     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent.get())
479         cmd->m_startingSelection = s;
480 }
481
482 void EditCommand::setEndingSelection(const Selection &s)
483 {
484     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent.get())
485         cmd->m_endingSelection = s;
486 }
487
488 void EditCommand::assignTypingStyle(CSSStyleDeclarationImpl *style)
489 {
490     CSSStyleDeclarationImpl *old = m_typingStyle;
491     m_typingStyle = style;
492     if (m_typingStyle)
493         m_typingStyle->ref();
494     if (old)
495         old->deref();
496 }
497
498 void EditCommand::setTypingStyle(CSSStyleDeclarationImpl *style)
499 {
500     // FIXME: Improve typing style.
501     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
502     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent.get())
503         cmd->assignTypingStyle(style);
504 }
505
506 bool EditCommand::preservesTypingStyle() const
507 {
508     return false;
509 }
510
511 bool EditCommand::isInputTextCommand() const
512 {
513     return false;
514 }
515
516 bool EditCommand::isTypingCommand() const
517 {
518     return false;
519 }
520
521 //------------------------------------------------------------------------------------------
522 // CompositeEditCommand
523
524 CompositeEditCommand::CompositeEditCommand(DocumentImpl *document) 
525     : EditCommand(document)
526 {
527 }
528
529 void CompositeEditCommand::doUnapply()
530 {
531     if (m_cmds.count() == 0) {
532         return;
533     }
534     
535     for (int i = m_cmds.count() - 1; i >= 0; --i)
536         m_cmds[i]->unapply();
537
538     setState(NotApplied);
539 }
540
541 void CompositeEditCommand::doReapply()
542 {
543     if (m_cmds.count() == 0) {
544         return;
545     }
546
547     for (QValueList<EditCommandPtr>::ConstIterator it = m_cmds.begin(); it != m_cmds.end(); ++it)
548         (*it)->reapply();
549
550     setState(Applied);
551 }
552
553 //
554 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
555 //
556 void CompositeEditCommand::applyCommandToComposite(EditCommandPtr &cmd)
557 {
558     cmd.setStartingSelection(endingSelection());
559     cmd.setEndingSelection(endingSelection());
560     cmd.setParent(this);
561     cmd.apply();
562     m_cmds.append(cmd);
563 }
564
565 void CompositeEditCommand::insertNodeBefore(NodeImpl *insertChild, NodeImpl *refChild)
566 {
567     EditCommandPtr cmd(new InsertNodeBeforeCommand(document(), insertChild, refChild));
568     applyCommandToComposite(cmd);
569 }
570
571 void CompositeEditCommand::insertNodeAfter(NodeImpl *insertChild, NodeImpl *refChild)
572 {
573     if (refChild->parentNode()->lastChild() == refChild) {
574         appendNode(insertChild, refChild->parentNode());
575     }
576     else {
577         ASSERT(refChild->nextSibling());
578         insertNodeBefore(insertChild, refChild->nextSibling());
579     }
580 }
581
582 void CompositeEditCommand::insertNodeAt(NodeImpl *insertChild, NodeImpl *refChild, long offset)
583 {
584     if (refChild->hasChildNodes() || (refChild->renderer() && refChild->renderer()->isBlockFlow())) {
585         NodeImpl *child = refChild->firstChild();
586         for (long i = 0; child && i < offset; i++)
587             child = child->nextSibling();
588         if (child)
589             insertNodeBefore(insertChild, child);
590         else
591             appendNode(insertChild, refChild);
592     } 
593     else if (refChild->caretMinOffset() >= offset) {
594         insertNodeBefore(insertChild, refChild);
595     } 
596     else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
597         splitTextNode(static_cast<TextImpl *>(refChild), offset);
598         insertNodeBefore(insertChild, refChild);
599     } 
600     else {
601         insertNodeAfter(insertChild, refChild);
602     }
603 }
604
605 void CompositeEditCommand::appendNode(NodeImpl *appendChild, NodeImpl *parent)
606 {
607     EditCommandPtr cmd(new AppendNodeCommand(document(), appendChild, parent));
608     applyCommandToComposite(cmd);
609 }
610
611 void CompositeEditCommand::removeFullySelectedNode(NodeImpl *node)
612 {
613     if (isTableStructureNode(node)) {
614         // Do not remove an element of table structure; remove its contents.
615         NodeImpl *child = node->firstChild();
616         while (child) {
617             NodeImpl *remove = child;
618             child = child->nextSibling();
619             removeFullySelectedNode(remove);
620         }
621     }
622     else {
623         EditCommandPtr cmd(new RemoveNodeCommand(document(), node));
624         applyCommandToComposite(cmd);
625     }
626 }
627
628 void CompositeEditCommand::removeNode(NodeImpl *removeChild)
629 {
630     EditCommandPtr cmd(new RemoveNodeCommand(document(), removeChild));
631     applyCommandToComposite(cmd);
632 }
633
634 void CompositeEditCommand::removeNodePreservingChildren(NodeImpl *removeChild)
635 {
636     EditCommandPtr cmd(new RemoveNodePreservingChildrenCommand(document(), removeChild));
637     applyCommandToComposite(cmd);
638 }
639
640 void CompositeEditCommand::splitTextNode(TextImpl *text, long offset)
641 {
642     EditCommandPtr cmd(new SplitTextNodeCommand(document(), text, offset));
643     applyCommandToComposite(cmd);
644 }
645
646 void CompositeEditCommand::joinTextNodes(TextImpl *text1, TextImpl *text2)
647 {
648     EditCommandPtr cmd(new JoinTextNodesCommand(document(), text1, text2));
649     applyCommandToComposite(cmd);
650 }
651
652 void CompositeEditCommand::inputText(const DOMString &text, bool selectInsertedText)
653 {
654     InputTextCommand *impl = new InputTextCommand(document());
655     EditCommandPtr cmd(impl);
656     applyCommandToComposite(cmd);
657     impl->input(text, selectInsertedText);
658 }
659
660 void CompositeEditCommand::insertText(TextImpl *node, long offset, const DOMString &text)
661 {
662     EditCommandPtr cmd(new InsertTextCommand(document(), node, offset, text));
663     applyCommandToComposite(cmd);
664 }
665
666 void CompositeEditCommand::deleteText(TextImpl *node, long offset, long count)
667 {
668     EditCommandPtr cmd(new DeleteTextCommand(document(), node, offset, count));
669     applyCommandToComposite(cmd);
670 }
671
672 void CompositeEditCommand::replaceText(TextImpl *node, long offset, long count, const DOMString &replacementText)
673 {
674     EditCommandPtr deleteCommand(new DeleteTextCommand(document(), node, offset, count));
675     applyCommandToComposite(deleteCommand);
676     EditCommandPtr insertCommand(new InsertTextCommand(document(), node, offset, replacementText));
677     applyCommandToComposite(insertCommand);
678 }
679
680 void CompositeEditCommand::deleteSelection(bool smartDelete)
681 {
682     if (endingSelection().isRange()) {
683         EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete));
684         applyCommandToComposite(cmd);
685     }
686 }
687
688 void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete)
689 {
690     if (selection.isRange()) {
691         EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete));
692         applyCommandToComposite(cmd);
693     }
694 }
695
696 void CompositeEditCommand::removeCSSProperty(CSSStyleDeclarationImpl *decl, int property)
697 {
698     EditCommandPtr cmd(new RemoveCSSPropertyCommand(document(), decl, property));
699     applyCommandToComposite(cmd);
700 }
701
702 void CompositeEditCommand::removeNodeAttribute(ElementImpl *element, int attribute)
703 {
704     EditCommandPtr cmd(new RemoveNodeAttributeCommand(document(), element, attribute));
705     applyCommandToComposite(cmd);
706 }
707
708 void CompositeEditCommand::setNodeAttribute(ElementImpl *element, int attribute, const DOMString &value)
709 {
710     EditCommandPtr cmd(new SetNodeAttributeCommand(document(), element, attribute, value));
711     applyCommandToComposite(cmd);
712 }
713
714 NodeImpl *CompositeEditCommand::applyTypingStyle(NodeImpl *child) const
715 {
716     // FIXME: This function should share code with ApplyStyleCommand::applyStyleIfNeeded
717     // and ApplyStyleCommand::computeStyleChange.
718     // Both function do similar work, and the common parts could be factored out.
719
720     // FIXME: Improve typing style.
721     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
722
723     // update document layout once before running the rest of the function
724     // so that we avoid the expense of updating before each and every call
725     // to check a computed style
726     document()->updateLayout();
727
728     StyleChange styleChange(document()->part()->typingStyle());
729
730     NodeImpl *childToAppend = child;
731     int exceptionCode = 0;
732
733     if (styleChange.applyItalic()) {
734         ElementImpl *italicElement = document()->createHTMLElement("I", exceptionCode);
735         ASSERT(exceptionCode == 0);
736         italicElement->appendChild(childToAppend, exceptionCode);
737         ASSERT(exceptionCode == 0);
738         childToAppend = italicElement;
739     }
740
741     if (styleChange.applyBold()) {
742         ElementImpl *boldElement = document()->createHTMLElement("B", exceptionCode);
743         ASSERT(exceptionCode == 0);
744         boldElement->appendChild(childToAppend, exceptionCode);
745         ASSERT(exceptionCode == 0);
746         childToAppend = boldElement;
747     }
748
749     if (styleChange.cssStyle().length() > 0) {
750         ElementImpl *styleElement = document()->createHTMLElement("SPAN", exceptionCode);
751         ASSERT(exceptionCode == 0);
752         styleElement->setAttribute(ATTR_STYLE, styleChange.cssStyle());
753         styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
754         styleElement->appendChild(childToAppend, exceptionCode);
755         ASSERT(exceptionCode == 0);
756         childToAppend = styleElement;
757     }
758
759     return childToAppend;
760 }
761
762 void CompositeEditCommand::deleteInsignificantText(TextImpl *textNode, int start, int end)
763 {
764     if (!textNode || !textNode->renderer() || start >= end)
765         return;
766
767     RenderText *textRenderer = static_cast<RenderText *>(textNode->renderer());
768     InlineTextBox *box = textRenderer->firstTextBox();
769     if (!box) {
770         // whole text node is empty
771         removeNode(textNode);
772         return;    
773     }
774     
775     long length = textNode->length();
776     if (start >= length || end > length)
777         return;
778
779     int removed = 0;
780     InlineTextBox *prevBox = 0;
781     DOMStringImpl *str = 0;
782
783     // This loop structure works to process all gaps preceding a box,
784     // and also will look at the gap after the last box.
785     while (prevBox || box) {
786         int gapStart = prevBox ? prevBox->m_start + prevBox->m_len : 0;
787         if (end < gapStart)
788             // No more chance for any intersections
789             break;
790
791         int gapEnd = box ? box->m_start : length;
792         bool indicesIntersect = start <= gapEnd && end >= gapStart;
793         int gapLen = gapEnd - gapStart;
794         if (indicesIntersect && gapLen > 0) {
795             gapStart = kMax(gapStart, start);
796             gapEnd = kMin(gapEnd, end);
797             if (!str) {
798                 str = textNode->string()->substring(start, end - start);
799                 str->ref();
800             }    
801             // remove text in the gap
802             str->remove(gapStart - start - removed, gapLen);
803             removed += gapLen;
804         }
805         
806         prevBox = box;
807         if (box)
808             box = box->nextTextBox();
809     }
810
811     if (str) {
812         // Replace the text between start and end with our pruned version.
813         if (str->l > 0) {
814             replaceText(textNode, start, end - start, str);
815         }
816         else {
817             // Assert that we are not going to delete all of the text in the node.
818             // If we were, that should have been done above with the call to 
819             // removeNode and return.
820             ASSERT(start > 0 || (unsigned long)end - start < textNode->length());
821             deleteText(textNode, start, end - start);
822         }
823         str->deref();
824     }
825 }
826
827 void CompositeEditCommand::deleteInsignificantText(const Position &start, const Position &end)
828 {
829     if (start.isNull() || end.isNull())
830         return;
831
832     if (RangeImpl::compareBoundaryPoints(start.node(), start.offset(), end.node(), end.offset()) >= 0)
833         return;
834
835     NodeImpl *node = start.node();
836     while (node) {
837         NodeImpl *next = node->traverseNextNode();
838     
839         if (node->isTextNode()) {
840             TextImpl *textNode = static_cast<TextImpl *>(node);
841             bool isStartNode = node == start.node();
842             bool isEndNode = node == end.node();
843             int startOffset = isStartNode ? start.offset() : 0;
844             int endOffset = isEndNode ? end.offset() : textNode->length();
845             deleteInsignificantText(textNode, startOffset, endOffset);
846         }
847             
848         if (node == end.node())
849             break;
850         node = next;
851     }
852 }
853
854 void CompositeEditCommand::deleteInsignificantTextDownstream(const DOM::Position &pos)
855 {
856     Position end = VisiblePosition(pos).next().deepEquivalent().downstream(StayInBlock);
857     deleteInsignificantText(pos, end);
858 }
859
860 void CompositeEditCommand::insertBlockPlaceholderIfNeeded(NodeImpl *node)
861 {
862     document()->updateLayout();
863
864     RenderObject *renderer = node->renderer();
865     if (!renderer->isBlockFlow())
866         return;
867     
868     if (renderer->height() > 0)
869         return;
870
871     int exceptionCode = 0;
872     ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
873     ASSERT(exceptionCode == 0);
874     breakNode->setAttribute(ATTR_CLASS, blockPlaceholderClassString());
875     appendNode(breakNode, node);
876 }
877
878 void CompositeEditCommand::removeBlockPlaceholderIfNeeded(NodeImpl *node)
879 {
880     document()->updateLayout();
881
882     RenderObject *renderer = node->renderer();
883     if (!renderer->isBlockFlow())
884         return;
885
886     // This code will remove a block placeholder if it still is at the end
887     // of a block, where we placed it in insertBlockPlaceholderIfNeeded().
888     // Of course, a person who hand-edits an HTML file could move a 
889     // placeholder around, but it seems OK to be unconcerned about that case.
890     NodeImpl *last = node->lastChild();
891     if (last && last->isHTMLElement()) {
892         ElementImpl *element = static_cast<ElementImpl *>(last);
893         if (element->getAttribute(ATTR_CLASS) == blockPlaceholderClassString())
894             removeNode(element);
895     }
896 }
897
898 //==========================================================================================
899 // Concrete commands
900 //------------------------------------------------------------------------------------------
901 // AppendNodeCommand
902
903 AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *appendChild, NodeImpl *parentNode)
904     : EditCommand(document), m_appendChild(appendChild), m_parentNode(parentNode)
905 {
906     ASSERT(m_appendChild);
907     m_appendChild->ref();
908
909     ASSERT(m_parentNode);
910     m_parentNode->ref();
911 }
912
913 AppendNodeCommand::~AppendNodeCommand()
914 {
915     ASSERT(m_appendChild);
916     m_appendChild->deref();
917
918     ASSERT(m_parentNode);
919     m_parentNode->deref();
920 }
921
922 void AppendNodeCommand::doApply()
923 {
924     ASSERT(m_appendChild);
925     ASSERT(m_parentNode);
926
927     int exceptionCode = 0;
928     m_parentNode->appendChild(m_appendChild, exceptionCode);
929     ASSERT(exceptionCode == 0);
930 }
931
932 void AppendNodeCommand::doUnapply()
933 {
934     ASSERT(m_appendChild);
935     ASSERT(m_parentNode);
936     ASSERT(state() == Applied);
937
938     int exceptionCode = 0;
939     m_parentNode->removeChild(m_appendChild, exceptionCode);
940     ASSERT(exceptionCode == 0);
941 }
942
943 //------------------------------------------------------------------------------------------
944 // ApplyStyleCommand
945
946 ApplyStyleCommand::ApplyStyleCommand(DocumentImpl *document, CSSStyleDeclarationImpl *style)
947     : CompositeEditCommand(document), m_style(style)
948 {   
949     ASSERT(m_style);
950     m_style->ref();
951 }
952
953 ApplyStyleCommand::~ApplyStyleCommand()
954 {
955     ASSERT(m_style);
956     m_style->deref();
957 }
958
959 void ApplyStyleCommand::doApply()
960 {
961     if (!endingSelection().isRange())
962         return;
963
964     // adjust to the positions we want to use for applying style
965     Position start(endingSelection().start().downstream(StayInBlock).equivalentRangeCompliantPosition());
966     Position end(endingSelection().end().upstream(StayInBlock));
967
968     // update document layout once before removing styles
969     // so that we avoid the expense of updating before each and every call
970     // to check a computed style
971     document()->updateLayout();
972
973     // Remove style from the selection.
974     // Use the upstream position of the start for removing style.
975     // This will ensure we remove all traces of the relevant styles from the selection
976     // and prevent us from adding redundant ones, as described in:
977     // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
978     removeStyle(start.upstream(), end);
979     
980     bool splitStart = splitTextAtStartIfNeeded(start, end); 
981     if (splitStart) {
982         start = endingSelection().start();
983         end = endingSelection().end();
984     }
985     splitTextAtEndIfNeeded(start, end);
986     start = endingSelection().start();
987     end = endingSelection().end();
988
989     // update document layout once before running the rest of the function
990     // so that we avoid the expense of updating before each and every call
991     // to check a computed style
992     document()->updateLayout();
993     
994     if (start.node() == end.node()) {
995         // simple case...start and end are the same node
996         applyStyleIfNeeded(start.node(), end.node());
997     }
998     else {
999         NodeImpl *node = start.node();
1000         while (1) {
1001             if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
1002                 NodeImpl *runStart = node;
1003                 while (1) {
1004                     NodeImpl *next = node->traverseNextNode();
1005                     // Break if node is the end node, or if the next node does not fit in with
1006                     // the current group.
1007                     if (node == end.node() || 
1008                         runStart->parentNode() != next->parentNode() || 
1009                         (next->isHTMLElement() && next->id() != ID_BR) || 
1010                         (next->renderer() && !next->renderer()->isInline()))
1011                         break;
1012                     node = next;
1013                 }
1014                 // Now apply style to the run we found.
1015                 applyStyleIfNeeded(runStart, node);
1016             }
1017             if (node == end.node())
1018                 break;
1019             node = node->traverseNextNode();
1020         }
1021     }
1022 }
1023
1024 //------------------------------------------------------------------------------------------
1025 // ApplyStyleCommand: style-removal helpers
1026
1027 bool ApplyStyleCommand::isHTMLStyleNode(HTMLElementImpl *elem)
1028 {
1029     for (QPtrListIterator<CSSProperty> it(*(style()->values())); it.current(); ++it) {
1030         CSSProperty *property = it.current();
1031         switch (property->id()) {
1032             case CSS_PROP_FONT_WEIGHT:
1033                 if (elem->id() == ID_B)
1034                     return true;
1035                 break;
1036             case CSS_PROP_FONT_STYLE:
1037                 if (elem->id() == ID_I)
1038                     return true;
1039                 break;
1040         }
1041     }
1042
1043     return false;
1044 }
1045
1046 void ApplyStyleCommand::removeHTMLStyleNode(HTMLElementImpl *elem)
1047 {
1048     // This node can be removed.
1049     // EDIT FIXME: This does not handle the case where the node
1050     // has attributes. But how often do people add attributes to <B> tags? 
1051     // Not so often I think.
1052     ASSERT(elem);
1053     removeNodePreservingChildren(elem);
1054 }
1055
1056 void ApplyStyleCommand::removeCSSStyle(HTMLElementImpl *elem)
1057 {
1058     ASSERT(elem);
1059
1060     CSSStyleDeclarationImpl *decl = elem->inlineStyleDecl();
1061     if (!decl)
1062         return;
1063
1064     for (QPtrListIterator<CSSProperty> it(*(style()->values())); it.current(); ++it) {
1065         CSSProperty *property = it.current();
1066         if (decl->getPropertyCSSValue(property->id()))
1067             removeCSSProperty(decl, property->id());
1068     }
1069
1070     if (elem->id() == ID_SPAN) {
1071         // Check to see if the span is one we added to apply style.
1072         // If it is, and there are no more attributes on the span other than our
1073         // class marker, remove the span.
1074         if (decl->values()->count() == 0) {
1075             removeNodeAttribute(elem, ATTR_STYLE);
1076             NamedAttrMapImpl *map = elem->attributes();
1077             if (map && map->length() == 1 && elem->getAttribute(ATTR_CLASS) == styleSpanClassString())
1078                 removeNodePreservingChildren(elem);
1079         }
1080     }
1081 }
1082
1083 void ApplyStyleCommand::removeStyle(const Position &start, const Position &end)
1084 {
1085     ASSERT(start.isNotNull());
1086     ASSERT(end.isNotNull());
1087     ASSERT(start.node()->inDocument());
1088     ASSERT(end.node()->inDocument());
1089     
1090     NodeImpl *node = start.node();
1091     while (node) {
1092         NodeImpl *next = node->traverseNextNode();
1093         if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
1094             HTMLElementImpl *elem = static_cast<HTMLElementImpl *>(node);
1095             if (isHTMLStyleNode(elem))
1096                 removeHTMLStyleNode(elem);
1097             else
1098                 removeCSSStyle(elem);
1099         }
1100         if (node == end.node())
1101             break;
1102         node = next;
1103     }
1104 }
1105
1106 bool ApplyStyleCommand::nodeFullySelected(NodeImpl *node, const Position &start, const Position &end) const
1107 {
1108     ASSERT(node);
1109
1110     Position pos = Position(node, node->childNodeCount()).upstream();
1111     return RangeImpl::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 &&
1112         RangeImpl::compareBoundaryPoints(pos.node(), pos.offset(), end.node(), end.offset()) <= 0;
1113 }
1114
1115 //------------------------------------------------------------------------------------------
1116 // ApplyStyleCommand: style-application helpers
1117
1118
1119 bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
1120 {
1121     if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
1122         long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
1123         TextImpl *text = static_cast<TextImpl *>(start.node());
1124         EditCommandPtr cmd(new SplitTextNodeCommand(document(), text, start.offset()));
1125         applyCommandToComposite(cmd);
1126         setEndingSelection(Selection(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)));
1127         return true;
1128     }
1129     return false;
1130 }
1131
1132 NodeImpl *ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end)
1133 {
1134     if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
1135         TextImpl *text = static_cast<TextImpl *>(end.node());
1136         SplitTextNodeCommand *impl = new SplitTextNodeCommand(document(), text, end.offset());
1137         EditCommandPtr cmd(impl);
1138         applyCommandToComposite(cmd);
1139         NodeImpl *startNode = start.node() == end.node() ? impl->node()->previousSibling() : start.node();
1140         ASSERT(startNode);
1141         setEndingSelection(Selection(Position(startNode, start.offset()), Position(impl->node()->previousSibling(), impl->node()->previousSibling()->caretMaxOffset())));
1142         return impl->node()->previousSibling();
1143     }
1144     return end.node();
1145 }
1146
1147 void ApplyStyleCommand::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
1148 {
1149     ASSERT(startNode);
1150     ASSERT(endNode);
1151     ASSERT(element);
1152     
1153     NodeImpl *node = startNode;
1154     while (1) {
1155         NodeImpl *next = node->traverseNextNode();
1156         if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
1157             removeNode(node);
1158             appendNode(node, element);
1159         }
1160         if (node == endNode)
1161             break;
1162         node = next;
1163     }
1164 }
1165
1166 void ApplyStyleCommand::applyStyleIfNeeded(NodeImpl *startNode, NodeImpl *endNode)
1167 {
1168     // FIXME: This function should share code with CompositeEditCommand::applyTypingStyle.
1169     // Both functions do similar work, and the common parts could be factored out.
1170
1171     StyleChange styleChange(style(), Position(startNode, 0));
1172     int exceptionCode = 0;
1173     
1174     if (styleChange.cssStyle().length() > 0) {
1175         ElementImpl *styleElement = document()->createHTMLElement("SPAN", exceptionCode);
1176         ASSERT(exceptionCode == 0);
1177         styleElement->setAttribute(ATTR_STYLE, styleChange.cssStyle());
1178         styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
1179         insertNodeBefore(styleElement, startNode);
1180         surroundNodeRangeWithElement(startNode, endNode, styleElement);
1181     }
1182
1183     if (styleChange.applyBold()) {
1184         ElementImpl *boldElement = document()->createHTMLElement("B", exceptionCode);
1185         ASSERT(exceptionCode == 0);
1186         insertNodeBefore(boldElement, startNode);
1187         surroundNodeRangeWithElement(startNode, endNode, boldElement);
1188     }
1189
1190     if (styleChange.applyItalic()) {
1191         ElementImpl *italicElement = document()->createHTMLElement("I", exceptionCode);
1192         ASSERT(exceptionCode == 0);
1193         insertNodeBefore(italicElement, startNode);
1194         surroundNodeRangeWithElement(startNode, endNode, italicElement);
1195     }
1196 }
1197
1198 Position ApplyStyleCommand::positionInsertionPoint(Position pos)
1199 {
1200     if (pos.node()->isTextNode() && (pos.offset() > 0 && pos.offset() < pos.node()->maxOffset())) {
1201         SplitTextNodeCommand *impl = new SplitTextNodeCommand(document(), static_cast<TextImpl *>(pos.node()), pos.offset());
1202         EditCommandPtr split(impl);
1203         split.apply();
1204         pos = Position(impl->node(), 0);
1205     }
1206
1207 #if 0
1208     // EDIT FIXME: If modified to work with the internals of applying style,
1209     // this code can work to optimize cases where a style change is taking place on
1210     // a boundary between nodes where one of the nodes has the desired style. In other
1211     // words, it is possible for content to be merged into existing nodes rather than adding
1212     // additional markup.
1213     if (currentlyHasStyle(pos))
1214         return pos;
1215         
1216     // try next node
1217     if (pos.offset() >= pos.node()->caretMaxOffset()) {
1218         NodeImpl *nextNode = pos.node()->traverseNextNode();
1219         if (nextNode) {
1220             Position next = Position(nextNode, 0);
1221             if (currentlyHasStyle(next))
1222                 return next;
1223         }
1224     }
1225
1226     // try previous node
1227     if (pos.offset() <= pos.node()->caretMinOffset()) {
1228         NodeImpl *prevNode = pos.node()->traversePreviousNode();
1229         if (prevNode) {
1230             Position prev = Position(prevNode, prevNode->maxOffset());
1231             if (currentlyHasStyle(prev))
1232                 return prev;
1233         }
1234     }
1235 #endif
1236     
1237     return pos;
1238 }
1239
1240 //------------------------------------------------------------------------------------------
1241 // DeleteSelectionCommand
1242
1243 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, bool smartDelete)
1244     : CompositeEditCommand(document), m_hasSelectionToDelete(false), m_smartDelete(smartDelete)
1245 {
1246 }
1247
1248 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, const Selection &selection, bool smartDelete)
1249     : CompositeEditCommand(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true), m_smartDelete(smartDelete)
1250 {
1251 }
1252
1253 // This function moves nodes in the block containing startNode to dstBlock, starting
1254 // from startNode and proceeding to the end of the block. Nodes in the block containing
1255 // startNode that appear in document order before startNode are not moved.
1256 // This function is an important helper for deleting selections that cross block
1257 // boundaries.
1258 void DeleteSelectionCommand::moveNodesAfterNode(NodeImpl *startNode, NodeImpl *dstNode)
1259 {
1260     if (!startNode || !dstNode)
1261         return;
1262
1263     NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
1264     if (isTableStructureNode(startBlock))
1265         // Do not move content between parts of a table
1266         return;
1267
1268     // Now that we are about to add content, check to see if a placeholder element
1269     // can be removed.
1270     removeBlockPlaceholderIfNeeded(startBlock);
1271
1272     NodeImpl *node = startNode == startBlock ? startBlock->firstChild() : startNode;
1273
1274     // Do the move.
1275     NodeImpl *refNode = dstNode;
1276     while (node && node->isAncestor(startBlock)) {
1277         NodeImpl *moveNode = node;
1278         node = node->nextSibling();
1279         removeNode(moveNode);
1280         insertNodeAfter(moveNode, refNode);
1281         refNode = moveNode;
1282     }
1283
1284     // If the startBlock no longer has any kids, we may need to deal with adding a BR
1285     // to make the layout come out right. Consider this document:
1286     //
1287     // One
1288     // <div>Two</div>
1289     // Three
1290     // 
1291     // Placing the insertion before before the 'T' of 'Two' and hitting delete will
1292     // move the contents of the div to the block containing 'One' and delete the div.
1293     // This will have the side effect of moving 'Three' on to the same line as 'One'
1294     // and 'Two'. This is undesirable. We fix this up by adding a BR before the 'Three'.
1295     // This may not be ideal, but it is better than nothing.
1296     document()->updateLayout();
1297     if (startBlock->renderer() && startBlock->renderer()->height() == 0) {
1298         removeNode(startBlock);
1299         if (refNode->renderer() && refNode->renderer()->inlineBox() && refNode->renderer()->inlineBox()->nextOnLineExists()) {
1300             int exceptionCode = 0;
1301             ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
1302             ASSERT(exceptionCode == 0);
1303             insertNodeAfter(breakNode, refNode);
1304         }
1305     }
1306 }
1307
1308 Position DeleteSelectionCommand::startPositionForDelete() const
1309 {
1310     Position pos = m_selectionToDelete.start();
1311     ASSERT(pos.node()->inDocument());
1312
1313     ElementImpl *rootElement = pos.node()->rootEditableElement();
1314     Position rootStart = Position(rootElement, 0);
1315     if (pos == VisiblePosition(rootStart).deepEquivalent())
1316         pos = rootStart;
1317     else if (m_smartDelete && pos.leadingWhitespacePosition().isNotNull())
1318         pos = VisiblePosition(pos).previous().deepEquivalent();
1319     return pos;
1320 }
1321
1322 Position DeleteSelectionCommand::endPositionForDelete() const
1323 {
1324     Position pos = m_selectionToDelete.end();
1325     ASSERT(pos.node()->inDocument());
1326
1327     ElementImpl *rootElement = pos.node()->rootEditableElement();
1328     Position rootEnd = Position(rootElement, rootElement ? rootElement->childNodeCount() : 0).equivalentDeepPosition();
1329     if (pos == VisiblePosition(rootEnd).deepEquivalent())
1330         pos = rootEnd;
1331     else if (m_smartDelete && pos.leadingWhitespacePosition().isNotNull())
1332         pos = VisiblePosition(pos).next().deepEquivalent();
1333     return pos;
1334 }
1335
1336 void DeleteSelectionCommand::doApply()
1337 {
1338     // If selection has not been set to a custom selection when the command was created,
1339     // use the current ending selection.
1340     if (!m_hasSelectionToDelete)
1341         m_selectionToDelete = endingSelection();
1342         
1343     if (!m_selectionToDelete.isRange())
1344         return;
1345
1346     Position start = startPositionForDelete();
1347     Position end = endPositionForDelete();
1348     Position upstreamStart(start.upstream(StayInBlock));
1349     Position downstreamStart(start.downstream(StayInBlock));
1350     Position upstreamEnd(end.upstream(StayInBlock));
1351     Position downstreamEnd(end.downstream(StayInBlock));
1352     Position endingPosition;
1353
1354     // Save away whitespace situation before doing any deletions
1355     Position leading = upstreamStart.leadingWhitespacePosition();
1356     Position trailing = downstreamEnd.trailingWhitespacePosition();
1357     bool trailingValid = true;
1358
1359     // Delete any text that may hinder our ability to fixup whitespace after the detele
1360     deleteInsignificantTextDownstream(trailing);    
1361     
1362     debugPosition("upstreamStart    ", upstreamStart);
1363     debugPosition("downstreamStart  ", downstreamStart);
1364     debugPosition("upstreamEnd      ", upstreamEnd);
1365     debugPosition("downstreamEnd    ", downstreamEnd);
1366     debugPosition("leading          ", leading);
1367     debugPosition("trailing         ", trailing);
1368     
1369     NodeImpl *startBlock = downstreamStart.node()->enclosingBlockFlowElement();
1370     NodeImpl *endBlock = upstreamEnd.node()->enclosingBlockFlowElement();
1371     if (!startBlock || !endBlock)
1372         // Can't figure out what blocks we're in. This can happen if
1373         // the document structure is not what we are expecting, like if
1374         // the document has no body element, or if the editable block
1375         // has been changed to display: inline. Some day it might
1376         // be nice to be able to deal with this, but for now, bail.
1377         return;
1378
1379     // Figure out the typing style in effect before the delete is done.
1380     // FIXME: Improve typing style.
1381     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
1382     CSSComputedStyleDeclarationImpl *computedStyle = downstreamStart.computedStyle();
1383     computedStyle->ref();
1384     CSSStyleDeclarationImpl *style = computedStyle->copyInheritableProperties();
1385     style->ref();
1386     computedStyle->deref();
1387
1388     NodeImpl *startNode = upstreamStart.node();
1389     int startOffset = upstreamStart.offset();
1390     if (startOffset >= startNode->caretMaxOffset()) {
1391         if (startNode->isTextNode()) {
1392             // Delete any insignificant text from this node.
1393             TextImpl *text = static_cast<TextImpl *>(startNode);
1394             if (text->length() > (unsigned)startNode->caretMaxOffset())
1395                 deleteText(text, startNode->caretMaxOffset(), text->length() - startNode->caretMaxOffset());
1396         }
1397         startNode = startNode->traverseNextNode();
1398         startOffset = 0;
1399     }
1400
1401     if (startNode == downstreamEnd.node()) {
1402         // handle delete in one node
1403         if (!startNode->renderer() || 
1404             (startOffset <= startNode->caretMinOffset() && downstreamEnd.offset() >= startNode->caretMaxOffset())) {
1405             // just delete
1406             removeFullySelectedNode(startNode);
1407         }
1408         else if (downstreamEnd.offset() - startOffset > 0) {
1409             // in a text node that needs to be trimmed
1410             TextImpl *text = static_cast<TextImpl *>(startNode);
1411             deleteText(text, startOffset, downstreamEnd.offset() - startOffset);
1412             trailingValid = false;
1413         }
1414     }
1415     else {
1416         NodeImpl *node = startNode;
1417         
1418         if (startOffset > 0) {
1419             // in a text node that needs to be trimmed
1420             TextImpl *text = static_cast<TextImpl *>(node);
1421             deleteText(text, startOffset, text->length() - startOffset);
1422             node = node->traverseNextNode();
1423         }
1424         
1425         // handle deleting all nodes that are completely selected
1426         while (node && node != downstreamEnd.node()) {
1427             if (!downstreamEnd.node()->isAncestor(node)) {
1428                 NodeImpl *nextNode = node->traverseNextSibling();
1429                 removeFullySelectedNode(node);
1430                 node = nextNode;
1431             }
1432             else {
1433                 NodeImpl *n = node->lastChild();
1434                 while (n && n->lastChild())
1435                     n = n->lastChild();
1436                 if (n == downstreamEnd.node() && downstreamEnd.offset() >= downstreamEnd.node()->caretMaxOffset()) {
1437                     NodeImpl *nextNode = node->traverseNextSibling();
1438                     removeFullySelectedNode(node);
1439                     node = nextNode;
1440                 } 
1441                 else {
1442                     node = node->traverseNextNode();
1443                 }
1444             }
1445         }
1446
1447         if (downstreamEnd.node() != startNode && downstreamEnd.node()->inDocument() && downstreamEnd.offset() >= downstreamEnd.node()->caretMinOffset()) {
1448             if (downstreamEnd.offset() >= downstreamEnd.node()->caretMaxOffset()) {
1449                 // need to delete whole node
1450                 // we can get here if this is the last node in the block
1451                 removeFullySelectedNode(downstreamEnd.node());
1452                 trailingValid = false;
1453             }
1454             else {
1455                 // in a text node that needs to be trimmed
1456                 TextImpl *text = static_cast<TextImpl *>(downstreamEnd.node());
1457                 if (downstreamEnd.offset() > 0) {
1458                     deleteText(text, 0, downstreamEnd.offset());
1459                     trailingValid = false;
1460                 }
1461             }
1462             if (!downstreamEnd.node()->inDocument() && downstreamEnd.node()->inDocument())
1463                 endingPosition = Position(downstreamEnd.node(), 0);
1464         }
1465     }
1466     
1467     // Do block merge if start and end of selection are in different blocks.
1468     if (endBlock != startBlock && downstreamEnd.node()->inDocument()) {
1469         LOG(Editing,  "merging content from end block");
1470         moveNodesAfterNode(downstreamEnd.node(), upstreamStart.node());
1471     }
1472       
1473     // Figure out where the end position should be
1474     if (endingPosition.isNotNull())
1475         goto FixupWhitespace;
1476
1477     endingPosition = upstreamStart;
1478     if (endingPosition.node()->inDocument())
1479         goto FixupWhitespace;
1480     
1481     endingPosition = downstreamEnd;
1482     if (endingPosition.node()->inDocument())
1483         goto FixupWhitespace;
1484
1485     endingPosition = Position(startBlock, 0);
1486     if (endingPosition.node()->inDocument())
1487         goto FixupWhitespace;
1488
1489     endingPosition = Position(endBlock, 0);
1490     if (endingPosition.node()->inDocument())
1491         goto FixupWhitespace;
1492
1493     endingPosition = Position(document()->documentElement(), 0);
1494
1495     // Perform whitespace fixup
1496     FixupWhitespace:
1497
1498     document()->updateLayout();
1499     if (leading.isNotNull() && (trailing.isNotNull() || !leading.isRenderedCharacter())) {
1500         LOG(Editing, "replace leading");
1501         TextImpl *textNode = static_cast<TextImpl *>(leading.node());
1502         replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
1503     }
1504     else if (trailing.isNotNull()) {
1505         if (trailingValid) {
1506             if (!trailing.isRenderedCharacter()) {
1507                 LOG(Editing, "replace trailing [valid]");
1508                 TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
1509                 replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
1510             }
1511         }
1512         else {
1513             Position pos = endingPosition.downstream(StayInBlock);
1514             pos = Position(pos.node(), pos.offset() - 1);
1515             if (isWS(pos) && !pos.isRenderedCharacter()) {
1516                 LOG(Editing, "replace trailing [invalid]");
1517                 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1518                 replaceText(textNode, pos.offset(), 1, nonBreakingSpaceString());
1519                 endingPosition = pos;
1520             }
1521         }
1522     }
1523
1524     debugPosition("endingPosition   ", endingPosition);
1525
1526     // If the delete emptied a block, add in a placeholder so the block does not
1527     // seem to disappear.
1528     insertBlockPlaceholderIfNeeded(endingPosition.node());
1529
1530     // Compute the difference between the style before the delete and the style now
1531     // after the delete has been done. Set this style on the part, so other editing
1532     // commands being composed with this one will work, and also cache it on the command,
1533     // so the KHTMLPart::appliedEditing can set it after the whole composite command 
1534     // has completed.
1535     // FIXME: Improve typing style.
1536     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
1537     if (startNode == endingPosition.node())
1538         document()->part()->setTypingStyle(0);
1539     else {
1540         CSSComputedStyleDeclarationImpl endingStyle(endingPosition.node());
1541         endingStyle.diff(style);
1542         if (!style->length()) {
1543             style->deref();
1544             style = 0;
1545         }
1546         document()->part()->setTypingStyle(style);
1547         setTypingStyle(style);
1548     }
1549     if (style)
1550         style->deref();
1551     setEndingSelection(endingPosition);
1552 }
1553
1554 bool DeleteSelectionCommand::preservesTypingStyle() const
1555 {
1556     return true;
1557 }
1558
1559 //------------------------------------------------------------------------------------------
1560 // DeleteTextCommand
1561
1562 DeleteTextCommand::DeleteTextCommand(DocumentImpl *document, TextImpl *node, long offset, long count)
1563     : EditCommand(document), m_node(node), m_offset(offset), m_count(count)
1564 {
1565     ASSERT(m_node);
1566     ASSERT(m_offset >= 0);
1567     ASSERT(m_offset < (long)m_node->length());
1568     ASSERT(m_count >= 0);
1569     
1570     m_node->ref();
1571 }
1572
1573 DeleteTextCommand::~DeleteTextCommand()
1574 {
1575     ASSERT(m_node);
1576     m_node->deref();
1577 }
1578
1579 void DeleteTextCommand::doApply()
1580 {
1581     ASSERT(m_node);
1582
1583     int exceptionCode = 0;
1584     m_text = m_node->substringData(m_offset, m_count, exceptionCode);
1585     ASSERT(exceptionCode == 0);
1586     
1587     m_node->deleteData(m_offset, m_count, exceptionCode);
1588     ASSERT(exceptionCode == 0);
1589 }
1590
1591 void DeleteTextCommand::doUnapply()
1592 {
1593     ASSERT(m_node);
1594     ASSERT(!m_text.isEmpty());
1595
1596     int exceptionCode = 0;
1597     m_node->insertData(m_offset, m_text, exceptionCode);
1598     ASSERT(exceptionCode == 0);
1599 }
1600
1601 //------------------------------------------------------------------------------------------
1602 // InputNewlineCommand
1603
1604 InputNewlineCommand::InputNewlineCommand(DocumentImpl *document) 
1605     : CompositeEditCommand(document)
1606 {
1607 }
1608
1609 void InputNewlineCommand::insertNodeAfterPosition(NodeImpl *node, const Position &pos)
1610 {
1611     // Insert the BR after the caret position. In the case the
1612     // position is a block, do an append. We don't want to insert
1613     // the BR *after* the block.
1614     Position upstream(pos.upstream(StayInBlock));
1615     NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1616     if (cb == pos.node())
1617         appendNode(node, cb);
1618     else
1619         insertNodeAfter(node, pos.node());
1620 }
1621
1622 void InputNewlineCommand::insertNodeBeforePosition(NodeImpl *node, const Position &pos)
1623 {
1624     // Insert the BR after the caret position. In the case the
1625     // position is a block, do an append. We don't want to insert
1626     // the BR *before* the block.
1627     Position upstream(pos.upstream(StayInBlock));
1628     NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1629     if (cb == pos.node())
1630         appendNode(node, cb);
1631     else
1632         insertNodeBefore(node, pos.node());
1633 }
1634
1635 void InputNewlineCommand::doApply()
1636 {
1637     deleteSelection();
1638     Selection selection = endingSelection();
1639
1640     int exceptionCode = 0;
1641     ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
1642     ASSERT(exceptionCode == 0);
1643
1644     NodeImpl *nodeToInsert = breakNode;
1645     
1646     // Handle the case where there is a typing style.
1647     // FIXME: Improve typing style.
1648     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
1649     CSSStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
1650     if (typingStyle && typingStyle->length() > 0)
1651         nodeToInsert = applyTypingStyle(breakNode);
1652     
1653     Position pos(selection.start().upstream(StayInBlock));
1654     bool atStart = pos.offset() <= pos.node()->caretMinOffset();
1655     bool atEnd = pos.offset() >= pos.node()->caretMaxOffset();
1656     bool atEndOfBlock = VisiblePosition(pos).isLastInBlock();
1657     
1658     if (atEndOfBlock) {
1659         LOG(Editing, "input newline case 1");
1660         // Check for a trailing BR. If there isn't one, we'll need to insert an "extra" one.
1661         // This makes the "real" BR we want to insert appear in the rendering without any 
1662         // significant side effects (and no real worries either since you can't arrow past 
1663         // this extra one.
1664         if (pos.node()->id() == ID_BR && pos.offset() == 0) {
1665             // Already placed in a trailing BR. Insert "real" BR before it and leave the selection alone.
1666             insertNodeBefore(nodeToInsert, pos.node());
1667         }
1668         else {
1669             NodeImpl *next = pos.node()->traverseNextNode();
1670             bool hasTrailingBR = next && next->id() == ID_BR && pos.node()->enclosingBlockFlowElement() == next->enclosingBlockFlowElement();
1671             insertNodeAfterPosition(nodeToInsert, pos);
1672             if (hasTrailingBR) {
1673                 setEndingSelection(Position(next, 0));
1674             }
1675             else {
1676                 // Insert an "extra" BR at the end of the block. 
1677                 ElementImpl *extraBreakNode = document()->createHTMLElement("BR", exceptionCode);
1678                 ASSERT(exceptionCode == 0);
1679                 insertNodeAfter(extraBreakNode, nodeToInsert);
1680                 setEndingSelection(Position(extraBreakNode, 0));
1681             }
1682         }
1683     }
1684     else if (atStart) {
1685         LOG(Editing, "input newline case 2");
1686         // Insert node before downstream position, and place caret there as well. 
1687         Position endingPosition = pos.downstream(StayInBlock);
1688         insertNodeBeforePosition(nodeToInsert, endingPosition);
1689         setEndingSelection(endingPosition);
1690     }
1691     else if (atEnd) {
1692         LOG(Editing, "input newline case 3");
1693         // Insert BR after this node. Place caret in the position that is downstream
1694         // of the current position, reckoned before inserting the BR in between.
1695         Position endingPosition = pos.downstream(StayInBlock);
1696         insertNodeAfterPosition(nodeToInsert, pos);
1697         setEndingSelection(endingPosition);
1698     }
1699     else {
1700         // Split a text node
1701         LOG(Editing, "input newline case 4");
1702         ASSERT(pos.node()->isTextNode());
1703         
1704         // Do the split
1705         TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1706         TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
1707         deleteText(textNode, 0, pos.offset());
1708         insertNodeBefore(textBeforeNode, textNode);
1709         insertNodeBefore(nodeToInsert, textNode);
1710         Position endingPosition = Position(textNode, 0);
1711         
1712         // Handle whitespace that occurs after the split
1713         document()->updateLayout();
1714         if (!endingPosition.isRenderedCharacter()) {
1715             // Clear out all whitespace and insert one non-breaking space
1716             deleteInsignificantTextDownstream(endingPosition);
1717             insertText(textNode, 0, nonBreakingSpaceString());
1718         }
1719         
1720         setEndingSelection(endingPosition);
1721     }
1722 }
1723
1724 //------------------------------------------------------------------------------------------
1725 // InputTextCommand
1726
1727 InputTextCommand::InputTextCommand(DocumentImpl *document) 
1728     : CompositeEditCommand(document), m_charactersAdded(0)
1729 {
1730 }
1731
1732 void InputTextCommand::doApply()
1733 {
1734 }
1735
1736 void InputTextCommand::deleteCharacter()
1737 {
1738     ASSERT(state() == Applied);
1739
1740     Selection selection = endingSelection();
1741
1742     if (!selection.start().node()->isTextNode())
1743         return;
1744
1745     int exceptionCode = 0;
1746     int offset = selection.start().offset() - 1;
1747     if (offset >= selection.start().node()->caretMinOffset()) {
1748         TextImpl *textNode = static_cast<TextImpl *>(selection.start().node());
1749         textNode->deleteData(offset, 1, exceptionCode);
1750         ASSERT(exceptionCode == 0);
1751         selection = Selection(Position(textNode, offset));
1752         setEndingSelection(selection);
1753         m_charactersAdded--;
1754     }
1755 }
1756
1757 Position InputTextCommand::prepareForTextInsertion(bool adjustDownstream)
1758 {
1759     // Prepare for text input by looking at the current position.
1760     // It may be necessary to insert a text node to receive characters.
1761     Selection selection = endingSelection();
1762     ASSERT(selection.isCaret());
1763     
1764     Position pos = selection.start();
1765     if (adjustDownstream)
1766         pos = pos.downstream(StayInBlock);
1767     else
1768         pos = pos.upstream(StayInBlock);
1769     
1770     if (!pos.node()->isTextNode()) {
1771         NodeImpl *textNode = document()->createEditingTextNode("");
1772         NodeImpl *nodeToInsert = textNode;
1773
1774         // Handle the case where there is a typing style.
1775         // FIXME: Improve typing style.
1776         // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
1777         CSSStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
1778         if (typingStyle && typingStyle->length() > 0)
1779             nodeToInsert = applyTypingStyle(textNode);
1780         
1781         // Now insert the node in the right place
1782         if (pos.node()->isEditableBlock()) {
1783             LOG(Editing, "prepareForTextInsertion case 1");
1784             appendNode(nodeToInsert, pos.node());
1785         }
1786         else if (pos.node()->caretMinOffset() == pos.offset()) {
1787             LOG(Editing, "prepareForTextInsertion case 2");
1788             insertNodeBefore(nodeToInsert, pos.node());
1789         }
1790         else if (pos.node()->caretMaxOffset() == pos.offset()) {
1791             LOG(Editing, "prepareForTextInsertion case 3");
1792             insertNodeAfter(nodeToInsert, pos.node());
1793         }
1794         else
1795             ASSERT_NOT_REACHED();
1796         
1797         pos = Position(textNode, 0);
1798     }
1799     else {
1800         // Handle the case where there is a typing style.
1801         // FIXME: Improve typing style.
1802         // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
1803         CSSStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
1804         if (typingStyle && typingStyle->length() > 0) {
1805             if (pos.node()->isTextNode() && pos.offset() > pos.node()->caretMinOffset() && pos.offset() < pos.node()->caretMaxOffset()) {
1806                 // Need to split current text node in order to insert a span.
1807                 TextImpl *text = static_cast<TextImpl *>(pos.node());
1808                 SplitTextNodeCommand *impl = new SplitTextNodeCommand(document(), text, pos.offset());
1809                 EditCommandPtr cmd(impl);
1810                 applyCommandToComposite(cmd);
1811                 setEndingSelection(Position(impl->node(), 0));
1812             }
1813             
1814             TextImpl *editingTextNode = document()->createEditingTextNode("");
1815             NodeImpl *node = endingSelection().start().upstream(StayInBlock).node();
1816             if (node->isBlockFlow())
1817                 insertNodeAt(applyTypingStyle(editingTextNode), node, 0);
1818             else
1819                 insertNodeAfter(applyTypingStyle(editingTextNode), node);
1820             pos = Position(editingTextNode, 0);
1821         }
1822     }
1823     return pos;
1824 }
1825
1826 void InputTextCommand::input(const DOMString &text, bool selectInsertedText)
1827 {
1828     Selection selection = endingSelection();
1829     bool adjustDownstream = selection.start().downstream(StayInBlock).isFirstRenderedPositionOnLine();
1830
1831     // Delete the current selection, or collapse whitespace, as needed
1832     if (selection.isRange())
1833         deleteSelection();
1834     
1835     // Delete any insignificant text that could get in the way of whitespace turning
1836     // out correctly after the insertion.
1837     deleteInsignificantTextDownstream(endingSelection().end().trailingWhitespacePosition());
1838     
1839     // Make sure the document is set up to receive text
1840     Position pos = prepareForTextInsertion(adjustDownstream);
1841     
1842     TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1843     long offset = pos.offset();
1844
1845     // Now that we are about to add content, check to see if a placeholder element
1846     // can be removed.
1847     removeBlockPlaceholderIfNeeded(textNode->enclosingBlockFlowElement());
1848     
1849     // These are temporary implementations for inserting adjoining spaces
1850     // into a document. We are working on a CSS-related whitespace solution
1851     // that will replace this some day. We hope.
1852     if (isTab(text)) {
1853         // Treat a tab like a number of spaces. This seems to be the HTML editing convention,
1854         // although the number of spaces varies (we choose four spaces). 
1855         // Note that there is no attempt to make this work like a real tab stop, it is merely 
1856         // a set number of spaces. This also seems to be the HTML editing convention.
1857         for (int i = 0; i < spacesPerTab; i++) {
1858             insertSpace(textNode, offset);
1859             document()->updateLayout();
1860         }
1861         if (selectInsertedText)
1862             setEndingSelection(Selection(Position(textNode, offset), Position(textNode, offset + spacesPerTab)));
1863         else
1864             setEndingSelection(Position(textNode, offset + spacesPerTab));
1865         m_charactersAdded += spacesPerTab;
1866     }
1867     else if (isWS(text)) {
1868         insertSpace(textNode, offset);
1869         if (selectInsertedText)
1870             setEndingSelection(Selection(Position(textNode, offset), Position(textNode, offset + 1)));
1871         else
1872             setEndingSelection(Position(textNode, offset + 1));
1873         m_charactersAdded++;
1874     }
1875     else {
1876         const DOMString &existingText = textNode->data();
1877         if (textNode->length() >= 2 && offset >= 2 && isNBSP(existingText[offset - 1]) && !isWS(existingText[offset - 2])) {
1878             // DOM looks like this:
1879             // character nbsp caret
1880             // As we are about to insert a non-whitespace character at the caret
1881             // convert the nbsp to a regular space.
1882             // EDIT FIXME: This needs to be improved some day to convert back only
1883             // those nbsp's added by the editor to make rendering come out right.
1884             replaceText(textNode, offset - 1, 1, " ");
1885         }
1886         insertText(textNode, offset, text);
1887         if (selectInsertedText)
1888             setEndingSelection(Selection(Position(textNode, offset), Position(textNode, offset + text.length())));
1889         else
1890             setEndingSelection(Position(textNode, offset + text.length()));
1891         m_charactersAdded += text.length();
1892     }
1893 }
1894
1895 void InputTextCommand::insertSpace(TextImpl *textNode, unsigned long offset)
1896 {
1897     ASSERT(textNode);
1898
1899     DOMString text(textNode->data());
1900
1901     // count up all spaces and newlines in front of the caret
1902     // delete all collapsed ones
1903     // this will work out OK since the offset we have been passed has been upstream-ized 
1904     int count = 0;
1905     for (unsigned int i = offset; i < text.length(); i++) {
1906         if (isWS(text[i]))
1907             count++;
1908         else 
1909             break;
1910     }
1911     if (count > 0) {
1912         // By checking the character at the downstream position, we can
1913         // check if there is a rendered WS at the caret
1914         Position pos(textNode, offset);
1915         Position downstream = pos.downstream();
1916         if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
1917             count--; // leave this WS in
1918         if (count > 0)
1919             deleteText(textNode, offset, count);
1920     }
1921
1922     if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
1923         // insert a "regular" space
1924         insertText(textNode, offset, " ");
1925         return;
1926     }
1927
1928     if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
1929         // DOM looks like this:
1930         // nbsp nbsp caret
1931         // insert a space between the two nbsps
1932         insertText(textNode, offset - 1, " ");
1933         return;
1934     }
1935
1936     // insert an nbsp
1937     insertText(textNode, offset, nonBreakingSpaceString());
1938 }
1939
1940 bool InputTextCommand::isInputTextCommand() const
1941 {
1942     return true;
1943 }
1944
1945 //------------------------------------------------------------------------------------------
1946 // InsertNodeBeforeCommand
1947
1948 InsertNodeBeforeCommand::InsertNodeBeforeCommand(DocumentImpl *document, NodeImpl *insertChild, NodeImpl *refChild)
1949     : EditCommand(document), m_insertChild(insertChild), m_refChild(refChild)
1950 {
1951     ASSERT(m_insertChild);
1952     m_insertChild->ref();
1953
1954     ASSERT(m_refChild);
1955     m_refChild->ref();
1956 }
1957
1958 InsertNodeBeforeCommand::~InsertNodeBeforeCommand()
1959 {
1960     ASSERT(m_insertChild);
1961     m_insertChild->deref();
1962
1963     ASSERT(m_refChild);
1964     m_refChild->deref();
1965 }
1966
1967 void InsertNodeBeforeCommand::doApply()
1968 {
1969     ASSERT(m_insertChild);
1970     ASSERT(m_refChild);
1971     ASSERT(m_refChild->parentNode());
1972
1973     int exceptionCode = 0;
1974     m_refChild->parentNode()->insertBefore(m_insertChild, m_refChild, exceptionCode);
1975     ASSERT(exceptionCode == 0);
1976 }
1977
1978 void InsertNodeBeforeCommand::doUnapply()
1979 {
1980     ASSERT(m_insertChild);
1981     ASSERT(m_refChild);
1982     ASSERT(m_refChild->parentNode());
1983
1984     int exceptionCode = 0;
1985     m_refChild->parentNode()->removeChild(m_insertChild, exceptionCode);
1986     ASSERT(exceptionCode == 0);
1987 }
1988
1989 //------------------------------------------------------------------------------------------
1990 // InsertTextCommand
1991
1992 InsertTextCommand::InsertTextCommand(DocumentImpl *document, TextImpl *node, long offset, const DOMString &text)
1993     : EditCommand(document), m_node(node), m_offset(offset)
1994 {
1995     ASSERT(m_node);
1996     ASSERT(m_offset >= 0);
1997     ASSERT(!text.isEmpty());
1998     
1999     m_node->ref();
2000     m_text = text.copy(); // make a copy to ensure that the string never changes
2001 }
2002
2003 InsertTextCommand::~InsertTextCommand()
2004 {
2005     if (m_node)
2006         m_node->deref();
2007 }
2008
2009 void InsertTextCommand::doApply()
2010 {
2011     ASSERT(m_node);
2012     ASSERT(m_offset >= 0);
2013     ASSERT(!m_text.isEmpty());
2014
2015     int exceptionCode = 0;
2016     m_node->insertData(m_offset, m_text, exceptionCode);
2017     ASSERT(exceptionCode == 0);
2018 }
2019
2020 void InsertTextCommand::doUnapply()
2021 {
2022     ASSERT(m_node);
2023     ASSERT(m_offset >= 0);
2024     ASSERT(!m_text.isEmpty());
2025
2026     int exceptionCode = 0;
2027     m_node->deleteData(m_offset, m_text.length(), exceptionCode);
2028     ASSERT(exceptionCode == 0);
2029 }
2030
2031 //------------------------------------------------------------------------------------------
2032 // JoinTextNodesCommand
2033
2034 JoinTextNodesCommand::JoinTextNodesCommand(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
2035     : EditCommand(document), m_text1(text1), m_text2(text2)
2036 {
2037     ASSERT(m_text1);
2038     ASSERT(m_text2);
2039     ASSERT(m_text1->nextSibling() == m_text2);
2040     ASSERT(m_text1->length() > 0);
2041     ASSERT(m_text2->length() > 0);
2042
2043     m_text1->ref();
2044     m_text2->ref();
2045 }
2046
2047 JoinTextNodesCommand::~JoinTextNodesCommand()
2048 {
2049     ASSERT(m_text1);
2050     m_text1->deref();
2051     ASSERT(m_text2);
2052     m_text2->deref();
2053 }
2054
2055 void JoinTextNodesCommand::doApply()
2056 {
2057     ASSERT(m_text1);
2058     ASSERT(m_text2);
2059     ASSERT(m_text1->nextSibling() == m_text2);
2060
2061     int exceptionCode = 0;
2062     m_text2->insertData(0, m_text1->data(), exceptionCode);
2063     ASSERT(exceptionCode == 0);
2064
2065     m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2066     ASSERT(exceptionCode == 0);
2067
2068     m_offset = m_text1->length();
2069 }
2070
2071 void JoinTextNodesCommand::doUnapply()
2072 {
2073     ASSERT(m_text2);
2074     ASSERT(m_offset > 0);
2075
2076     int exceptionCode = 0;
2077
2078     m_text2->deleteData(0, m_offset, exceptionCode);
2079     ASSERT(exceptionCode == 0);
2080
2081     m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2082     ASSERT(exceptionCode == 0);
2083         
2084     ASSERT(m_text2->previousSibling()->isTextNode());
2085     ASSERT(m_text2->previousSibling() == m_text1);
2086 }
2087
2088 //------------------------------------------------------------------------------------------
2089 // ReplaceSelectionCommand
2090
2091 ReplaceSelectionCommand::ReplaceSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, bool selectReplacement, bool smartReplace) 
2092     : CompositeEditCommand(document), m_fragment(fragment), m_selectReplacement(selectReplacement), m_smartReplace(smartReplace)
2093 {
2094     ASSERT(m_fragment);
2095     m_fragment->ref();
2096 }
2097
2098 ReplaceSelectionCommand::~ReplaceSelectionCommand()
2099 {
2100     ASSERT(m_fragment);
2101     m_fragment->deref();
2102 }
2103
2104 void ReplaceSelectionCommand::doApply()
2105 {
2106     NodeImpl *firstChild = m_fragment->firstChild();
2107     NodeImpl *lastChild = m_fragment->lastChild();
2108
2109     Selection selection = endingSelection();
2110
2111     // Delete the current selection, or collapse whitespace, as needed
2112     if (selection.isRange())
2113         deleteSelection();
2114     
2115     // This command does not use any typing style that is set as a residual effect of
2116     // a delete.
2117     // FIXME: Improve typing style.
2118     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
2119     document()->part()->clearTypingStyle();
2120     setTypingStyle(0);
2121     
2122     selection = endingSelection();
2123     ASSERT(selection.isCaret());
2124
2125     // Now that we are about to add content, check to see if a placeholder element
2126     // can be removed.
2127     removeBlockPlaceholderIfNeeded(selection.start().node()->enclosingBlockFlowElement());
2128     
2129     bool addLeadingSpace = false;
2130     bool addTrailingSpace = false;
2131     if (m_smartReplace) {
2132         addLeadingSpace = selection.start().leadingWhitespacePosition().isNull();
2133         addTrailingSpace = selection.start().trailingWhitespacePosition().isNull();
2134     }
2135     
2136     if (!firstChild) {
2137         // Pasting something that didn't parse or was empty.
2138         ASSERT(!lastChild);
2139     } else if (firstChild == lastChild && firstChild->isTextNode()) {
2140         // FIXME: HTML fragment case needs to be improved to the point
2141         // where we can remove this separate case.
2142         
2143         // Simple text paste. Treat as if the text were typed.
2144         Position upstreamStart(selection.start().upstream(StayInBlock));
2145         DOMString text = static_cast<TextImpl *>(firstChild)->data();
2146         if (addLeadingSpace) {
2147             text = " " + text;
2148         }
2149         if (addTrailingSpace) {
2150             text += " ";
2151         }
2152         inputText(text, m_selectReplacement);
2153     } 
2154     else {
2155         // HTML fragment paste.
2156         
2157         // FIXME: Add leading and trailing spaces to the fragment?
2158         // Or just insert them as we insert it?
2159         
2160         NodeImpl *beforeNode = firstChild;
2161         NodeImpl *node = firstChild->nextSibling();
2162         
2163         insertNodeAt(firstChild, selection.start().node(), selection.start().offset());
2164         
2165         // Insert the nodes from the fragment
2166         while (node) {
2167             NodeImpl *next = node->nextSibling();
2168             insertNodeAfter(node, beforeNode);
2169             beforeNode = node;
2170             node = next;
2171         }
2172         ASSERT(beforeNode);
2173         
2174         // Find the last leaf.
2175         NodeImpl *lastLeaf = lastChild;
2176         while (1) {
2177             NodeImpl *nextChild = lastLeaf->lastChild();
2178             if (!nextChild)
2179                 break;
2180             lastLeaf = nextChild;
2181         }
2182
2183         // Find the first leaf.
2184         NodeImpl *firstLeaf = firstChild;
2185         while (1) {
2186             NodeImpl *nextChild = firstLeaf->firstChild();
2187             if (!nextChild)
2188                 break;
2189             firstLeaf = nextChild;
2190         }
2191         
2192         Selection replacementSelection(Position(firstLeaf, firstLeaf->caretMinOffset()), Position(lastLeaf, lastLeaf->caretMaxOffset()));
2193         if (m_selectReplacement) {
2194             // Select what was inserted.
2195             setEndingSelection(replacementSelection);
2196         } 
2197         else {
2198             // Place the cursor after what was inserted, and mark misspellings in the inserted content.
2199             selection = Selection(Position(lastLeaf, lastLeaf->caretMaxOffset()));
2200             setEndingSelection(selection);
2201         }
2202     }
2203 }
2204
2205 //------------------------------------------------------------------------------------------
2206 // MoveSelectionCommand
2207
2208 MoveSelectionCommand::MoveSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, Position &position, bool smartMove) 
2209     : CompositeEditCommand(document), m_fragment(fragment), m_position(position), m_smartMove(smartMove)
2210 {
2211     ASSERT(m_fragment);
2212     m_fragment->ref();
2213 }
2214
2215 MoveSelectionCommand::~MoveSelectionCommand()
2216 {
2217     ASSERT(m_fragment);
2218     m_fragment->deref();
2219 }
2220
2221 void MoveSelectionCommand::doApply()
2222 {
2223     Selection selection = endingSelection();
2224     ASSERT(selection.isRange());
2225
2226     // Update the position otherwise it may become invalid after the selection is deleted.
2227     NodeImpl *positionNode = m_position.node();
2228     long positionOffset = m_position.offset();
2229     Position selectionEnd = selection.end();
2230     long selectionEndOffset = selectionEnd.offset();    
2231     if (selectionEnd.node() == positionNode && selectionEndOffset < positionOffset) {
2232         positionOffset -= selectionEndOffset;
2233         Position selectionStart = selection.start();
2234         if (selectionStart.node() == positionNode) {
2235             positionOffset += selectionStart.offset();
2236         }
2237     }
2238     
2239     deleteSelection(m_smartMove);
2240
2241     setEndingSelection(Position(positionNode, positionOffset));
2242     EditCommandPtr cmd(new ReplaceSelectionCommand(document(), m_fragment, true, m_smartMove));
2243     applyCommandToComposite(cmd);
2244 }
2245
2246 //------------------------------------------------------------------------------------------
2247 // RemoveCSSPropertyCommand
2248
2249 RemoveCSSPropertyCommand::RemoveCSSPropertyCommand(DocumentImpl *document, CSSStyleDeclarationImpl *decl, int property)
2250     : EditCommand(document), m_decl(decl), m_property(property), m_important(false)
2251 {
2252     ASSERT(m_decl);
2253     m_decl->ref();
2254 }
2255
2256 RemoveCSSPropertyCommand::~RemoveCSSPropertyCommand()
2257 {
2258     ASSERT(m_decl);
2259     m_decl->deref();
2260 }
2261
2262 void RemoveCSSPropertyCommand::doApply()
2263 {
2264     ASSERT(m_decl);
2265
2266     m_oldValue = m_decl->getPropertyValue(m_property);
2267     ASSERT(!m_oldValue.isNull());
2268
2269     m_important = m_decl->getPropertyPriority(m_property);
2270     m_decl->removeProperty(m_property);
2271 }
2272
2273 void RemoveCSSPropertyCommand::doUnapply()
2274 {
2275     ASSERT(m_decl);
2276     ASSERT(!m_oldValue.isNull());
2277
2278     m_decl->setProperty(m_property, m_oldValue, m_important);
2279 }
2280
2281 //------------------------------------------------------------------------------------------
2282 // RemoveNodeAttributeCommand
2283
2284 RemoveNodeAttributeCommand::RemoveNodeAttributeCommand(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute)
2285     : EditCommand(document), m_element(element), m_attribute(attribute)
2286 {
2287     ASSERT(m_element);
2288     m_element->ref();
2289 }
2290
2291 RemoveNodeAttributeCommand::~RemoveNodeAttributeCommand()
2292 {
2293     ASSERT(m_element);
2294     m_element->deref();
2295 }
2296
2297 void RemoveNodeAttributeCommand::doApply()
2298 {
2299     ASSERT(m_element);
2300
2301     m_oldValue = m_element->getAttribute(m_attribute);
2302     ASSERT(!m_oldValue.isNull());
2303
2304     int exceptionCode = 0;
2305     m_element->removeAttribute(m_attribute, exceptionCode);
2306     ASSERT(exceptionCode == 0);
2307 }
2308
2309 void RemoveNodeAttributeCommand::doUnapply()
2310 {
2311     ASSERT(m_element);
2312     ASSERT(!m_oldValue.isNull());
2313
2314     int exceptionCode = 0;
2315     m_element->setAttribute(m_attribute, m_oldValue.implementation(), exceptionCode);
2316     ASSERT(exceptionCode == 0);
2317 }
2318
2319 //------------------------------------------------------------------------------------------
2320 // RemoveNodeCommand
2321
2322 RemoveNodeCommand::RemoveNodeCommand(DocumentImpl *document, NodeImpl *removeChild)
2323     : EditCommand(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
2324 {
2325     ASSERT(m_removeChild);
2326     m_removeChild->ref();
2327
2328     m_parent = m_removeChild->parentNode();
2329     ASSERT(m_parent);
2330     m_parent->ref();
2331     
2332     m_refChild = m_removeChild->nextSibling();
2333     if (m_refChild)
2334         m_refChild->ref();
2335 }
2336
2337 RemoveNodeCommand::~RemoveNodeCommand()
2338 {
2339     ASSERT(m_parent);
2340     m_parent->deref();
2341
2342     ASSERT(m_removeChild);
2343     m_removeChild->deref();
2344
2345     if (m_refChild)
2346         m_refChild->deref();
2347 }
2348
2349 void RemoveNodeCommand::doApply()
2350 {
2351     ASSERT(m_parent);
2352     ASSERT(m_removeChild);
2353
2354     int exceptionCode = 0;
2355     m_parent->removeChild(m_removeChild, exceptionCode);
2356     ASSERT(exceptionCode == 0);
2357 }
2358
2359 void RemoveNodeCommand::doUnapply()
2360 {
2361     ASSERT(m_parent);
2362     ASSERT(m_removeChild);
2363
2364     int exceptionCode = 0;
2365     m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
2366     ASSERT(exceptionCode == 0);
2367 }
2368
2369 //------------------------------------------------------------------------------------------
2370 // RemoveNodePreservingChildrenCommand
2371
2372 RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand(DocumentImpl *document, NodeImpl *node)
2373     : CompositeEditCommand(document), m_node(node)
2374 {
2375     ASSERT(m_node);
2376     m_node->ref();
2377 }
2378
2379 RemoveNodePreservingChildrenCommand::~RemoveNodePreservingChildrenCommand()
2380 {
2381     ASSERT(m_node);
2382     m_node->deref();
2383 }
2384
2385 void RemoveNodePreservingChildrenCommand::doApply()
2386 {
2387     while (NodeImpl* curr = node()->firstChild()) {
2388         removeNode(curr);
2389         insertNodeBefore(curr, node());
2390     }
2391     removeNode(node());
2392 }
2393
2394 //------------------------------------------------------------------------------------------
2395 // SetNodeAttributeCommand
2396
2397 SetNodeAttributeCommand::SetNodeAttributeCommand(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute, const DOMString &value)
2398     : EditCommand(document), m_element(element), m_attribute(attribute), m_value(value)
2399 {
2400     ASSERT(m_element);
2401     m_element->ref();
2402     ASSERT(!m_value.isNull());
2403 }
2404
2405 SetNodeAttributeCommand::~SetNodeAttributeCommand()
2406 {
2407     ASSERT(m_element);
2408     m_element->deref();
2409 }
2410
2411 void SetNodeAttributeCommand::doApply()
2412 {
2413     ASSERT(m_element);
2414     ASSERT(!m_value.isNull());
2415
2416     int exceptionCode = 0;
2417     m_oldValue = m_element->getAttribute(m_attribute);
2418     m_element->setAttribute(m_attribute, m_value.implementation(), exceptionCode);
2419     ASSERT(exceptionCode == 0);
2420 }
2421
2422 void SetNodeAttributeCommand::doUnapply()
2423 {
2424     ASSERT(m_element);
2425     ASSERT(!m_oldValue.isNull());
2426
2427     int exceptionCode = 0;
2428     m_element->setAttribute(m_attribute, m_oldValue.implementation(), exceptionCode);
2429     ASSERT(exceptionCode == 0);
2430 }
2431
2432 //------------------------------------------------------------------------------------------
2433 // SplitTextNodeCommand
2434
2435 SplitTextNodeCommand::SplitTextNodeCommand(DocumentImpl *document, TextImpl *text, long offset)
2436     : EditCommand(document), m_text1(0), m_text2(text), m_offset(offset)
2437 {
2438     ASSERT(m_text2);
2439     ASSERT(m_text2->length() > 0);
2440
2441     m_text2->ref();
2442 }
2443
2444 SplitTextNodeCommand::~SplitTextNodeCommand()
2445 {
2446     if (m_text1)
2447         m_text1->deref();
2448
2449     ASSERT(m_text2);
2450     m_text2->deref();
2451 }
2452
2453 void SplitTextNodeCommand::doApply()
2454 {
2455     ASSERT(m_text2);
2456     ASSERT(m_offset > 0);
2457
2458     int exceptionCode = 0;
2459
2460     // EDIT FIXME: This should use better smarts for figuring out which portion
2461     // of the split to copy (based on their comparitive sizes). We should also
2462     // just use the DOM's splitText function.
2463     
2464     if (!m_text1) {
2465         // create only if needed.
2466         // if reapplying, this object will already exist.
2467         m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
2468         ASSERT(exceptionCode == 0);
2469         ASSERT(m_text1);
2470         m_text1->ref();
2471     }
2472
2473     m_text2->deleteData(0, m_offset, exceptionCode);
2474     ASSERT(exceptionCode == 0);
2475
2476     m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2477     ASSERT(exceptionCode == 0);
2478         
2479     ASSERT(m_text2->previousSibling()->isTextNode());
2480     ASSERT(m_text2->previousSibling() == m_text1);
2481 }
2482
2483 void SplitTextNodeCommand::doUnapply()
2484 {
2485     ASSERT(m_text1);
2486     ASSERT(m_text2);
2487     
2488     ASSERT(m_text1->nextSibling() == m_text2);
2489
2490     int exceptionCode = 0;
2491     m_text2->insertData(0, m_text1->data(), exceptionCode);
2492     ASSERT(exceptionCode == 0);
2493
2494     m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2495     ASSERT(exceptionCode == 0);
2496
2497     m_offset = m_text1->length();
2498 }
2499
2500 //------------------------------------------------------------------------------------------
2501 // TypingCommand
2502
2503 TypingCommand::TypingCommand(DocumentImpl *document, ETypingCommand commandType, const DOMString &textToInsert, bool selectInsertedText)
2504     : CompositeEditCommand(document), m_commandType(commandType), m_textToInsert(textToInsert), m_openForMoreTyping(true), m_applyEditing(false), m_selectInsertedText(selectInsertedText)
2505 {
2506 }
2507
2508 void TypingCommand::deleteKeyPressed(DocumentImpl *document)
2509 {
2510     ASSERT(document);
2511     
2512     KHTMLPart *part = document->part();
2513     ASSERT(part);
2514     
2515     EditCommandPtr lastEditCommand = part->lastEditCommand();
2516     if (isOpenForMoreTypingCommand(lastEditCommand)) {
2517         static_cast<TypingCommand *>(lastEditCommand.get())->deleteKeyPressed();
2518     }
2519     else {
2520         EditCommandPtr cmd(new TypingCommand(document, DeleteKey));
2521         cmd.apply();
2522     }
2523 }
2524
2525 void TypingCommand::insertText(DocumentImpl *document, const DOMString &text, bool selectInsertedText)
2526 {
2527     ASSERT(document);
2528     
2529     KHTMLPart *part = document->part();
2530     ASSERT(part);
2531     
2532     EditCommandPtr lastEditCommand = part->lastEditCommand();
2533     if (isOpenForMoreTypingCommand(lastEditCommand)) {
2534         static_cast<TypingCommand *>(lastEditCommand.get())->insertText(text, selectInsertedText);
2535     }
2536     else {
2537         EditCommandPtr cmd(new TypingCommand(document, InsertText, text, selectInsertedText));
2538         cmd.apply();
2539     }
2540 }
2541
2542 void TypingCommand::insertNewline(DocumentImpl *document)
2543 {
2544     ASSERT(document);
2545     
2546     KHTMLPart *part = document->part();
2547     ASSERT(part);
2548     
2549     EditCommandPtr lastEditCommand = part->lastEditCommand();
2550     if (isOpenForMoreTypingCommand(lastEditCommand)) {
2551         static_cast<TypingCommand *>(lastEditCommand.get())->insertNewline();
2552     }
2553     else {
2554         EditCommandPtr cmd(new TypingCommand(document, InsertNewline));
2555         cmd.apply();
2556     }
2557 }
2558
2559 bool TypingCommand::isOpenForMoreTypingCommand(const EditCommandPtr &cmd)
2560 {
2561     return cmd.isTypingCommand() &&
2562         static_cast<const TypingCommand *>(cmd.get())->openForMoreTyping();
2563 }
2564
2565 void TypingCommand::closeTyping(const EditCommandPtr &cmd)
2566 {
2567     if (isOpenForMoreTypingCommand(cmd))
2568         static_cast<TypingCommand *>(cmd.get())->closeTyping();
2569 }
2570
2571 void TypingCommand::doApply()
2572 {
2573     if (endingSelection().isNone())
2574         return;
2575
2576     switch (m_commandType) {
2577         case DeleteKey:
2578             deleteKeyPressed();
2579             return;
2580         case InsertText:
2581             insertText(m_textToInsert, m_selectInsertedText);
2582             return;
2583         case InsertNewline:
2584             insertNewline();
2585             return;
2586     }
2587
2588     ASSERT_NOT_REACHED();
2589 }
2590
2591 void TypingCommand::markMisspellingsAfterTyping()
2592 {
2593     // Take a look at the selection that results after typing and determine whether we need to spellcheck. 
2594     // Since the word containing the current selection is never marked, this does a check to
2595     // see if typing made a new word that is not in the current selection. Basically, you
2596     // get this by being at the end of a word and typing a space.    
2597     VisiblePosition start(endingSelection().start());
2598     VisiblePosition previous = start.previous();
2599     if (previous.isNotNull()) {
2600         VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
2601         VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
2602         if (p1 != p2)
2603             KWQ(document()->part())->markMisspellingsInAdjacentWords(p1);
2604     }
2605 }
2606
2607 void TypingCommand::typingAddedToOpenCommand()
2608 {
2609     markMisspellingsAfterTyping();
2610     // Do not apply editing to the part on the first time through.
2611     // The part will get told in the same way as all other commands.
2612     // But since this command stays open and is used for additional typing, 
2613     // we need to tell the part here as other commands are added.
2614     if (m_applyEditing) {
2615         EditCommandPtr cmd(this);
2616         document()->part()->appliedEditing(cmd);
2617     }
2618     m_applyEditing = true;
2619 }
2620
2621 void TypingCommand::insertText(const DOMString &text, bool selectInsertedText)
2622 {
2623     // FIXME: Improve typing style.
2624     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
2625     if (document()->part()->typingStyle() || m_cmds.count() == 0) {
2626         InputTextCommand *impl = new InputTextCommand(document());
2627         EditCommandPtr cmd(impl);
2628         applyCommandToComposite(cmd);
2629         impl->input(text, selectInsertedText);
2630     }
2631     else {
2632         EditCommandPtr lastCommand = m_cmds.last();
2633         if (lastCommand.isInputTextCommand()) {
2634             InputTextCommand *impl = static_cast<InputTextCommand *>(lastCommand.get());
2635             impl->input(text, selectInsertedText);
2636         }
2637         else {
2638             InputTextCommand *impl = new InputTextCommand(document());
2639             EditCommandPtr cmd(impl);
2640             applyCommandToComposite(cmd);
2641             impl->input(text, selectInsertedText);
2642         }
2643     }
2644     typingAddedToOpenCommand();
2645 }
2646
2647 void TypingCommand::insertNewline()
2648 {
2649     EditCommandPtr cmd(new InputNewlineCommand(document()));
2650     applyCommandToComposite(cmd);
2651     typingAddedToOpenCommand();
2652 }
2653
2654 void TypingCommand::issueCommandForDeleteKey()
2655 {
2656     Selection selectionToDelete;
2657     
2658     switch (endingSelection().state()) {
2659         case Selection::RANGE:
2660             selectionToDelete = endingSelection();
2661             break;
2662         case Selection::CARET: {
2663             // Handle delete at beginning-of-block case.
2664             // Do nothing in the case that the caret is at the start of a
2665             // root editable element or at the start of a document.
2666             Position pos(endingSelection().start());
2667             Position start = VisiblePosition(pos).previous().deepEquivalent();
2668             Position end = VisiblePosition(pos).deepEquivalent();
2669             if (start.isNotNull() && end.isNotNull() && start.node()->rootEditableElement() == end.node()->rootEditableElement())
2670                 selectionToDelete = Selection(start, end);
2671             break;
2672         }
2673         case Selection::NONE:
2674             ASSERT_NOT_REACHED();
2675             break;
2676     }
2677     
2678     if (selectionToDelete.isCaretOrRange()) {
2679         deleteSelection(selectionToDelete);
2680         typingAddedToOpenCommand();
2681     }
2682 }
2683
2684 void TypingCommand::deleteKeyPressed()
2685 {
2686 // EDIT FIXME: The ifdef'ed out code below should be re-enabled.
2687 // In order for this to happen, the deleteCharacter case
2688 // needs work. Specifically, the caret-positioning code
2689 // and whitespace-handling code in DeleteSelectionCommand::doApply()
2690 // needs to be factored out so it can be used again here.
2691 // Until that work is done, issueCommandForDeleteKey() does the
2692 // right thing, but less efficiently and with the cost of more
2693 // objects.
2694     issueCommandForDeleteKey();
2695 #if 0    
2696     if (m_cmds.count() == 0) {
2697         issueCommandForDeleteKey();
2698     }
2699     else {
2700         EditCommandPtr lastCommand = m_cmds.last();
2701         if (lastCommand.isInputTextCommand()) {
2702             InputTextCommand &cmd = static_cast<InputTextCommand &>(lastCommand);
2703             cmd.deleteCharacter();
2704             if (cmd.charactersAdded() == 0) {
2705                 removeCommand(lastCommand);
2706             }
2707         }
2708         else if (lastCommand.isInputNewlineCommand()) {
2709             lastCommand.unapply();
2710             removeCommand(lastCommand);
2711         }
2712         else {
2713             issueCommandForDeleteKey();
2714         }
2715     }
2716 #endif
2717 }
2718
2719 void TypingCommand::removeCommand(const EditCommandPtr &cmd)
2720 {
2721     // NOTE: If the passed-in command is the last command in the
2722     // composite, we could remove all traces of this typing command
2723     // from the system, including the undo chain. Other editors do
2724     // not do this, but we could.
2725
2726     m_cmds.remove(cmd);
2727     if (m_cmds.count() == 0)
2728         setEndingSelection(startingSelection());
2729     else
2730         setEndingSelection(m_cmds.last().endingSelection());
2731 }
2732
2733 bool TypingCommand::preservesTypingStyle() const
2734 {
2735     switch (m_commandType) {
2736         case DeleteKey:
2737             return true;
2738         case InsertText:
2739         case InsertNewline:
2740             return false;
2741     }
2742     ASSERT_NOT_REACHED();
2743     return false;
2744 }
2745
2746 bool TypingCommand::isTypingCommand() const
2747 {
2748     return true;
2749 }
2750
2751 } // namespace khtml