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