1dfc336ae83289be3fa8cb84994958f148b1e977
[WebKit-https.git] / WebCore / khtml / editing / htmlediting_impl.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_impl.h"
27
28 #include "dom/dom_position.h"
29 #include "html/html_elementimpl.h"
30 #include "html/html_imageimpl.h"
31 #include "htmlattrs.h"
32 #include "htmltags.h"
33 #include "khtml_part.h"
34 #include "khtml_selection.h"
35 #include "khtmlview.h"
36 #include "rendering/render_object.h"
37 #include "rendering/render_style.h"
38 #include "rendering/render_text.h"
39 #include "xml/dom_docimpl.h"
40 #include "xml/dom_elementimpl.h"
41 #include "xml/dom_edititerator.h"
42 #include "xml/dom_nodeimpl.h"
43 #include "xml/dom_stringimpl.h"
44 #include "xml/dom_textimpl.h"
45 #include "xml/dom2_rangeimpl.h"
46
47 #if APPLE_CHANGES
48 #include "KWQAssertions.h"
49 #include "KWQLogging.h"
50 #endif
51
52 using DOM::DocumentFragmentImpl;
53 using DOM::DocumentImpl;
54 using DOM::DOMPosition;
55 using DOM::DOMString;
56 using DOM::DOMStringImpl;
57 using DOM::EditingTextImpl;
58 using DOM::EditIterator;
59 using DOM::ElementImpl;
60 using DOM::HTMLElementImpl;
61 using DOM::HTMLImageElementImpl;
62 using DOM::Node;
63 using DOM::NodeImpl;
64 using DOM::NodeListImpl;
65 using DOM::Range;
66 using DOM::RangeImpl;
67 using DOM::TextImpl;
68
69 using khtml::AppendNodeCommand;
70 using khtml::AppendNodeCommandImpl;
71 using khtml::CompositeEditCommand;
72 using khtml::CompositeEditCommandImpl;
73 using khtml::DeleteCollapsibleWhitespaceCommand;
74 using khtml::DeleteCollapsibleWhitespaceCommandImpl;
75 using khtml::DeleteSelectionCommand;
76 using khtml::DeleteSelectionCommandImpl;
77 using khtml::DeleteTextCommand;
78 using khtml::DeleteTextCommandImpl;
79 using khtml::EditCommand;
80 using khtml::EditCommandImpl;
81 using khtml::InlineTextBox;
82 using khtml::InputNewlineCommand;
83 using khtml::InputNewlineCommandImpl;
84 using khtml::InputTextCommand;
85 using khtml::InputTextCommandImpl;
86 using khtml::InsertNodeBeforeCommand;
87 using khtml::InsertNodeBeforeCommandImpl;
88 using khtml::InsertTextCommand;
89 using khtml::InsertTextCommandImpl;
90 using khtml::JoinTextNodesCommand;
91 using khtml::JoinTextNodesCommandImpl;
92 using khtml::RemoveNodeCommand;
93 using khtml::RemoveNodeCommandImpl;
94 using khtml::RemoveNodeAndPruneCommand;
95 using khtml::RemoveNodeAndPruneCommandImpl;
96 using khtml::RenderObject;
97 using khtml::RenderStyle;
98 using khtml::RenderText;
99 using khtml::PasteMarkupCommand;
100 using khtml::PasteMarkupCommandImpl;
101 using khtml::SplitTextNodeCommand;
102 using khtml::SplitTextNodeCommandImpl;
103 using khtml::TypingCommand;
104 using khtml::TypingCommandImpl;
105
106 #if !APPLE_CHANGES
107 #define ASSERT(assertion) ((void)0)
108 #define ASSERT_WITH_MESSAGE(assertion, formatAndArgs...) ((void)0)
109 #define ASSERT_NOT_REACHED() ((void)0)
110 #define LOG(channel, formatAndArgs...) ((void)0)
111 #define ERROR(formatAndArgs...) ((void)0)
112 #if LOG_DISABLED
113 #define debugPosition(a,b) ((void)0)
114 #endif
115 #endif
116
117 static inline bool isNBSP(const QChar &c)
118 {
119     return c == QChar(0xa0);
120 }
121
122 static inline bool isWS(const QChar &c)
123 {
124     return c.isSpace() && c != QChar(0xa0);
125 }
126
127 static inline bool isWS(const DOMString &text)
128 {
129     if (text.length() != 1)
130         return false;
131     
132     return isWS(text[0]);
133 }
134
135 static inline bool isWS(const DOMPosition &pos)
136 {
137     if (!pos.node())
138         return false;
139         
140     if (!pos.node()->isTextNode())
141         return false;
142
143     const DOMString &string = static_cast<TextImpl *>(pos.node())->data();
144     return isWS(string[pos.offset()]);
145 }
146
147 static bool shouldPruneNode(NodeImpl *node)
148 {
149     if (!node)
150         return false;
151
152     RenderObject *renderer = node->renderer();
153     if (!renderer)
154         return true;
155
156     if (node->hasChildNodes())
157         return false;
158         
159     if (renderer->isBR() || renderer->isBlockFlow() || renderer->isReplaced())
160         return false;
161         
162     if (node->isTextNode()) {
163         TextImpl *text = static_cast<TextImpl *>(node);
164         if (text->length() == 0)
165             return true;
166         return false;
167     }
168     
169     if (!node->isHTMLElement() && !node->isXMLElementNode())
170         return false;
171     
172     if (node->id() == ID_BODY)
173         return false;
174             
175     if (!node->isContentEditable())
176         return false;
177             
178     return true;
179 }
180
181 static DOMPosition leadingWhitespacePosition(const DOMPosition &pos)
182 {
183     ASSERT(pos.notEmpty());
184
185     KHTMLSelection selection(pos);
186     DOMPosition prev = pos.previousCharacterPosition();
187     if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node()) && prev.node()->isTextNode()) {
188         DOMString string = static_cast<TextImpl *>(prev.node())->data();
189         if (isWS(string[prev.offset()]))
190             return prev;
191     }
192
193     return DOMPosition();
194 }
195
196 static DOMPosition trailingWhitespacePosition(const DOMPosition &pos)
197 {
198     ASSERT(pos.notEmpty());
199
200     if (pos.node()->isTextNode()) {
201         TextImpl *textNode = static_cast<TextImpl *>(pos.node());
202         if (pos.offset() >= (long)textNode->length()) {
203             DOMPosition next = pos.nextCharacterPosition();
204             if (next != pos && next.node()->inSameContainingEditableBlock(pos.node()) && next.node()->isTextNode()) {
205                 DOMString string = static_cast<TextImpl *>(next.node())->data();
206                 if (isWS(string[0]))
207                     return next;
208             }
209         }
210         else {
211             DOMString string = static_cast<TextImpl *>(pos.node())->data();
212             if (isWS(string[pos.offset()]))
213                 return pos;
214         }
215     }
216
217     return DOMPosition();
218 }
219
220 static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
221 {
222     ASSERT(text1);
223     ASSERT(text2);
224     
225     return (text1->nextSibling() == text2);
226 }
227
228 static DOMString &nonBreakingSpaceString()
229 {
230     static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
231     return nonBreakingSpaceString;
232 }
233
234 static void debugPosition(const char *prefix, const DOMPosition &pos)
235 {
236     LOG(Editing, "%s%s %p : %d", prefix, getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
237 }
238
239 //------------------------------------------------------------------------------------------
240 // EditCommandImpl
241
242 EditCommandImpl::EditCommandImpl(DocumentImpl *document) 
243     : SharedCommandImpl(), m_document(document), m_state(NotApplied), m_parent(0)
244 {
245     ASSERT(m_document);
246     ASSERT(m_document->part());
247     m_document->ref();
248     m_startingSelection = m_document->part()->selection();
249     m_endingSelection = m_startingSelection;
250 }
251
252 EditCommandImpl::~EditCommandImpl()
253 {
254     ASSERT(m_document);
255     m_document->deref();
256 }
257
258 int EditCommandImpl::commandID() const
259 {
260     return EditCommandID;
261 }
262
263 void EditCommandImpl::apply()
264 {
265     ASSERT(m_document);
266     ASSERT(m_document->part());
267     ASSERT(state() == NotApplied);
268     
269     doApply();
270     
271     m_state = Applied;
272
273     if (!isCompositeStep()) {
274         EditCommand cmd(this);
275         m_document->part()->appliedEditing(cmd);
276     }
277 }
278
279 void EditCommandImpl::unapply()
280 {
281     ASSERT(m_document);
282     ASSERT(m_document->part());
283     ASSERT(state() == Applied);
284     
285     doUnapply();
286     
287     m_state = NotApplied;
288
289     if (!isCompositeStep()) {
290         EditCommand cmd(this);
291         m_document->part()->unappliedEditing(cmd);
292     }
293 }
294
295 void EditCommandImpl::reapply()
296 {
297     ASSERT(m_document);
298     ASSERT(m_document->part());
299     ASSERT(state() == NotApplied);
300     
301     doReapply();
302     
303     m_state = Applied;
304
305     if (!isCompositeStep()) {
306         EditCommand cmd(this);
307         m_document->part()->reappliedEditing(cmd);
308     }
309 }
310
311 void EditCommandImpl::doReapply()
312 {
313     doApply();
314 }
315
316 void EditCommandImpl::setStartingSelection(const KHTMLSelection &s)
317 {
318     m_startingSelection = s;
319     EditCommand cmd = parent();
320     while (cmd.notNull()) {
321         cmd.setStartingSelection(s);
322         cmd = cmd.parent();
323     }
324     moveToStartingSelection();
325 }
326
327 void EditCommandImpl::setEndingSelection(const KHTMLSelection &s)
328 {
329     m_endingSelection = s;
330     EditCommand cmd = parent();
331     while (cmd.notNull()) {
332         cmd.setEndingSelection(s);
333         cmd = cmd.parent();
334     }
335     moveToEndingSelection();
336 }
337
338 void EditCommandImpl::moveToStartingSelection()
339 {
340     ASSERT(m_document);
341     ASSERT(m_document->part());
342     m_document->part()->takeSelectionFrom(this, false);
343 }
344
345 void EditCommandImpl::moveToEndingSelection()
346 {
347     ASSERT(m_document);
348     ASSERT(m_document->part());
349     m_document->part()->takeSelectionFrom(this, true);
350 }
351
352 EditCommand EditCommandImpl::parent() const
353 {
354     return m_parent;
355 }
356
357 void EditCommandImpl::setParent(const EditCommand &cmd)
358 {
359     m_parent = cmd;
360 }
361
362 //------------------------------------------------------------------------------------------
363 // CompositeEditCommandImpl
364
365 CompositeEditCommandImpl::CompositeEditCommandImpl(DocumentImpl *document) 
366     : EditCommandImpl(document)
367 {
368 }
369
370 CompositeEditCommandImpl::~CompositeEditCommandImpl()
371 {
372 }
373
374 int CompositeEditCommandImpl::commandID() const
375 {
376     return CompositeEditCommandID;
377 }
378
379 void CompositeEditCommandImpl::doUnapply()
380 {
381     if (m_cmds.count() == 0) {
382         return;
383     }
384     
385     for (int i = m_cmds.count() - 1; i >= 0; --i)
386         m_cmds[i]->unapply();
387
388     moveToStartingSelection();
389     setState(NotApplied);
390 }
391
392 void CompositeEditCommandImpl::doReapply()
393 {
394     if (m_cmds.count() == 0) {
395         return;
396     }
397
398     for (QValueList<EditCommand>::ConstIterator it = m_cmds.begin(); it != m_cmds.end(); ++it)
399         (*it)->reapply();
400
401     moveToEndingSelection();
402     setState(Applied);
403 }
404
405 //
406 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
407 //
408 void CompositeEditCommandImpl::applyCommandToComposite(EditCommand &cmd)
409 {
410     cmd.setStartingSelection(endingSelection());
411     cmd.setEndingSelection(endingSelection());
412     cmd.setParent(this);
413     cmd.apply();
414     m_cmds.append(cmd);
415 }
416
417 void CompositeEditCommandImpl::insertNodeBefore(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
418 {
419     InsertNodeBeforeCommand cmd(document(), insertChild, refChild);
420     applyCommandToComposite(cmd);
421 }
422
423 void CompositeEditCommandImpl::insertNodeAfter(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
424 {
425     if (refChild->parentNode()->lastChild() == refChild) {
426         appendNode(refChild->parentNode(), insertChild);
427     }
428     else {
429         ASSERT(refChild->nextSibling());
430         insertNodeBefore(insertChild, refChild->nextSibling());
431     }
432 }
433
434 void CompositeEditCommandImpl::insertNodeAt(NodeImpl *insertChild, NodeImpl *refChild, long offset)
435 {
436     if (refChild->hasChildNodes() || (refChild->renderer() && refChild->renderer()->isBlockFlow())) {
437         NodeImpl *child = refChild->firstChild();
438         for (long i = 0; child && i < offset; i++)
439             child = child->nextSibling();
440         if (child)
441             insertNodeBefore(insertChild, child);
442         else
443             appendNode(refChild, insertChild);
444     } 
445     else if (refChild->caretMinOffset() >= offset) {
446         insertNodeBefore(insertChild, refChild);
447     } 
448     else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
449         splitTextNode(static_cast<TextImpl *>(refChild), offset);
450         insertNodeBefore(insertChild, refChild);
451     } 
452     else {
453         insertNodeAfter(insertChild, refChild);
454     }
455 }
456
457 void CompositeEditCommandImpl::appendNode(DOM::NodeImpl *parent, DOM::NodeImpl *appendChild)
458 {
459     AppendNodeCommand cmd(document(), parent, appendChild);
460     applyCommandToComposite(cmd);
461 }
462
463 void CompositeEditCommandImpl::removeNode(DOM::NodeImpl *removeChild)
464 {
465     RemoveNodeCommand cmd(document(), removeChild);
466     applyCommandToComposite(cmd);
467 }
468
469 void CompositeEditCommandImpl::removeNodeAndPrune(DOM::NodeImpl *removeChild)
470 {
471     RemoveNodeAndPruneCommand cmd(document(), removeChild);
472     applyCommandToComposite(cmd);
473 }
474
475 void CompositeEditCommandImpl::splitTextNode(DOM::TextImpl *text, long offset)
476 {
477     SplitTextNodeCommand cmd(document(), text, offset);
478     applyCommandToComposite(cmd);
479 }
480
481 void CompositeEditCommandImpl::joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2)
482 {
483     JoinTextNodesCommand cmd(document(), text1, text2);
484     applyCommandToComposite(cmd);
485 }
486
487 void CompositeEditCommandImpl::inputText(const DOMString &text)
488 {
489     InputTextCommand cmd(document());
490     applyCommandToComposite(cmd);
491     cmd.input(text);
492 }
493
494 void CompositeEditCommandImpl::insertText(DOM::TextImpl *node, long offset, const DOM::DOMString &text)
495 {
496     InsertTextCommand cmd(document(), node, offset, text);
497     applyCommandToComposite(cmd);
498 }
499
500 void CompositeEditCommandImpl::deleteText(DOM::TextImpl *node, long offset, long count)
501 {
502     DeleteTextCommand cmd(document(), node, offset, count);
503     applyCommandToComposite(cmd);
504 }
505
506 void CompositeEditCommandImpl::replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText)
507 {
508     DeleteTextCommand deleteCommand(document(), node, offset, count);
509     applyCommandToComposite(deleteCommand);
510     InsertTextCommand insertCommand(document(), node, offset, replacementText);
511     applyCommandToComposite(insertCommand);
512 }
513
514 void CompositeEditCommandImpl::deleteSelection()
515 {
516     if (endingSelection().state() == KHTMLSelection::RANGE) {
517         DeleteSelectionCommand cmd(document());
518         applyCommandToComposite(cmd);
519     }
520 }
521
522 void CompositeEditCommandImpl::deleteSelection(const KHTMLSelection &selection)
523 {
524     if (selection.state() == KHTMLSelection::RANGE) {
525         DeleteSelectionCommand cmd(document(), selection);
526         applyCommandToComposite(cmd);
527     }
528 }
529
530 void CompositeEditCommandImpl::deleteCollapsibleWhitespace()
531 {
532     DeleteCollapsibleWhitespaceCommand cmd(document());
533     applyCommandToComposite(cmd);
534 }
535
536 void CompositeEditCommandImpl::deleteCollapsibleWhitespace(const KHTMLSelection &selection)
537 {
538     DeleteCollapsibleWhitespaceCommand cmd(document(), selection);
539     applyCommandToComposite(cmd);
540 }
541
542 //==========================================================================================
543 // Concrete commands
544 //------------------------------------------------------------------------------------------
545 // AppendNodeCommandImpl
546
547 AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *parentNode, NodeImpl *appendChild)
548     : EditCommandImpl(document), m_parentNode(parentNode), m_appendChild(appendChild)
549 {
550     ASSERT(m_parentNode);
551     m_parentNode->ref();
552
553     ASSERT(m_appendChild);
554     m_appendChild->ref();
555 }
556
557 AppendNodeCommandImpl::~AppendNodeCommandImpl()
558 {
559     if (m_parentNode)
560         m_parentNode->deref();
561     if (m_appendChild)
562         m_appendChild->deref();
563 }
564
565 int AppendNodeCommandImpl::commandID() const
566 {
567     return AppendNodeCommandID;
568 }
569
570 void AppendNodeCommandImpl::doApply()
571 {
572     ASSERT(m_parentNode);
573     ASSERT(m_appendChild);
574
575     int exceptionCode = 0;
576     m_parentNode->appendChild(m_appendChild, exceptionCode);
577     ASSERT(exceptionCode == 0);
578 }
579
580 void AppendNodeCommandImpl::doUnapply()
581 {
582     ASSERT(m_parentNode);
583     ASSERT(m_appendChild);
584     ASSERT(state() == Applied);
585
586     int exceptionCode = 0;
587     m_parentNode->removeChild(m_appendChild, exceptionCode);
588     ASSERT(exceptionCode == 0);
589 }
590
591 //------------------------------------------------------------------------------------------
592 // DeleteCollapsibleWhitespaceCommandImpl
593
594 DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document)
595     : CompositeEditCommandImpl(document), m_selectionToCollapse(endingSelection()), m_charactersDeleted(0)
596 {
597 }
598
599 DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const KHTMLSelection &selection)
600     : CompositeEditCommandImpl(document), m_selectionToCollapse(selection), m_charactersDeleted(0)
601 {
602 }
603
604 DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
605 {
606 }
607
608 int DeleteCollapsibleWhitespaceCommandImpl::commandID() const
609 {
610     return DeleteCollapsibleWhitespaceCommandID;
611 }
612
613 static bool shouldDeleteUpstreamPosition(const DOMPosition &pos)
614 {
615     if (!pos.node()->isTextNode())
616         return false;
617         
618     RenderObject *renderer = pos.node()->renderer();
619     if (!renderer)
620         return true;
621         
622     TextImpl *textNode = static_cast<TextImpl *>(pos.node());
623     if (pos.offset() >= (long)textNode->length())
624         return false;
625
626     if (pos.isLastRenderedPositionInEditableBlock())
627         return false;
628
629     if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine())
630         return false;
631
632     RenderText *textRenderer = static_cast<RenderText *>(renderer);
633
634     for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
635         if (pos.offset() < box->m_start) {
636             return true;
637         }
638         if (pos.offset() >= box->m_start && pos.offset() < box->m_start + box->m_len)
639             return false;
640     }
641     
642     return true;
643 }
644
645 DOMPosition DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const DOMPosition &pos)
646 {
647     DOMPosition upstream = pos.equivalentUpstreamPosition();
648     DOMPosition downstream = pos.equivalentDownstreamPosition();
649     
650     bool del = shouldDeleteUpstreamPosition(upstream);
651
652     LOG(Editing, "pos:        %s [%p:%d]\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
653     if (upstream == downstream) {
654         LOG(Editing, "same:       %s [%p:%d]\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
655     }
656     else {
657         LOG(Editing, "upstream:   %s %s [%p:%d]\n", del ? "DELETE" : "SKIP", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
658         EditIterator it(upstream);
659         for (it.next(); it.current() != downstream; it.next()) {
660             if (it.current().node()->isTextNode() && (long)static_cast<TextImpl *>(it.current().node())->length() == it.current().offset())
661                 LOG(Editing, "   node:    AT END %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset());
662             else
663                 LOG(Editing, "   node:    DELETE %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset());
664         }
665         LOG(Editing, "downstream: %s [%p:%d]\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
666     }
667
668     if (upstream == downstream)
669         return upstream;
670         
671     EditIterator it(upstream);
672     DOMPosition deleteStart = upstream;
673     if (!del) {
674         deleteStart = it.peekNext();
675         if (deleteStart == downstream)
676             return upstream;
677     }
678     
679     DOMPosition endingPosition = upstream;
680     
681     while (it.current() != downstream) {
682
683         DOMPosition next = it.peekNext();
684         if (next.node() != deleteStart.node()) {
685             ASSERT(deleteStart.node()->isTextNode());
686             TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
687             unsigned long count = it.current().offset() - deleteStart.offset();
688             if (count == textNode->length()) {
689                 LOG(Editing, "   removeNodeAndPrune 1: [%p]\n", textNode);
690                 if (textNode == endingPosition.node())
691                     endingPosition = DOMPosition(next.node(), next.node()->caretMinOffset());
692                 removeNodeAndPrune(textNode);
693             }
694             else {
695                 LOG(Editing, "   deleteText 1: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), it.current().offset() - deleteStart.offset());
696                 deleteText(textNode, deleteStart.offset(), count);
697             }
698             deleteStart = next;
699         }
700         else if (next == downstream) {
701             ASSERT(deleteStart.node() == downstream.node());
702             ASSERT(downstream.node()->isTextNode());
703             TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
704             unsigned long count = downstream.offset() - deleteStart.offset();
705             ASSERT(count <= textNode->length());
706             if (count == textNode->length()) {
707                 LOG(Editing, "   removeNodeAndPrune 2: [%p]\n", textNode);
708                 removeNodeAndPrune(textNode);
709             }
710             else {
711                 LOG(Editing, "   deleteText 2: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), count);
712                 deleteText(textNode, deleteStart.offset(), count);
713                 m_charactersDeleted = count;
714                 endingPosition = DOMPosition(downstream.node(), downstream.offset() - m_charactersDeleted);
715             }
716         }
717         
718         it.setPosition(next);
719     }
720     
721     return endingPosition;
722 }
723
724 void DeleteCollapsibleWhitespaceCommandImpl::doApply()
725 {
726     int state = m_selectionToCollapse.state();
727     if (state == KHTMLSelection::CARET) {
728         DOMPosition endPosition = deleteWhitespace(m_selectionToCollapse.startPosition());
729         setEndingSelection(endPosition);
730         LOG(Editing, "-----------------------------------------------------\n");
731     }
732     else if (state == KHTMLSelection::RANGE) {
733         DOMPosition startPosition = deleteWhitespace(m_selectionToCollapse.startPosition());
734         LOG(Editing, "-----------------------------------------------------\n");
735         DOMPosition endPosition = m_selectionToCollapse.endPosition();
736         if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
737             LOG(Editing, "adjust end position by %d\n", m_charactersDeleted);
738             endPosition = DOMPosition(endPosition.node(), endPosition.offset() - m_charactersDeleted);
739         }
740         endPosition = deleteWhitespace(endPosition);
741         setEndingSelection(KHTMLSelection(startPosition, endPosition));
742         LOG(Editing, "=====================================================\n");
743     }
744 }
745
746 //------------------------------------------------------------------------------------------
747 // DeleteSelectionCommandImpl
748
749 DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DOM::DocumentImpl *document)
750     : CompositeEditCommandImpl(document)
751 {
752     m_selectionToDelete = endingSelection();
753 }
754
755 DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DOM::DocumentImpl *document, const KHTMLSelection &selection)
756     : CompositeEditCommandImpl(document)
757 {
758     m_selectionToDelete = selection;
759 }
760
761 DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl()
762 {
763 }
764         
765 int DeleteSelectionCommandImpl::commandID() const
766 {
767     return DeleteSelectionCommandID;
768 }
769
770 void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
771 {
772     KHTMLSelection selection = endingSelection();
773
774     if (selection.state() != KHTMLSelection::CARET)
775         return;
776
777     DOMPosition pos = selection.startPosition();
778     
779     if (!pos.node()->isTextNode())
780         return;
781
782     TextImpl *textNode = static_cast<TextImpl *>(pos.node());
783     
784     if (pos.offset() == 0) {
785         EditIterator it(pos);
786         DOMPosition prev = it.previous();
787         if (prev == pos)
788             return;
789         if (prev.node()->isTextNode()) {
790             TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node());
791             if (textNodesAreJoinable(prevTextNode, textNode)) {
792                 joinTextNodes(prevTextNode, textNode);
793                 setEndingSelection(DOMPosition(textNode, prevTextNode->length()));
794                 LOG(Editing, "joinTextNodesWithSameStyle [1]\n");
795             }
796         }
797     }
798     else if (pos.offset() == (long)textNode->length()) {
799         EditIterator it(pos);
800         DOMPosition next = it.next();
801         if (next == pos)
802             return;
803         if (next.node()->isTextNode()) {
804             TextImpl *nextTextNode = static_cast<TextImpl *>(next.node());
805             if (textNodesAreJoinable(textNode, nextTextNode)) {
806                 joinTextNodes(textNode, nextTextNode);
807                 setEndingSelection(DOMPosition(nextTextNode, pos.offset()));
808                 LOG(Editing, "joinTextNodesWithSameStyle [2]\n");
809             }
810         }
811     }
812 }
813
814 bool DeleteSelectionCommandImpl::containsOnlyWhitespace(const DOMPosition &start, const DOMPosition &end)
815 {
816     // Returns whether the range contains only whitespace characters.
817     // This is inclusive of the start, but not of the end.
818     EditIterator it(start);
819     while (!it.atEnd()) {
820         if (!it.current().node()->isTextNode())
821             return false;
822         const DOMString &text = static_cast<TextImpl *>(it.current().node())->data();
823         // EDIT FIXME: signed/unsigned mismatch
824         if (text.length() > INT_MAX)
825             return false;
826         if (it.current().offset() < (int)text.length() && !isWS(text[it.current().offset()]))
827             return false;
828         it.next();
829         if (it.current() == end)
830             break;
831     }
832     return true;
833 }
834
835 void DeleteSelectionCommandImpl::doApply()
836 {
837     if (m_selectionToDelete.state() != KHTMLSelection::RANGE)
838         return;
839
840     deleteCollapsibleWhitespace(m_selectionToDelete);
841     KHTMLSelection selection = endingSelection();
842
843     DOMPosition endingPosition;
844     bool adjustEndingPositionDownstream = false;
845
846     DOMPosition upstreamStart = selection.startPosition().equivalentUpstreamPosition();
847     DOMPosition downstreamStart = selection.startPosition().equivalentDownstreamPosition();
848     DOMPosition upstreamEnd = selection.endPosition().equivalentUpstreamPosition();
849     DOMPosition downstreamEnd = selection.endPosition().equivalentDownstreamPosition();
850
851     bool onlyWhitespace = containsOnlyWhitespace(upstreamStart, downstreamEnd);
852  
853     bool startCompletelySelected = !onlyWhitespace &&
854         (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
855         ((downstreamStart.node() != upstreamEnd.node()) ||
856          (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset())));
857
858     bool endCompletelySelected = !onlyWhitespace &&
859         (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
860         ((downstreamStart.node() != upstreamEnd.node()) ||
861          (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset())));
862
863     unsigned long startRenderedOffset = downstreamStart.renderedOffset();
864     
865     bool startAtStartOfRootEditableBlock = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableBlock();
866     bool startAtStartOfBlock = startAtStartOfRootEditableBlock || 
867         (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
868     bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
869
870     debugPosition("upstreamStart:       ", upstreamStart);
871     debugPosition("downstreamStart:     ", downstreamStart);
872     debugPosition("upstreamEnd:         ", upstreamEnd);
873     debugPosition("downstreamEnd:       ", downstreamEnd);
874     LOG(Editing,  "start selected:      %s", startCompletelySelected ? "YES" : "NO");
875     LOG(Editing,  "at start block:      %s", startAtStartOfBlock ? "YES" : "NO");
876     LOG(Editing,  "at start root block: %s", startAtStartOfRootEditableBlock ? "YES" : "NO");
877     LOG(Editing,  "at end block:        %s", endAtEndOfBlock ? "YES" : "NO");
878     LOG(Editing,  "only whitespace:     %s", onlyWhitespace ? "YES" : "NO");
879
880     // Start is not completely selected
881     if (startAtStartOfBlock) {
882         LOG(Editing,  "ending position case 1");
883         endingPosition = DOMPosition(downstreamStart.node()->containingEditableBlock(), 1);
884         adjustEndingPositionDownstream = true;
885     }
886     else if (!startCompletelySelected) {
887         LOG(Editing,  "ending position case 2");
888         endingPosition = upstreamStart;
889         if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
890             adjustEndingPositionDownstream = true;
891     }
892     else if (upstreamStart != downstreamStart) {
893         LOG(Editing,  "ending position case 3");
894         endingPosition = upstreamStart;
895         if (downstreamStart.node()->id() == ID_BR && downstreamStart.offset() == 0)
896             adjustEndingPositionDownstream = true;
897         if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
898             adjustEndingPositionDownstream = true;
899     }
900    
901     //
902     // Figure out the whitespace conversions to do
903     //
904     if ((startAtStartOfBlock && !endAtEndOfBlock) || (!startCompletelySelected && adjustEndingPositionDownstream)) {
905         // convert trailing whitespace
906         DOMPosition trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
907         if (trailing.notEmpty()) {
908             debugPosition("convertTrailingWhitespace: ", trailing);
909             DOMPosition collapse = trailing.nextCharacterPosition();
910             if (collapse != trailing)
911                 deleteCollapsibleWhitespace(collapse);
912             TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
913             replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
914         }
915     }
916     else if (!startAtStartOfBlock && endAtEndOfBlock) {
917         // convert leading whitespace
918         DOMPosition leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
919         if (leading.notEmpty()) {
920             debugPosition("convertLeadingWhitespace:  ", leading);
921             TextImpl *textNode = static_cast<TextImpl *>(leading.node());
922             replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
923         }
924     }
925     else if (!startAtStartOfBlock && !endAtEndOfBlock) {
926         // convert contiguous whitespace
927         DOMPosition leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
928         DOMPosition trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
929         if (leading.notEmpty() && trailing.notEmpty()) {
930             debugPosition("convertLeadingWhitespace [contiguous]:  ", leading);
931             TextImpl *textNode = static_cast<TextImpl *>(leading.node());
932             replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
933         }
934     }
935         
936     //
937     // Do the delete
938     //
939     NodeImpl *n = downstreamStart.node()->traverseNextNode();
940
941     // work on start node
942     if (startCompletelySelected) {
943         LOG(Editing,  "start node delete case 1");
944         removeNodeAndPrune(downstreamStart.node());
945     }
946     else if (onlyWhitespace) {
947         // Selection only contains whitespace. This is really a special-case to 
948         // handle significant whitespace that is collapsed at the end of a line,
949         // but also handles deleting a space in mid-line.
950         LOG(Editing,  "start node delete case 2");
951         ASSERT(upstreamStart.node()->isTextNode());
952         TextImpl *text = static_cast<TextImpl *>(upstreamStart.node());
953         int length = downstreamStart.node() == upstreamStart.node() ? 
954             kMax(downstreamStart.offset() - upstreamStart.offset(), 1L) : 
955             text->length() - upstreamStart.offset();
956         deleteText(text, upstreamStart.offset(), length);
957     }
958     else if (downstreamStart.node()->isTextNode()) {
959         LOG(Editing,  "start node delete case 3");
960         TextImpl *text = static_cast<TextImpl *>(downstreamStart.node());
961         int endOffset = text == upstreamEnd.node() ? upstreamEnd.offset() : text->length();
962         if (endOffset > downstreamStart.offset()) {
963             deleteText(text, downstreamStart.offset(), endOffset - downstreamStart.offset());
964         }
965     }
966     else {
967         // we have clipped the end of a non-text element
968         // the offset must be 1 here. if it is, do nothing and move on.
969         LOG(Editing,  "start node delete case 4");
970         ASSERT(downstreamStart.offset() == 1);
971     }
972
973     if (!onlyWhitespace && downstreamStart.node() != upstreamEnd.node()) {
974         // work on intermediate nodes
975         while (n != upstreamEnd.node()) {
976             NodeImpl *d = n;
977             n = n->traverseNextNode();
978             if (d->renderer() && d->renderer()->isEditable())
979                 removeNodeAndPrune(d);
980         }
981         
982         // work on end node
983         ASSERT(n == upstreamEnd.node());
984         if (endCompletelySelected) {
985             removeNodeAndPrune(upstreamEnd.node());
986         }
987         else if (upstreamEnd.node()->isTextNode()) {
988             if (upstreamEnd.offset() > 0) {
989                 TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
990                 deleteText(text, 0, upstreamEnd.offset());
991             }
992         }
993         else {
994             // we have clipped the beginning of a non-text element
995             // the offset must be 0 here. if it is, do nothing and move on.
996             ASSERT(downstreamStart.offset() == 0);
997         }
998     }
999
1000     if (adjustEndingPositionDownstream) {
1001         LOG(Editing,  "adjust ending position downstream");
1002         endingPosition = endingPosition.equivalentDownstreamPosition();
1003     }
1004
1005     debugPosition("ending position:     ", endingPosition);
1006     setEndingSelection(endingPosition);
1007
1008     LOG(Editing, "-----------------------------------------------------\n");
1009 }
1010
1011 //------------------------------------------------------------------------------------------
1012 // DeleteTextCommandImpl
1013
1014 DeleteTextCommandImpl::DeleteTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, long count)
1015     : EditCommandImpl(document), m_node(node), m_offset(offset), m_count(count)
1016 {
1017     ASSERT(m_node);
1018     ASSERT(m_offset >= 0);
1019     ASSERT(m_count >= 0);
1020     
1021     m_node->ref();
1022 }
1023
1024 DeleteTextCommandImpl::~DeleteTextCommandImpl()
1025 {
1026     if (m_node)
1027         m_node->deref();
1028 }
1029
1030 int DeleteTextCommandImpl::commandID() const
1031 {
1032     return DeleteTextCommandID;
1033 }
1034
1035 void DeleteTextCommandImpl::doApply()
1036 {
1037     ASSERT(m_node);
1038
1039     int exceptionCode = 0;
1040     m_text = m_node->substringData(m_offset, m_count, exceptionCode);
1041     ASSERT(exceptionCode == 0);
1042     
1043     m_node->deleteData(m_offset, m_count, exceptionCode);
1044     ASSERT(exceptionCode == 0);
1045 }
1046
1047 void DeleteTextCommandImpl::doUnapply()
1048 {
1049     ASSERT(m_node);
1050     ASSERT(!m_text.isEmpty());
1051
1052     int exceptionCode = 0;
1053     m_node->insertData(m_offset, m_text, exceptionCode);
1054     ASSERT(exceptionCode == 0);
1055 }
1056
1057 //------------------------------------------------------------------------------------------
1058 // InputNewlineCommandImpl
1059
1060 InputNewlineCommandImpl::InputNewlineCommandImpl(DocumentImpl *document) 
1061     : CompositeEditCommandImpl(document)
1062 {
1063 }
1064
1065 InputNewlineCommandImpl::~InputNewlineCommandImpl() 
1066 {
1067 }
1068
1069 int InputNewlineCommandImpl::commandID() const
1070 {
1071     return InputNewlineCommandID;
1072 }
1073
1074 void InputNewlineCommandImpl::doApply()
1075 {
1076     deleteSelection();
1077     KHTMLSelection selection = endingSelection();
1078
1079     int exceptionCode = 0;
1080     ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
1081     ASSERT(exceptionCode == 0);
1082
1083     DOMPosition pos = selection.startPosition().equivalentDownstreamPosition();
1084     bool atEnd = pos.offset() >= pos.node()->caretMaxOffset();
1085     bool atStart = pos.offset() <= pos.node()->caretMinOffset();
1086     bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
1087     
1088     if (atEndOfBlock) {
1089         LOG(Editing, "input newline case 1");
1090         appendNode(pos.node()->containingEditableBlock(), breakNode);
1091         // EDIT FIXME: This should not insert a non-breaking space after the BR.
1092         // But for right now, it gets the BR to render.
1093         TextImpl *editingTextNode = document()->createEditingTextNode(nonBreakingSpaceString());
1094         insertNodeAfter(editingTextNode, breakNode);
1095         setEndingSelection(DOMPosition(editingTextNode, 1));
1096         editingTextNode->deref();
1097     }
1098     else if (atEnd) {
1099         LOG(Editing, "input newline case 2");
1100         insertNodeAfter(breakNode, pos.node());
1101         setEndingSelection(DOMPosition(breakNode, 0));
1102     }
1103     else if (atStart) {
1104         LOG(Editing, "input newline case 3");
1105         insertNodeAt(breakNode, pos.node(), 0);
1106         setEndingSelection(DOMPosition(pos.node(), 0));
1107     }
1108     else {
1109         LOG(Editing, "input newline case 4");
1110         ASSERT(pos.node()->isTextNode());
1111         TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1112         TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.startOffset(), exceptionCode));
1113         deleteText(textNode, 0, selection.startOffset());
1114         insertNodeBefore(textBeforeNode, textNode);
1115         insertNodeBefore(breakNode, textNode);
1116         textBeforeNode->deref();
1117         setEndingSelection(DOMPosition(textNode, 0));
1118     }
1119         
1120     breakNode->deref();
1121 }
1122
1123 //------------------------------------------------------------------------------------------
1124 // InputTextCommandImpl
1125
1126 InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document) 
1127     : CompositeEditCommandImpl(document), m_insertedTextNode(0), m_charactersAdded(0)
1128 {
1129 }
1130
1131 InputTextCommandImpl::~InputTextCommandImpl() 
1132 {
1133     if (m_insertedTextNode)
1134         m_insertedTextNode->deref();
1135 }
1136
1137 int InputTextCommandImpl::commandID() const
1138 {
1139     return InputTextCommandID;
1140 }
1141
1142 void InputTextCommandImpl::doApply()
1143 {
1144 }
1145
1146 void InputTextCommandImpl::input(const DOMString &text)
1147 {
1148     execute(text);
1149 }
1150
1151 void InputTextCommandImpl::deleteCharacter()
1152 {
1153     ASSERT(state() == Applied);
1154
1155     KHTMLSelection selection = endingSelection();
1156
1157     if (!selection.startNode()->isTextNode())
1158         return;
1159
1160     int exceptionCode = 0;
1161     int offset = selection.startOffset() - 1;
1162     if (offset >= selection.startNode()->caretMinOffset()) {
1163         TextImpl *textNode = static_cast<TextImpl *>(selection.startNode());
1164         textNode->deleteData(offset, 1, exceptionCode);
1165         ASSERT(exceptionCode == 0);
1166         selection = KHTMLSelection(textNode, offset);
1167         setEndingSelection(selection);
1168         m_charactersAdded--;
1169     }
1170 }
1171
1172 DOMPosition InputTextCommandImpl::prepareForTextInsertion(bool adjustDownstream)
1173 {
1174     // Prepare for text input by looking at the current position.
1175     // It may be necessary to insert a text node to receive characters.
1176     KHTMLSelection selection = endingSelection();
1177     ASSERT(selection.state() == KHTMLSelection::CARET);
1178     
1179     DOMPosition pos = selection.startPosition();
1180     if (adjustDownstream)
1181         pos = pos.equivalentDownstreamPosition();
1182     else
1183         pos = pos.equivalentUpstreamPosition();
1184     
1185     if (!pos.node()->isTextNode()) {
1186         if (!m_insertedTextNode) {
1187             m_insertedTextNode = document()->createEditingTextNode("");
1188             m_insertedTextNode->ref();
1189         }
1190         
1191         if (pos.node()->isEditableBlock()) {
1192             LOG(Editing, "prepareForTextInsertion case 1");
1193             appendNode(pos.node(), m_insertedTextNode);
1194         }
1195         else if (pos.node()->id() == ID_BR && pos.offset() == 1) {
1196             LOG(Editing, "prepareForTextInsertion case 2");
1197             insertNodeBefore(m_insertedTextNode, pos.node());
1198         }
1199         else if (pos.node()->caretMinOffset() == pos.offset()) {
1200             LOG(Editing, "prepareForTextInsertion case 3");
1201             insertNodeBefore(m_insertedTextNode, pos.node());
1202         }
1203         else if (pos.node()->caretMaxOffset() == pos.offset()) {
1204             LOG(Editing, "prepareForTextInsertion case 4");
1205             insertNodeAfter(m_insertedTextNode, pos.node());
1206         }
1207         else
1208             ASSERT_NOT_REACHED();
1209         
1210         pos = DOMPosition(m_insertedTextNode, 0);
1211     }
1212     
1213     return pos;
1214 }
1215
1216 void InputTextCommandImpl::execute(const DOMString &text)
1217 {
1218     KHTMLSelection selection = endingSelection();
1219     bool adjustDownstream = selection.startPosition().isFirstRenderedPositionOnLine();
1220
1221     // Delete the current selection, or collapse whitespace, as needed
1222     if (selection.state() == KHTMLSelection::RANGE)
1223         deleteSelection();
1224     else
1225         deleteCollapsibleWhitespace();
1226
1227     // EDIT FIXME: Need to take typing style from upstream text, if any.
1228     
1229     // Make sure the document is set up to receive text
1230     DOMPosition pos = prepareForTextInsertion(adjustDownstream);
1231     
1232     TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1233     long offset = pos.offset();
1234     
1235     // This is a temporary implementation for inserting adjoining spaces
1236     // into a document. We are working on a CSS-related whitespace solution
1237     // that will replace this some day.
1238     if (isWS(text))
1239         insertSpace(textNode, offset);
1240     else {
1241         const DOMString &existingText = textNode->data();
1242         if (textNode->length() >= 2 && offset >= 2 && isNBSP(existingText[offset - 1]) && !isWS(existingText[offset - 2])) {
1243             // DOM looks like this:
1244             // character nbsp caret
1245             // As we are about to insert a non-whitespace character at the caret
1246             // convert the nbsp to a regular space.
1247             // EDIT FIXME: This needs to be improved some day to convert back only
1248             // those nbsp's added by the editor to make rendering come out right.
1249             replaceText(textNode, offset - 1, 1, " ");
1250         }
1251         insertText(textNode, offset, text);
1252     }
1253     setEndingSelection(DOMPosition(textNode, offset + text.length()));
1254     m_charactersAdded += text.length();
1255 }
1256
1257 void InputTextCommandImpl::insertSpace(TextImpl *textNode, unsigned long offset)
1258 {
1259     ASSERT(textNode);
1260
1261     DOMString text(textNode->data());
1262
1263     // count up all spaces and newlines in front of the caret
1264     // delete all collapsed ones
1265     // this will work out OK since the offset we have been passed has been upstream-ized 
1266     int count = 0;
1267     for (unsigned int i = offset; i < text.length(); i++) {
1268         if (isWS(text[i]))
1269             count++;
1270         else 
1271             break;
1272     }
1273     if (count > 0) {
1274         // By checking the character at the downstream position, we can
1275         // check if there is a rendered WS at the caret
1276         DOMPosition pos(textNode, offset);
1277         DOMPosition downstream = pos.equivalentDownstreamPosition();
1278         if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
1279             count--; // leave this WS in
1280         if (count > 0)
1281             deleteText(textNode, offset, count);
1282     }
1283
1284     if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
1285         // insert a "regular" space
1286         insertText(textNode, offset, " ");
1287         return;
1288     }
1289
1290     if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
1291         // DOM looks like this:
1292         // nbsp nbsp caret
1293         // insert a space between the two nbsps
1294         insertText(textNode, offset - 1, " ");
1295         return;
1296     }
1297
1298     // insert an nbsp
1299     insertText(textNode, offset, nonBreakingSpaceString());
1300 }
1301
1302 //------------------------------------------------------------------------------------------
1303 // InsertNodeBeforeCommandImpl
1304
1305 InsertNodeBeforeCommandImpl::InsertNodeBeforeCommandImpl(DocumentImpl *document, NodeImpl *insertChild, NodeImpl *refChild)
1306     : EditCommandImpl(document), m_insertChild(insertChild), m_refChild(refChild)
1307 {
1308     ASSERT(m_insertChild);
1309     m_insertChild->ref();
1310
1311     ASSERT(m_refChild);
1312     m_refChild->ref();
1313 }
1314
1315 InsertNodeBeforeCommandImpl::~InsertNodeBeforeCommandImpl()
1316 {
1317     if (m_insertChild)
1318         m_insertChild->deref();
1319     if (m_refChild)
1320         m_refChild->deref();
1321 }
1322
1323 int InsertNodeBeforeCommandImpl::commandID() const
1324 {
1325     return InsertNodeBeforeCommandID;
1326 }
1327
1328 void InsertNodeBeforeCommandImpl::doApply()
1329 {
1330     ASSERT(m_insertChild);
1331     ASSERT(m_refChild);
1332     ASSERT(m_refChild->parent());
1333
1334     int exceptionCode = 0;
1335     m_refChild->parent()->insertBefore(m_insertChild, m_refChild, exceptionCode);
1336     ASSERT(exceptionCode == 0);
1337 }
1338
1339 void InsertNodeBeforeCommandImpl::doUnapply()
1340 {
1341     ASSERT(m_insertChild);
1342     ASSERT(m_refChild);
1343     ASSERT(m_refChild->parent());
1344
1345     int exceptionCode = 0;
1346     m_refChild->parent()->removeChild(m_insertChild, exceptionCode);
1347     ASSERT(exceptionCode == 0);
1348 }
1349
1350 //------------------------------------------------------------------------------------------
1351 // InsertTextCommandImpl
1352
1353 InsertTextCommandImpl::InsertTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, const DOMString &text)
1354     : EditCommandImpl(document), m_node(node), m_offset(offset)
1355 {
1356     ASSERT(m_node);
1357     ASSERT(m_offset >= 0);
1358     ASSERT(text.length() > 0);
1359     
1360     m_node->ref();
1361     m_text = text.copy(); // make a copy to ensure that the string never changes
1362 }
1363
1364 InsertTextCommandImpl::~InsertTextCommandImpl()
1365 {
1366     if (m_node)
1367         m_node->deref();
1368 }
1369
1370 int InsertTextCommandImpl::commandID() const
1371 {
1372     return InsertTextCommandID;
1373 }
1374
1375 void InsertTextCommandImpl::doApply()
1376 {
1377     ASSERT(m_node);
1378     ASSERT(!m_text.isEmpty());
1379
1380     int exceptionCode = 0;
1381     m_node->insertData(m_offset, m_text, exceptionCode);
1382     ASSERT(exceptionCode == 0);
1383 }
1384
1385 void InsertTextCommandImpl::doUnapply()
1386 {
1387     ASSERT(m_node);
1388     ASSERT(!m_text.isEmpty());
1389
1390     int exceptionCode = 0;
1391     m_node->deleteData(m_offset, m_text.length(), exceptionCode);
1392     ASSERT(exceptionCode == 0);
1393 }
1394
1395 //------------------------------------------------------------------------------------------
1396 // JoinTextNodesCommandImpl
1397
1398 JoinTextNodesCommandImpl::JoinTextNodesCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
1399     : EditCommandImpl(document), m_text1(text1), m_text2(text2)
1400 {
1401     ASSERT(m_text1);
1402     ASSERT(m_text2);
1403     ASSERT(m_text1->nextSibling() == m_text2);
1404     ASSERT(m_text1->length() > 0);
1405     ASSERT(m_text2->length() > 0);
1406
1407     m_text1->ref();
1408     m_text2->ref();
1409 }
1410
1411 JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl()
1412 {
1413     if (m_text1)
1414         m_text1->deref();
1415     if (m_text2)
1416         m_text2->deref();
1417 }
1418
1419 int JoinTextNodesCommandImpl::commandID() const
1420 {
1421     return JoinTextNodesCommandID;
1422 }
1423
1424 void JoinTextNodesCommandImpl::doApply()
1425 {
1426     ASSERT(m_text1);
1427     ASSERT(m_text2);
1428     ASSERT(m_text1->nextSibling() == m_text2);
1429
1430     int exceptionCode = 0;
1431     m_text2->insertData(0, m_text1->data(), exceptionCode);
1432     ASSERT(exceptionCode == 0);
1433
1434     m_text2->parent()->removeChild(m_text1, exceptionCode);
1435     ASSERT(exceptionCode == 0);
1436
1437     m_offset = m_text1->length();
1438 }
1439
1440 void JoinTextNodesCommandImpl::doUnapply()
1441 {
1442     ASSERT(m_text2);
1443     ASSERT(m_offset > 0);
1444
1445     int exceptionCode = 0;
1446
1447     m_text2->deleteData(0, m_offset, exceptionCode);
1448     ASSERT(exceptionCode == 0);
1449
1450     m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
1451     ASSERT(exceptionCode == 0);
1452         
1453     ASSERT(m_text2->previousSibling()->isTextNode());
1454     ASSERT(m_text2->previousSibling() == m_text1);
1455 }
1456
1457 //------------------------------------------------------------------------------------------
1458 // PasteMarkupCommandImpl
1459
1460 PasteMarkupCommandImpl::PasteMarkupCommandImpl(DocumentImpl *document, const DOMString &markupString, const DOM::DOMString &baseURL) 
1461     : CompositeEditCommandImpl(document), m_markupString(markupString), m_baseURL(baseURL)
1462 {
1463     ASSERT(!m_markupString.isEmpty());
1464 }
1465
1466 PasteMarkupCommandImpl::~PasteMarkupCommandImpl()
1467 {
1468 }
1469
1470 int PasteMarkupCommandImpl::commandID() const
1471 {
1472     return PasteMarkupCommandID;
1473 }
1474
1475 void PasteMarkupCommandImpl::doApply()
1476 {
1477     DocumentFragmentImpl *root = static_cast<HTMLElementImpl *>(document()->documentElement())->createContextualFragment(m_markupString);
1478     ASSERT(root);
1479     
1480     if (!m_baseURL.isEmpty() && m_baseURL != document()->baseURL()) {
1481         root->recursive_completeURLs(m_baseURL.string());
1482     }
1483     
1484     NodeImpl *firstChild = root->firstChild();
1485     NodeImpl *lastChild = root->lastChild();
1486     ASSERT(firstChild);
1487     ASSERT(lastChild);
1488     
1489     KHTMLSelection selection = endingSelection();
1490
1491     // Delete the current selection, or collapse whitespace, as needed
1492     if (selection.state() == KHTMLSelection::RANGE)
1493         deleteSelection();
1494     else
1495         deleteCollapsibleWhitespace();
1496     
1497     selection = endingSelection();
1498     ASSERT(!selection.isEmpty());
1499     
1500     if (firstChild == lastChild && firstChild->isTextNode()) {
1501         // Simple text paste. Treat as if the text were typed.
1502         inputText(static_cast<TextImpl *>(firstChild)->data());
1503     } 
1504     else {
1505         // HTML fragment paste.
1506         NodeImpl *beforeNode = firstChild;
1507         NodeImpl *node = firstChild->nextSibling();
1508
1509         insertNodeAt(firstChild, selection.startNode(), selection.startOffset());
1510         
1511         // Insert the nodes from the fragment
1512         while (node) {
1513             NodeImpl *next = node->nextSibling();
1514             insertNodeAfter(node, beforeNode);
1515             beforeNode = node;
1516             node = next;
1517         }
1518         ASSERT(beforeNode);
1519                 
1520                 // Find the last leaf and place the caret after it.
1521         NodeImpl *leaf = lastChild;
1522         while (1) {
1523             NodeImpl *nextChild = leaf->lastChild();
1524             if (!nextChild)
1525                 break;
1526             leaf = nextChild;
1527         }
1528         
1529         setEndingSelection(DOMPosition(leaf, leaf->caretMaxOffset()));
1530     }
1531 }
1532
1533 //------------------------------------------------------------------------------------------
1534 // RemoveNodeCommandImpl
1535
1536 RemoveNodeCommandImpl::RemoveNodeCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
1537     : EditCommandImpl(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
1538 {
1539     ASSERT(m_removeChild);
1540     m_removeChild->ref();
1541
1542     m_parent = m_removeChild->parentNode();
1543     ASSERT(m_parent);
1544     m_parent->ref();
1545     
1546     NodeListImpl *children = m_parent->childNodes();
1547     for (int i = children->length(); i >= 0; i--) {
1548         NodeImpl *node = children->item(i);
1549         if (node == m_removeChild)
1550             break;
1551         m_refChild = node;
1552     }
1553     
1554     if (m_refChild)
1555         m_refChild->ref();
1556 }
1557
1558 RemoveNodeCommandImpl::~RemoveNodeCommandImpl()
1559 {
1560     if (m_parent)
1561         m_parent->deref();
1562     if (m_removeChild)
1563         m_removeChild->deref();
1564     if (m_refChild)
1565         m_refChild->deref();
1566 }
1567
1568 int RemoveNodeCommandImpl::commandID() const
1569 {
1570     return RemoveNodeCommandID;
1571 }
1572
1573 void RemoveNodeCommandImpl::doApply()
1574 {
1575     ASSERT(m_parent);
1576     ASSERT(m_removeChild);
1577
1578     int exceptionCode = 0;
1579     m_parent->removeChild(m_removeChild, exceptionCode);
1580     ASSERT(exceptionCode == 0);
1581 }
1582
1583 void RemoveNodeCommandImpl::doUnapply()
1584 {
1585     ASSERT(m_parent);
1586     ASSERT(m_removeChild);
1587
1588     int exceptionCode = 0;
1589     if (m_refChild)
1590         m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
1591     else
1592         m_parent->appendChild(m_removeChild, exceptionCode);
1593     ASSERT(exceptionCode == 0);
1594 }
1595
1596 //------------------------------------------------------------------------------------------
1597 // RemoveNodeAndPruneCommandImpl
1598
1599 RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
1600     : CompositeEditCommandImpl(document), m_removeChild(removeChild)
1601 {
1602     ASSERT(m_removeChild);
1603     m_removeChild->ref();
1604 }
1605
1606 RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl()
1607 {
1608     if (m_removeChild)
1609         m_removeChild->deref();
1610 }
1611
1612 int RemoveNodeAndPruneCommandImpl::commandID() const
1613 {
1614     return RemoveNodeAndPruneCommandID;
1615 }
1616
1617 void RemoveNodeAndPruneCommandImpl::doApply()
1618 {
1619     NodeImpl *editableBlock = m_removeChild->containingEditableBlock();
1620     NodeImpl *pruneNode = m_removeChild;
1621     NodeImpl *node = pruneNode->traversePreviousNode();
1622     removeNode(pruneNode);
1623     while (1) {
1624         if (editableBlock != node->containingEditableBlock() || !shouldPruneNode(node))
1625             break;
1626         pruneNode = node;
1627         node = node->traversePreviousNode();
1628         removeNode(pruneNode);
1629     }
1630 }
1631
1632 //------------------------------------------------------------------------------------------
1633 // SplitTextNodeCommandImpl
1634
1635 SplitTextNodeCommandImpl::SplitTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
1636     : EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
1637 {
1638     ASSERT(m_text2);
1639     ASSERT(m_text2->length() > 0);
1640
1641     m_text2->ref();
1642 }
1643
1644 SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl()
1645 {
1646     if (m_text1)
1647         m_text1->deref();
1648     if (m_text2)
1649         m_text2->deref();
1650 }
1651
1652 int SplitTextNodeCommandImpl::commandID() const
1653 {
1654     return SplitTextNodeCommandID;
1655 }
1656
1657 void SplitTextNodeCommandImpl::doApply()
1658 {
1659     ASSERT(m_text2);
1660     ASSERT(m_offset > 0);
1661
1662     int exceptionCode = 0;
1663
1664     // EDIT FIXME: This should use better smarts for figuring out which portion
1665     // of the split to copy (based on their comparitive sizes). We should also
1666     // just use the DOM's splitText function.
1667     
1668     if (!m_text1) {
1669         // create only if needed.
1670         // if reapplying, this object will already exist.
1671         m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
1672         ASSERT(exceptionCode == 0);
1673         ASSERT(m_text1);
1674         m_text1->ref();
1675     }
1676
1677     m_text2->deleteData(0, m_offset, exceptionCode);
1678     ASSERT(exceptionCode == 0);
1679
1680     m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
1681     ASSERT(exceptionCode == 0);
1682         
1683     ASSERT(m_text2->previousSibling()->isTextNode());
1684     ASSERT(m_text2->previousSibling() == m_text1);
1685 }
1686
1687 void SplitTextNodeCommandImpl::doUnapply()
1688 {
1689     ASSERT(m_text1);
1690     ASSERT(m_text2);
1691     
1692     ASSERT(m_text1->nextSibling() == m_text2);
1693
1694     int exceptionCode = 0;
1695     m_text2->insertData(0, m_text1->data(), exceptionCode);
1696     ASSERT(exceptionCode == 0);
1697
1698     m_text2->parent()->removeChild(m_text1, exceptionCode);
1699     ASSERT(exceptionCode == 0);
1700
1701     m_offset = m_text1->length();
1702 }
1703
1704 //------------------------------------------------------------------------------------------
1705 // TypingCommandImpl
1706
1707 TypingCommandImpl::TypingCommandImpl(DocumentImpl *document)
1708     : CompositeEditCommandImpl(document), m_openForMoreTyping(true)
1709 {
1710 }
1711
1712 TypingCommandImpl::~TypingCommandImpl()
1713 {
1714 }
1715
1716 int TypingCommandImpl::commandID() const
1717 {
1718     return TypingCommandID;
1719 }
1720
1721 void TypingCommandImpl::doApply()
1722 {
1723 }
1724
1725 void TypingCommandImpl::insertText(const DOM::DOMString &text)
1726 {
1727     if (m_cmds.count() == 0) {
1728         InputTextCommand cmd(document());
1729         applyCommandToComposite(cmd);
1730         cmd.input(text);
1731     }
1732     else {
1733         EditCommand lastCommand = m_cmds.last();
1734         if (lastCommand.commandID() == InputTextCommandID) {
1735             static_cast<InputTextCommand &>(lastCommand).input(text);
1736         }
1737         else {
1738             InputTextCommand cmd(document());
1739             applyCommandToComposite(cmd);
1740             cmd.input(text);
1741         }
1742     }
1743 }
1744
1745 void TypingCommandImpl::insertNewline()
1746 {
1747     InputNewlineCommand cmd(document());
1748     applyCommandToComposite(cmd);
1749 }
1750
1751 void TypingCommandImpl::issueCommandForDeleteKey()
1752 {
1753     KHTMLSelection selectionToDelete = endingSelection();
1754     ASSERT(selectionToDelete.state() != KHTMLSelection::NONE);
1755     
1756     if (selectionToDelete.state() == KHTMLSelection::CARET)
1757         selectionToDelete = KHTMLSelection(selectionToDelete.startPosition().previousCharacterPosition(), selectionToDelete.startPosition());
1758     deleteSelection(selectionToDelete);
1759 }
1760
1761 void TypingCommandImpl::deleteKeyPressed()
1762 {
1763 // EDIT FIXME: The ifdef'ed out code below should be re-enabled.
1764 // In order for this to happen, the deleteCharacter case
1765 // needs work. Specifically, the caret-positioning code
1766 // and whitespace-handling code in DeleteSelectionCommandImpl::doApply()
1767 // needs to be factored out so it can be used again here.
1768 // Until that work is done, issueCommandForDeleteKey() does the
1769 // right thing, but less efficiently and with the cost of more
1770 // objects.
1771     issueCommandForDeleteKey();
1772 #if 0    
1773     if (m_cmds.count() == 0) {
1774         issueCommandForDeleteKey();
1775     }
1776     else {
1777         EditCommand lastCommand = m_cmds.last();
1778         if (lastCommand.commandID() == InputTextCommandID) {
1779             InputTextCommand cmd = static_cast<InputTextCommand &>(lastCommand);
1780             cmd.deleteCharacter();
1781             if (cmd.charactersAdded() == 0) {
1782                 removeCommand(cmd);
1783             }
1784         }
1785         else if (lastCommand.commandID() == InputNewlineCommandID) {
1786             lastCommand.unapply();
1787             removeCommand(lastCommand);
1788         }
1789         else {
1790             issueCommandForDeleteKey();
1791         }
1792     }
1793 #endif
1794 }
1795
1796 void TypingCommandImpl::removeCommand(const EditCommand &cmd)
1797 {
1798     // NOTE: If the passed-in command is the last command in the
1799     // composite, we could remove all traces of this typing command
1800     // from the system, including the undo chain. Other editors do
1801     // not do this, but we could.
1802
1803     m_cmds.remove(cmd);
1804     if (m_cmds.count() == 0)
1805         setEndingSelection(startingSelection());
1806     else
1807         setEndingSelection(m_cmds.last().endingSelection());
1808 }
1809
1810 //------------------------------------------------------------------------------------------
1811