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