29940a74e97d3ef54dba57293109f214c69e706d
[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 void DeleteSelectionCommandImpl::doApply()
815 {
816     if (m_selectionToDelete.state() != KHTMLSelection::RANGE)
817         return;
818
819     deleteCollapsibleWhitespace(m_selectionToDelete);
820     KHTMLSelection selection = endingSelection();
821
822     DOMPosition endingPosition;
823     bool adjustEndingPositionDownstream = false;
824
825     DOMPosition upstreamStart = selection.startPosition().equivalentUpstreamPosition();
826     DOMPosition downstreamStart = selection.startPosition().equivalentDownstreamPosition();
827     DOMPosition upstreamEnd = selection.endPosition().equivalentUpstreamPosition();
828     DOMPosition downstreamEnd = selection.endPosition().equivalentDownstreamPosition();
829
830     bool startCompletelySelected = 
831         downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
832         ((downstreamStart.node() != upstreamEnd.node()) ||
833          (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset()));
834
835     bool endCompletelySelected = 
836         upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
837         ((downstreamStart.node() != upstreamEnd.node()) ||
838          (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset()));
839
840     unsigned long startRenderedOffset = downstreamStart.renderedOffset();
841     
842     bool startAtStartOfRootEditableBlock = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableBlock();
843     bool startAtStartOfBlock = startAtStartOfRootEditableBlock || 
844         (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
845     bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
846
847     debugPosition("upstreamStart:       ", upstreamStart);
848     debugPosition("downstreamStart:     ", downstreamStart);
849     debugPosition("upstreamEnd:         ", upstreamEnd);
850     debugPosition("downstreamEnd:       ", downstreamEnd);
851     LOG(Editing,  "start selected:      %s", startCompletelySelected ? "YES" : "NO");
852     LOG(Editing,  "at start block:      %s", startAtStartOfBlock ? "YES" : "NO");
853     LOG(Editing,  "at start root block: %s", startAtStartOfRootEditableBlock ? "YES" : "NO");
854     LOG(Editing,  "at end block:        %s", endAtEndOfBlock ? "YES" : "NO");
855
856     // Start is not completely selected
857     if (startAtStartOfBlock) {
858         LOG(Editing,  "ending position case 1");
859         endingPosition = DOMPosition(downstreamStart.node()->containingEditableBlock(), 1);
860         adjustEndingPositionDownstream = true;
861     }
862     else if (!startCompletelySelected) {
863         LOG(Editing,  "ending position case 2");
864         endingPosition = upstreamStart;
865         if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
866             adjustEndingPositionDownstream = true;
867     }
868     else if (upstreamStart != downstreamStart) {
869         LOG(Editing,  "ending position case 3");
870         endingPosition = upstreamStart;
871         if (downstreamStart.node()->id() == ID_BR && downstreamStart.offset() == 0)
872             adjustEndingPositionDownstream = true;
873         if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
874             adjustEndingPositionDownstream = true;
875     }
876    
877     //
878     // Figure out the whitespace conversions to do
879     //
880     if ((startAtStartOfBlock && !endAtEndOfBlock) || (!startCompletelySelected && adjustEndingPositionDownstream)) {
881         // convert trailing whitespace
882         DOMPosition trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
883         if (trailing.notEmpty()) {
884             debugPosition("convertTrailingWhitespace: ", trailing);
885             DOMPosition collapse = trailing.nextCharacterPosition();
886             if (collapse != trailing)
887                 deleteCollapsibleWhitespace(collapse);
888             TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
889             replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
890         }
891     }
892     else if (!startAtStartOfBlock && endAtEndOfBlock) {
893         // convert leading whitespace
894         DOMPosition leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
895         if (leading.notEmpty()) {
896             debugPosition("convertLeadingWhitespace:  ", leading);
897             TextImpl *textNode = static_cast<TextImpl *>(leading.node());
898             replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
899         }
900     }
901     else if (!startAtStartOfBlock && !endAtEndOfBlock) {
902         // convert contiguous whitespace
903         DOMPosition leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
904         DOMPosition trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
905         if (leading.notEmpty() && trailing.notEmpty()) {
906             debugPosition("convertLeadingWhitespace [contiguous]:  ", leading);
907             TextImpl *textNode = static_cast<TextImpl *>(leading.node());
908             replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
909         }
910     }
911         
912     //
913     // Do the delete
914     //
915     NodeImpl *n = downstreamStart.node()->traverseNextNode();
916
917     // work on start node
918     if (startCompletelySelected) {
919         removeNodeAndPrune(downstreamStart.node());
920     }
921     else if (downstreamStart.node()->isTextNode()) {
922         TextImpl *text = static_cast<TextImpl *>(downstreamStart.node());
923         int endOffset = text == upstreamEnd.node() ? upstreamEnd.offset() : text->length();
924         if (endOffset > downstreamStart.offset()) {
925             deleteText(text, downstreamStart.offset(), endOffset - downstreamStart.offset());
926         }
927     }
928     else {
929         // we have clipped the end of a non-text element
930         // the offset must be 1 here. if it is, do nothing and move on.
931         ASSERT(downstreamStart.offset() == 1);
932     }
933
934     if (downstreamStart.node() != upstreamEnd.node()) {
935         // work on intermediate nodes
936         while (n != upstreamEnd.node()) {
937             NodeImpl *d = n;
938             n = n->traverseNextNode();
939             if (d->renderer() && d->renderer()->isEditable())
940                 removeNodeAndPrune(d);
941         }
942         
943         // work on end node
944         ASSERT(n == upstreamEnd.node());
945         if (endCompletelySelected) {
946             removeNodeAndPrune(upstreamEnd.node());
947         }
948         else if (upstreamEnd.node()->isTextNode()) {
949             if (upstreamEnd.offset() > 0) {
950                 TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
951                 deleteText(text, 0, upstreamEnd.offset());
952             }
953         }
954         else {
955             // we have clipped the beginning of a non-text element
956             // the offset must be 0 here. if it is, do nothing and move on.
957             ASSERT(downstreamStart.offset() == 0);
958         }
959     }
960
961     if (adjustEndingPositionDownstream) {
962         LOG(Editing,  "adjust ending position downstream");
963         endingPosition = endingPosition.equivalentDownstreamPosition();
964     }
965
966     debugPosition("ending position:     ", endingPosition);
967     setEndingSelection(endingPosition);
968
969     LOG(Editing, "-----------------------------------------------------\n");
970 }
971
972 //------------------------------------------------------------------------------------------
973 // DeleteTextCommandImpl
974
975 DeleteTextCommandImpl::DeleteTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, long count)
976     : EditCommandImpl(document), m_node(node), m_offset(offset), m_count(count)
977 {
978     ASSERT(m_node);
979     ASSERT(m_offset >= 0);
980     ASSERT(m_count >= 0);
981     
982     m_node->ref();
983 }
984
985 DeleteTextCommandImpl::~DeleteTextCommandImpl()
986 {
987     if (m_node)
988         m_node->deref();
989 }
990
991 int DeleteTextCommandImpl::commandID() const
992 {
993     return DeleteTextCommandID;
994 }
995
996 void DeleteTextCommandImpl::doApply()
997 {
998     ASSERT(m_node);
999
1000     int exceptionCode = 0;
1001     m_text = m_node->substringData(m_offset, m_count, exceptionCode);
1002     ASSERT(exceptionCode == 0);
1003     
1004     m_node->deleteData(m_offset, m_count, exceptionCode);
1005     ASSERT(exceptionCode == 0);
1006 }
1007
1008 void DeleteTextCommandImpl::doUnapply()
1009 {
1010     ASSERT(m_node);
1011     ASSERT(!m_text.isEmpty());
1012
1013     int exceptionCode = 0;
1014     m_node->insertData(m_offset, m_text, exceptionCode);
1015     ASSERT(exceptionCode == 0);
1016 }
1017
1018 //------------------------------------------------------------------------------------------
1019 // InputNewlineCommandImpl
1020
1021 InputNewlineCommandImpl::InputNewlineCommandImpl(DocumentImpl *document) 
1022     : CompositeEditCommandImpl(document)
1023 {
1024 }
1025
1026 InputNewlineCommandImpl::~InputNewlineCommandImpl() 
1027 {
1028 }
1029
1030 int InputNewlineCommandImpl::commandID() const
1031 {
1032     return InputNewlineCommandID;
1033 }
1034
1035 void InputNewlineCommandImpl::doApply()
1036 {
1037     deleteSelection();
1038     KHTMLSelection selection = endingSelection();
1039
1040     int exceptionCode = 0;
1041     ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
1042     ASSERT(exceptionCode == 0);
1043
1044     DOMPosition pos = selection.startPosition().equivalentDownstreamPosition();
1045     bool atEnd = pos.offset() >= pos.node()->caretMaxOffset();
1046     bool atStart = pos.offset() <= pos.node()->caretMinOffset();
1047     bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
1048     
1049     if (atEndOfBlock) {
1050         LOG(Editing, "input newline case 1");
1051         appendNode(pos.node()->containingEditableBlock(), breakNode);
1052         // EDIT FIXME: This should not insert a non-breaking space after the BR.
1053         // But for right now, it gets the BR to render.
1054         TextImpl *editingTextNode = document()->createEditingTextNode(nonBreakingSpaceString());
1055         insertNodeAfter(editingTextNode, breakNode);
1056         setEndingSelection(DOMPosition(editingTextNode, 1));
1057         editingTextNode->deref();
1058     }
1059     else if (atEnd) {
1060         LOG(Editing, "input newline case 2");
1061         insertNodeAfter(breakNode, pos.node());
1062         setEndingSelection(DOMPosition(breakNode, 0));
1063     }
1064     else if (atStart) {
1065         LOG(Editing, "input newline case 3");
1066         insertNodeAt(breakNode, pos.node(), 0);
1067         setEndingSelection(DOMPosition(pos.node(), 0));
1068     }
1069     else {
1070         LOG(Editing, "input newline case 4");
1071         ASSERT(pos.node()->isTextNode());
1072         TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1073         TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.startOffset(), exceptionCode));
1074         deleteText(textNode, 0, selection.startOffset());
1075         insertNodeBefore(textBeforeNode, textNode);
1076         insertNodeBefore(breakNode, textNode);
1077         textBeforeNode->deref();
1078         setEndingSelection(DOMPosition(textNode, 0));
1079     }
1080         
1081     breakNode->deref();
1082 }
1083
1084 //------------------------------------------------------------------------------------------
1085 // InputTextCommandImpl
1086
1087 InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document) 
1088     : CompositeEditCommandImpl(document), m_insertedTextNode(0), m_charactersAdded(0)
1089 {
1090 }
1091
1092 InputTextCommandImpl::~InputTextCommandImpl() 
1093 {
1094     if (m_insertedTextNode)
1095         m_insertedTextNode->deref();
1096 }
1097
1098 int InputTextCommandImpl::commandID() const
1099 {
1100     return InputTextCommandID;
1101 }
1102
1103 void InputTextCommandImpl::doApply()
1104 {
1105 }
1106
1107 void InputTextCommandImpl::input(const DOMString &text)
1108 {
1109     execute(text);
1110 }
1111
1112 void InputTextCommandImpl::deleteCharacter()
1113 {
1114     ASSERT(state() == Applied);
1115
1116     KHTMLSelection selection = endingSelection();
1117
1118     if (!selection.startNode()->isTextNode())
1119         return;
1120
1121     int exceptionCode = 0;
1122     int offset = selection.startOffset() - 1;
1123     if (offset >= selection.startNode()->caretMinOffset()) {
1124         TextImpl *textNode = static_cast<TextImpl *>(selection.startNode());
1125         textNode->deleteData(offset, 1, exceptionCode);
1126         ASSERT(exceptionCode == 0);
1127         selection = KHTMLSelection(textNode, offset);
1128         setEndingSelection(selection);
1129         m_charactersAdded--;
1130     }
1131 }
1132
1133 DOMPosition InputTextCommandImpl::prepareForTextInsertion(bool adjustDownstream)
1134 {
1135     // Prepare for text input by looking at the current position.
1136     // It may be necessary to insert a text node to receive characters.
1137     KHTMLSelection selection = endingSelection();
1138     ASSERT(selection.state() == KHTMLSelection::CARET);
1139     
1140     DOMPosition pos = selection.startPosition();
1141     if (adjustDownstream)
1142         pos = pos.equivalentDownstreamPosition();
1143     else
1144         pos = pos.equivalentUpstreamPosition();
1145     
1146     if (!pos.node()->isTextNode()) {
1147         if (!m_insertedTextNode) {
1148             m_insertedTextNode = document()->createEditingTextNode("");
1149             m_insertedTextNode->ref();
1150         }
1151         
1152         if (pos.node()->isEditableBlock()) {
1153             LOG(Editing, "prepareForTextInsertion case 1");
1154             appendNode(pos.node(), m_insertedTextNode);
1155         }
1156         else if (pos.node()->id() == ID_BR && pos.offset() == 1) {
1157             LOG(Editing, "prepareForTextInsertion case 2");
1158             insertNodeBefore(m_insertedTextNode, pos.node());
1159         }
1160         else if (pos.node()->caretMinOffset() == pos.offset()) {
1161             LOG(Editing, "prepareForTextInsertion case 3");
1162             insertNodeBefore(m_insertedTextNode, pos.node());
1163         }
1164         else if (pos.node()->caretMaxOffset() == pos.offset()) {
1165             LOG(Editing, "prepareForTextInsertion case 4");
1166             insertNodeAfter(m_insertedTextNode, pos.node());
1167         }
1168         else
1169             ASSERT_NOT_REACHED();
1170         
1171         pos = DOMPosition(m_insertedTextNode, 0);
1172     }
1173     
1174     return pos;
1175 }
1176
1177 void InputTextCommandImpl::execute(const DOMString &text)
1178 {
1179     KHTMLSelection selection = endingSelection();
1180     bool adjustDownstream = selection.startPosition().isFirstRenderedPositionOnLine();
1181
1182     // Delete the current selection, or collapse whitespace, as needed
1183     if (selection.state() == KHTMLSelection::RANGE)
1184         deleteSelection();
1185     else
1186         deleteCollapsibleWhitespace();
1187
1188     // EDIT FIXME: Need to take typing style from upstream text, if any.
1189     
1190     // Make sure the document is set up to receive text
1191     DOMPosition pos = prepareForTextInsertion(adjustDownstream);
1192     
1193     TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1194     long offset = pos.offset();
1195     
1196     // This is a temporary implementation for inserting adjoining spaces
1197     // into a document. We are working on a CSS-related whitespace solution
1198     // that will replace this some day.
1199     if (isWS(text))
1200         insertSpace(textNode, offset);
1201     else {
1202         const DOMString &existingText = textNode->data();
1203         if (textNode->length() >= 2 && offset >= 2 && isNBSP(existingText[offset - 1]) && !isWS(existingText[offset - 2])) {
1204             // DOM looks like this:
1205             // character nbsp caret
1206             // As we are about to insert a non-whitespace character at the caret
1207             // convert the nbsp to a regular space.
1208             // EDIT FIXME: This needs to be improved some day to convert back only
1209             // those nbsp's added by the editor to make rendering come out right.
1210             replaceText(textNode, offset - 1, 1, " ");
1211         }
1212         insertText(textNode, offset, text);
1213     }
1214     setEndingSelection(DOMPosition(textNode, offset + text.length()));
1215     m_charactersAdded += text.length();
1216 }
1217
1218 void InputTextCommandImpl::insertSpace(TextImpl *textNode, unsigned long offset)
1219 {
1220     ASSERT(textNode);
1221
1222     DOMString text(textNode->data());
1223
1224     // count up all spaces and newlines in front of the caret
1225     // delete all collapsed ones
1226     // this will work out OK since the offset we have been passed has been upstream-ized 
1227     int count = 0;
1228     for (unsigned int i = offset; i < text.length(); i++) {
1229         if (isWS(text[i]))
1230             count++;
1231         else 
1232             break;
1233     }
1234     if (count > 0) {
1235         // By checking the character at the downstream position, we can
1236         // check if there is a rendered WS at the caret
1237         DOMPosition pos(textNode, offset);
1238         DOMPosition downstream = pos.equivalentDownstreamPosition();
1239         if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
1240             count--; // leave this WS in
1241         if (count > 0)
1242             deleteText(textNode, offset, count);
1243     }
1244
1245     if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
1246         // insert a "regular" space
1247         insertText(textNode, offset, " ");
1248         return;
1249     }
1250
1251     if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
1252         // DOM looks like this:
1253         // nbsp nbsp caret
1254         // insert a space between the two nbsps
1255         insertText(textNode, offset - 1, " ");
1256         return;
1257     }
1258
1259     // insert an nbsp
1260     insertText(textNode, offset, nonBreakingSpaceString());
1261 }
1262
1263 //------------------------------------------------------------------------------------------
1264 // InsertNodeBeforeCommandImpl
1265
1266 InsertNodeBeforeCommandImpl::InsertNodeBeforeCommandImpl(DocumentImpl *document, NodeImpl *insertChild, NodeImpl *refChild)
1267     : EditCommandImpl(document), m_insertChild(insertChild), m_refChild(refChild)
1268 {
1269     ASSERT(m_insertChild);
1270     m_insertChild->ref();
1271
1272     ASSERT(m_refChild);
1273     m_refChild->ref();
1274 }
1275
1276 InsertNodeBeforeCommandImpl::~InsertNodeBeforeCommandImpl()
1277 {
1278     if (m_insertChild)
1279         m_insertChild->deref();
1280     if (m_refChild)
1281         m_refChild->deref();
1282 }
1283
1284 int InsertNodeBeforeCommandImpl::commandID() const
1285 {
1286     return InsertNodeBeforeCommandID;
1287 }
1288
1289 void InsertNodeBeforeCommandImpl::doApply()
1290 {
1291     ASSERT(m_insertChild);
1292     ASSERT(m_refChild);
1293     ASSERT(m_refChild->parent());
1294
1295     int exceptionCode = 0;
1296     m_refChild->parent()->insertBefore(m_insertChild, m_refChild, exceptionCode);
1297     ASSERT(exceptionCode == 0);
1298 }
1299
1300 void InsertNodeBeforeCommandImpl::doUnapply()
1301 {
1302     ASSERT(m_insertChild);
1303     ASSERT(m_refChild);
1304     ASSERT(m_refChild->parent());
1305
1306     int exceptionCode = 0;
1307     m_refChild->parent()->removeChild(m_insertChild, exceptionCode);
1308     ASSERT(exceptionCode == 0);
1309 }
1310
1311 //------------------------------------------------------------------------------------------
1312 // InsertTextCommandImpl
1313
1314 InsertTextCommandImpl::InsertTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, const DOMString &text)
1315     : EditCommandImpl(document), m_node(node), m_offset(offset)
1316 {
1317     ASSERT(m_node);
1318     ASSERT(m_offset >= 0);
1319     ASSERT(text.length() > 0);
1320     
1321     m_node->ref();
1322     m_text = text.copy(); // make a copy to ensure that the string never changes
1323 }
1324
1325 InsertTextCommandImpl::~InsertTextCommandImpl()
1326 {
1327     if (m_node)
1328         m_node->deref();
1329 }
1330
1331 int InsertTextCommandImpl::commandID() const
1332 {
1333     return InsertTextCommandID;
1334 }
1335
1336 void InsertTextCommandImpl::doApply()
1337 {
1338     ASSERT(m_node);
1339     ASSERT(!m_text.isEmpty());
1340
1341     int exceptionCode = 0;
1342     m_node->insertData(m_offset, m_text, exceptionCode);
1343     ASSERT(exceptionCode == 0);
1344 }
1345
1346 void InsertTextCommandImpl::doUnapply()
1347 {
1348     ASSERT(m_node);
1349     ASSERT(!m_text.isEmpty());
1350
1351     int exceptionCode = 0;
1352     m_node->deleteData(m_offset, m_text.length(), exceptionCode);
1353     ASSERT(exceptionCode == 0);
1354 }
1355
1356 //------------------------------------------------------------------------------------------
1357 // JoinTextNodesCommandImpl
1358
1359 JoinTextNodesCommandImpl::JoinTextNodesCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
1360     : EditCommandImpl(document), m_text1(text1), m_text2(text2)
1361 {
1362     ASSERT(m_text1);
1363     ASSERT(m_text2);
1364     ASSERT(m_text1->nextSibling() == m_text2);
1365     ASSERT(m_text1->length() > 0);
1366     ASSERT(m_text2->length() > 0);
1367
1368     m_text1->ref();
1369     m_text2->ref();
1370 }
1371
1372 JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl()
1373 {
1374     if (m_text1)
1375         m_text1->deref();
1376     if (m_text2)
1377         m_text2->deref();
1378 }
1379
1380 int JoinTextNodesCommandImpl::commandID() const
1381 {
1382     return JoinTextNodesCommandID;
1383 }
1384
1385 void JoinTextNodesCommandImpl::doApply()
1386 {
1387     ASSERT(m_text1);
1388     ASSERT(m_text2);
1389     ASSERT(m_text1->nextSibling() == m_text2);
1390
1391     int exceptionCode = 0;
1392     m_text2->insertData(0, m_text1->data(), exceptionCode);
1393     ASSERT(exceptionCode == 0);
1394
1395     m_text2->parent()->removeChild(m_text1, exceptionCode);
1396     ASSERT(exceptionCode == 0);
1397
1398     m_offset = m_text1->length();
1399 }
1400
1401 void JoinTextNodesCommandImpl::doUnapply()
1402 {
1403     ASSERT(m_text2);
1404     ASSERT(m_offset > 0);
1405
1406     int exceptionCode = 0;
1407
1408     m_text2->deleteData(0, m_offset, exceptionCode);
1409     ASSERT(exceptionCode == 0);
1410
1411     m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
1412     ASSERT(exceptionCode == 0);
1413         
1414     ASSERT(m_text2->previousSibling()->isTextNode());
1415     ASSERT(m_text2->previousSibling() == m_text1);
1416 }
1417
1418 //------------------------------------------------------------------------------------------
1419 // PasteMarkupCommandImpl
1420
1421 PasteMarkupCommandImpl::PasteMarkupCommandImpl(DocumentImpl *document, const DOMString &markupString, const DOM::DOMString &baseURL) 
1422     : CompositeEditCommandImpl(document), m_markupString(markupString), m_baseURL(baseURL)
1423 {
1424     ASSERT(!m_markupString.isEmpty());
1425 }
1426
1427 PasteMarkupCommandImpl::~PasteMarkupCommandImpl()
1428 {
1429 }
1430
1431 int PasteMarkupCommandImpl::commandID() const
1432 {
1433     return PasteMarkupCommandID;
1434 }
1435
1436 void PasteMarkupCommandImpl::doApply()
1437 {
1438     DocumentFragmentImpl *root = static_cast<HTMLElementImpl *>(document()->documentElement())->createContextualFragment(m_markupString);
1439     ASSERT(root);
1440     
1441     if (!m_baseURL.isEmpty() && m_baseURL != document()->baseURL()) {
1442         root->recursive_completeURLs(m_baseURL.string());
1443     }
1444     
1445     NodeImpl *firstChild = root->firstChild();
1446     NodeImpl *lastChild = root->lastChild();
1447     ASSERT(firstChild);
1448     ASSERT(lastChild);
1449     
1450     KHTMLSelection selection = endingSelection();
1451
1452     // Delete the current selection, or collapse whitespace, as needed
1453     if (selection.state() == KHTMLSelection::RANGE)
1454         deleteSelection();
1455     else
1456         deleteCollapsibleWhitespace();
1457     
1458     selection = endingSelection();
1459     ASSERT(!selection.isEmpty());
1460     
1461     if (firstChild == lastChild && firstChild->isTextNode()) {
1462         // Simple text paste. Treat as if the text were typed.
1463         inputText(static_cast<TextImpl *>(firstChild)->data());
1464     } 
1465     else {
1466         // HTML fragment paste.
1467         NodeImpl *beforeNode = firstChild;
1468         NodeImpl *node = firstChild->nextSibling();
1469
1470         insertNodeAt(firstChild, selection.startNode(), selection.startOffset());
1471         
1472         // Insert the nodes from the fragment
1473         while (node) {
1474             NodeImpl *next = node->nextSibling();
1475             insertNodeAfter(node, beforeNode);
1476             beforeNode = node;
1477             node = next;
1478         }
1479         ASSERT(beforeNode);
1480                 
1481                 // Find the last leaf and place the caret after it.
1482         NodeImpl *leaf = lastChild;
1483         while (1) {
1484             NodeImpl *nextChild = leaf->lastChild();
1485             if (!nextChild)
1486                 break;
1487             leaf = nextChild;
1488         }
1489         
1490         setEndingSelection(DOMPosition(leaf, leaf->caretMaxOffset()));
1491     }
1492 }
1493
1494 //------------------------------------------------------------------------------------------
1495 // RemoveNodeCommandImpl
1496
1497 RemoveNodeCommandImpl::RemoveNodeCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
1498     : EditCommandImpl(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
1499 {
1500     ASSERT(m_removeChild);
1501     m_removeChild->ref();
1502
1503     m_parent = m_removeChild->parentNode();
1504     ASSERT(m_parent);
1505     m_parent->ref();
1506     
1507     NodeListImpl *children = m_parent->childNodes();
1508     for (int i = children->length(); i >= 0; i--) {
1509         NodeImpl *node = children->item(i);
1510         if (node == m_removeChild)
1511             break;
1512         m_refChild = node;
1513     }
1514     
1515     if (m_refChild)
1516         m_refChild->ref();
1517 }
1518
1519 RemoveNodeCommandImpl::~RemoveNodeCommandImpl()
1520 {
1521     if (m_parent)
1522         m_parent->deref();
1523     if (m_removeChild)
1524         m_removeChild->deref();
1525     if (m_refChild)
1526         m_refChild->deref();
1527 }
1528
1529 int RemoveNodeCommandImpl::commandID() const
1530 {
1531     return RemoveNodeCommandID;
1532 }
1533
1534 void RemoveNodeCommandImpl::doApply()
1535 {
1536     ASSERT(m_parent);
1537     ASSERT(m_removeChild);
1538
1539     int exceptionCode = 0;
1540     m_parent->removeChild(m_removeChild, exceptionCode);
1541     ASSERT(exceptionCode == 0);
1542 }
1543
1544 void RemoveNodeCommandImpl::doUnapply()
1545 {
1546     ASSERT(m_parent);
1547     ASSERT(m_removeChild);
1548
1549     int exceptionCode = 0;
1550     if (m_refChild)
1551         m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
1552     else
1553         m_parent->appendChild(m_removeChild, exceptionCode);
1554     ASSERT(exceptionCode == 0);
1555 }
1556
1557 //------------------------------------------------------------------------------------------
1558 // RemoveNodeAndPruneCommandImpl
1559
1560 RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
1561     : CompositeEditCommandImpl(document), m_removeChild(removeChild)
1562 {
1563     ASSERT(m_removeChild);
1564     m_removeChild->ref();
1565 }
1566
1567 RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl()
1568 {
1569     if (m_removeChild)
1570         m_removeChild->deref();
1571 }
1572
1573 int RemoveNodeAndPruneCommandImpl::commandID() const
1574 {
1575     return RemoveNodeAndPruneCommandID;
1576 }
1577
1578 void RemoveNodeAndPruneCommandImpl::doApply()
1579 {
1580     NodeImpl *editableBlock = m_removeChild->containingEditableBlock();
1581     NodeImpl *pruneNode = m_removeChild;
1582     NodeImpl *node = pruneNode->traversePreviousNode();
1583     removeNode(pruneNode);
1584     while (1) {
1585         if (editableBlock != node->containingEditableBlock() || !shouldPruneNode(node))
1586             break;
1587         pruneNode = node;
1588         node = node->traversePreviousNode();
1589         removeNode(pruneNode);
1590     }
1591 }
1592
1593 //------------------------------------------------------------------------------------------
1594 // SplitTextNodeCommandImpl
1595
1596 SplitTextNodeCommandImpl::SplitTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
1597     : EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
1598 {
1599     ASSERT(m_text2);
1600     ASSERT(m_text2->length() > 0);
1601
1602     m_text2->ref();
1603 }
1604
1605 SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl()
1606 {
1607     if (m_text1)
1608         m_text1->deref();
1609     if (m_text2)
1610         m_text2->deref();
1611 }
1612
1613 int SplitTextNodeCommandImpl::commandID() const
1614 {
1615     return SplitTextNodeCommandID;
1616 }
1617
1618 void SplitTextNodeCommandImpl::doApply()
1619 {
1620     ASSERT(m_text2);
1621     ASSERT(m_offset > 0);
1622
1623     int exceptionCode = 0;
1624
1625     // EDIT FIXME: This should use better smarts for figuring out which portion
1626     // of the split to copy (based on their comparitive sizes). We should also
1627     // just use the DOM's splitText function.
1628     
1629     if (!m_text1) {
1630         // create only if needed.
1631         // if reapplying, this object will already exist.
1632         m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
1633         ASSERT(exceptionCode == 0);
1634         ASSERT(m_text1);
1635         m_text1->ref();
1636     }
1637
1638     m_text2->deleteData(0, m_offset, exceptionCode);
1639     ASSERT(exceptionCode == 0);
1640
1641     m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
1642     ASSERT(exceptionCode == 0);
1643         
1644     ASSERT(m_text2->previousSibling()->isTextNode());
1645     ASSERT(m_text2->previousSibling() == m_text1);
1646 }
1647
1648 void SplitTextNodeCommandImpl::doUnapply()
1649 {
1650     ASSERT(m_text1);
1651     ASSERT(m_text2);
1652     
1653     ASSERT(m_text1->nextSibling() == m_text2);
1654
1655     int exceptionCode = 0;
1656     m_text2->insertData(0, m_text1->data(), exceptionCode);
1657     ASSERT(exceptionCode == 0);
1658
1659     m_text2->parent()->removeChild(m_text1, exceptionCode);
1660     ASSERT(exceptionCode == 0);
1661
1662     m_offset = m_text1->length();
1663 }
1664
1665 //------------------------------------------------------------------------------------------
1666 // TypingCommandImpl
1667
1668 TypingCommandImpl::TypingCommandImpl(DocumentImpl *document)
1669     : CompositeEditCommandImpl(document), m_openForMoreTyping(true)
1670 {
1671 }
1672
1673 TypingCommandImpl::~TypingCommandImpl()
1674 {
1675 }
1676
1677 int TypingCommandImpl::commandID() const
1678 {
1679     return TypingCommandID;
1680 }
1681
1682 void TypingCommandImpl::doApply()
1683 {
1684 }
1685
1686 void TypingCommandImpl::insertText(const DOM::DOMString &text)
1687 {
1688     if (m_cmds.count() == 0) {
1689         InputTextCommand cmd(document());
1690         applyCommandToComposite(cmd);
1691         cmd.input(text);
1692     }
1693     else {
1694         EditCommand lastCommand = m_cmds.last();
1695         if (lastCommand.commandID() == InputTextCommandID) {
1696             static_cast<InputTextCommand &>(lastCommand).input(text);
1697         }
1698         else {
1699             InputTextCommand cmd(document());
1700             applyCommandToComposite(cmd);
1701             cmd.input(text);
1702         }
1703     }
1704 }
1705
1706 void TypingCommandImpl::insertNewline()
1707 {
1708     InputNewlineCommand cmd(document());
1709     applyCommandToComposite(cmd);
1710 }
1711
1712 void TypingCommandImpl::issueCommandForDeleteKey()
1713 {
1714     KHTMLSelection selectionToDelete = endingSelection();
1715     ASSERT(selectionToDelete.state() != KHTMLSelection::NONE);
1716     
1717     if (selectionToDelete.state() == KHTMLSelection::CARET)
1718         selectionToDelete = KHTMLSelection(selectionToDelete.startPosition().previousCharacterPosition(), selectionToDelete.startPosition());
1719     deleteSelection(selectionToDelete);
1720 }
1721
1722 void TypingCommandImpl::deleteKeyPressed()
1723 {
1724 // EDIT FIXME: The ifdef'ed out code below should be re-enabled.
1725 // In order for this to happen, the deleteCharacter case
1726 // needs work. Specifically, the caret-positioning code
1727 // and whitespace-handling code in DeleteSelectionCommandImpl::doApply()
1728 // needs to be factored out so it can be used again here.
1729 // Until that work is done, issueCommandForDeleteKey() does the
1730 // right thing, but less efficiently and with the cost of more
1731 // objects.
1732     issueCommandForDeleteKey();
1733 #if 0    
1734     if (m_cmds.count() == 0) {
1735         issueCommandForDeleteKey();
1736     }
1737     else {
1738         EditCommand lastCommand = m_cmds.last();
1739         if (lastCommand.commandID() == InputTextCommandID) {
1740             InputTextCommand cmd = static_cast<InputTextCommand &>(lastCommand);
1741             cmd.deleteCharacter();
1742             if (cmd.charactersAdded() == 0) {
1743                 removeCommand(cmd);
1744             }
1745         }
1746         else if (lastCommand.commandID() == InputNewlineCommandID) {
1747             lastCommand.unapply();
1748             removeCommand(lastCommand);
1749         }
1750         else {
1751             issueCommandForDeleteKey();
1752         }
1753     }
1754 #endif
1755 }
1756
1757 void TypingCommandImpl::removeCommand(const EditCommand &cmd)
1758 {
1759     // NOTE: If the passed-in command is the last command in the
1760     // composite, we could remove all traces of this typing command
1761     // from the system, including the undo chain. Other editors do
1762     // not do this, but we could.
1763
1764     m_cmds.remove(cmd);
1765     if (m_cmds.count() == 0)
1766         setEndingSelection(startingSelection());
1767     else
1768         setEndingSelection(m_cmds.last().endingSelection());
1769 }
1770
1771 //------------------------------------------------------------------------------------------
1772