Reviewed by me.
[WebKit-https.git] / WebCore / khtml / editing / htmlediting.cpp
1 /*
2  * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "htmlediting.h"
27
28 #include "css_computedstyle.h"
29 #include "css_value.h"
30 #include "css_valueimpl.h"
31 #include "cssparser.h"
32 #include "cssproperties.h"
33 #include "dom_doc.h"
34 #include "dom_docimpl.h"
35 #include "dom_elementimpl.h"
36 #include "dom_nodeimpl.h"
37 #include "dom_position.h"
38 #include "dom_positioniterator.h"
39 #include "dom_stringimpl.h"
40 #include "dom_textimpl.h"
41 #include "dom2_range.h"
42 #include "dom2_rangeimpl.h"
43 #include "html_elementimpl.h"
44 #include "html_imageimpl.h"
45 #include "html_interchange.h"
46 #include "htmlattrs.h"
47 #include "htmltags.h"
48 #include "khtml_part.h"
49 #include "khtml_part.h"
50 #include "khtmlview.h"
51 #include "qcolor.h"
52 #include "qptrlist.h"
53 #include "render_object.h"
54 #include "render_style.h"
55 #include "render_text.h"
56 #include "visible_position.h"
57 #include "visible_text.h"
58 #include "visible_units.h"
59
60 using DOM::AttrImpl;
61 using DOM::CSSComputedStyleDeclarationImpl;
62 using DOM::CSSMutableStyleDeclarationImpl;
63 using DOM::CSSParser;
64 using DOM::CSSPrimitiveValue;
65 using DOM::CSSPrimitiveValueImpl;
66 using DOM::CSSProperty;
67 using DOM::CSSStyleDeclarationImpl;
68 using DOM::CSSValue;
69 using DOM::CSSValueImpl;
70 using DOM::DocumentFragmentImpl;
71 using DOM::DocumentImpl;
72 using DOM::DOMString;
73 using DOM::DOMStringImpl;
74 using DOM::DoNotStayInBlock;
75 using DOM::DoNotUpdateLayout;
76 using DOM::EditingTextImpl;
77 using DOM::ElementImpl;
78 using DOM::EStayInBlock;
79 using DOM::HTMLElementImpl;
80 using DOM::HTMLImageElementImpl;
81 using DOM::NamedAttrMapImpl;
82 using DOM::Node;
83 using DOM::NodeImpl;
84 using DOM::NodeListImpl;
85 using DOM::Position;
86 using DOM::PositionIterator;
87 using DOM::Range;
88 using DOM::RangeImpl;
89 using DOM::StayInBlock;
90 using DOM::TextImpl;
91 using DOM::TreeWalkerImpl;
92
93 #if APPLE_CHANGES
94 #include "KWQAssertions.h"
95 #include "KWQLogging.h"
96 #include "KWQKHTMLPart.h"
97 #endif
98
99 #if !APPLE_CHANGES
100 #define ASSERT(assertion) ((void)0)
101 #define ASSERT_WITH_MESSAGE(assertion, formatAndArgs...) ((void)0)
102 #define ASSERT_NOT_REACHED() ((void)0)
103 #define LOG(channel, formatAndArgs...) ((void)0)
104 #define ERROR(formatAndArgs...) ((void)0)
105 #define ASSERT(assertion) assert(assertion)
106 #if LOG_DISABLED
107 #define debugPosition(a,b) ((void)0)
108 #define debugNode(a,b) ((void)0)
109 #endif
110 #endif
111
112 #define IF_IMPL_NULL_RETURN_ARG(arg) do { \
113         if (isNull()) { return arg; } \
114     } while (0)
115         
116 #define IF_IMPL_NULL_RETURN do { \
117         if (isNull()) { return; } \
118     } while (0)
119
120 namespace khtml {
121
122 static inline bool isNBSP(const QChar &c)
123 {
124     return c.unicode() == 0xa0;
125 }
126
127 // FIXME: Can't really determine this without taking white-space mode into account.
128 static inline bool nextCharacterIsCollapsibleWhitespace(const Position &pos)
129 {
130     if (!pos.node())
131         return false;
132     if (!pos.node()->isTextNode())
133         return false;
134     return isCollapsibleWhitespace(static_cast<TextImpl *>(pos.node())->data()[pos.offset()]);
135 }
136
137 static const int spacesPerTab = 4;
138
139 static bool isTableStructureNode(const NodeImpl *node)
140 {
141     RenderObject *r = node->renderer();
142     return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
143 }
144
145 static bool isListStructureNode(const NodeImpl *node)
146 {
147     // FIXME: Irritating that we can get away with just going at the render tree for isTableStructureNode,
148     // but here we also have to peek at the type of DOM node?
149     RenderObject *r = node->renderer();
150     NodeImpl::Id nodeID = node->id();
151     return (r && r->isListItem())
152         || (nodeID == ID_OL || nodeID == ID_UL || nodeID == ID_DD || nodeID == ID_DT || nodeID == ID_DIR || nodeID == ID_MENU);
153 }
154
155 static DOMString &nonBreakingSpaceString()
156 {
157     static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
158     return nonBreakingSpaceString;
159 }
160
161 static DOMString &styleSpanClassString()
162 {
163     static DOMString styleSpanClassString = AppleStyleSpanClass;
164     return styleSpanClassString;
165 }
166
167 static bool isEmptyStyleSpan(const NodeImpl *node)
168 {
169     if (!node || !node->isHTMLElement() || node->id() != ID_SPAN)
170         return false;
171
172     const HTMLElementImpl *elem = static_cast<const HTMLElementImpl *>(node);
173     CSSMutableStyleDeclarationImpl *inlineStyleDecl = elem->inlineStyleDecl();
174     return (!inlineStyleDecl || inlineStyleDecl->length() == 0) && elem->getAttribute(ATTR_CLASS) == styleSpanClassString();
175 }
176
177 static bool isStyleSpan(const NodeImpl *node)
178 {
179     if (!node || !node->isHTMLElement())
180         return false;
181
182     const HTMLElementImpl *elem = static_cast<const HTMLElementImpl *>(node);
183     return elem->id() == ID_SPAN && elem->getAttribute(ATTR_CLASS) == styleSpanClassString();
184 }
185
186 static bool isEmptyFontTag(const NodeImpl *node)
187 {
188     if (!node || node->id() != ID_FONT)
189         return false;
190
191     const ElementImpl *elem = static_cast<const ElementImpl *>(node);
192     NamedAttrMapImpl *map = elem->attributes(true); // true for read-only
193     return (!map || map->length() == 1) && elem->getAttribute(ATTR_CLASS) == styleSpanClassString();
194 }
195
196 static DOMString &blockPlaceholderClassString()
197 {
198     static DOMString blockPlaceholderClassString = "khtml-block-placeholder";
199     return blockPlaceholderClassString;
200 }
201
202 static DOMString &matchNearestBlockquoteColorString()
203 {
204     static DOMString matchNearestBlockquoteColorString = "match";
205     return matchNearestBlockquoteColorString;
206 }
207
208 static void derefNodesInList(QPtrList<NodeImpl> &list)
209 {
210     for (QPtrListIterator<NodeImpl> it(list); it.current(); ++it)
211         it.current()->deref();
212 }
213
214 static int maxRangeOffset(NodeImpl *n)
215 {
216     if (DOM::offsetInCharacters(n->nodeType()))
217         return n->maxOffset();
218
219     if (n->isElementNode())
220         return n->childNodeCount();
221
222     return 1;
223 }
224
225 static int maxDeepOffset(NodeImpl *n)
226 {
227     if (n->isAtomicNode())
228         return n->caretMaxOffset();
229
230     if (n->isElementNode())
231         return n->childNodeCount();
232
233     return 1;
234 }
235
236 static void debugPosition(const char *prefix, const Position &pos)
237 {
238     if (!prefix)
239         prefix = "";
240     if (pos.isNull())
241         LOG(Editing, "%s <null>", prefix);
242     else
243         LOG(Editing, "%s%s %p : %d", prefix, pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
244 }
245
246 static void debugNode(const char *prefix, const NodeImpl *node)
247 {
248     if (!prefix)
249         prefix = "";
250     if (!node)
251         LOG(Editing, "%s <null>", prefix);
252     else
253         LOG(Editing, "%s%s %p", prefix, node->nodeName().string().latin1(), node);
254 }
255
256 //------------------------------------------------------------------------------------------
257 // EditCommandPtr
258
259 EditCommandPtr::EditCommandPtr()
260 {
261 }
262
263 EditCommandPtr::EditCommandPtr(EditCommand *impl) : SharedPtr<EditCommand>(impl)
264 {
265 }
266
267 EditCommandPtr::EditCommandPtr(const EditCommandPtr &o) : SharedPtr<EditCommand>(o)
268 {
269 }
270
271 EditCommandPtr::~EditCommandPtr()
272 {
273 }
274
275 EditCommandPtr &EditCommandPtr::operator=(const EditCommandPtr &c)
276 {
277     static_cast<SharedPtr<EditCommand> &>(*this) = c;
278     return *this;
279 }
280
281 bool EditCommandPtr::isCompositeStep() const
282 {
283     IF_IMPL_NULL_RETURN_ARG(false);        
284     return get()->isCompositeStep();
285 }
286
287 bool EditCommandPtr::isInsertTextCommand() const
288 {
289     IF_IMPL_NULL_RETURN_ARG(false);        
290     return get()->isInsertTextCommand();
291 }
292
293 bool EditCommandPtr::isTypingCommand() const
294 {
295     IF_IMPL_NULL_RETURN_ARG(false);        
296     return get()->isTypingCommand();
297 }
298
299 void EditCommandPtr::apply() const
300 {
301     IF_IMPL_NULL_RETURN;
302     get()->apply();
303 }
304
305 void EditCommandPtr::unapply() const
306 {
307     IF_IMPL_NULL_RETURN;
308     get()->unapply();
309 }
310
311 void EditCommandPtr::reapply() const
312 {
313     IF_IMPL_NULL_RETURN;
314     get()->reapply();
315 }
316
317 EditAction EditCommandPtr::editingAction() const
318 {
319     IF_IMPL_NULL_RETURN_ARG(EditActionUnspecified);
320     return get()->editingAction();
321 }
322
323 DocumentImpl * const EditCommandPtr::document() const
324 {
325     IF_IMPL_NULL_RETURN_ARG(0);
326     return get()->document();
327 }
328
329 Selection EditCommandPtr::startingSelection() const
330 {
331     IF_IMPL_NULL_RETURN_ARG(Selection());
332     return get()->startingSelection();
333 }
334
335 Selection EditCommandPtr::endingSelection() const
336 {
337     IF_IMPL_NULL_RETURN_ARG(Selection());
338     return get()->endingSelection();
339 }
340
341 void EditCommandPtr::setStartingSelection(const Selection &s) const
342 {
343     IF_IMPL_NULL_RETURN;
344     get()->setStartingSelection(s);
345 }
346
347 void EditCommandPtr::setStartingSelection(const VisiblePosition &p) const
348 {
349     IF_IMPL_NULL_RETURN;
350     get()->setStartingSelection(p);
351 }
352
353 void EditCommandPtr::setStartingSelection(const Position &p, EAffinity affinity) const
354 {
355     IF_IMPL_NULL_RETURN;
356     Selection s = Selection(p, affinity);
357     get()->setStartingSelection(s);
358 }
359
360 void EditCommandPtr::setEndingSelection(const Selection &s) const
361 {
362     IF_IMPL_NULL_RETURN;
363     get()->setEndingSelection(s);
364 }
365
366 void EditCommandPtr::setEndingSelection(const VisiblePosition &p) const
367 {
368     IF_IMPL_NULL_RETURN;
369     get()->setEndingSelection(p);
370 }
371
372 void EditCommandPtr::setEndingSelection(const Position &p, EAffinity affinity) const
373 {
374     IF_IMPL_NULL_RETURN;
375     Selection s = Selection(p, affinity);
376     get()->setEndingSelection(s);
377 }
378
379 CSSMutableStyleDeclarationImpl *EditCommandPtr::typingStyle() const
380 {
381     IF_IMPL_NULL_RETURN_ARG(0);
382     return get()->typingStyle();
383 }
384
385 void EditCommandPtr::setTypingStyle(CSSMutableStyleDeclarationImpl *style) const
386 {
387     IF_IMPL_NULL_RETURN;
388     get()->setTypingStyle(style);
389 }
390
391 EditCommandPtr EditCommandPtr::parent() const
392 {
393     IF_IMPL_NULL_RETURN_ARG(0);
394     return get()->parent();
395 }
396
397 void EditCommandPtr::setParent(const EditCommandPtr &cmd) const
398 {
399     IF_IMPL_NULL_RETURN;
400     get()->setParent(cmd.get());
401 }
402
403 EditCommandPtr &EditCommandPtr::emptyCommand()
404 {
405     static EditCommandPtr m_emptyCommand;
406     return m_emptyCommand;
407 }
408
409 //------------------------------------------------------------------------------------------
410 // StyleChange
411
412 StyleChange::StyleChange(CSSStyleDeclarationImpl *style, ELegacyHTMLStyles usesLegacyStyles)
413     : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles)
414 {
415     init(style, Position());
416 }
417
418 StyleChange::StyleChange(CSSStyleDeclarationImpl *style, const Position &position, ELegacyHTMLStyles usesLegacyStyles)
419     : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles)
420 {
421     init(style, position);
422 }
423
424 void StyleChange::init(CSSStyleDeclarationImpl *style, const Position &position)
425 {
426     style->ref();
427     CSSMutableStyleDeclarationImpl *mutableStyle = style->makeMutable();
428     mutableStyle->ref();
429     style->deref();
430     
431     QString styleText("");
432
433     QValueListConstIterator<CSSProperty> end;
434     for (QValueListConstIterator<CSSProperty> it = mutableStyle->valuesIterator(); it != end; ++it) {
435         const CSSProperty *property = &*it;
436
437         // If position is empty or the position passed in already has the 
438         // style, just move on.
439         if (position.isNotNull() && currentlyHasStyle(position, property))
440             continue;
441         
442         // If needed, figure out if this change is a legacy HTML style change.
443         if (m_usesLegacyStyles && checkForLegacyHTMLStyleChange(property))
444             continue;
445
446         // Add this property
447
448         if (property->id() == CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT) {
449             // we have to special-case text decorations
450             CSSProperty alteredProperty = CSSProperty(CSS_PROP_TEXT_DECORATION, property->value(), property->isImportant());
451             styleText += alteredProperty.cssText().string();
452         } else {
453             styleText += property->cssText().string();
454         }
455     }
456
457     mutableStyle->deref();
458
459     // Save the result for later
460     m_cssStyle = styleText.stripWhiteSpace();
461 }
462
463 StyleChange::ELegacyHTMLStyles StyleChange::styleModeForParseMode(bool isQuirksMode)
464 {
465     return isQuirksMode ? UseLegacyHTMLStyles : DoNotUseLegacyHTMLStyles;
466 }
467
468 bool StyleChange::checkForLegacyHTMLStyleChange(const CSSProperty *property)
469 {
470     if (!property || !property->value()) {
471         return false;
472     }
473     
474     DOMString valueText(property->value()->cssText());
475     switch (property->id()) {
476         case CSS_PROP_FONT_WEIGHT:
477             if (strcasecmp(valueText, "bold") == 0) {
478                 m_applyBold = true;
479                 return true;
480             }
481             break;
482         case CSS_PROP_FONT_STYLE:
483             if (strcasecmp(valueText, "italic") == 0 || strcasecmp(valueText, "oblique") == 0) {
484                 m_applyItalic = true;
485                 return true;
486             }
487             break;
488         case CSS_PROP_COLOR: {
489             QColor color(CSSParser::parseColor(valueText));
490             m_applyFontColor = color.name();
491             return true;
492         }
493         case CSS_PROP_FONT_FAMILY:
494             m_applyFontFace = valueText;
495             return true;
496         case CSS_PROP_FONT_SIZE:
497             if (property->value()->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
498                 CSSPrimitiveValueImpl *value = static_cast<CSSPrimitiveValueImpl *>(property->value());
499                 float number = value->getFloatValue(CSSPrimitiveValue::CSS_PX);
500                 if (number <= 9)
501                     m_applyFontSize = "1";
502                 else if (number <= 10)
503                     m_applyFontSize = "2";
504                 else if (number <= 13)
505                     m_applyFontSize = "3";
506                 else if (number <= 16)
507                     m_applyFontSize = "4";
508                 else if (number <= 18)
509                     m_applyFontSize = "5";
510                 else if (number <= 24)
511                     m_applyFontSize = "6";
512                 else
513                     m_applyFontSize = "7";
514                 // Huge quirk in Microsft Entourage is that they understand CSS font-size, but also write 
515                 // out legacy 1-7 values in font tags (I guess for mailers that are not CSS-savvy at all, 
516                 // like Eudora). Yes, they write out *both*. We need to write out both as well. Return false.
517                 return false; 
518             }
519             else {
520                 // Can't make sense of the number. Put no font size.
521                 return true;
522             }
523     }
524     return false;
525 }
526
527 bool StyleChange::currentlyHasStyle(const Position &pos, const CSSProperty *property)
528 {
529     ASSERT(pos.isNotNull());
530     CSSComputedStyleDeclarationImpl *style = pos.computedStyle();
531     ASSERT(style);
532     style->ref();
533     CSSValueImpl *value = style->getPropertyCSSValue(property->id(), DoNotUpdateLayout);
534     style->deref();
535     if (!value)
536         return false;
537     value->ref();
538     bool result = strcasecmp(value->cssText(), property->value()->cssText()) == 0;
539     value->deref();
540     return result;
541 }
542
543 //------------------------------------------------------------------------------------------
544 // EditCommand
545
546 EditCommand::EditCommand(DocumentImpl *document) 
547     : m_document(document), m_state(NotApplied), m_typingStyle(0), m_parent(0)
548 {
549     ASSERT(m_document);
550     ASSERT(m_document->part());
551     m_document->ref();
552     m_startingSelection = m_document->part()->selection();
553     m_endingSelection = m_startingSelection;
554
555     m_document->part()->setSelection(Selection(), false, true);
556 }
557
558 EditCommand::~EditCommand()
559 {
560     ASSERT(m_document);
561     m_document->deref();
562     if (m_typingStyle)
563         m_typingStyle->deref();
564 }
565
566 void EditCommand::apply()
567 {
568     ASSERT(m_document);
569     ASSERT(m_document->part());
570     ASSERT(state() == NotApplied);
571  
572     KHTMLPart *part = m_document->part();
573
574     ASSERT(part->selection().isNone());
575
576     doApply();
577     
578     m_state = Applied;
579
580     // FIXME: Improve typing style.
581     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
582     if (!preservesTypingStyle())
583         setTypingStyle(0);
584
585     if (!isCompositeStep()) {
586         document()->updateLayout();
587         EditCommandPtr cmd(this);
588         part->appliedEditing(cmd);
589     }
590 }
591
592 void EditCommand::unapply()
593 {
594     ASSERT(m_document);
595     ASSERT(m_document->part());
596     ASSERT(state() == Applied);
597
598     bool topLevel = !isCompositeStep();
599  
600     KHTMLPart *part = m_document->part();
601
602     if (topLevel) {
603         part->setSelection(Selection(), false, true);
604     }
605     ASSERT(part->selection().isNone());
606     
607     doUnapply();
608     
609     m_state = NotApplied;
610
611     if (topLevel) {
612         document()->updateLayout();
613         EditCommandPtr cmd(this);
614         part->unappliedEditing(cmd);
615     }
616 }
617
618 void EditCommand::reapply()
619 {
620     ASSERT(m_document);
621     ASSERT(m_document->part());
622     ASSERT(state() == NotApplied);
623     
624     bool topLevel = !isCompositeStep();
625  
626     KHTMLPart *part = m_document->part();
627
628     if (topLevel) {
629         part->setSelection(Selection(), false, true);
630     }
631     ASSERT(part->selection().isNone());
632     
633     doReapply();
634     
635     m_state = Applied;
636
637     if (topLevel) {
638         document()->updateLayout();
639         EditCommandPtr cmd(this);
640         part->reappliedEditing(cmd);
641     }
642 }
643
644 void EditCommand::doReapply()
645 {
646     doApply();
647 }
648
649 EditAction EditCommand::editingAction() const
650 {
651     return EditActionUnspecified;
652 }
653
654 void EditCommand::setStartingSelection(const Selection &s)
655 {
656     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
657         cmd->m_startingSelection = s;
658 }
659
660 void EditCommand::setStartingSelection(const VisiblePosition &p)
661 {
662     Selection s = Selection(p);
663     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
664         cmd->m_startingSelection = s;
665 }
666
667 void EditCommand::setStartingSelection(const Position &p, EAffinity affinity)
668 {
669     Selection s = Selection(p, affinity);
670     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
671         cmd->m_startingSelection = s;
672 }
673
674 void EditCommand::setEndingSelection(const Selection &s)
675 {
676     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
677         cmd->m_endingSelection = s;
678 }
679
680 void EditCommand::setEndingSelection(const VisiblePosition &p)
681 {
682     Selection s = Selection(p);
683     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
684         cmd->m_endingSelection = s;
685 }
686
687 void EditCommand::setEndingSelection(const Position &p, EAffinity affinity)
688 {
689     Selection s = Selection(p, affinity);
690     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
691         cmd->m_endingSelection = s;
692 }
693
694 void EditCommand::assignTypingStyle(CSSMutableStyleDeclarationImpl *style)
695 {
696     if (m_typingStyle == style)
697         return;
698         
699     CSSMutableStyleDeclarationImpl *old = m_typingStyle;
700     m_typingStyle = style;
701     if (m_typingStyle)
702         m_typingStyle->ref();
703     if (old)
704         old->deref();
705 }
706
707 void EditCommand::setTypingStyle(CSSMutableStyleDeclarationImpl *style)
708 {
709     // FIXME: Improve typing style.
710     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
711     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
712         cmd->assignTypingStyle(style);
713 }
714
715 bool EditCommand::preservesTypingStyle() const
716 {
717     return false;
718 }
719
720 bool EditCommand::isInsertTextCommand() const
721 {
722     return false;
723 }
724
725 bool EditCommand::isTypingCommand() const
726 {
727     return false;
728 }
729
730 CSSMutableStyleDeclarationImpl *EditCommand::styleAtPosition(const Position &pos)
731 {
732     CSSComputedStyleDeclarationImpl *computedStyle = pos.computedStyle();
733     computedStyle->ref();
734     CSSMutableStyleDeclarationImpl *style = computedStyle->copyInheritableProperties();
735     computedStyle->deref();
736  
737     // FIXME: Improve typing style.
738     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
739     CSSMutableStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
740     if (typingStyle)
741         style->merge(typingStyle);
742     
743     return style;
744 }
745
746
747 //------------------------------------------------------------------------------------------
748 // CompositeEditCommand
749
750 CompositeEditCommand::CompositeEditCommand(DocumentImpl *document) 
751     : EditCommand(document)
752 {
753 }
754
755 void CompositeEditCommand::doUnapply()
756 {
757     if (m_cmds.count() == 0) {
758         return;
759     }
760     
761     for (int i = m_cmds.count() - 1; i >= 0; --i)
762         m_cmds[i]->unapply();
763
764     setState(NotApplied);
765 }
766
767 void CompositeEditCommand::doReapply()
768 {
769     if (m_cmds.count() == 0) {
770         return;
771     }
772
773     for (QValueList<EditCommandPtr>::ConstIterator it = m_cmds.begin(); it != m_cmds.end(); ++it)
774         (*it)->reapply();
775
776     setState(Applied);
777 }
778
779 //
780 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
781 //
782 void CompositeEditCommand::applyCommandToComposite(EditCommandPtr &cmd)
783 {
784     cmd.setStartingSelection(endingSelection());
785     cmd.setEndingSelection(endingSelection());
786     cmd.setParent(this);
787     cmd.apply();
788     m_cmds.append(cmd);
789 }
790
791 void CompositeEditCommand::applyStyle(CSSStyleDeclarationImpl *style, EditAction editingAction)
792 {
793     EditCommandPtr cmd(new ApplyStyleCommand(document(), style, editingAction));
794     applyCommandToComposite(cmd);
795 }
796
797 void CompositeEditCommand::insertParagraphSeparator()
798 {
799     EditCommandPtr cmd(new InsertParagraphSeparatorCommand(document()));
800     applyCommandToComposite(cmd);
801 }
802
803 void CompositeEditCommand::insertNodeBefore(NodeImpl *insertChild, NodeImpl *refChild)
804 {
805     ASSERT(refChild->id() != ID_BODY);
806     EditCommandPtr cmd(new InsertNodeBeforeCommand(document(), insertChild, refChild));
807     applyCommandToComposite(cmd);
808 }
809
810 void CompositeEditCommand::insertNodeAfter(NodeImpl *insertChild, NodeImpl *refChild)
811 {
812     ASSERT(refChild->id() != ID_BODY);
813     if (refChild->parentNode()->lastChild() == refChild) {
814         appendNode(insertChild, refChild->parentNode());
815     }
816     else {
817         ASSERT(refChild->nextSibling());
818         insertNodeBefore(insertChild, refChild->nextSibling());
819     }
820 }
821
822 void CompositeEditCommand::insertNodeAt(NodeImpl *insertChild, NodeImpl *refChild, long offset)
823 {
824     if (refChild->hasChildNodes() || (refChild->renderer() && refChild->renderer()->isBlockFlow())) {
825         NodeImpl *child = refChild->firstChild();
826         for (long i = 0; child && i < offset; i++)
827             child = child->nextSibling();
828         if (child)
829             insertNodeBefore(insertChild, child);
830         else
831             appendNode(insertChild, refChild);
832     } 
833     else if (refChild->caretMinOffset() >= offset) {
834         insertNodeBefore(insertChild, refChild);
835     } 
836     else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
837         splitTextNode(static_cast<TextImpl *>(refChild), offset);
838         insertNodeBefore(insertChild, refChild);
839     } 
840     else {
841         insertNodeAfter(insertChild, refChild);
842     }
843 }
844
845 void CompositeEditCommand::appendNode(NodeImpl *appendChild, NodeImpl *parent)
846 {
847     EditCommandPtr cmd(new AppendNodeCommand(document(), appendChild, parent));
848     applyCommandToComposite(cmd);
849 }
850
851 void CompositeEditCommand::removeFullySelectedNode(NodeImpl *node)
852 {
853     if (isTableStructureNode(node) || node == node->rootEditableElement()) {
854         // Do not remove an element of table structure; remove its contents.
855         // Likewise for the root editable element.
856         NodeImpl *child = node->firstChild();
857         while (child) {
858             NodeImpl *remove = child;
859             child = child->nextSibling();
860             removeFullySelectedNode(remove);
861         }
862     }
863     else {
864         removeNode(node);
865     }
866 }
867
868 void CompositeEditCommand::removeChildrenInRange(NodeImpl *node, int from, int to)
869 {
870     NodeImpl *nodeToRemove = node->childNode(from);
871     for (int i = from; i < to; i++) {
872         ASSERT(nodeToRemove);
873         NodeImpl *next = nodeToRemove->nextSibling();
874         removeNode(nodeToRemove);
875         nodeToRemove = next;
876     }
877 }
878
879 void CompositeEditCommand::removeNode(NodeImpl *removeChild)
880 {
881     EditCommandPtr cmd(new RemoveNodeCommand(document(), removeChild));
882     applyCommandToComposite(cmd);
883 }
884
885 void CompositeEditCommand::removeNodePreservingChildren(NodeImpl *removeChild)
886 {
887     EditCommandPtr cmd(new RemoveNodePreservingChildrenCommand(document(), removeChild));
888     applyCommandToComposite(cmd);
889 }
890
891 void CompositeEditCommand::splitTextNode(TextImpl *text, long offset)
892 {
893     EditCommandPtr cmd(new SplitTextNodeCommand(document(), text, offset));
894     applyCommandToComposite(cmd);
895 }
896
897 void CompositeEditCommand::splitElement(ElementImpl *element, NodeImpl *atChild)
898 {
899     EditCommandPtr cmd(new SplitElementCommand(document(), element, atChild));
900     applyCommandToComposite(cmd);
901 }
902
903 void CompositeEditCommand::mergeIdenticalElements(DOM::ElementImpl *first, DOM::ElementImpl *second)
904 {
905     EditCommandPtr cmd(new MergeIdenticalElementsCommand(document(), first, second));
906     applyCommandToComposite(cmd);
907 }
908
909 void CompositeEditCommand::wrapContentsInDummySpan(DOM::ElementImpl *element)
910 {
911     EditCommandPtr cmd(new WrapContentsInDummySpanCommand(document(), element));
912     applyCommandToComposite(cmd);
913 }
914
915 void CompositeEditCommand::splitTextNodeContainingElement(DOM::TextImpl *text, long offset)
916 {
917     EditCommandPtr cmd(new SplitTextNodeContainingElementCommand(document(), text, offset));
918     applyCommandToComposite(cmd);
919 }
920
921 void CompositeEditCommand::joinTextNodes(TextImpl *text1, TextImpl *text2)
922 {
923     EditCommandPtr cmd(new JoinTextNodesCommand(document(), text1, text2));
924     applyCommandToComposite(cmd);
925 }
926
927 void CompositeEditCommand::inputText(const DOMString &text, bool selectInsertedText)
928 {
929     InsertTextCommand *impl = new InsertTextCommand(document());
930     EditCommandPtr cmd(impl);
931     applyCommandToComposite(cmd);
932     impl->input(text, selectInsertedText);
933 }
934
935 void CompositeEditCommand::insertTextIntoNode(TextImpl *node, long offset, const DOMString &text)
936 {
937     EditCommandPtr cmd(new InsertIntoTextNode(document(), node, offset, text));
938     applyCommandToComposite(cmd);
939 }
940
941 void CompositeEditCommand::deleteTextFromNode(TextImpl *node, long offset, long count)
942 {
943     EditCommandPtr cmd(new DeleteFromTextNodeCommand(document(), node, offset, count));
944     applyCommandToComposite(cmd);
945 }
946
947 void CompositeEditCommand::replaceTextInNode(TextImpl *node, long offset, long count, const DOMString &replacementText)
948 {
949     EditCommandPtr deleteCommand(new DeleteFromTextNodeCommand(document(), node, offset, count));
950     applyCommandToComposite(deleteCommand);
951     EditCommandPtr insertCommand(new InsertIntoTextNode(document(), node, offset, replacementText));
952     applyCommandToComposite(insertCommand);
953 }
954
955 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete)
956 {
957     if (endingSelection().isRange()) {
958         EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete, mergeBlocksAfterDelete));
959         applyCommandToComposite(cmd);
960     }
961 }
962
963 void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
964 {
965     if (selection.isRange()) {
966         EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete, mergeBlocksAfterDelete));
967         applyCommandToComposite(cmd);
968     }
969 }
970
971 void CompositeEditCommand::removeCSSProperty(CSSStyleDeclarationImpl *decl, int property)
972 {
973     EditCommandPtr cmd(new RemoveCSSPropertyCommand(document(), decl, property));
974     applyCommandToComposite(cmd);
975 }
976
977 void CompositeEditCommand::removeNodeAttribute(ElementImpl *element, int attribute)
978 {
979     DOMString value = element->getAttribute(attribute);
980     if (value.isEmpty())
981         return;
982     EditCommandPtr cmd(new RemoveNodeAttributeCommand(document(), element, attribute));
983     applyCommandToComposite(cmd);
984 }
985
986 void CompositeEditCommand::setNodeAttribute(ElementImpl *element, int attribute, const DOMString &value)
987 {
988     EditCommandPtr cmd(new SetNodeAttributeCommand(document(), element, attribute, value));
989     applyCommandToComposite(cmd);
990 }
991
992 void CompositeEditCommand::rebalanceWhitespace()
993 {
994     Selection selection = endingSelection();
995     if (selection.isCaretOrRange()) {
996         EditCommandPtr startCmd(new RebalanceWhitespaceCommand(document(), endingSelection().start()));
997         applyCommandToComposite(startCmd);
998         if (selection.isRange()) {
999             EditCommandPtr endCmd(new RebalanceWhitespaceCommand(document(), endingSelection().end()));
1000             applyCommandToComposite(endCmd);
1001         }
1002     }
1003 }
1004
1005 void CompositeEditCommand::deleteInsignificantText(TextImpl *textNode, int start, int end)
1006 {
1007     if (!textNode || !textNode->renderer() || start >= end)
1008         return;
1009
1010     RenderText *textRenderer = static_cast<RenderText *>(textNode->renderer());
1011     InlineTextBox *box = textRenderer->firstTextBox();
1012     if (!box) {
1013         // whole text node is empty
1014         removeNode(textNode);
1015         return;    
1016     }
1017     
1018     long length = textNode->length();
1019     if (start >= length || end > length)
1020         return;
1021
1022     int removed = 0;
1023     InlineTextBox *prevBox = 0;
1024     DOMStringImpl *str = 0;
1025
1026     // This loop structure works to process all gaps preceding a box,
1027     // and also will look at the gap after the last box.
1028     while (prevBox || box) {
1029         int gapStart = prevBox ? prevBox->m_start + prevBox->m_len : 0;
1030         if (end < gapStart)
1031             // No more chance for any intersections
1032             break;
1033
1034         int gapEnd = box ? box->m_start : length;
1035         bool indicesIntersect = start <= gapEnd && end >= gapStart;
1036         int gapLen = gapEnd - gapStart;
1037         if (indicesIntersect && gapLen > 0) {
1038             gapStart = kMax(gapStart, start);
1039             gapEnd = kMin(gapEnd, end);
1040             if (!str) {
1041                 str = textNode->string()->substring(start, end - start);
1042                 str->ref();
1043             }    
1044             // remove text in the gap
1045             str->remove(gapStart - start - removed, gapLen);
1046             removed += gapLen;
1047         }
1048         
1049         prevBox = box;
1050         if (box)
1051             box = box->nextTextBox();
1052     }
1053
1054     if (str) {
1055         // Replace the text between start and end with our pruned version.
1056         if (str->l > 0) {
1057             replaceTextInNode(textNode, start, end - start, str);
1058         }
1059         else {
1060             // Assert that we are not going to delete all of the text in the node.
1061             // If we were, that should have been done above with the call to 
1062             // removeNode and return.
1063             ASSERT(start > 0 || (unsigned long)end - start < textNode->length());
1064             deleteTextFromNode(textNode, start, end - start);
1065         }
1066         str->deref();
1067     }
1068 }
1069
1070 void CompositeEditCommand::deleteInsignificantText(const Position &start, const Position &end)
1071 {
1072     if (start.isNull() || end.isNull())
1073         return;
1074
1075     if (RangeImpl::compareBoundaryPoints(start, end) >= 0)
1076         return;
1077
1078     NodeImpl *node = start.node();
1079     while (node) {
1080         NodeImpl *next = node->traverseNextNode();
1081     
1082         if (node->isTextNode()) {
1083             TextImpl *textNode = static_cast<TextImpl *>(node);
1084             bool isStartNode = node == start.node();
1085             bool isEndNode = node == end.node();
1086             int startOffset = isStartNode ? start.offset() : 0;
1087             int endOffset = isEndNode ? end.offset() : textNode->length();
1088             deleteInsignificantText(textNode, startOffset, endOffset);
1089         }
1090             
1091         if (node == end.node())
1092             break;
1093         node = next;
1094     }
1095 }
1096
1097 void CompositeEditCommand::deleteInsignificantTextDownstream(const DOM::Position &pos)
1098 {
1099     Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream(StayInBlock);
1100     deleteInsignificantText(pos, end);
1101 }
1102
1103 NodeImpl *CompositeEditCommand::appendBlockPlaceholder(NodeImpl *node)
1104 {
1105     if (!node)
1106         return NULL;
1107
1108     ASSERT(node->renderer() && node->renderer()->isBlockFlow());
1109
1110     NodeImpl *placeholder = createBlockPlaceholderElement(document());
1111     appendNode(placeholder, node);
1112     return placeholder;
1113 }
1114
1115 NodeImpl *CompositeEditCommand::insertBlockPlaceholder(const Position &pos)
1116 {
1117     if (pos.isNull())
1118         return NULL;
1119
1120     ASSERT(pos.node()->renderer() && pos.node()->renderer()->isBlockFlow());
1121
1122     NodeImpl *placeholder = createBlockPlaceholderElement(document());
1123     insertNodeAt(placeholder, pos.node(), pos.offset());
1124     return placeholder;
1125 }
1126
1127 NodeImpl *CompositeEditCommand::addBlockPlaceholderIfNeeded(NodeImpl *node)
1128 {
1129     if (!node)
1130         return false;
1131
1132     document()->updateLayout();
1133
1134     RenderObject *renderer = node->renderer();
1135     if (!renderer || !renderer->isBlockFlow())
1136         return false;
1137     
1138     // append the placeholder to make sure it follows
1139     // any unrendered blocks
1140     if (renderer->height() == 0) {
1141         return appendBlockPlaceholder(node);
1142     }
1143
1144     return NULL;
1145 }
1146
1147 bool CompositeEditCommand::removeBlockPlaceholder(NodeImpl *node)
1148 {
1149     NodeImpl *placeholder = findBlockPlaceholder(node);
1150     if (placeholder) {
1151         removeNode(placeholder);
1152         return true;
1153     }
1154     return false;
1155 }
1156
1157 NodeImpl *CompositeEditCommand::findBlockPlaceholder(NodeImpl *node)
1158 {
1159     if (!node)
1160         return 0;
1161
1162     document()->updateLayout();
1163
1164     RenderObject *renderer = node->renderer();
1165     if (!renderer || !renderer->isBlockFlow())
1166         return 0;
1167
1168     for (NodeImpl *checkMe = node; checkMe; checkMe = checkMe->traverseNextNode(node)) {
1169         if (checkMe->isElementNode()) {
1170             ElementImpl *element = static_cast<ElementImpl *>(checkMe);
1171             if (element->enclosingBlockFlowElement() == node && 
1172                 element->getAttribute(ATTR_CLASS) == blockPlaceholderClassString()) {
1173                 return element;
1174             }
1175         }
1176     }
1177     
1178     return 0;
1179 }
1180
1181 void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
1182 {
1183     if (pos.isNull())
1184         return;
1185     
1186     document()->updateLayout();
1187     
1188     VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
1189     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
1190     VisiblePosition visibleParagraphEnd(endOfParagraph(visiblePos, IncludeLineBreak));
1191     Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream(StayInBlock);
1192     Position paragraphEnd = visibleParagraphEnd.deepEquivalent().upstream(StayInBlock);
1193     Position beforeParagraphStart = paragraphStart.upstream(DoNotStayInBlock);
1194     
1195     // Perform some checks to see if we need to perform work in this function.
1196     if (paragraphStart.node()->isBlockFlow()) {
1197         if (paragraphEnd.node()->isBlockFlow()) {
1198             if (!paragraphEnd.node()->isAncestor(paragraphStart.node())) {
1199                 // If the paragraph end is a descendant of paragraph start, then we need to run
1200                 // the rest of this function. If not, we can bail here.
1201                 return;
1202             }
1203         }
1204         else if (paragraphEnd.node()->enclosingBlockFlowElement() != paragraphStart.node()) {
1205             // The paragraph end is in another block that is an ancestor of the paragraph start.
1206             // We can bail as we have a full block to work with.
1207             ASSERT(paragraphStart.node()->isAncestor(paragraphEnd.node()->enclosingBlockFlowElement()));
1208             return;
1209         }
1210         else if (isEndOfDocument(visibleParagraphEnd)) {
1211             // At the end of the document. We can bail here as well.
1212             return;
1213         }
1214     }
1215     
1216     // Create the block to insert. Most times, this will be a shallow clone of the block containing
1217     // the start of the selection (the start block), except for two cases:
1218     //    1) When the start block is a body element.
1219     //    2) When the start block is a mail blockquote and we are not in a position to insert
1220     //       the new block as a peer of the start block. This prevents creating an unwanted 
1221     //       additional level of quoting.
1222     NodeImpl *startBlock = paragraphStart.node()->enclosingBlockFlowElement();
1223     NodeImpl *newBlock = 0;
1224     if (startBlock->id() == ID_BODY || (isMailBlockquote(startBlock) && paragraphStart.node() != startBlock))
1225         newBlock = createDefaultParagraphElement(document());
1226     else
1227         newBlock = startBlock->cloneNode(false);
1228
1229     NodeImpl *moveNode = paragraphStart.node();
1230     if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
1231         moveNode = moveNode->traverseNextNode();
1232     NodeImpl *endNode = paragraphEnd.node();
1233
1234     if (paragraphStart.node()->id() == ID_BODY) {
1235         insertNodeAt(newBlock, paragraphStart.node(), 0);
1236     }
1237     else if (paragraphStart.node()->id() == ID_BR) {
1238         insertNodeAfter(newBlock, paragraphStart.node());
1239     }
1240     else if (paragraphStart.node()->isBlockFlow()) {
1241         insertNodeBefore(newBlock, paragraphStart.node());
1242     }
1243     else if (beforeParagraphStart.node()->enclosingBlockFlowElement()->id() != ID_BODY) {
1244         insertNodeAfter(newBlock, beforeParagraphStart.node()->enclosingBlockFlowElement());
1245     }
1246     else {
1247         insertNodeAfter(newBlock, beforeParagraphStart.node());
1248     }
1249
1250     while (moveNode && !moveNode->isBlockFlow()) {
1251         NodeImpl *next = moveNode->traverseNextSibling();
1252         removeNode(moveNode);
1253         appendNode(moveNode, newBlock);
1254         if (moveNode == endNode)
1255             break;
1256         moveNode = next;
1257     }
1258 }
1259
1260 static bool isSpecialElement(NodeImpl *n)
1261 {
1262     if (!n->isHTMLElement())
1263         return false;
1264
1265     if (n->id() == ID_A && n->hasAnchor())
1266         return true;
1267
1268     if (n->id() == ID_UL || n->id() == ID_OL || n->id() == ID_DL)
1269         return true;
1270
1271     RenderObject *renderer = n->renderer();
1272
1273     if (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE))
1274         return true;
1275
1276     if (renderer && renderer->style()->isFloating())
1277         return true;
1278
1279     if (renderer && renderer->style()->position() != STATIC)
1280         return true;
1281
1282     return false;
1283 }
1284
1285 // This version of the function is meant to be called on positions in a document fragment,
1286 // so it does not check for a root editable element, it is assumed these nodes will be put
1287 // somewhere editable in the future
1288 static bool isFirstVisiblePositionInSpecialElementInFragment(const Position& pos)
1289 {
1290     VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
1291
1292     for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
1293         if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
1294             return false;
1295         if (isSpecialElement(n))
1296             return true;
1297     }
1298
1299     return false;
1300 }
1301
1302 static bool isFirstVisiblePositionInSpecialElement(const Position& pos)
1303 {
1304     VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
1305
1306     for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
1307         if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
1308             return false;
1309         if (n->rootEditableElement() == NULL)
1310             return false;
1311         if (isSpecialElement(n))
1312             return true;
1313     }
1314
1315     return false;
1316 }
1317
1318 static Position positionBeforeNode(NodeImpl *node)
1319 {
1320     return Position(node->parentNode(), node->nodeIndex());
1321 }
1322
1323 static Position positionBeforeContainingSpecialElement(const Position& pos)
1324 {
1325     ASSERT(isFirstVisiblePositionInSpecialElement(pos));
1326
1327     VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
1328     
1329     NodeImpl *outermostSpecialElement = NULL;
1330
1331     for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
1332         if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
1333             break;
1334         if (n->rootEditableElement() == NULL)
1335             break;
1336         if (isSpecialElement(n))
1337             outermostSpecialElement = n;
1338     }
1339     
1340     ASSERT(outermostSpecialElement);
1341
1342     Position result = positionBeforeNode(outermostSpecialElement);
1343     if (result.isNull() || !result.node()->rootEditableElement())
1344         return pos;
1345     
1346     return result;
1347 }
1348
1349 static bool isLastVisiblePositionInSpecialElement(const Position& pos)
1350 {
1351     // make sure to get a range-compliant version of the position
1352     Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
1353
1354     VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
1355
1356     for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
1357         if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
1358             return false;
1359         if (n->rootEditableElement() == NULL)
1360             return false;
1361         if (isSpecialElement(n))
1362             return true;
1363     }
1364
1365     return false;
1366 }
1367
1368 static Position positionAfterNode(NodeImpl *node)
1369 {
1370     return Position(node->parentNode(), node->nodeIndex() + 1);
1371 }
1372
1373 static Position positionAfterContainingSpecialElement(const Position& pos)
1374 {
1375     ASSERT(isLastVisiblePositionInSpecialElement(pos));
1376
1377     // make sure to get a range-compliant version of the position
1378     Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
1379
1380     VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
1381
1382     NodeImpl *outermostSpecialElement = NULL;
1383
1384     for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
1385         if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
1386             break;
1387         if (n->rootEditableElement() == NULL)
1388             break;
1389         if (isSpecialElement(n))
1390             outermostSpecialElement = n;
1391     }
1392     
1393     ASSERT(outermostSpecialElement);
1394
1395     Position result = positionAfterNode(outermostSpecialElement);
1396     if (result.isNull() || !result.node()->rootEditableElement())
1397         return pos;
1398     
1399     return result;
1400 }
1401
1402 static Position positionOutsideContainingSpecialElement(const Position &pos)
1403 {
1404     if (isFirstVisiblePositionInSpecialElement(pos)) {
1405         return positionBeforeContainingSpecialElement(pos);
1406     } else if (isLastVisiblePositionInSpecialElement(pos)) {
1407         return positionAfterContainingSpecialElement(pos);
1408     }
1409
1410     return pos;
1411 }
1412
1413 static Position positionBeforePossibleContainingSpecialElement(const Position &pos)
1414 {
1415     if (isFirstVisiblePositionInSpecialElement(pos)) {
1416         return positionBeforeContainingSpecialElement(pos);
1417     } 
1418
1419     return pos;
1420 }
1421
1422 static Position positionAfterPossibleContainingSpecialElement(const Position &pos)
1423 {
1424     if (isLastVisiblePositionInSpecialElement(pos)) {
1425         return positionAfterContainingSpecialElement(pos);
1426     }
1427
1428     return pos;
1429 }
1430
1431 //==========================================================================================
1432 // Concrete commands
1433 //------------------------------------------------------------------------------------------
1434 // AppendNodeCommand
1435
1436 AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *appendChild, NodeImpl *parentNode)
1437     : EditCommand(document), m_appendChild(appendChild), m_parentNode(parentNode)
1438 {
1439     ASSERT(m_appendChild);
1440     m_appendChild->ref();
1441
1442     ASSERT(m_parentNode);
1443     m_parentNode->ref();
1444 }
1445
1446 AppendNodeCommand::~AppendNodeCommand()
1447 {
1448     ASSERT(m_appendChild);
1449     m_appendChild->deref();
1450
1451     ASSERT(m_parentNode);
1452     m_parentNode->deref();
1453 }
1454
1455 void AppendNodeCommand::doApply()
1456 {
1457     ASSERT(m_appendChild);
1458     ASSERT(m_parentNode);
1459
1460     int exceptionCode = 0;
1461     m_parentNode->appendChild(m_appendChild, exceptionCode);
1462     ASSERT(exceptionCode == 0);
1463 }
1464
1465 void AppendNodeCommand::doUnapply()
1466 {
1467     ASSERT(m_appendChild);
1468     ASSERT(m_parentNode);
1469     ASSERT(state() == Applied);
1470
1471     int exceptionCode = 0;
1472     m_parentNode->removeChild(m_appendChild, exceptionCode);
1473     ASSERT(exceptionCode == 0);
1474 }
1475
1476 //------------------------------------------------------------------------------------------
1477 // ApplyStyleCommand
1478
1479 ApplyStyleCommand::ApplyStyleCommand(DocumentImpl *document, CSSStyleDeclarationImpl *style, EditAction editingAction, EPropertyLevel propertyLevel)
1480     : CompositeEditCommand(document), m_style(style->makeMutable()), m_editingAction(editingAction), m_propertyLevel(propertyLevel)
1481 {   
1482     ASSERT(m_style);
1483     m_style->ref();
1484 }
1485
1486 ApplyStyleCommand::~ApplyStyleCommand()
1487 {
1488     ASSERT(m_style);
1489     m_style->deref();
1490 }
1491
1492 void ApplyStyleCommand::doApply()
1493 {
1494     switch (m_propertyLevel) {
1495         case PropertyDefault: {
1496             // apply the block-centric properties of the style
1497             CSSMutableStyleDeclarationImpl *blockStyle = m_style->copyBlockProperties();
1498             blockStyle->ref();
1499             applyBlockStyle(blockStyle);
1500             // apply any remaining styles to the inline elements
1501             // NOTE: hopefully, this string comparison is the same as checking for a non-null diff
1502             if (blockStyle->length() < m_style->length()) {
1503                 CSSMutableStyleDeclarationImpl *inlineStyle = m_style->copy();
1504                 inlineStyle->ref();
1505                 applyRelativeFontStyleChange(inlineStyle);
1506                 blockStyle->diff(inlineStyle);
1507                 applyInlineStyle(inlineStyle);
1508                 inlineStyle->deref();
1509             }
1510             blockStyle->deref();
1511             break;
1512         }
1513         case ForceBlockProperties:
1514             // Force all properties to be applied as block styles.
1515             applyBlockStyle(m_style);
1516             break;
1517     }
1518    
1519     setEndingSelectionNeedsLayout();
1520 }
1521
1522 EditAction ApplyStyleCommand::editingAction() const
1523 {
1524     return m_editingAction;
1525 }
1526
1527 void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclarationImpl *style)
1528 {
1529     // update document layout once before removing styles
1530     // so that we avoid the expense of updating before each and every call
1531     // to check a computed style
1532     document()->updateLayout();
1533
1534     // get positions we want to use for applying style
1535     Position start(endingSelection().start());
1536     Position end(endingSelection().end());
1537     
1538     // remove current values, if any, of the specified styles from the blocks
1539     // NOTE: tracks the previous block to avoid repeated processing
1540     // Also, gather up all the nodes we want to process in a QPtrList before
1541     // doing anything. This averts any bugs iterating over these nodes
1542     // once you start removing and applying style.
1543     NodeImpl *beyondEnd = end.node()->traverseNextNode();
1544     QPtrList<NodeImpl> nodes;
1545     for (NodeImpl *node = start.node(); node != beyondEnd; node = node->traverseNextNode())
1546         nodes.append(node);
1547         
1548     NodeImpl *prevBlock = 0;
1549     for (QPtrListIterator<NodeImpl> it(nodes); it.current(); ++it) {
1550         NodeImpl *block = it.current()->enclosingBlockFlowElement();
1551         if (block != prevBlock && block->isHTMLElement()) {
1552             removeCSSStyle(style, static_cast<HTMLElementImpl *>(block));
1553             prevBlock = block;
1554         }
1555     }
1556     
1557     // apply specified styles to the block flow elements in the selected range
1558     prevBlock = 0;
1559     for (QPtrListIterator<NodeImpl> it(nodes); it.current(); ++it) {
1560         NodeImpl *node = it.current();
1561         if (node->renderer()) {
1562             NodeImpl *block = node->enclosingBlockFlowElement();
1563             if (block != prevBlock) {
1564                 addBlockStyleIfNeeded(style, node);
1565                 prevBlock = block;
1566             }
1567         }
1568     }
1569 }
1570
1571 #define NoFontDelta (0.0f)
1572 #define MinimumFontSize (0.1f)
1573
1574 void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclarationImpl *style)
1575 {
1576     if (style->getPropertyCSSValue(CSS_PROP_FONT_SIZE)) {
1577         // Explicit font size overrides any delta.
1578         style->removeProperty(CSS_PROP__KHTML_FONT_SIZE_DELTA);
1579         return;
1580     }
1581
1582     // Get the adjustment amount out of the style.
1583     CSSValueImpl *value = style->getPropertyCSSValue(CSS_PROP__KHTML_FONT_SIZE_DELTA);
1584     if (!value)
1585         return;
1586     value->ref();
1587     float adjustment = NoFontDelta;
1588     if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
1589         CSSPrimitiveValueImpl *primitiveValue = static_cast<CSSPrimitiveValueImpl *>(value);
1590         if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_PX) {
1591             // Only PX handled now. If we handle more types in the future, perhaps
1592             // a switch statement here would be more appropriate.
1593             adjustment = primitiveValue->getFloatValue(CSSPrimitiveValue::CSS_PX);
1594         }
1595     }
1596     style->removeProperty(CSS_PROP__KHTML_FONT_SIZE_DELTA);
1597     value->deref();
1598     if (adjustment == NoFontDelta)
1599         return;
1600     
1601     // Adjust to the positions we want to use for applying style.
1602     Selection selection = endingSelection();
1603     Position start(selection.start().downstream(StayInBlock));
1604     Position end(selection.end().upstream(StayInBlock));
1605     if (RangeImpl::compareBoundaryPoints(end, start) < 0) {
1606         Position swap = start;
1607         start = end;
1608         end = swap;
1609     }
1610
1611     // Join up any adjacent text nodes.
1612     if (start.node()->isTextNode()) {
1613         joinChildTextNodes(start.node()->parentNode(), start, end);
1614         selection = endingSelection();
1615         start = selection.start();
1616         end = selection.end();
1617     }
1618     if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) {
1619         joinChildTextNodes(end.node()->parentNode(), start, end);
1620         selection = endingSelection();
1621         start = selection.start();
1622         end = selection.end();
1623     }
1624
1625     // Split the start text nodes if needed to apply style.
1626     bool splitStart = splitTextAtStartIfNeeded(start, end); 
1627     if (splitStart) {
1628         start = endingSelection().start();
1629         end = endingSelection().end();
1630     }
1631     bool splitEnd = splitTextAtEndIfNeeded(start, end);
1632     if (splitEnd) {
1633         start = endingSelection().start();
1634         end = endingSelection().end();
1635     }
1636
1637     NodeImpl *beyondEnd = end.node()->traverseNextNode(); // Calculate loop end point.
1638     start = start.upstream(StayInBlock); // Move upstream to ensure we do not add redundant spans.
1639     NodeImpl *startNode = start.node();
1640     if (startNode->isTextNode() && start.offset() >= startNode->caretMaxOffset()) // Move out of text node if range does not include its characters.
1641         startNode = startNode->traverseNextNode();
1642
1643     // Store away font size before making any changes to the document.
1644     // This ensures that changes to one node won't effect another.
1645     QMap<const NodeImpl *,float> startingFontSizes;
1646     for (const NodeImpl *node = startNode; node != beyondEnd; node = node->traverseNextNode())
1647         startingFontSizes.insert(node, computedFontSize(node));
1648
1649     // These spans were added by us. If empty after font size changes, they can be removed.
1650     QPtrList<NodeImpl> emptySpans;
1651     
1652     NodeImpl *lastStyledNode = 0;
1653     for (NodeImpl *node = startNode; node != beyondEnd; node = node->traverseNextNode()) {
1654         HTMLElementImpl *elem = 0;
1655         if (node->isHTMLElement()) {
1656             // Only work on fully selected nodes.
1657             if (!nodeFullySelected(node, start, end))
1658                 continue;
1659             elem = static_cast<HTMLElementImpl *>(node);
1660         }
1661         else if (node->isTextNode() && node->parentNode() != lastStyledNode) {
1662             // Last styled node was not parent node of this text node, but we wish to style this
1663             // text node. To make this possible, add a style span to surround this text node.
1664             elem = static_cast<HTMLElementImpl *>(createStyleSpanElement(document()));
1665             insertNodeBefore(elem, node);
1666             surroundNodeRangeWithElement(node, node, elem);
1667         }
1668         else {
1669             // Only handle HTML elements and text nodes.
1670             continue;
1671         }
1672         lastStyledNode = node;
1673         
1674         CSSMutableStyleDeclarationImpl *inlineStyleDecl = elem->getInlineStyleDecl();
1675         float currentFontSize = computedFontSize(node);
1676         float desiredFontSize = kMax(MinimumFontSize, startingFontSizes[node] + adjustment);
1677         if (inlineStyleDecl->getPropertyCSSValue(CSS_PROP_FONT_SIZE)) {
1678             inlineStyleDecl->removeProperty(CSS_PROP_FONT_SIZE, true);
1679             currentFontSize = computedFontSize(node);
1680         }
1681         if (currentFontSize != desiredFontSize) {
1682             QString desiredFontSizeString = QString::number(desiredFontSize);
1683             desiredFontSizeString += "px";
1684             inlineStyleDecl->setProperty(CSS_PROP_FONT_SIZE, desiredFontSizeString, false, false);
1685             setNodeAttribute(elem, ATTR_STYLE, inlineStyleDecl->cssText());
1686         }
1687         if (inlineStyleDecl->length() == 0) {
1688             removeNodeAttribute(elem, ATTR_STYLE);
1689             if (isEmptyStyleSpan(elem))
1690                 emptySpans.append(elem);
1691         }
1692     }
1693
1694     for (QPtrListIterator<NodeImpl> it(emptySpans); it.current(); ++it)
1695         removeNodePreservingChildren(it.current());
1696 }
1697
1698 #undef NoFontDelta
1699 #undef MinimumFontSize
1700
1701 void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclarationImpl *style)
1702 {
1703     // adjust to the positions we want to use for applying style
1704     Position start(endingSelection().start().downstream(StayInBlock).equivalentRangeCompliantPosition());
1705     Position end(endingSelection().end().upstream(StayInBlock));
1706
1707     if (RangeImpl::compareBoundaryPoints(end, start) < 0) {
1708         Position swap = start;
1709         start = end;
1710         end = swap;
1711     }
1712
1713     // update document layout once before removing styles
1714     // so that we avoid the expense of updating before each and every call
1715     // to check a computed style
1716     document()->updateLayout();
1717
1718     // split the start node and containing element if the selection starts inside of it
1719     bool splitStart = splitTextElementAtStartIfNeeded(start, end); 
1720     if (splitStart) {
1721         start = endingSelection().start();
1722         end = endingSelection().end();
1723     }
1724
1725     // split the end node and containing element if the selection ends inside of it
1726     bool splitEnd = splitTextElementAtEndIfNeeded(start, end);
1727     start = endingSelection().start();
1728     end = endingSelection().end();
1729
1730     // Remove style from the selection.
1731     // Use the upstream position of the start for removing style.
1732     // This will ensure we remove all traces of the relevant styles from the selection
1733     // and prevent us from adding redundant ones, as described in:
1734     // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
1735     removeInlineStyle(style, start.upstream(StayInBlock), end);
1736     start = endingSelection().start();
1737     end = endingSelection().end();
1738
1739     if (splitStart) {
1740         bool mergedStart = mergeStartWithPreviousIfIdentical(start, end);
1741         if (mergedStart) {
1742             start = endingSelection().start();
1743             end = endingSelection().end();
1744         }
1745     }
1746
1747     if (splitEnd) {
1748         mergeEndWithNextIfIdentical(start, end);
1749         start = endingSelection().start();
1750         end = endingSelection().end();
1751     }
1752
1753     // update document layout once before running the rest of the function
1754     // so that we avoid the expense of updating before each and every call
1755     // to check a computed style
1756     document()->updateLayout();
1757     
1758     if (start.node() == end.node()) {
1759         // simple case...start and end are the same node
1760         addInlineStyleIfNeeded(style, start.node(), end.node());
1761     }
1762     else {
1763         NodeImpl *node = start.node();
1764         if (start.offset() >= start.node()->caretMaxOffset())
1765             node = node->traverseNextNode();
1766         while (1) {
1767             if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
1768                 NodeImpl *runStart = node;
1769                 while (1) {
1770                     NodeImpl *next = node->traverseNextNode();
1771                     // Break if node is the end node, or if the next node does not fit in with
1772                     // the current group.
1773                     if (node == end.node() || 
1774                         runStart->parentNode() != next->parentNode() || 
1775                         (next->isHTMLElement() && next->id() != ID_BR) || 
1776                         (next->renderer() && !next->renderer()->isInline()))
1777                         break;
1778                     node = next;
1779                 }
1780                 // Now apply style to the run we found.
1781                 addInlineStyleIfNeeded(style, runStart, node);
1782             }
1783             if (node == end.node())
1784                 break;
1785             node = node->traverseNextNode();
1786         }
1787     }
1788
1789     if (splitStart || splitEnd) {
1790         cleanUpEmptyStyleSpans(start, end);
1791     }
1792 }
1793
1794 //------------------------------------------------------------------------------------------
1795 // ApplyStyleCommand: style-removal helpers
1796
1797 bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclarationImpl *style, HTMLElementImpl *elem)
1798 {
1799     QValueListConstIterator<CSSProperty> end;
1800     for (QValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
1801         switch ((*it).id()) {
1802             case CSS_PROP_FONT_WEIGHT:
1803                 if (elem->id() == ID_B)
1804                     return true;
1805                 break;
1806             case CSS_PROP_FONT_STYLE:
1807                 if (elem->id() == ID_I)
1808                     return true;
1809         }
1810     }
1811
1812     return false;
1813 }
1814
1815 void ApplyStyleCommand::removeHTMLStyleNode(HTMLElementImpl *elem)
1816 {
1817     // This node can be removed.
1818     // EDIT FIXME: This does not handle the case where the node
1819     // has attributes. But how often do people add attributes to <B> tags? 
1820     // Not so often I think.
1821     ASSERT(elem);
1822     removeNodePreservingChildren(elem);
1823 }
1824
1825 void ApplyStyleCommand::removeHTMLFontStyle(CSSMutableStyleDeclarationImpl *style, HTMLElementImpl *elem)
1826 {
1827     ASSERT(style);
1828     ASSERT(elem);
1829
1830     if (elem->id() != ID_FONT)
1831         return;
1832
1833     int exceptionCode = 0;
1834     QValueListConstIterator<CSSProperty> end;
1835     for (QValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
1836         switch ((*it).id()) {
1837             case CSS_PROP_COLOR:
1838                 elem->removeAttribute(ATTR_COLOR, exceptionCode);
1839                 ASSERT(exceptionCode == 0);
1840                 break;
1841             case CSS_PROP_FONT_FAMILY:
1842                 elem->removeAttribute(ATTR_FACE, exceptionCode);
1843                 ASSERT(exceptionCode == 0);
1844                 break;
1845             case CSS_PROP_FONT_SIZE:
1846                 elem->removeAttribute(ATTR_SIZE, exceptionCode);
1847                 ASSERT(exceptionCode == 0);
1848                 break;
1849         }
1850     }
1851
1852     if (isEmptyFontTag(elem))
1853         removeNodePreservingChildren(elem);
1854 }
1855
1856 void ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclarationImpl *style, HTMLElementImpl *elem)
1857 {
1858     ASSERT(style);
1859     ASSERT(elem);
1860
1861     CSSMutableStyleDeclarationImpl *decl = elem->inlineStyleDecl();
1862     if (!decl)
1863         return;
1864
1865     QValueListConstIterator<CSSProperty> end;
1866     for (QValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
1867         int propertyID = (*it).id();
1868         CSSValueImpl *value = decl->getPropertyCSSValue(propertyID);
1869         if (value) {
1870             value->ref();
1871             removeCSSProperty(decl, propertyID);
1872             value->deref();
1873         }
1874     }
1875
1876     if (isEmptyStyleSpan(elem))
1877         removeNodePreservingChildren(elem);
1878 }
1879
1880 void ApplyStyleCommand::removeBlockStyle(CSSMutableStyleDeclarationImpl *style, const Position &start, const Position &end)
1881 {
1882     ASSERT(start.isNotNull());
1883     ASSERT(end.isNotNull());
1884     ASSERT(start.node()->inDocument());
1885     ASSERT(end.node()->inDocument());
1886     ASSERT(RangeImpl::compareBoundaryPoints(start, end) <= 0);
1887     
1888 }
1889
1890 static bool hasTextDecorationProperty(NodeImpl *node)
1891 {
1892     if (!node->isElementNode())
1893         return false;
1894
1895     ElementImpl *element = static_cast<ElementImpl *>(node);
1896     CSSComputedStyleDeclarationImpl style(element);
1897
1898     CSSValueImpl *value = style.getPropertyCSSValue(CSS_PROP_TEXT_DECORATION, DoNotUpdateLayout);
1899
1900     if (value) {
1901         value->ref();
1902         DOMString valueText(value->cssText());
1903         value->deref();
1904         if (strcasecmp(valueText,"none") != 0)
1905             return true;
1906     }
1907
1908     return false;
1909 }
1910
1911 static NodeImpl* highestAncestorWithTextDecoration(NodeImpl *node)
1912 {
1913     NodeImpl *result = NULL;
1914
1915     for (NodeImpl *n = node; n; n = n->parentNode()) {
1916         if (hasTextDecorationProperty(n))
1917             result = n;
1918     }
1919
1920     return result;
1921 }
1922
1923 CSSMutableStyleDeclarationImpl *ApplyStyleCommand::extractTextDecorationStyle(NodeImpl *node)
1924 {
1925     ASSERT(node);
1926     ASSERT(node->isElementNode());
1927     
1928     // non-html elements not handled yet
1929     if (!node->isHTMLElement())
1930         return 0;
1931
1932     HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
1933     CSSMutableStyleDeclarationImpl *style = element->inlineStyleDecl();
1934     if (!style)
1935         return 0;
1936
1937     style->ref();
1938     int properties[1] = { CSS_PROP_TEXT_DECORATION };
1939     CSSMutableStyleDeclarationImpl *textDecorationStyle = style->copyPropertiesInSet(properties, 1);
1940
1941     CSSValueImpl *property = style->getPropertyCSSValue(CSS_PROP_TEXT_DECORATION);
1942     if (property && strcasecmp(property->cssText(), "none") != 0) {
1943         removeCSSProperty(style, CSS_PROP_TEXT_DECORATION);
1944     }
1945
1946     style->deref();
1947
1948     return textDecorationStyle;
1949 }
1950
1951 CSSMutableStyleDeclarationImpl *ApplyStyleCommand::extractAndNegateTextDecorationStyle(NodeImpl *node)
1952 {
1953     ASSERT(node);
1954     ASSERT(node->isElementNode());
1955     
1956     // non-html elements not handled yet
1957     if (!node->isHTMLElement())
1958         return 0;
1959
1960     HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
1961     CSSComputedStyleDeclarationImpl *computedStyle = new CSSComputedStyleDeclarationImpl(element);
1962     ASSERT(computedStyle);
1963
1964     computedStyle->ref();
1965
1966     int properties[1] = { CSS_PROP_TEXT_DECORATION };
1967     CSSMutableStyleDeclarationImpl *textDecorationStyle = computedStyle->copyPropertiesInSet(properties, 1);
1968     
1969
1970     CSSValueImpl *property = computedStyle->getPropertyCSSValue(CSS_PROP_TEXT_DECORATION);
1971     if (property && strcasecmp(property->cssText(), "none") != 0) {
1972         property->ref();
1973         CSSMutableStyleDeclarationImpl *newStyle = textDecorationStyle->copy();
1974
1975         newStyle->ref();
1976         newStyle->setProperty(CSS_PROP_TEXT_DECORATION, "none");
1977         applyTextDecorationStyle(node, newStyle);
1978         newStyle->deref();
1979
1980         property->deref();
1981     }
1982
1983     computedStyle->deref();
1984
1985     return textDecorationStyle;
1986 }
1987
1988 void ApplyStyleCommand::applyTextDecorationStyle(NodeImpl *node, CSSMutableStyleDeclarationImpl *style)
1989 {
1990     ASSERT(node);
1991
1992     if (!style || !style->cssText().length())
1993         return;
1994
1995     if (node->isTextNode()) {
1996         HTMLElementImpl *styleSpan = static_cast<HTMLElementImpl *>(createStyleSpanElement(document()));
1997         insertNodeBefore(styleSpan, node);
1998         surroundNodeRangeWithElement(node, node, styleSpan);
1999         node = styleSpan;
2000     }
2001
2002     if (!node->isElementNode())
2003         return;
2004
2005     HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
2006         
2007     StyleChange styleChange(style, Position(element, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
2008     if (styleChange.cssStyle().length() > 0) {
2009         DOMString cssText = styleChange.cssStyle();
2010         CSSMutableStyleDeclarationImpl *decl = element->inlineStyleDecl();
2011         if (decl)
2012             cssText += decl->cssText();
2013         setNodeAttribute(element, ATTR_STYLE, cssText);
2014     }
2015 }
2016
2017 void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(NodeImpl *node, const Position &start, const Position &end, bool force)
2018 {
2019     NodeImpl *highestAncestor = highestAncestorWithTextDecoration(node);
2020     
2021     if (highestAncestor) {
2022         NodeImpl *nextCurrent;
2023         NodeImpl *nextChild;
2024         for (NodeImpl *current = highestAncestor; current != node; current = nextCurrent) {
2025             ASSERT(current);
2026             
2027             nextCurrent = NULL;
2028             
2029             CSSMutableStyleDeclarationImpl *decoration = force ? extractAndNegateTextDecorationStyle(current) : extractTextDecorationStyle(current);
2030             if (decoration)
2031                 decoration->ref();
2032
2033             for (NodeImpl *child = current->firstChild(); child; child = nextChild) {
2034                 nextChild = child->nextSibling();
2035
2036                 if (node == child) {
2037                     nextCurrent = child;
2038                 } else if (node->isAncestor(child)) {
2039                     applyTextDecorationStyle(child, decoration);
2040                     nextCurrent = child;
2041                 } else {
2042                     applyTextDecorationStyle(child, decoration);
2043                 }
2044             }
2045
2046             if (decoration)
2047                 decoration->deref();
2048         }
2049     }
2050 }
2051
2052 void ApplyStyleCommand::pushDownTextDecorationStyleAtBoundaries(const Position &start, const Position &end)
2053 {
2054     // We need to work in two passes. First we push down any inline
2055     // styles that set text decoration. Then we look for any remaining
2056     // styles (caused by stylesheets) and explicitly negate text
2057     // decoration while pushing down.
2058
2059     pushDownTextDecorationStyleAroundNode(start.node(), start, end, false);
2060     document()->updateLayout();
2061     pushDownTextDecorationStyleAroundNode(start.node(), start, end, true);
2062
2063     pushDownTextDecorationStyleAroundNode(end.node(), start, end, false);
2064     document()->updateLayout();
2065     pushDownTextDecorationStyleAroundNode(end.node(), start, end, true);
2066 }
2067
2068 void ApplyStyleCommand::removeInlineStyle(CSSMutableStyleDeclarationImpl *style, const Position &start, const Position &end)
2069 {
2070     ASSERT(start.isNotNull());
2071     ASSERT(end.isNotNull());
2072     ASSERT(start.node()->inDocument());
2073     ASSERT(end.node()->inDocument());
2074     ASSERT(RangeImpl::compareBoundaryPoints(start, end) < 0);
2075     
2076     CSSValueImpl *textDecorationSpecialProperty = style->getPropertyCSSValue(CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT);
2077
2078     if (textDecorationSpecialProperty) {
2079         pushDownTextDecorationStyleAtBoundaries(start.downstream(StayInBlock), end.upstream(StayInBlock));
2080         style = style->copy();
2081         style->setProperty(CSS_PROP_TEXT_DECORATION, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT));
2082     }
2083
2084     // The s and e variables store the positions used to set the ending selection after style removal
2085     // takes place. This will help callers to recognize when either the start node or the end node
2086     // are removed from the document during the work of this function.
2087     Position s = start;
2088     Position e = end;
2089
2090     NodeImpl *node = start.node();
2091     while (node) {
2092         NodeImpl *next = node->traverseNextNode();
2093         if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
2094             HTMLElementImpl *elem = static_cast<HTMLElementImpl *>(node);
2095             NodeImpl *prev = elem->traversePreviousNodePostOrder();
2096             NodeImpl *next = elem->traverseNextNode();
2097             if (isHTMLStyleNode(style, elem)) {
2098                 removeHTMLStyleNode(elem);
2099             }
2100             else {
2101                 removeHTMLFontStyle(style, elem);
2102                 removeCSSStyle(style, elem);
2103             }
2104             if (!elem->inDocument()) {
2105                 if (s.node() == elem) {
2106                     // Since elem must have been fully selected, and it is at the start
2107                     // of the selection, it is clear we can set the new s offset to 0.
2108                     ASSERT(s.offset() <= s.node()->caretMinOffset());
2109                     s = Position(next, 0);
2110                 }
2111                 if (e.node() == elem) {
2112                     // Since elem must have been fully selected, and it is at the end
2113                     // of the selection, it is clear we can set the new e offset to
2114                     // the max range offset of prev.
2115                     ASSERT(e.offset() >= maxRangeOffset(e.node()));
2116                     e = Position(prev, maxRangeOffset(prev));
2117                 }
2118             }
2119         }
2120         if (node == end.node())
2121             break;
2122         node = next;
2123     }
2124
2125
2126     if (textDecorationSpecialProperty) {
2127         style->deref();
2128     }
2129     
2130     ASSERT(s.node()->inDocument());
2131     ASSERT(e.node()->inDocument());
2132     setEndingSelection(Selection(s, VP_DEFAULT_AFFINITY, e, VP_DEFAULT_AFFINITY));
2133 }
2134
2135 bool ApplyStyleCommand::nodeFullySelected(NodeImpl *node, const Position &start, const Position &end) const
2136 {
2137     ASSERT(node);
2138     ASSERT(node->isElementNode());
2139
2140     Position pos = Position(node, node->childNodeCount()).upstream();
2141     return RangeImpl::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 &&
2142         RangeImpl::compareBoundaryPoints(pos, end) <= 0;
2143 }
2144
2145 bool ApplyStyleCommand::nodeFullyUnselected(NodeImpl *node, const Position &start, const Position &end) const
2146 {
2147     ASSERT(node);
2148     ASSERT(node->isElementNode());
2149
2150     Position pos = Position(node, node->childNodeCount()).upstream();
2151     bool isFullyBeforeStart = RangeImpl::compareBoundaryPoints(pos, start) < 0;
2152     bool isFullyAfterEnd = RangeImpl::compareBoundaryPoints(node, 0, end.node(), end.offset()) > 0;
2153
2154     return isFullyBeforeStart || isFullyAfterEnd;
2155 }
2156
2157
2158 //------------------------------------------------------------------------------------------
2159 // ApplyStyleCommand: style-application helpers
2160
2161 bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
2162 {
2163     if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
2164         long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
2165         TextImpl *text = static_cast<TextImpl *>(start.node());
2166         splitTextNode(text, start.offset());
2167         setEndingSelection(Selection(Position(start.node(), 0), SEL_DEFAULT_AFFINITY, Position(end.node(), end.offset() - endOffsetAdjustment), SEL_DEFAULT_AFFINITY));
2168         return true;
2169     }
2170     return false;
2171 }
2172
2173 bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end)
2174 {
2175     if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
2176         TextImpl *text = static_cast<TextImpl *>(end.node());
2177         splitTextNode(text, end.offset());
2178         
2179         NodeImpl *prevNode = text->previousSibling();
2180         ASSERT(prevNode);
2181         NodeImpl *startNode = start.node() == end.node() ? prevNode : start.node();
2182         ASSERT(startNode);
2183         setEndingSelection(Selection(Position(startNode, start.offset()), SEL_DEFAULT_AFFINITY, Position(prevNode, prevNode->caretMaxOffset()), SEL_DEFAULT_AFFINITY));
2184         return true;
2185     }
2186     return false;
2187 }
2188
2189 bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end)
2190 {
2191     if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
2192         long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
2193         TextImpl *text = static_cast<TextImpl *>(start.node());
2194         splitTextNodeContainingElement(text, start.offset());
2195
2196         setEndingSelection(Selection(Position(start.node()->parentNode(), start.node()->nodeIndex()), SEL_DEFAULT_AFFINITY, Position(end.node(), end.offset() - endOffsetAdjustment), SEL_DEFAULT_AFFINITY));
2197         return true;
2198     }
2199     return false;
2200 }
2201
2202 bool ApplyStyleCommand::splitTextElementAtEndIfNeeded(const Position &start, const Position &end)
2203 {
2204     if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
2205         TextImpl *text = static_cast<TextImpl *>(end.node());
2206         splitTextNodeContainingElement(text, end.offset());
2207
2208         NodeImpl *prevNode = text->parent()->previousSibling()->lastChild();
2209         ASSERT(prevNode);
2210         NodeImpl *startNode = start.node() == end.node() ? prevNode : start.node();
2211         ASSERT(startNode);
2212         setEndingSelection(Selection(Position(startNode, start.offset()), SEL_DEFAULT_AFFINITY, Position(prevNode->parent(), prevNode->nodeIndex() + 1), SEL_DEFAULT_AFFINITY));
2213         return true;
2214     }
2215     return false;
2216 }
2217
2218 static bool areIdenticalElements(NodeImpl *first, NodeImpl *second)
2219 {
2220     // check that tag name and all attribute names and values are identical
2221
2222     if (!first->isElementNode())
2223         return false;
2224     
2225     if (!second->isElementNode())
2226         return false;
2227
2228     ElementImpl *firstElement = static_cast<ElementImpl *>(first);
2229     ElementImpl *secondElement = static_cast<ElementImpl *>(second);
2230     
2231     if (firstElement->id() != secondElement->id())
2232         return false;
2233
2234     NamedAttrMapImpl *firstMap = firstElement->attributes();
2235     NamedAttrMapImpl *secondMap = secondElement->attributes();
2236
2237     unsigned firstLength = firstMap->length();
2238
2239     if (firstLength != secondMap->length())
2240         return false;
2241
2242     for (unsigned i = 0; i < firstLength; i++) {
2243         DOM::AttributeImpl *attribute = firstMap->attributeItem(i);
2244         DOM::AttributeImpl *secondAttribute = secondMap->getAttributeItem(attribute->id());
2245
2246         if (!secondAttribute || attribute->value() != secondAttribute->value())
2247             return false;
2248     }
2249     
2250     return true;
2251 }
2252
2253 bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end)
2254 {
2255     NodeImpl *startNode = start.node();
2256     long startOffset = start.offset();
2257
2258     if (start.node()->isAtomicNode()) {
2259         if (start.offset() != 0)
2260             return false;
2261
2262         if (start.node()->previousSibling())
2263             return false;
2264
2265         startNode = start.node()->parent();
2266         startOffset = 0;
2267     }
2268
2269     if (!startNode->isElementNode())
2270         return false;
2271
2272     if (startOffset != 0)
2273         return false;
2274
2275     NodeImpl *previousSibling = startNode->previousSibling();
2276
2277     if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
2278         ElementImpl *previousElement = static_cast<ElementImpl *>(previousSibling);
2279         ElementImpl *element = static_cast<ElementImpl *>(startNode);
2280         NodeImpl *startChild = element->firstChild();
2281         ASSERT(startChild);
2282         mergeIdenticalElements(previousElement, element);
2283
2284         long startOffsetAdjustment = startChild->nodeIndex();
2285         long endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0;
2286
2287         setEndingSelection(Selection(Position(startNode, startOffsetAdjustment), SEL_DEFAULT_AFFINITY,
2288                                      Position(end.node(), end.offset() + endOffsetAdjustment), SEL_DEFAULT_AFFINITY)); 
2289
2290         return true;
2291     }
2292
2293     return false;
2294 }
2295
2296 bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end)
2297 {
2298     NodeImpl *endNode = end.node();
2299     int endOffset = end.offset();
2300
2301     if (endNode->isAtomicNode()) {
2302         if (endOffset < endNode->caretMaxOffset())
2303             return false;
2304
2305         unsigned parentLastOffset = end.node()->parent()->childNodes()->length() - 1;
2306         if (end.node()->nextSibling())
2307             return false;
2308
2309         endNode = end.node()->parent();
2310         endOffset = parentLastOffset;
2311     }
2312
2313     if (!endNode->isElementNode() || endNode->id() == ID_BR)
2314         return false;
2315
2316     NodeImpl *nextSibling = endNode->nextSibling();
2317
2318     if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
2319         ElementImpl *nextElement = static_cast<ElementImpl *>(nextSibling);
2320         ElementImpl *element = static_cast<ElementImpl *>(endNode);
2321         NodeImpl *nextChild = nextElement->firstChild();
2322
2323         mergeIdenticalElements(element, nextElement);
2324
2325         NodeImpl *startNode = start.node() == endNode ? nextElement : start.node();
2326         ASSERT(startNode);
2327
2328         int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
2329
2330         setEndingSelection(Selection(Position(startNode, start.offset()), SEL_DEFAULT_AFFINITY, 
2331                                      Position(nextElement, endOffset), SEL_DEFAULT_AFFINITY));
2332         return true;
2333     }
2334
2335     return false;
2336 }
2337
2338 void ApplyStyleCommand::cleanUpEmptyStyleSpans(const Position &start, const Position &end)
2339 {
2340     NodeImpl *node;
2341     for (node = start.node(); node && !node->previousSibling(); node = node->parentNode()) {
2342     }
2343
2344     if (node && isEmptyStyleSpan(node->previousSibling())) {
2345         removeNodePreservingChildren(node->previousSibling());
2346     }
2347
2348     if (start.node() == end.node()) {
2349         if (start.node()->isTextNode()) {
2350             for (NodeImpl *last = start.node(), *cur = last->parentNode(); cur && !last->previousSibling() && !last->nextSibling(); last = cur, cur = cur->parentNode()) {
2351                 if (isEmptyStyleSpan(cur)) {
2352                     removeNodePreservingChildren(cur);
2353                     break;
2354                 }
2355             }
2356
2357         }
2358     } else {
2359         if (start.node()->isTextNode()) {
2360             for (NodeImpl *last = start.node(), *cur = last->parentNode(); cur && !last->previousSibling(); last = cur, cur = cur->parentNode()) {
2361                 if (isEmptyStyleSpan(cur)) {
2362                     removeNodePreservingChildren(cur);
2363                     break;
2364                 }
2365             }
2366         }
2367
2368         if (end.node()->isTextNode()) {
2369             for (NodeImpl *last = end.node(), *cur = last->parentNode(); cur && !last->nextSibling(); last = cur, cur = cur->parentNode()) {
2370                 if (isEmptyStyleSpan(cur)) {
2371                     removeNodePreservingChildren(cur);
2372                     break;
2373                 }
2374             }
2375         }
2376     }
2377     
2378     for (node = end.node(); node && !node->nextSibling(); node = node->parentNode()) {
2379     }
2380     if (node && isEmptyStyleSpan(node->nextSibling())) {
2381         removeNodePreservingChildren(node->nextSibling());
2382     }
2383 }
2384
2385 void ApplyStyleCommand::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
2386 {
2387     ASSERT(startNode);
2388     ASSERT(endNode);
2389     ASSERT(element);
2390     
2391     NodeImpl *node = startNode;
2392     while (1) {
2393         NodeImpl *next = node->traverseNextNode();
2394         if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
2395             removeNode(node);
2396             appendNode(node, element);
2397         }
2398         if (node == endNode)
2399             break;
2400         node = next;
2401     }
2402 }
2403
2404 void ApplyStyleCommand::addBlockStyleIfNeeded(CSSMutableStyleDeclarationImpl *style, NodeImpl *node)
2405 {
2406     // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
2407     // inline content.
2408     if (!node)
2409         return;
2410     
2411     HTMLElementImpl *block = static_cast<HTMLElementImpl *>(node->enclosingBlockFlowElement());
2412     if (!block)
2413         return;
2414         
2415     StyleChange styleChange(style, Position(block, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
2416     if (styleChange.cssStyle().length() > 0) {
2417         moveParagraphContentsToNewBlockIfNecessary(Position(node, 0));
2418         block = static_cast<HTMLElementImpl *>(node->enclosingBlockFlowElement());
2419         DOMString cssText = styleChange.cssStyle();
2420         CSSMutableStyleDeclarationImpl *decl = block->inlineStyleDecl();
2421         if (decl)
2422             cssText += decl->cssText();
2423         setNodeAttribute(block, ATTR_STYLE, cssText);
2424     }
2425 }
2426
2427 void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclarationImpl *style, NodeImpl *startNode, NodeImpl *endNode)
2428 {
2429     StyleChange styleChange(style, Position(startNode, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
2430     int exceptionCode = 0;
2431     
2432     //
2433     // Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
2434     //
2435     if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) {
2436         ElementImpl *fontElement = createFontElement(document());
2437         ASSERT(exceptionCode == 0);
2438         insertNodeBefore(fontElement, startNode);
2439         if (styleChange.applyFontColor())
2440             fontElement->setAttribute(ATTR_COLOR, styleChange.fontColor());
2441         if (styleChange.applyFontFace())
2442             fontElement->setAttribute(ATTR_FACE, styleChange.fontFace());
2443         if (styleChange.applyFontSize())
2444             fontElement->setAttribute(ATTR_SIZE, styleChange.fontSize());
2445         surroundNodeRangeWithElement(startNode, endNode, fontElement);
2446     }
2447
2448     if (styleChange.cssStyle().length() > 0) {
2449         ElementImpl *styleElement = createStyleSpanElement(document());
2450         styleElement->ref();
2451         styleElement->setAttribute(ATTR_STYLE, styleChange.cssStyle());
2452         insertNodeBefore(styleElement, startNode);
2453         styleElement->deref();
2454         surroundNodeRangeWithElement(startNode, endNode, styleElement);
2455     }
2456
2457     if (styleChange.applyBold()) {
2458         ElementImpl *boldElement = document()->createHTMLElement("B", exceptionCode);
2459         ASSERT(exceptionCode == 0);
2460         insertNodeBefore(boldElement, startNode);
2461         surroundNodeRangeWithElement(startNode, endNode, boldElement);
2462     }
2463
2464     if (styleChange.applyItalic()) {
2465         ElementImpl *italicElement = document()->createHTMLElement("I", exceptionCode);
2466         ASSERT(exceptionCode == 0);
2467         insertNodeBefore(italicElement, startNode);
2468         surroundNodeRangeWithElement(startNode, endNode, italicElement);
2469     }
2470 }
2471
2472 float ApplyStyleCommand::computedFontSize(const NodeImpl *node)
2473 {
2474     float size = 0.0f;
2475     
2476     if (!node)
2477         return size;
2478     
2479     Position pos(const_cast<NodeImpl *>(node), 0);
2480     CSSComputedStyleDeclarationImpl *computedStyle = pos.computedStyle();
2481     if (!computedStyle)
2482         return size;
2483     computedStyle->ref();
2484
2485     CSSPrimitiveValueImpl *value = static_cast<CSSPrimitiveValueImpl *>(computedStyle->getPropertyCSSValue(CSS_PROP_FONT_SIZE));
2486     if (value) {
2487         value->ref();
2488         size = value->getFloatValue(CSSPrimitiveValue::CSS_PX);
2489         value->deref();
2490     }
2491
2492     computedStyle->deref();
2493     return size;
2494 }
2495
2496 void ApplyStyleCommand::joinChildTextNodes(NodeImpl *node, const Position &start, const Position &end)
2497 {
2498     if (!node)
2499         return;
2500
2501     Position newStart = start;
2502     Position newEnd = end;
2503     
2504     NodeImpl *child = node->firstChild();
2505     while (child) {
2506         NodeImpl *next = child->nextSibling();
2507         if (child->isTextNode() && next && next->isTextNode()) {
2508             TextImpl *childText = static_cast<TextImpl *>(child);
2509             TextImpl *nextText = static_cast<TextImpl *>(next);
2510             if (next == start.node())
2511                 newStart = Position(childText, childText->length() + start.offset());
2512             if (next == end.node())
2513                 newEnd = Position(childText, childText->length() + end.offset());
2514             DOMString textToMove = nextText->data();
2515             insertTextIntoNode(childText, childText->length(), textToMove);
2516             removeNode(next);
2517             // don't move child node pointer. it may want to merge with more text nodes.
2518         }
2519         else {
2520             child = child->nextSibling();
2521         }
2522     }
2523
2524     setEndingSelection(Selection(newStart, SEL_DEFAULT_AFFINITY, newEnd, SEL_DEFAULT_AFFINITY));
2525 }
2526
2527 //------------------------------------------------------------------------------------------
2528 // DeleteFromTextNodeCommand
2529
2530 DeleteFromTextNodeCommand::DeleteFromTextNodeCommand(DocumentImpl *document, TextImpl *node, long offset, long count)
2531     : EditCommand(document), m_node(node), m_offset(offset), m_count(count)
2532 {
2533     ASSERT(m_node);
2534     ASSERT(m_offset >= 0);
2535     ASSERT(m_offset < (long)m_node->length());
2536     ASSERT(m_count >= 0);
2537     
2538     m_node->ref();
2539 }
2540
2541 DeleteFromTextNodeCommand::~DeleteFromTextNodeCommand()
2542 {
2543     ASSERT(m_node);
2544     m_node->deref();
2545 }
2546
2547 void DeleteFromTextNodeCommand::doApply()
2548 {
2549     ASSERT(m_node);
2550
2551     int exceptionCode = 0;
2552     m_text = m_node->substringData(m_offset, m_count, exceptionCode);
2553     ASSERT(exceptionCode == 0);
2554     
2555     m_node->deleteData(m_offset, m_count, exceptionCode);
2556     ASSERT(exceptionCode == 0);
2557 }
2558
2559 void DeleteFromTextNodeCommand::doUnapply()
2560 {
2561     ASSERT(m_node);
2562     ASSERT(!m_text.isEmpty());
2563
2564     int exceptionCode = 0;
2565     m_node->insertData(m_offset, m_text, exceptionCode);
2566     ASSERT(exceptionCode == 0);
2567 }
2568
2569 //------------------------------------------------------------------------------------------
2570 // DeleteSelectionCommand
2571
2572 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, bool smartDelete, bool mergeBlocksAfterDelete)
2573     : CompositeEditCommand(document), 
2574       m_hasSelectionToDelete(false), 
2575       m_smartDelete(smartDelete), 
2576       m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
2577       m_startBlock(0),
2578       m_endBlock(0),
2579       m_startNode(0),
2580       m_typingStyle(0)
2581 {
2582 }
2583
2584 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
2585     : CompositeEditCommand(document), 
2586       m_hasSelectionToDelete(true), 
2587       m_smartDelete(smartDelete), 
2588       m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
2589       m_selectionToDelete(selection),
2590       m_startBlock(0),
2591       m_endBlock(0),
2592       m_startNode(0),
2593       m_typingStyle(0)
2594 {
2595 }
2596
2597 void DeleteSelectionCommand::initializePositionData()
2598 {
2599     //
2600     // Handle setting some basic positions
2601     //
2602     Position start = m_selectionToDelete.start();
2603     start = positionOutsideContainingSpecialElement(start);
2604     Position end = m_selectionToDelete.end();
2605     end = positionOutsideContainingSpecialElement(end);
2606
2607     m_upstreamStart = positionBeforePossibleContainingSpecialElement(start.upstream(StayInBlock));
2608     m_downstreamStart = positionBeforePossibleContainingSpecialElement(start.downstream(StayInBlock));
2609     m_upstreamEnd = positionAfterPossibleContainingSpecialElement(end.upstream(StayInBlock));
2610     m_downstreamEnd = positionAfterPossibleContainingSpecialElement(end.downstream(StayInBlock));
2611
2612     //
2613     // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection
2614     //
2615     m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.startAffinity());
2616     // NOTE: Workaround for bug <rdar://problem/4103339> is to avoid calculating trailingWhitespacePosition
2617     // if the m_downstreamEnd is at the end of a paragraph.
2618     if (!isEndOfParagraph(VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY)))
2619         m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY);
2620
2621     if (m_smartDelete) {
2622     
2623         // skip smart delete if the selection to delete already starts or ends with whitespace
2624         Position pos = VisiblePosition(m_upstreamStart, m_selectionToDelete.startAffinity()).deepEquivalent();
2625         bool skipSmartDelete = pos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull();
2626         if (!skipSmartDelete)
2627             skipSmartDelete = m_downstreamEnd.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull();
2628
2629         // extend selection upstream if there is whitespace there
2630         bool hasLeadingWhitespaceBeforeAdjustment = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.startAffinity(), true).isNotNull();
2631         if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) {
2632             VisiblePosition visiblePos = VisiblePosition(start, m_selectionToDelete.startAffinity()).previous();
2633             pos = visiblePos.deepEquivalent();
2634             // Expand out one character upstream for smart delete and recalculate
2635             // positions based on this change.
2636             m_upstreamStart = pos.upstream(StayInBlock);
2637             m_downstreamStart = pos.downstream(StayInBlock);
2638             m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(visiblePos.affinity());
2639         }
2640         
2641         // trailing whitespace is only considered for smart delete if there is no leading
2642         // whitespace, as in the case where you double-click the first word of a paragraph.
2643         if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment && m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull()) {
2644             // Expand out one character downstream for smart delete and recalculate
2645             // positions based on this change.
2646             pos = VisiblePosition(end, m_selectionToDelete.endAffinity()).next().deepEquivalent();
2647             m_upstreamEnd = pos.upstream(StayInBlock);
2648             m_downstreamEnd = pos.downstream(StayInBlock);
2649             m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY);
2650         }
2651     }
2652     
2653     m_trailingWhitespaceValid = true;
2654     
2655     //
2656     // Handle setting start and end blocks and the start node.
2657     //
2658     m_startBlock = m_downstreamStart.node()->enclosingBlockFlowElement();
2659     m_startBlock->ref();
2660     m_endBlock = m_upstreamEnd.node()->enclosingBlockFlowElement();
2661     m_endBlock->ref();
2662     m_startNode = m_upstreamStart.node();
2663     m_startNode->ref();
2664
2665     //
2666     // Handle detecting if the line containing the selection end is itself fully selected.
2667     // This is one of the tests that determines if block merging of content needs to be done.
2668     //
2669     VisiblePosition visibleEnd(end, m_selectionToDelete.endAffinity());
2670     if (isFirstVisiblePositionInParagraph(visibleEnd) || isLastVisiblePositionInParagraph(visibleEnd)) {
2671         Position previousLineStart = previousLinePosition(visibleEnd, 0).deepEquivalent();
2672         if (previousLineStart.isNull() || RangeImpl::compareBoundaryPoints(previousLineStart, m_downstreamStart) >= 0)
2673             m_mergeBlocksAfterDelete = false;
2674     }
2675
2676     debugPosition("m_upstreamStart      ", m_upstreamStart);
2677     debugPosition("m_downstreamStart    ", m_downstreamStart);
2678     debugPosition("m_upstreamEnd        ", m_upstreamEnd);
2679     debugPosition("m_downstreamEnd      ", m_downstreamEnd);
2680     debugPosition("m_leadingWhitespace  ", m_leadingWhitespace);
2681     debugPosition("m_trailingWhitespace ", m_trailingWhitespace);
2682     debugNode(    "m_startBlock         ", m_startBlock);
2683     debugNode(    "m_endBlock           ", m_endBlock);    
2684     debugNode(    "m_startNode          ", m_startNode);    
2685 }
2686
2687 void DeleteSelectionCommand::insertPlaceholderForAncestorBlockContent()
2688 {
2689     // This code makes sure a line does not disappear when deleting in this case:
2690     // <p>foo</p>bar<p>baz</p>
2691     // Select "bar" and hit delete. If nothing is done, the line containing bar will disappear.
2692     // It needs to be held open by inserting a placeholder.
2693     // Also see:
2694     // <rdar://problem/3928305> selecting an entire line and typing over causes new inserted text at top of document
2695     //
2696     // The checks below detect the case where the selection contains content in an ancestor block 
2697     // surrounded by child blocks.
2698     //
2699     NodeImpl *upstreamBlock = m_upstreamStart.node()->enclosingBlockFlowElement();
2700     NodeImpl *beforeUpstreamBlock = m_upstreamStart.upstream().node()->enclosingBlockFlowElement();
2701     
2702     if (upstreamBlock != beforeUpstreamBlock && 
2703         beforeUpstreamBlock->isAncestor(upstreamBlock) &&
2704         upstreamBlock != m_upstreamStart.node()) {
2705         NodeImpl *downstreamBlock = m_downstreamEnd.node()->enclosingBlockFlowElement();
2706         NodeImpl *afterDownstreamBlock = m_downstreamEnd.downstream().node()->enclosingBlockFlowElement();
2707         
2708         if ((afterDownstreamBlock != downstreamBlock && afterDownstreamBlock != upstreamBlock) ||
2709             (m_downstreamEnd == m_selectionToDelete.end() && isEndOfParagraph(VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY)))) {
2710             NodeImpl *block = createDefaultParagraphElement(document());
2711             insertNodeBefore(block, m_upstreamStart.node());
2712             addBlockPlaceholderIfNeeded(block);
2713             m_endingPosition = Position(block, 0);
2714         }
2715     }
2716 }
2717
2718 void DeleteSelectionCommand::saveTypingStyleState()
2719 {
2720     // Figure out the typing style in effect before the delete is done.
2721     // FIXME: Improve typing style.
2722     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
2723     CSSComputedStyleDeclarationImpl *computedStyle = m_selectionToDelete.start().computedStyle();
2724     computedStyle->ref();
2725     m_typingStyle = computedStyle->copyInheritableProperties();
2726     m_typingStyle->ref();
2727     computedStyle->deref();
2728 }
2729
2730 bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
2731 {
2732     // Check for special-case where the selection contains only a BR on a line by itself after another BR.
2733     bool upstreamStartIsBR = m_startNode->id() == ID_BR;
2734     bool downstreamStartIsBR = m_downstreamStart.node()->id() == ID_BR;
2735     bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.node() == m_upstreamEnd.node();
2736     if (isBROnLineByItself) {
2737         removeNode(m_downstreamStart.node());
2738         m_endingPosition = m_upstreamStart;
2739         m_mergeBlocksAfterDelete = false;
2740         return true;
2741     }
2742
2743     // Not a special-case delete per se, but we can detect that the merging of content between blocks
2744     // should not be done.
2745     if (upstreamStartIsBR && downstreamStartIsBR)
2746         m_mergeBlocksAfterDelete = false;
2747
2748     return false;
2749 }
2750
2751 void DeleteSelectionCommand::setStartNode(NodeImpl *node)
2752 {
2753     NodeImpl *old = m_startNode;
2754     m_startNode = node;
2755     if (m_startNode)
2756         m_startNode->ref();
2757     if (old)
2758         old->deref();
2759 }
2760
2761 void DeleteSelectionCommand::handleGeneralDelete()
2762 {
2763     int startOffset = m_upstreamStart.offset();
2764     VisiblePosition visibleEnd = VisiblePosition(m_downstreamEnd, m_selectionToDelete.endAffinity());
2765     bool endAtEndOfBlock = isEndOfBlock(visibleEnd);
2766
2767     // Handle some special cases where the selection begins and ends on specific visible units.
2768     // Sometimes a node that is actually selected needs to be retained in order to maintain
2769     // user expectations for the delete operation. Here is an example:
2770     //     1. Open a new Blot or Mail document
2771     //     2. hit Return ten times or so
2772     //     3. Type a letter (do not hit Return after it)
2773     //     4. Type shift-up-arrow to select the line containing the letter and the previous blank line
2774     //     5. Hit Delete
2775     // You expect the insertion point to wind up at the start of the line where your selection began.
2776     // Because of the nature of HTML, the editing code needs to perform a special check to get
2777     // this behavior. So:
2778     // If the entire start block is selected, and the selection does not extend to the end of the 
2779     // end of a block other than the block containing the selection start, then do not delete the 
2780     // start block, otherwise delete the start block.
2781     // A similar case is provided to cover selections starting in BR elements.
2782     if (startOffset == 1 && m_startNode && m_startNode->id() == ID_BR) {
2783         setStartNode(m_startNode->traverseNextNode());
2784         startOffset = 0;
2785     }
2786     if (m_startBlock != m_endBlock && startOffset == 0 && m_startNode && m_startNode->id() == ID_BR && endAtEndOfBlock) {
2787         // Don't delete the BR element
2788         setStartNode(m_startNode->traverseNextNode());
2789     }
2790     else if (m_startBlock != m_endBlock && isStartOfBlock(VisiblePosition(m_upstreamStart, m_selectionToDelete.startAffinity()))) {
2791         if (!m_startBlock->isAncestor(m_endBlock) && !isStartOfBlock(visibleEnd) && endAtEndOfBlock) {
2792             // Delete all the children of the block, but not the block itself.
2793             setStartNode(m_startBlock->firstChild());
2794             startOffset = 0;
2795         }
2796     }
2797     else if (startOffset >= m_startNode->caretMaxOffset() &&
2798              (m_startNode->isAtomicNode() || startOffset == 0)) {
2799         // Move the start node to the next node in the tree since the startOffset is equal to
2800         // or beyond the start node's caretMaxOffset This means there is nothing visible to delete. 
2801         // But don't do this if the node is not atomic - we don't want to move into the first child.
2802
2803         // Also, before moving on, delete any insignificant text that may be present in a text node.
2804         if (m_startNode->isTextNode()) {
2805             // Delete any insignificant text from this node.
2806             TextImpl *text = static_cast<TextImpl *>(m_startNode);
2807             if (text->length() > (unsigned)m_startNode->caretMaxOffset())
2808                 deleteTextFromNode(text, m_startNode->caretMaxOffset(), text->length() - m_startNode->caretMaxOffset());
2809         }
2810         
2811         // shift the start node to the next
2812         setStartNode(m_startNode->traverseNextNode());
2813         startOffset = 0;
2814     }
2815
2816     // Done adjusting the start.  See if we're all done.
2817     if (!m_startNode)
2818         return;
2819
2820     if (m_startNode == m_downstreamEnd.node()) {
2821         // The selection to delete is all in one node.
2822         if (!m_startNode->renderer() || 
2823             (startOffset == 0 && m_downstreamEnd.offset() >= maxDeepOffset(m_startNode))) {
2824             // just delete
2825             removeFullySelectedNode(m_startNode);
2826         } else if (m_downstreamEnd.offset() - startOffset > 0) {
2827             if (m_startNode->isTextNode()) {
2828                 // in a text node that needs to be trimmed
2829                 TextImpl *text = static_cast<TextImpl *>(m_startNode);
2830                 deleteTextFromNode(text, startOffset, m_downstreamEnd.offset() - startOffset);
2831                 m_trailingWhitespaceValid = false;
2832             } else {
2833                 removeChildrenInRange(m_startNode, startOffset, m_downstreamEnd.offset());
2834                 m_endingPosition = m_upstreamStart;
2835             }
2836         }
2837     }
2838     else {
2839         // The selection to delete spans more than one node.
2840         NodeImpl *node = m_startNode;
2841         
2842         if (startOffset > 0) {
2843             if (m_startNode->isTextNode()) {
2844                 // in a text node that needs to be trimmed
2845                 TextImpl *text = static_cast<TextImpl *>(node);
2846                 deleteTextFromNode(text, startOffset, text->length() - startOffset);
2847                 node = node->traverseNextNode();
2848             } else {
2849                 node = m_startNode->childNode(startOffset);
2850             }
2851         }
2852         
2853         // handle deleting all nodes that are completely selected
2854         while (node && node != m_downstreamEnd.node()) {
2855             if (RangeImpl::compareBoundaryPoints(Position(node, 0), m_downstreamEnd) >= 0) {
2856                 // traverseNextSibling just blew past the end position, so stop deleting
2857                 node = 0;
2858             } else if (!m_downstreamEnd.node()->isAncestor(node)) {
2859                 NodeImpl *nextNode = node->traverseNextSibling();
2860                 // if we just removed a node from the end container, update end position so the
2861                 // check above will work
2862                 if (node->parentNode() == m_downstreamEnd.node()) {
2863                     ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.offset());
2864                     m_downstreamEnd = Position(m_downstreamEnd.node(), m_downstreamEnd.offset() - 1);
2865                 }
2866                 removeFullySelectedNode(node);
2867                 node = nextNode;
2868             } else {
2869                 NodeImpl *n = node->lastChild();
2870                 while (n && n->lastChild())
2871                     n = n->lastChild();
2872                 if (n == m_downstreamEnd.node() && m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMaxOffset()) {
2873                     removeFullySelectedNode(node);
2874                     m_trailingWhitespaceValid = false;
2875                     node = 0;
2876                 } 
2877                 else {
2878                     node = node->traverseNextNode();
2879                 }
2880             }
2881         }
2882
2883         
2884         if (m_downstreamEnd.node() != m_startNode && !m_upstreamStart.node()->isAncestor(m_downstreamEnd.node()) && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMinOffset()) {
2885             if (m_downstreamEnd.offset() >= maxDeepOffset(m_downstreamEnd.node())) {
2886                 // need to delete whole node
2887                 // we can get here if this is the last node in the block
2888                 // remove an ancestor of m_downstreamEnd.node(), and thus m_downstreamEnd.node() itself
2889                 if (m_upstreamStart.node() == m_downstreamEnd.node() ||
2890                     m_upstreamStart.node()->isAncestor(m_downstreamEnd.node())) {
2891                     m_upstreamStart = Position(m_downstreamEnd.node()->parentNode(), m_downstreamEnd.node()->nodeIndex());
2892                 }
2893                 
2894                 removeFullySelectedNode(m_downstreamEnd.node());
2895                 m_trailingWhitespaceValid = false;
2896             } else {
2897                 if (m_downstreamEnd.node()->isTextNode()) {
2898                     // in a text node that needs to be trimmed
2899                     TextImpl *text = static_cast<TextImpl *>(m_downstreamEnd.node());
2900                     if (m_downstreamEnd.offset() > 0) {
2901                         deleteTextFromNode(text, 0, m_downstreamEnd.offset());
2902                         m_downstreamEnd = Position(text, 0);
2903                         m_trailingWhitespaceValid = false;
2904                     }
2905                 } else {
2906                     int offset = 0;
2907                     if (m_upstreamStart.node()->isAncestor(m_downstreamEnd.node())) {
2908                         NodeImpl *n = m_upstreamStart.node();
2909                         while (n && n->parentNode() != m_downstreamEnd.node())
2910                             n = n->parentNode();
2911                         if (n)
2912                             offset = n->nodeIndex() + 1;
2913                     }
2914                     removeChildrenInRange(m_downstreamEnd.node(), offset, m_downstreamEnd.offset());
2915                     m_downstreamEnd = Position(m_downstreamEnd.node(), offset);
2916                 }
2917             }
2918         }
2919     }
2920 }
2921
2922 void DeleteSelectionCommand::fixupWhitespace()
2923 {
2924     document()->updateLayout();
2925     if (m_leadingWhitespace.isNotNull() && (m_trailingWhitespace.isNotNull() || !m_leadingWhitespace.isRenderedCharacter())) {
2926         LOG(Editing, "replace leading");
2927         TextImpl *textNode = static_cast<TextImpl *>(m_leadingWhitespace.node());
2928         replaceTextInNode(textNode, m_leadingWhitespace.offset(), 1, nonBreakingSpaceString());
2929     }
2930     else if (m_trailingWhitespace.isNotNull()) {
2931         if (m_trailingWhitespaceValid) {
2932             if (!m_trailingWhitespace.isRenderedCharacter()) {
2933                 LOG(Editing, "replace trailing [valid]");
2934                 TextImpl *textNode = static_cast<TextImpl *>(m_trailingWhitespace.node());
2935                 replaceTextInNode(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString());
2936             }
2937         }
2938         else {
2939             Position pos = m_endingPosition.downstream(StayInBlock);
2940             pos = Position(pos.node(), pos.offset() - 1);
2941             if (nextCharacterIsCollapsibleWhitespace(pos) && !pos.isRenderedCharacter()) {
2942                 LOG(Editing, "replace trailing [invalid]");
2943                 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
2944                 replaceTextInNode(textNode, pos.offset(), 1, nonBreakingSpaceString());
2945                 // need to adjust ending position since the trailing position is not valid.
2946                 m_endingPosition = pos;
2947             }
2948         }
2949     }
2950 }
2951
2952 // This function moves nodes in the block containing startNode to dstBlock, starting
2953 // from startNode and proceeding to the end of the paragraph. Nodes in the block containing
2954 // startNode that appear in document order before startNode are not moved.
2955 // This function is an important helper for deleting selections that cross paragraph
2956 // boundaries.
2957 void DeleteSelectionCommand::moveNodesAfterNode()
2958 {
2959     if (!m_mergeBlocksAfterDelete)
2960         return;
2961
2962     if (m_endBlock == m_startBlock)
2963         return;
2964
2965     NodeImpl *startNode = m_downstreamEnd.node();
2966     NodeImpl *dstNode = m_upstreamStart.node();
2967
2968     if (!startNode->inDocument() || !dstNode->inDocument())
2969         return;
2970
2971     NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
2972     if (isTableStructureNode(startBlock) || isListStructureNode(startBlock))
2973         // Do not move content between parts of a table or list.
2974         return;
2975
2976     // Now that we are about to add content, check to see if a placeholder element
2977     // can be removed.
2978     removeBlockPlaceholder(startBlock);
2979
2980     // Move the subtree containing node
2981     NodeImpl *node = startNode->enclosingInlineElement();
2982
2983     // Insert after the subtree containing destNode
2984     NodeImpl *refNode = dstNode->enclosingInlineElement();
2985
2986     // Nothing to do if start is already at the beginning of dstBlock
2987     NodeImpl *dstBlock = refNode->enclosingBlockFlowElement();
2988     if (startBlock == dstBlock->firstChild())
2989         return;
2990
2991     // Do the move.
2992     NodeImpl *rootNode = refNode->rootEditableElement();
2993     while (node && node->isAncestor(startBlock)) {
2994         NodeImpl *moveNode = node;
2995         node = node->nextSibling();
2996         removeNode(moveNode);
2997         if (moveNode->id() == ID_BR && !moveNode->renderer()) {
2998             // Just remove this node, and don't put it back.
2999             // If the BR was not rendered (since it was at the end of a block, for instance), 
3000             // putting it back in the document might make it appear, and that is not desirable.
3001             break;
3002         }
3003         if (refNode == rootNode)
3004             insertNodeAt(moveNode, refNode, 0);
3005         else
3006             insertNodeAfter(moveNode, refNode);
3007         refNode = moveNode;
3008         if (moveNode->id() == ID_BR)
3009             break;
3010     }
3011
3012     // If the startBlock no longer has any kids, we may need to deal with adding a BR
3013     // to make the layout come out right. Consider this document:
3014     //
3015     // One
3016     // <div>Two</div>
3017     // Three
3018     // 
3019     // Placing the insertion before before the 'T' of 'Two' and hitting delete will
3020     // move the contents of the div to the block containing 'One' and delete the div.
3021     // This will have the side effect of moving 'Three' on to the same line as 'One'
3022     // and 'Two'. This is undesirable. We fix this up by adding a BR before the 'Three'.
3023     // This may not be ideal, but it is better than nothing.
3024     document()->updateLayout();
3025     if (!startBlock->renderer() || !startBlock->renderer()->firstChild()) {
3026         removeNode(startBlock);
3027         document()->updateLayout();
3028         if (refNode->renderer() && refNode->renderer()->inlineBox() && refNode->renderer()->inlineBox()->nextOnLineExists()) {
3029             insertNodeAfter(createBreakElement(document()), refNode);
3030         }
3031     }
3032 }
3033
3034 void DeleteSelectionCommand::calculateEndingPosition()
3035 {
3036     if (m_endingPosition.isNotNull() && m_endingPosition.node()->inDocument())
3037         return;
3038
3039     m_endingPosition = m_upstreamStart;
3040     if (m_endingPosition.node()->inDocument())
3041         return;
3042     
3043     m_endingPosition = m_downstreamEnd;
3044     if (m_endingPosition.node()->inDocument())
3045         return;
3046
3047     m_endingPosition = Position(m_startBlock, 0);
3048     if (m_endingPosition.node()->inDocument())
3049         return;
3050
3051     m_endingPosition = Position(m_endBlock, 0);
3052     if (m_endingPosition.node()->inDocument())
3053         return;
3054
3055     m_endingPosition = Position(document()->documentElement(), 0);
3056 }
3057
3058 void DeleteSelectionCommand::calculateTypingStyleAfterDelete(NodeImpl *insertedPlaceholder)
3059 {
3060     // Compute the difference between the style before the delete and the style now
3061     // after the delete has been done. Set this style on the part, so other editing
3062     // commands being composed with this one will work, and also cache it on the command,
3063     // so the KHTMLPart::appliedEditing can set it after the whole composite command 
3064     // has completed.
3065     // FIXME: Improve typing style.
3066     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
3067     CSSComputedStyleDeclarationImpl endingStyle(m_endingPosition.node());
3068     endingStyle.diff(m_typingStyle);
3069     if (!m_typingStyle->length()) {
3070         m_typingStyle->deref();
3071         m_typingStyle = 0;
3072     }
3073     if (insertedPlaceholder && m_typingStyle) {
3074         // Apply style to the placeholder. This makes sure that the single line in the
3075         // paragraph has the right height, and that the paragraph takes on the style
3076         // of the preceding line and retains it even if you click away, click back, and
3077         // then start typing. In this case, the typing style is applied right now, and
3078         // is not retained until the next typing action.
3079
3080         // FIXME: is this even right? I don't think post-deletion typing style is supposed 
3081         // to be saved across clicking away and clicking back, it certainly isn't in TextEdit
3082
3083         Position pastPlaceholder(insertedPlaceholder, 1);
3084
3085         setEndingSelection(Selection(m_endingPosition, m_selectionToDelete.endAffinity(), pastPlaceholder, DOWNSTREAM));
3086
3087         applyStyle(m_typingStyle, EditActionUnspecified);
3088
3089         m_typingStyle->deref();
3090         m_typingStyle = 0;
3091     }
3092     // Set m_typingStyle as the typing style.
3093     // It's perfectly OK for m_typingStyle to be null.
3094     document()->part()->setTypingStyle(m_typingStyle);
3095     setTypingStyle(m_typingStyle);
3096 }
3097
3098 void DeleteSelectionCommand::clearTransientState()
3099 {
3100     m_selectionToDelete.clear();
3101     m_upstreamStart.clear();
3102     m_downstreamStart.clear();
3103     m_upstreamEnd.clear();
3104     m_downstreamEnd.clear();
3105     m_endingPosition.clear();
3106     m_leadingWhitespace.clear();
3107     m_trailingWhitespace.clear();
3108
3109     if (m_startBlock) {
3110         m_startBlock->deref();
3111         m_startBlock = 0;
3112     }
3113     if (m_endBlock) {
3114         m_endBlock->deref();
3115         m_endBlock = 0;
3116     }
3117     if (m_startNode) {
3118         m_startNode->deref();
3119         m_startNode = 0;
3120     }
3121     if (m_typingStyle) {
3122         m_typingStyle->deref();
3123         m_typingStyle = 0;
3124     }
3125 }
3126
3127 void DeleteSelectionCommand::doApply()
3128 {
3129     // If selection has not been set to a custom selection when the command was created,
3130     // use the current ending selection.
3131     if (!m_hasSelectionToDelete)
3132         m_selectionToDelete = endingSelection();
3133         
3134     if (!m_selectionToDelete.isRange())
3135         return;
3136
3137     // save this to later make the selection with
3138     EAffinity affinity = m_selectionToDelete.startAffinity();
3139     
3140     // set up our state
3141     initializePositionData();
3142
3143     if (!m_startBlock || !m_endBlock) {
3144         // Can't figure out what blocks we're in. This can happen if
3145         // the document structure is not what we are expecting, like if
3146         // the document has no body element, or if the editable block
3147         // has been changed to display: inline. Some day it might
3148         // be nice to be able to deal with this, but for now, bail.
3149         clearTransientState();
3150         return;
3151     }
3152     
3153     // if all we are deleting is complete paragraph(s), we need to make
3154     // sure a blank paragraph remains when we are done
3155     bool forceBlankParagraph = isStartOfParagraph(VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY)) &&
3156                                isEndOfParagraph(VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY));
3157
3158     // Delete any text that may hinder our ability to fixup whitespace after the detele
3159     deleteInsignificantTextDownstream(m_trailingWhitespace);    
3160
3161     saveTypingStyleState();
3162     insertPlaceholderForAncestorBlockContent();
3163     
3164     if (!handleSpecialCaseBRDelete())
3165         handleGeneralDelete();
3166     
3167     // Do block merge if start and end of selection are in different blocks.
3168     moveNodesAfterNode();
3169     
3170     calculateEndingPosition();
3171     fixupWhitespace();
3172
3173     // if the m_endingPosition is already a blank paragraph, there is
3174     // no need to force a new one
3175     if (forceBlankParagraph &&
3176         isStartOfParagraph(VisiblePosition(m_endingPosition, VP_DEFAULT_AFFINITY)) &&
3177         isEndOfParagraph(VisiblePosition(m_endingPosition, VP_DEFAULT_AFFINITY))) {
3178         forceBlankParagraph = false;
3179     }
3180     
3181     NodeImpl *addedPlaceholder = forceBlankParagraph ? insertBlockPlaceholder(m_endingPosition) :
3182         addBlockPlaceholderIfNeeded(m_endingPosition.node());
3183
3184     calculateTypingStyleAfterDelete(addedPlaceholder);
3185     debugPosition("endingPosition   ", m_endingPosition);
3186     setEndingSelection(Selection(m_endingPosition, affinity));
3187     clearTransientState();
3188     rebalanceWhitespace();
3189 }
3190
3191 EditAction DeleteSelectionCommand::editingAction() const
3192 {
3193     // Note that DeleteSelectionCommand is also used when the user presses the Delete key,
3194     // but in that case there's a TypingCommand that supplies the editingAction(), so
3195     // the Undo menu correctly shows "Undo Typing"
3196     return EditActionCut;
3197 }
3198
3199 bool DeleteSelectionCommand::preservesTypingStyle() const
3200 {
3201     return true;
3202 }
3203
3204 //------------------------------------------------------------------------------------------
3205 // InsertIntoTextNode
3206
3207 InsertIntoTextNode::InsertIntoTextNode(DocumentImpl *document, TextImpl *node, long offset, const DOMString &text)
3208     : EditCommand(document), m_node(node), m_offset(offset)
3209 {
3210     ASSERT(m_node);
3211     ASSERT(m_offset >= 0);
3212     ASSERT(!text.isEmpty());
3213     
3214     m_node->ref();
3215     m_text = text.copy(); // make a copy to ensure that the string never changes
3216 }
3217
3218 InsertIntoTextNode::~InsertIntoTextNode()
3219 {
3220     if (m_node)
3221         m_node->deref();
3222 }
3223
3224 void InsertIntoTextNode::doApply()
3225 {
3226     ASSERT(m_node);
3227     ASSERT(m_offset >= 0);
3228     ASSERT(!m_text.isEmpty());
3229
3230     int exceptionCode = 0;
3231     m_node->insertData(m_offset, m_text, exceptionCode);
3232     ASSERT(exceptionCode == 0);
3233 }
3234
3235 void InsertIntoTextNode::doUnapply()
3236 {
3237     ASSERT(m_node);
3238     ASSERT(m_offset >= 0);
3239     ASSERT(!m_text.isEmpty());
3240
3241     int exceptionCode = 0;
3242     m_node->deleteData(m_offset, m_text.length(), exceptionCode);
3243     ASSERT(exceptionCode == 0);
3244 }
3245
3246 //------------------------------------------------------------------------------------------
3247 // InsertLineBreakCommand
3248
3249 InsertLineBreakCommand::InsertLineBreakCommand(DocumentImpl *document) 
3250     : CompositeEditCommand(document)
3251 {
3252 }
3253
3254 bool InsertLineBreakCommand::preservesTypingStyle() const
3255 {
3256     return true;
3257 }
3258
3259 void InsertLineBreakCommand::insertNodeAfterPosition(NodeImpl *node, const Position &pos)
3260 {
3261     // Insert the BR after the caret position. In the case the
3262     // position is a block, do an append. We don't want to insert
3263     // the BR *after* the block.
3264     Position upstream(pos.upstream(StayInBlock));
3265     NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
3266     if (cb == pos.node())
3267         appendNode(node, cb);
3268     else
3269         insertNodeAfter(node, pos.node());
3270 }
3271
3272 void InsertLineBreakCommand::insertNodeBeforePosition(NodeImpl *node, const Position &pos)
3273 {
3274     // Insert the BR after the caret position. In the case the
3275     // position is a block, do an append. We don't want to insert
3276     // the BR *before* the block.
3277     Position upstream(pos.upstream(StayInBlock));
3278     NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
3279     if (cb == pos.node())
3280         appendNode(node, cb);
3281     else
3282         insertNodeBefore(node, pos.node());
3283 }
3284
3285 void InsertLineBreakCommand::doApply()
3286 {
3287     deleteSelection();
3288     Selection selection = endingSelection();
3289
3290     ElementImpl *breakNode = createBreakElement(document());
3291     NodeImpl *nodeToInsert = breakNode;
3292     
3293     Position pos(selection.start().upstream(StayInBlock));
3294
3295     pos = positionOutsideContainingSpecialElement(pos);
3296
3297     bool atStart = pos.offset() <= pos.node()->caretMinOffset();
3298     bool atEnd = pos.offset() >= pos.node()->caretMaxOffset();
3299     bool atEndOfBlock = isLastVisiblePositionInBlock(VisiblePosition(pos, selection.startAffinity()));
3300     
3301     if (atEndOfBlock) {
3302         LOG(Editing, "input newline case 1");
3303         // Check for a trailing BR. If there isn't one, we'll need to insert an "extra" one.
3304         // This makes the "real" BR we want to insert appear in the rendering without any 
3305         // significant side effects (and no real worries either since you can't arrow past 
3306         // this extra one.
3307         if (pos.node()->id() == ID_BR && pos.offset() == 0) {
3308             // Already placed in a trailing BR. Insert "real" BR before it and leave the selection alone.
3309             insertNodeBefore(nodeToInsert, pos.node());
3310         }
3311         else {
3312             NodeImpl *next = pos.node()->traverseNextNode();
3313             bool hasTrailingBR = next && next->id() == ID_BR && pos.node()->enclosingBlockFlowElement() == next->enclosingBlockFlowElement();
3314             insertNodeAfterPosition(nodeToInsert, pos);
3315             if (hasTrailingBR) {
3316                 setEndingSelection(Selection(Position(next, 0), DOWNSTREAM));
3317             }
3318             else if (!document()->inStrictMode()) {
3319                 // Insert an "extra" BR at the end of the block. 
3320                 ElementImpl *extraBreakNode = createBreakElement(document());
3321                 insertNodeAfter(extraBreakNode, nodeToInsert);
3322                 setEndingSelection(Position(extraBreakNode, 0), DOWNSTREAM);
3323             }
3324         }
3325     }
3326     else if (atStart) {
3327         LOG(Editing, "input newline case 2");
3328         // Insert node before downstream position, and place caret there as well. 
3329         Position endingPosition = pos.downstream(StayInBlock);
3330         insertNodeBeforePosition(nodeToInsert, endingPosition);
3331         setEndingSelection(endingPosition, DOWNSTREAM);
3332     }
3333     else if (atEnd) {
3334         LOG(Editing, "input newline case 3");
3335         // Insert BR after this node. Place caret in the position that is downstream
3336         // of the current position, reckoned before inserting the BR in between.
3337         Position endingPosition = pos.downstream(StayInBlock);
3338         insertNodeAfterPosition(nodeToInsert, pos);
3339         setEndingSelection(endingPosition, DOWNSTREAM);
3340     }
3341     else {
3342         // Split a text node
3343         LOG(Editing, "input newline case 4");
3344         ASSERT(pos.node()->isTextNode());
3345         
3346         // Do the split
3347         int exceptionCode = 0;
3348         TextImpl *textNode = static_cast<TextImpl *>(pos.node());
3349         TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
3350         deleteTextFromNode(textNode, 0, pos.offset());
3351         insertNodeBefore(textBeforeNode, textNode);
3352         insertNodeBefore(nodeToInsert, textNode);
3353         Position endingPosition = Position(textNode, 0);
3354         
3355         // Handle whitespace that occurs after the split
3356         document()->updateLayout();
3357         if (!endingPosition.isRenderedCharacter()) {
3358             // Clear out all whitespace and insert one non-breaking space
3359             deleteInsignificantTextDownstream(endingPosition);
3360             insertTextIntoNode(textNode, 0, nonBreakingSpaceString());
3361         }
3362         
3363         setEndingSelection(endingPosition, DOWNSTREAM);
3364     }
3365
3366     // Handle the case where there is a typing style.
3367     // FIXME: Improve typing style.
3368     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
3369     
3370     CSSMutableStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
3371     
3372     if (typingStyle && typingStyle->length() > 0) {
3373         Selection selectionBeforeStyle = endingSelection();
3374
3375         DOM::RangeImpl *rangeAroundNode = document()->createRange();
3376         int exception;
3377         rangeAroundNode->selectNode(nodeToInsert, exception);
3378
3379         // affinity is not really important since this is a temp selection
3380         // just for calling applyStyle
3381         setEndingSelection(Selection(rangeAroundNode, khtml::SEL_DEFAULT_AFFINITY, khtml::SEL_DEFAULT_AFFINITY));
3382         applyStyle(typingStyle);
3383
3384         setEndingSelection(selectionBeforeStyle);
3385     }
3386
3387     rebalanceWhitespace();
3388 }
3389
3390 //------------------------------------------------------------------------------------------
3391 // InsertNodeBeforeCommand
3392
3393 InsertNodeBeforeCommand::InsertNodeBeforeCommand(DocumentImpl *document, NodeImpl *insertChild, NodeImpl *refChild)
3394     : EditCommand(document), m_insertChild(insertChild), m_refChild(refChild)
3395 {
3396     ASSERT(m_insertChild);
3397     m_insertChild->ref();
3398
3399     ASSERT(m_refChild);
3400     m_refChild->ref();
3401 }
3402
3403 InsertNodeBeforeCommand::~InsertNodeBeforeCommand()
3404 {
3405     ASSERT(m_insertChild);
3406     m_insertChild->deref();
3407
3408     ASSERT(m_refChild);
3409     m_refChild->deref();
3410 }
3411
3412 void InsertNodeBeforeCommand::doApply()
3413 {
3414     ASSERT(m_insertChild);
3415     ASSERT(m_refChild);
3416     ASSERT(m_refChild->parentNode());
3417
3418     int exceptionCode = 0;
3419     m_refChild->parentNode()->insertBefore(m_insertChild, m_refChild, exceptionCode);
3420     ASSERT(exceptionCode == 0);
3421 }
3422
3423 void InsertNodeBeforeCommand::doUnapply()
3424 {
3425     ASSERT(m_insertChild);
3426     ASSERT(m_refChild);
3427     ASSERT(m_refChild->parentNode());
3428
3429     int exceptionCode = 0;
3430     m_refChild->parentNode()->removeChild(m_insertChild, exceptionCode);
3431     ASSERT(exceptionCode == 0);
3432 }
3433
3434 //------------------------------------------------------------------------------------------
3435 // InsertParagraphSeparatorCommand
3436
3437 InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(DocumentImpl *document) 
3438     : CompositeEditCommand(document), m_style(0)
3439 {
3440 }
3441
3442 InsertParagraphSeparatorCommand::~InsertParagraphSeparatorCommand() 
3443 {
3444     derefNodesInList(clonedNodes);
3445     if (m_style)
3446         m_style->deref();
3447 }
3448
3449 bool InsertParagraphSeparatorCommand::preservesTypingStyle() const
3450 {
3451     return true;
3452 }
3453
3454 ElementImpl *InsertParagraphSeparatorCommand::createParagraphElement()
3455 {
3456     ElementImpl *element = createDefaultParagraphElement(document());
3457     element->ref();
3458     clonedNodes.append(element);
3459     return element;
3460 }
3461
3462 void InsertParagraphSeparatorCommand::calculateStyleBeforeInsertion(const Position &pos)
3463 {
3464     // It is only important to set a style to apply later if we're at the boundaries of
3465     // a paragraph. Otherwise, content that is moved as part of the work of the command
3466     // will lend their styles to the new paragraph without any extra work needed.
3467     VisiblePosition visiblePos(pos, UPSTREAM);
3468     if (!isFirstVisiblePositionInParagraph(visiblePos) && !isLastVisiblePositionInParagraph(visiblePos))
3469         return;
3470     
3471     if (m_style)
3472         m_style->deref();
3473     m_style = styleAtPosition(pos);
3474     m_style->ref();
3475 }
3476
3477 void InsertParagraphSeparatorCommand::applyStyleAfterInsertion()
3478 {
3479     // FIXME: Improve typing style.
3480     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
3481     if (!m_style)
3482         return;
3483
3484     CSSComputedStyleDeclarationImpl endingStyle(endingSelection().start().node());
3485   &nb