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