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