1ea906cec3bb2d8c6bd30e26b13149ee569e7a22
[WebKit-https.git] / WebCore / editing / ApplyStyleCommand.cpp
1 /*
2  * Copyright (C) 2005, 2006 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 "config.h"
27 #include "ApplyStyleCommand.h"
28
29 #include "CSSComputedStyleDeclaration.h"
30 #include "CSSProperty.h"
31 #include "CSSPropertyNames.h"
32 #include "Document.h"
33 #include "HTMLElement.h"
34 #include "HTMLInterchange.h"
35 #include "HTMLNames.h"
36 #include "NodeList.h"
37 #include "Range.h"
38 #include "RenderObject.h"
39 #include "Text.h"
40 #include "cssparser.h"
41 #include "htmlediting.h"
42
43 namespace WebCore {
44
45 using namespace HTMLNames;
46
47 class StyleChange {
48 public:
49     enum ELegacyHTMLStyles { DoNotUseLegacyHTMLStyles, UseLegacyHTMLStyles };
50
51     explicit StyleChange(CSSStyleDeclaration *, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles);
52     StyleChange(CSSStyleDeclaration *, const Position &, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles);
53
54     static ELegacyHTMLStyles styleModeForParseMode(bool);
55
56     String cssStyle() const { return m_cssStyle; }
57     bool applyBold() const { return m_applyBold; }
58     bool applyItalic() const { return m_applyItalic; }
59     bool applyFontColor() const { return m_applyFontColor.length() > 0; }
60     bool applyFontFace() const { return m_applyFontFace.length() > 0; }
61     bool applyFontSize() const { return m_applyFontSize.length() > 0; }
62
63     String fontColor() { return m_applyFontColor; }
64     String fontFace() { return m_applyFontFace; }
65     String fontSize() { return m_applyFontSize; }
66
67     bool usesLegacyStyles() const { return m_usesLegacyStyles; }
68
69 private:
70     void init(PassRefPtr<CSSStyleDeclaration>, const Position &);
71     bool checkForLegacyHTMLStyleChange(const CSSProperty *);
72     static bool currentlyHasStyle(const Position &, const CSSProperty *);
73     
74     String m_cssStyle;
75     bool m_applyBold;
76     bool m_applyItalic;
77     String m_applyFontColor;
78     String m_applyFontFace;
79     String m_applyFontSize;
80     bool m_usesLegacyStyles;
81 };
82
83
84
85 StyleChange::StyleChange(CSSStyleDeclaration *style, ELegacyHTMLStyles usesLegacyStyles)
86     : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles)
87 {
88     init(style, Position());
89 }
90
91 StyleChange::StyleChange(CSSStyleDeclaration *style, const Position &position, ELegacyHTMLStyles usesLegacyStyles)
92     : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles)
93 {
94     init(style, position);
95 }
96
97 void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position &position)
98 {
99     RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
100     
101     String styleText("");
102
103     DeprecatedValueListConstIterator<CSSProperty> end;
104     for (DeprecatedValueListConstIterator<CSSProperty> it = mutableStyle->valuesIterator(); it != end; ++it) {
105         const CSSProperty *property = &*it;
106
107         // If position is empty or the position passed in already has the 
108         // style, just move on.
109         if (position.isNotNull() && currentlyHasStyle(position, property))
110             continue;
111         
112         // If needed, figure out if this change is a legacy HTML style change.
113         if (m_usesLegacyStyles && checkForLegacyHTMLStyleChange(property))
114             continue;
115
116         // Add this property
117
118         if (property->id() == CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT) {
119             // we have to special-case text decorations
120             CSSProperty alteredProperty = CSSProperty(CSS_PROP_TEXT_DECORATION, property->value(), property->isImportant());
121             styleText += alteredProperty.cssText();
122         } else
123             styleText += property->cssText();
124     }
125
126     // Save the result for later
127     m_cssStyle = styleText.deprecatedString().stripWhiteSpace();
128 }
129
130 StyleChange::ELegacyHTMLStyles StyleChange::styleModeForParseMode(bool isQuirksMode)
131 {
132     return isQuirksMode ? UseLegacyHTMLStyles : DoNotUseLegacyHTMLStyles;
133 }
134
135 bool StyleChange::checkForLegacyHTMLStyleChange(const CSSProperty *property)
136 {
137     if (!property || !property->value()) {
138         return false;
139     }
140     
141     String valueText(property->value()->cssText());
142     switch (property->id()) {
143         case CSS_PROP_FONT_WEIGHT:
144             if (equalIgnoringCase(valueText, "bold")) {
145                 m_applyBold = true;
146                 return true;
147             }
148             break;
149         case CSS_PROP_FONT_STYLE:
150             if (equalIgnoringCase(valueText, "italic") || equalIgnoringCase(valueText, "oblique")) {
151                 m_applyItalic = true;
152                 return true;
153             }
154             break;
155         case CSS_PROP_COLOR: {
156             Color color(CSSParser::parseColor(valueText));
157             m_applyFontColor = color.name();
158             return true;
159         }
160         case CSS_PROP_FONT_FAMILY:
161             m_applyFontFace = valueText;
162             return true;
163         case CSS_PROP_FONT_SIZE:
164             if (property->value()->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
165                 CSSPrimitiveValue *value = static_cast<CSSPrimitiveValue *>(property->value());
166                 float number = value->getFloatValue(CSSPrimitiveValue::CSS_PX);
167                 if (number <= 9)
168                     m_applyFontSize = "1";
169                 else if (number <= 10)
170                     m_applyFontSize = "2";
171                 else if (number <= 13)
172                     m_applyFontSize = "3";
173                 else if (number <= 16)
174                     m_applyFontSize = "4";
175                 else if (number <= 18)
176                     m_applyFontSize = "5";
177                 else if (number <= 24)
178                     m_applyFontSize = "6";
179                 else
180                     m_applyFontSize = "7";
181                 // Huge quirk in Microsft Entourage is that they understand CSS font-size, but also write 
182                 // out legacy 1-7 values in font tags (I guess for mailers that are not CSS-savvy at all, 
183                 // like Eudora). Yes, they write out *both*. We need to write out both as well. Return false.
184                 return false; 
185             }
186             else {
187                 // Can't make sense of the number. Put no font size.
188                 return true;
189             }
190     }
191     return false;
192 }
193
194 bool StyleChange::currentlyHasStyle(const Position &pos, const CSSProperty *property)
195 {
196     ASSERT(pos.isNotNull());
197     RefPtr<CSSComputedStyleDeclaration> style = pos.computedStyle();
198     RefPtr<CSSValue> value = style->getPropertyCSSValue(property->id(), DoNotUpdateLayout);
199     if (!value)
200         return false;
201     return equalIgnoringCase(value->cssText(), property->value()->cssText());
202 }
203
204 static String &styleSpanClassString()
205 {
206     static String styleSpanClassString = AppleStyleSpanClass;
207     return styleSpanClassString;
208 }
209
210 bool isStyleSpan(const Node *node)
211 {
212     if (!node || !node->isHTMLElement())
213         return false;
214
215     const HTMLElement *elem = static_cast<const HTMLElement *>(node);
216     return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styleSpanClassString();
217 }
218
219 static bool isEmptyStyleSpan(const Node *node)
220 {
221     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
222         return false;
223
224     const HTMLElement *elem = static_cast<const HTMLElement *>(node);
225     CSSMutableStyleDeclaration *inlineStyleDecl = elem->inlineStyleDecl();
226     return (!inlineStyleDecl || inlineStyleDecl->length() == 0) && elem->getAttribute(classAttr) == styleSpanClassString();
227 }
228
229 static bool isEmptyFontTag(const Node *node)
230 {
231     if (!node || !node->hasTagName(fontTag))
232         return false;
233
234     const Element *elem = static_cast<const Element *>(node);
235     NamedAttrMap *map = elem->attributes(true); // true for read-only
236     return (!map || map->length() == 1) && elem->getAttribute(classAttr) == styleSpanClassString();
237 }
238
239 static PassRefPtr<Element> createFontElement(Document* document)
240 {
241     ExceptionCode ec = 0;
242     RefPtr<Element> fontNode = document->createElementNS(xhtmlNamespaceURI, "font", ec);
243     ASSERT(ec == 0);
244     fontNode->setAttribute(classAttr, styleSpanClassString());
245     return fontNode.release();
246 }
247
248 PassRefPtr<HTMLElement> createStyleSpanElement(Document* document)
249 {
250     ExceptionCode ec = 0;
251     RefPtr<Element> styleElement = document->createElementNS(xhtmlNamespaceURI, "span", ec);
252     ASSERT(ec == 0);
253     styleElement->setAttribute(classAttr, styleSpanClassString());
254     return static_pointer_cast<HTMLElement>(styleElement.release());
255 }
256
257 ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, EditAction editingAction, EPropertyLevel propertyLevel)
258     : CompositeEditCommand(document)
259     , m_style(style->makeMutable())
260     , m_editingAction(editingAction)
261     , m_propertyLevel(propertyLevel)
262     , m_start(endingSelection().start().downstream())
263     , m_end(endingSelection().end().upstream())
264     , m_useEndingSelection(true)
265     , m_styledInlineElement(0)
266     , m_removeOnly(false)
267 {
268 }
269
270 ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, const Position& start, const Position& end, EditAction editingAction, EPropertyLevel propertyLevel)
271     : CompositeEditCommand(document)
272     , m_style(style->makeMutable())
273     , m_editingAction(editingAction)
274     , m_propertyLevel(propertyLevel)
275     , m_start(start)
276     , m_end(end)
277     , m_useEndingSelection(false)
278     , m_styledInlineElement(0)
279     , m_removeOnly(false)
280 {
281 }
282
283 ApplyStyleCommand::ApplyStyleCommand(Document* document, Element* element, bool removeOnly, EditAction editingAction)
284     : CompositeEditCommand(document)
285     , m_style(new CSSMutableStyleDeclaration())
286     , m_editingAction(editingAction)
287     , m_propertyLevel(PropertyDefault)
288     , m_start(endingSelection().start().downstream())
289     , m_end(endingSelection().end().upstream())
290     , m_useEndingSelection(true)
291     , m_styledInlineElement(element)
292     , m_removeOnly(removeOnly)
293 {
294 }
295
296 void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position& newEnd)
297 {
298     ASSERT(Range::compareBoundaryPoints(newEnd, newStart) >= 0);
299
300     if (!m_useEndingSelection && (newStart != m_start || newEnd != m_end))
301         m_useEndingSelection = true;
302
303     setEndingSelection(Selection(newStart, newEnd, VP_DEFAULT_AFFINITY));
304     m_start = newStart;
305     m_end = newEnd;
306 }
307
308 Position ApplyStyleCommand::startPosition()
309 {
310     if (m_useEndingSelection)
311         return endingSelection().start();
312     
313     return m_start;
314 }
315
316 Position ApplyStyleCommand::endPosition()
317 {
318     if (m_useEndingSelection)
319         return endingSelection().end();
320     
321     return m_end;
322 }
323
324 void ApplyStyleCommand::doApply()
325 {
326     switch (m_propertyLevel) {
327         case PropertyDefault: {
328             // apply the block-centric properties of the style
329             RefPtr<CSSMutableStyleDeclaration> blockStyle = m_style->copyBlockProperties();
330             applyBlockStyle(blockStyle.get());
331             // apply any remaining styles to the inline elements
332             // NOTE: hopefully, this string comparison is the same as checking for a non-null diff
333             if (blockStyle->length() < m_style->length() || m_styledInlineElement) {
334                 RefPtr<CSSMutableStyleDeclaration> inlineStyle = m_style->copy();
335                 applyRelativeFontStyleChange(inlineStyle.get());
336                 blockStyle->diff(inlineStyle.get());
337                 applyInlineStyle(inlineStyle.get());
338             }
339             break;
340         }
341         case ForceBlockProperties:
342             // Force all properties to be applied as block styles.
343             applyBlockStyle(m_style.get());
344             break;
345     }
346 }
347
348 EditAction ApplyStyleCommand::editingAction() const
349 {
350     return m_editingAction;
351 }
352
353 void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclaration *style)
354 {
355     // update document layout once before removing styles
356     // so that we avoid the expense of updating before each and every call
357     // to check a computed style
358     updateLayout();
359
360     // get positions we want to use for applying style
361     Position start = startPosition();
362     Position end = endPosition();
363     if (Range::compareBoundaryPoints(end, start) < 0) {
364         Position swap = start;
365         start = end;
366         end = swap;
367     }
368
369     // remove current values, if any, of the specified styles from the blocks
370     // NOTE: tracks the previous block to avoid repeated processing
371     // Also, gather up all the nodes we want to process in a DeprecatedPtrList before
372     // doing anything. This averts any bugs iterating over these nodes
373     // once you start removing and applying style.
374     Node *beyondEnd = end.node()->traverseNextNode();
375     DeprecatedPtrList<Node> nodes;
376     for (Node *node = start.node(); node != beyondEnd; node = node->traverseNextNode())
377         nodes.append(node);
378         
379     Node *prevBlock = 0;
380     for (DeprecatedPtrListIterator<Node> it(nodes); it.current(); ++it) {
381         Node *block = it.current()->enclosingBlockFlowElement();
382         if (block != prevBlock && block->isHTMLElement()) {
383             removeCSSStyle(style, static_cast<HTMLElement *>(block));
384             prevBlock = block;
385         }
386     }
387     
388     if (m_removeOnly)
389         return;
390     
391     // apply specified styles to the block flow elements in the selected range
392     prevBlock = 0;
393     for (DeprecatedPtrListIterator<Node> it(nodes); it.current(); ++it) {
394         Node *node = it.current();
395         if (node->renderer()) {
396             Node *block = node->enclosingBlockFlowElement();
397             if (block != prevBlock) {
398                 addBlockStyleIfNeeded(style, node);
399                 prevBlock = block;
400             }
401         }
402     }
403 }
404
405 #define NoFontDelta (0.0f)
406 #define MinimumFontSize (0.1f)
407
408 void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclaration *style)
409 {
410     RefPtr<CSSValue> value = style->getPropertyCSSValue(CSS_PROP_FONT_SIZE);
411     if (value) {
412         // Explicit font size overrides any delta.
413         style->removeProperty(CSS_PROP__WEBKIT_FONT_SIZE_DELTA);
414         return;
415     }
416
417     // Get the adjustment amount out of the style.
418     value = style->getPropertyCSSValue(CSS_PROP__WEBKIT_FONT_SIZE_DELTA);
419     if (!value)
420         return;
421     float adjustment = NoFontDelta;
422     if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
423         CSSPrimitiveValue *primitiveValue = static_cast<CSSPrimitiveValue *>(value.get());
424         if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_PX) {
425             // Only PX handled now. If we handle more types in the future, perhaps
426             // a switch statement here would be more appropriate.
427             adjustment = primitiveValue->getFloatValue();
428         }
429     }
430     style->removeProperty(CSS_PROP__WEBKIT_FONT_SIZE_DELTA);
431     if (adjustment == NoFontDelta)
432         return;
433     
434     Position start = startPosition();
435     Position end = endPosition();
436     if (Range::compareBoundaryPoints(end, start) < 0) {
437         Position swap = start;
438         start = end;
439         end = swap;
440     }
441     
442     // Join up any adjacent text nodes.
443     if (start.node()->isTextNode()) {
444         joinChildTextNodes(start.node()->parentNode(), start, end);
445         start = startPosition();
446         end = endPosition();
447     }
448     if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) {
449         joinChildTextNodes(end.node()->parentNode(), start, end);
450         start = startPosition();
451         end = endPosition();
452     }
453
454     // Split the start text nodes if needed to apply style.
455     bool splitStart = splitTextAtStartIfNeeded(start, end); 
456     if (splitStart) {
457         start = startPosition();
458         end = endPosition();
459     }
460     bool splitEnd = splitTextAtEndIfNeeded(start, end);
461     if (splitEnd) {
462         start = startPosition();
463         end = endPosition();
464     }
465
466     Node *beyondEnd = end.node()->traverseNextNode(); // Calculate loop end point.
467     start = start.upstream(); // Move upstream to ensure we do not add redundant spans.
468     Node *startNode = start.node();
469     if (startNode->isTextNode() && start.offset() >= startNode->caretMaxOffset()) // Move out of text node if range does not include its characters.
470         startNode = startNode->traverseNextNode();
471
472     // Store away font size before making any changes to the document.
473     // This ensures that changes to one node won't effect another.
474     HashMap<Node*, float> startingFontSizes;
475     for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode())
476         startingFontSizes.set(node, computedFontSize(node));
477
478     // These spans were added by us. If empty after font size changes, they can be removed.
479     DeprecatedPtrList<Node> emptySpans;
480     
481     Node *lastStyledNode = 0;
482     for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode()) {
483         HTMLElement *elem = 0;
484         if (node->isHTMLElement()) {
485             // Only work on fully selected nodes.
486             if (!nodeFullySelected(node, start, end))
487                 continue;
488             elem = static_cast<HTMLElement *>(node);
489         } else if (node->isTextNode() && node->parentNode() != lastStyledNode) {
490             // Last styled node was not parent node of this text node, but we wish to style this
491             // text node. To make this possible, add a style span to surround this text node.
492             RefPtr<HTMLElement> span = createStyleSpanElement(document());
493             insertNodeBefore(span.get(), node);
494             surroundNodeRangeWithElement(node, node, span.get());
495             elem = span.get();
496         }  else {
497             // Only handle HTML elements and text nodes.
498             continue;
499         }
500         lastStyledNode = node;
501         
502         CSSMutableStyleDeclaration* inlineStyleDecl = elem->getInlineStyleDecl();
503         float currentFontSize = computedFontSize(node);
504         float desiredFontSize = max(MinimumFontSize, startingFontSizes.get(node) + adjustment);
505         RefPtr<CSSValue> value = inlineStyleDecl->getPropertyCSSValue(CSS_PROP_FONT_SIZE);
506         if (value) {
507             inlineStyleDecl->removeProperty(CSS_PROP_FONT_SIZE, true);
508             currentFontSize = computedFontSize(node);
509         }
510         if (currentFontSize != desiredFontSize) {
511             inlineStyleDecl->setProperty(CSS_PROP_FONT_SIZE, String::number(desiredFontSize) + "px", false, false);
512             setNodeAttribute(elem, styleAttr, inlineStyleDecl->cssText());
513         }
514         if (inlineStyleDecl->length() == 0) {
515             removeNodeAttribute(elem, styleAttr);
516             if (isEmptyStyleSpan(elem))
517                 emptySpans.append(elem);
518         }
519     }
520
521     for (DeprecatedPtrListIterator<Node> it(emptySpans); it.current(); ++it)
522         removeNodePreservingChildren(it.current());
523 }
524
525 #undef NoFontDelta
526 #undef MinimumFontSize
527
528 void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style)
529 {
530     // update document layout once before removing styles
531     // so that we avoid the expense of updating before each and every call
532     // to check a computed style
533     updateLayout();
534     
535     // adjust to the positions we want to use for applying style
536     Position start = startPosition();
537     Position end = endPosition();
538     if (Range::compareBoundaryPoints(end, start) < 0) {
539         Position swap = start;
540         start = end;
541         end = swap;
542     }
543
544     // split the start node and containing element if the selection starts inside of it
545     bool splitStart = splitTextElementAtStartIfNeeded(start, end); 
546     if (splitStart) {
547         start = startPosition();
548         end = endPosition();
549     }
550
551     // split the end node and containing element if the selection ends inside of it
552     bool splitEnd = splitTextElementAtEndIfNeeded(start, end);
553     if (splitEnd) {
554         start = startPosition();
555         end = endPosition();
556     }
557
558     // Remove style from the selection.
559     // Use the upstream position of the start for removing style.
560     // This will ensure we remove all traces of the relevant styles from the selection
561     // and prevent us from adding redundant ones, as described in:
562     // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
563     removeInlineStyle(style, start.upstream(), end);
564     start = startPosition();
565     end = endPosition();
566
567     if (splitStart) {
568         bool mergedStart = mergeStartWithPreviousIfIdentical(start, end);
569         if (mergedStart) {
570             start = startPosition();
571             end = endPosition();
572         }
573     }
574
575     if (splitEnd) {
576         mergeEndWithNextIfIdentical(start, end);
577         start = startPosition();
578         end = endPosition();
579     }
580
581     // update document layout once before running the rest of the function
582     // so that we avoid the expense of updating before each and every call
583     // to check a computed style
584     updateLayout();
585     
586     Node* node = start.node();
587     Node* endNode = end.node();
588
589     if (start.offset() >= start.node()->caretMaxOffset())
590         node = node->traverseNextNode();
591
592     if (end.node()->renderer()->isTable() && end.offset() == maxDeepOffset(end.node()))
593         endNode = end.node()->lastDescendant();
594     
595     if (start.node() == endNode) {
596         addInlineStyleIfNeeded(style, node, node);
597     } else {
598         while (1) {
599             if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline() && node->isContentRichlyEditable()) {
600                 Node *runStart = node;
601                 while (1) {
602                     Node *next = node->traverseNextNode();
603                     // Break if node is the end node, or if the next node does not fit in with
604                     // the current group.
605                     if (node == endNode || 
606                         runStart->parentNode() != next->parentNode() || 
607                         (next->isElementNode() && !next->hasTagName(brTag)) || 
608                         (next->renderer() && !next->renderer()->isInline()))
609                         break;
610                     node = next;
611                 }
612                 // Now apply style to the run we found.
613                 addInlineStyleIfNeeded(style, runStart, node);
614             }
615             if (node == endNode)
616                 break;
617             node = node->traverseNextNode();
618         }
619     }
620
621     if (splitStart || splitEnd) {
622         cleanUpEmptyStyleSpans(start, end);
623     }
624 }
625
626 bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclaration *style, HTMLElement *elem)
627 {
628     DeprecatedValueListConstIterator<CSSProperty> end;
629     for (DeprecatedValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
630         switch ((*it).id()) {
631             case CSS_PROP_FONT_WEIGHT:
632                 if (elem->hasLocalName(bTag))
633                     return true;
634                 break;
635             case CSS_PROP_FONT_STYLE:
636                 if (elem->hasLocalName(iTag))
637                     return true;
638         }
639     }
640
641     return false;
642 }
643
644 void ApplyStyleCommand::removeHTMLStyleNode(HTMLElement *elem)
645 {
646     // This node can be removed.
647     // EDIT FIXME: This does not handle the case where the node
648     // has attributes. But how often do people add attributes to <B> tags? 
649     // Not so often I think.
650     ASSERT(elem);
651     removeNodePreservingChildren(elem);
652 }
653
654 void ApplyStyleCommand::removeHTMLFontStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem)
655 {
656     ASSERT(style);
657     ASSERT(elem);
658
659     if (!elem->hasLocalName(fontTag))
660         return;
661
662     ExceptionCode ec = 0;
663     DeprecatedValueListConstIterator<CSSProperty> end;
664     for (DeprecatedValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
665         switch ((*it).id()) {
666             case CSS_PROP_COLOR:
667                 elem->removeAttribute(colorAttr, ec);
668                 ASSERT(ec == 0);
669                 break;
670             case CSS_PROP_FONT_FAMILY:
671                 elem->removeAttribute(faceAttr, ec);
672                 ASSERT(ec == 0);
673                 break;
674             case CSS_PROP_FONT_SIZE:
675                 elem->removeAttribute(sizeAttr, ec);
676                 ASSERT(ec == 0);
677                 break;
678         }
679     }
680
681     if (isEmptyFontTag(elem))
682         removeNodePreservingChildren(elem);
683 }
684
685 void ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem)
686 {
687     ASSERT(style);
688     ASSERT(elem);
689
690     CSSMutableStyleDeclaration *decl = elem->inlineStyleDecl();
691     if (!decl)
692         return;
693
694     DeprecatedValueListConstIterator<CSSProperty> end;
695     for (DeprecatedValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
696         int propertyID = (*it).id();
697         RefPtr<CSSValue> value = decl->getPropertyCSSValue(propertyID);
698         if (value && (propertyID != CSS_PROP_WHITE_SPACE || !isTabSpanNode(elem)))
699             removeCSSProperty(decl, propertyID);
700     }
701
702     if (isEmptyStyleSpan(elem))
703         removeNodePreservingChildren(elem);
704 }
705
706 void ApplyStyleCommand::removeBlockStyle(CSSMutableStyleDeclaration *style, const Position &start, const Position &end)
707 {
708     ASSERT(start.isNotNull());
709     ASSERT(end.isNotNull());
710     ASSERT(start.node()->inDocument());
711     ASSERT(end.node()->inDocument());
712     ASSERT(Range::compareBoundaryPoints(start, end) <= 0);
713     
714 }
715
716 static bool hasTextDecorationProperty(Node *node)
717 {
718     if (!node->isElementNode())
719         return false;
720
721     Element *element = static_cast<Element *>(node);
722     CSSComputedStyleDeclaration style(element);
723     RefPtr<CSSValue> value = style.getPropertyCSSValue(CSS_PROP_TEXT_DECORATION, DoNotUpdateLayout);
724     return value && !equalIgnoringCase(value->cssText(), "none");
725 }
726
727 static Node* highestAncestorWithTextDecoration(Node *node)
728 {
729     Node *result = NULL;
730
731     for (Node *n = node; n; n = n->parentNode()) {
732         if (hasTextDecorationProperty(n))
733             result = n;
734     }
735
736     return result;
737 }
738
739 PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractTextDecorationStyle(Node* node)
740 {
741     ASSERT(node);
742     ASSERT(node->isElementNode());
743     
744     // non-html elements not handled yet
745     if (!node->isHTMLElement())
746         return 0;
747
748     HTMLElement *element = static_cast<HTMLElement *>(node);
749     RefPtr<CSSMutableStyleDeclaration> style = element->inlineStyleDecl();
750     if (!style)
751         return 0;
752
753     int properties[1] = { CSS_PROP_TEXT_DECORATION };
754     RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = style->copyPropertiesInSet(properties, 1);
755
756     RefPtr<CSSValue> property = style->getPropertyCSSValue(CSS_PROP_TEXT_DECORATION);
757     if (property && !equalIgnoringCase(property->cssText(), "none"))
758         removeCSSProperty(style.get(), CSS_PROP_TEXT_DECORATION);
759
760     return textDecorationStyle.release();
761 }
762
763 PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractAndNegateTextDecorationStyle(Node *node)
764 {
765     ASSERT(node);
766     ASSERT(node->isElementNode());
767     
768     // non-html elements not handled yet
769     if (!node->isHTMLElement())
770         return 0;
771
772     HTMLElement *element = static_cast<HTMLElement *>(node);
773     RefPtr<CSSComputedStyleDeclaration> computedStyle = new CSSComputedStyleDeclaration(element);
774     ASSERT(computedStyle);
775
776     int properties[1] = { CSS_PROP_TEXT_DECORATION };
777     RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = computedStyle->copyPropertiesInSet(properties, 1);
778
779     RefPtr<CSSValue> property = computedStyle->getPropertyCSSValue(CSS_PROP_TEXT_DECORATION);
780     if (property && !equalIgnoringCase(property->cssText(), "none")) {
781         RefPtr<CSSMutableStyleDeclaration> newStyle = textDecorationStyle->copy();
782         newStyle->setProperty(CSS_PROP_TEXT_DECORATION, "none");
783         applyTextDecorationStyle(node, newStyle.get());
784     }
785
786     return textDecorationStyle.release();
787 }
788
789 void ApplyStyleCommand::applyTextDecorationStyle(Node *node, CSSMutableStyleDeclaration *style)
790 {
791     ASSERT(node);
792
793     if (!style || !style->cssText().length())
794         return;
795
796     if (node->isTextNode()) {
797         RefPtr<HTMLElement> styleSpan = createStyleSpanElement(document());
798         insertNodeBefore(styleSpan.get(), node);
799         surroundNodeRangeWithElement(node, node, styleSpan.get());
800         node = styleSpan.get();
801     }
802
803     if (!node->isElementNode())
804         return;
805
806     HTMLElement *element = static_cast<HTMLElement *>(node);
807         
808     StyleChange styleChange(style, Position(element, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
809     if (styleChange.cssStyle().length() > 0) {
810         String cssText = styleChange.cssStyle();
811         CSSMutableStyleDeclaration *decl = element->inlineStyleDecl();
812         if (decl)
813             cssText += decl->cssText();
814         setNodeAttribute(element, styleAttr, cssText);
815     }
816 }
817
818 void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node *node, const Position &start, const Position &end, bool force)
819 {
820     Node *highestAncestor = highestAncestorWithTextDecoration(node);
821     
822     if (highestAncestor) {
823         Node *nextCurrent;
824         Node *nextChild;
825         for (Node *current = highestAncestor; current != node; current = nextCurrent) {
826             ASSERT(current);
827             
828             nextCurrent = NULL;
829             
830             RefPtr<CSSMutableStyleDeclaration> decoration = force ? extractAndNegateTextDecorationStyle(current) : extractTextDecorationStyle(current);
831
832             for (Node *child = current->firstChild(); child; child = nextChild) {
833                 nextChild = child->nextSibling();
834
835                 if (node == child) {
836                     nextCurrent = child;
837                 } else if (node->isAncestor(child)) {
838                     applyTextDecorationStyle(child, decoration.get());
839                     nextCurrent = child;
840                 } else {
841                     applyTextDecorationStyle(child, decoration.get());
842                 }
843             }
844         }
845     }
846 }
847
848 void ApplyStyleCommand::pushDownTextDecorationStyleAtBoundaries(const Position &start, const Position &end)
849 {
850     // We need to work in two passes. First we push down any inline
851     // styles that set text decoration. Then we look for any remaining
852     // styles (caused by stylesheets) and explicitly negate text
853     // decoration while pushing down.
854
855     pushDownTextDecorationStyleAroundNode(start.node(), start, end, false);
856     updateLayout();
857     pushDownTextDecorationStyleAroundNode(start.node(), start, end, true);
858
859     pushDownTextDecorationStyleAroundNode(end.node(), start, end, false);
860     updateLayout();
861     pushDownTextDecorationStyleAroundNode(end.node(), start, end, true);
862 }
863
864 static int maxRangeOffset(Node *n)
865 {
866     if (n->offsetInCharacters())
867         return n->maxOffset();
868
869     if (n->isElementNode())
870         return n->childNodeCount();
871
872     return 1;
873 }
874
875 void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration> style, const Position &start, const Position &end)
876 {
877     ASSERT(start.isNotNull());
878     ASSERT(end.isNotNull());
879     ASSERT(start.node()->inDocument());
880     ASSERT(end.node()->inDocument());
881     ASSERT(Range::compareBoundaryPoints(start, end) <= 0);
882     
883     RefPtr<CSSValue> textDecorationSpecialProperty = style->getPropertyCSSValue(CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT);
884
885     if (textDecorationSpecialProperty) {
886         pushDownTextDecorationStyleAtBoundaries(start.downstream(), end.upstream());
887         style = style->copy();
888         style->setProperty(CSS_PROP_TEXT_DECORATION, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT));
889     }
890
891     // The s and e variables store the positions used to set the ending selection after style removal
892     // takes place. This will help callers to recognize when either the start node or the end node
893     // are removed from the document during the work of this function.
894     Position s = start;
895     Position e = end;
896
897     Node *node = start.node();
898     while (node) {
899         Node *next = node->traverseNextNode();
900         if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
901             HTMLElement *elem = static_cast<HTMLElement *>(node);
902             Node *prev = elem->traversePreviousNodePostOrder();
903             Node *next = elem->traverseNextNode();
904             if (m_styledInlineElement && elem->hasTagName(m_styledInlineElement->tagQName()))
905                 removeNodePreservingChildren(elem);
906             if (isHTMLStyleNode(style.get(), elem))
907                 removeHTMLStyleNode(elem);
908             else {
909                 removeHTMLFontStyle(style.get(), elem);
910                 removeCSSStyle(style.get(), elem);
911             }
912             if (!elem->inDocument()) {
913                 if (s.node() == elem) {
914                     // Since elem must have been fully selected, and it is at the start
915                     // of the selection, it is clear we can set the new s offset to 0.
916                     ASSERT(s.offset() <= s.node()->caretMinOffset());
917                     s = Position(next, 0);
918                 }
919                 if (e.node() == elem) {
920                     // Since elem must have been fully selected, and it is at the end
921                     // of the selection, it is clear we can set the new e offset to
922                     // the max range offset of prev.
923                     ASSERT(e.offset() >= maxRangeOffset(e.node()));
924                     e = Position(prev, maxRangeOffset(prev));
925                 }
926             }
927         }
928         if (node == end.node())
929             break;
930         node = next;
931     }
932     
933     ASSERT(s.node()->inDocument());
934     ASSERT(e.node()->inDocument());
935     updateStartEnd(s, e);
936 }
937
938 bool ApplyStyleCommand::nodeFullySelected(Node *node, const Position &start, const Position &end) const
939 {
940     ASSERT(node);
941     ASSERT(node->isElementNode());
942
943     Position pos = Position(node, node->childNodeCount()).upstream();
944     return Range::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 &&
945         Range::compareBoundaryPoints(pos, end) <= 0;
946 }
947
948 bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, const Position &end) const
949 {
950     ASSERT(node);
951     ASSERT(node->isElementNode());
952
953     Position pos = Position(node, node->childNodeCount()).upstream();
954     bool isFullyBeforeStart = Range::compareBoundaryPoints(pos, start) < 0;
955     bool isFullyAfterEnd = Range::compareBoundaryPoints(node, 0, end.node(), end.offset()) > 0;
956
957     return isFullyBeforeStart || isFullyAfterEnd;
958 }
959
960
961 bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
962 {
963     if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
964         int endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
965         Text *text = static_cast<Text *>(start.node());
966         splitTextNode(text, start.offset());
967         updateStartEnd(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment));
968         return true;
969     }
970     return false;
971 }
972
973 bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end)
974 {
975     if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
976         Text *text = static_cast<Text *>(end.node());
977         splitTextNode(text, end.offset());
978         
979         Node *prevNode = text->previousSibling();
980         ASSERT(prevNode);
981         Node *startNode = start.node() == end.node() ? prevNode : start.node();
982         ASSERT(startNode);
983         updateStartEnd(Position(startNode, start.offset()), Position(prevNode, prevNode->caretMaxOffset()));
984         return true;
985     }
986     return false;
987 }
988
989 bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end)
990 {
991     if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
992         int endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
993         Text *text = static_cast<Text *>(start.node());
994         splitTextNodeContainingElement(text, start.offset());
995
996         updateStartEnd(Position(start.node()->parentNode(), start.node()->nodeIndex()), Position(end.node(), end.offset() - endOffsetAdjustment));
997         return true;
998     }
999     return false;
1000 }
1001
1002 bool ApplyStyleCommand::splitTextElementAtEndIfNeeded(const Position &start, const Position &end)
1003 {
1004     if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
1005         Text *text = static_cast<Text *>(end.node());
1006         splitTextNodeContainingElement(text, end.offset());
1007
1008         Node *prevNode = text->parent()->previousSibling()->lastChild();
1009         ASSERT(prevNode);
1010         Node *startNode = start.node() == end.node() ? prevNode : start.node();
1011         ASSERT(startNode);
1012         updateStartEnd(Position(startNode, start.offset()), Position(prevNode->parent(), prevNode->nodeIndex() + 1));
1013         return true;
1014     }
1015     return false;
1016 }
1017
1018 static bool areIdenticalElements(Node *first, Node *second)
1019 {
1020     // check that tag name and all attribute names and values are identical
1021
1022     if (!first->isElementNode())
1023         return false;
1024     
1025     if (!second->isElementNode())
1026         return false;
1027
1028     Element *firstElement = static_cast<Element *>(first);
1029     Element *secondElement = static_cast<Element *>(second);
1030     
1031     if (!firstElement->tagQName().matches(secondElement->tagQName()))
1032         return false;
1033
1034     NamedAttrMap *firstMap = firstElement->attributes();
1035     NamedAttrMap *secondMap = secondElement->attributes();
1036
1037     unsigned firstLength = firstMap->length();
1038
1039     if (firstLength != secondMap->length())
1040         return false;
1041
1042     for (unsigned i = 0; i < firstLength; i++) {
1043         Attribute *attribute = firstMap->attributeItem(i);
1044         Attribute *secondAttribute = secondMap->getAttributeItem(attribute->name());
1045
1046         if (!secondAttribute || attribute->value() != secondAttribute->value())
1047             return false;
1048     }
1049     
1050     return true;
1051 }
1052
1053 bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end)
1054 {
1055     Node *startNode = start.node();
1056     int startOffset = start.offset();
1057
1058     if (isAtomicNode(start.node())) {
1059         if (start.offset() != 0)
1060             return false;
1061
1062         if (start.node()->previousSibling())
1063             return false;
1064
1065         startNode = start.node()->parent();
1066         startOffset = 0;
1067     }
1068
1069     if (!startNode->isElementNode())
1070         return false;
1071
1072     if (startOffset != 0)
1073         return false;
1074
1075     Node *previousSibling = startNode->previousSibling();
1076
1077     if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
1078         Element *previousElement = static_cast<Element *>(previousSibling);
1079         Element *element = static_cast<Element *>(startNode);
1080         Node *startChild = element->firstChild();
1081         ASSERT(startChild);
1082         mergeIdenticalElements(previousElement, element);
1083
1084         int startOffsetAdjustment = startChild->nodeIndex();
1085         int endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0;
1086         updateStartEnd(Position(startNode, startOffsetAdjustment), Position(end.node(), end.offset() + endOffsetAdjustment)); 
1087         return true;
1088     }
1089
1090     return false;
1091 }
1092
1093 bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end)
1094 {
1095     Node *endNode = end.node();
1096     int endOffset = end.offset();
1097
1098     if (isAtomicNode(endNode)) {
1099         if (endOffset < endNode->caretMaxOffset())
1100             return false;
1101
1102         unsigned parentLastOffset = end.node()->parent()->childNodes()->length() - 1;
1103         if (end.node()->nextSibling())
1104             return false;
1105
1106         endNode = end.node()->parent();
1107         endOffset = parentLastOffset;
1108     }
1109
1110     if (!endNode->isElementNode() || endNode->hasTagName(brTag))
1111         return false;
1112
1113     Node *nextSibling = endNode->nextSibling();
1114
1115     if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
1116         Element *nextElement = static_cast<Element *>(nextSibling);
1117         Element *element = static_cast<Element *>(endNode);
1118         Node *nextChild = nextElement->firstChild();
1119
1120         mergeIdenticalElements(element, nextElement);
1121
1122         Node *startNode = start.node() == endNode ? nextElement : start.node();
1123         ASSERT(startNode);
1124
1125         int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
1126         updateStartEnd(Position(startNode, start.offset()), Position(nextElement, endOffset));
1127         return true;
1128     }
1129
1130     return false;
1131 }
1132
1133 void ApplyStyleCommand::cleanUpEmptyStyleSpans(const Position &start, const Position &end)
1134 {
1135     Node *node;
1136     for (node = start.node(); node && !node->previousSibling(); node = node->parentNode()) {
1137     }
1138
1139     if (node && isEmptyStyleSpan(node->previousSibling())) {
1140         removeNodePreservingChildren(node->previousSibling());
1141     }
1142
1143     if (start.node() == end.node()) {
1144         if (start.node()->isTextNode()) {
1145             for (Node *last = start.node(), *cur = last->parentNode(); cur && !last->previousSibling() && !last->nextSibling(); last = cur, cur = cur->parentNode()) {
1146                 if (isEmptyStyleSpan(cur)) {
1147                     removeNodePreservingChildren(cur);
1148                     break;
1149                 }
1150             }
1151
1152         }
1153     } else {
1154         if (start.node()->isTextNode()) {
1155             for (Node *last = start.node(), *cur = last->parentNode(); cur && !last->previousSibling(); last = cur, cur = cur->parentNode()) {
1156                 if (isEmptyStyleSpan(cur)) {
1157                     removeNodePreservingChildren(cur);
1158                     break;
1159                 }
1160             }
1161         }
1162
1163         if (end.node()->isTextNode()) {
1164             for (Node *last = end.node(), *cur = last->parentNode(); cur && !last->nextSibling(); last = cur, cur = cur->parentNode()) {
1165                 if (isEmptyStyleSpan(cur)) {
1166                     removeNodePreservingChildren(cur);
1167                     break;
1168                 }
1169             }
1170         }
1171     }
1172     
1173     for (node = end.node(); node && !node->nextSibling(); node = node->parentNode()) {
1174     }
1175     if (node && isEmptyStyleSpan(node->nextSibling())) {
1176         removeNodePreservingChildren(node->nextSibling());
1177     }
1178 }
1179
1180 void ApplyStyleCommand::surroundNodeRangeWithElement(Node *startNode, Node *endNode, Element *element)
1181 {
1182     ASSERT(startNode);
1183     ASSERT(endNode);
1184     ASSERT(element);
1185     
1186     Node *node = startNode;
1187     while (1) {
1188         Node *next = node->traverseNextNode();
1189         if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
1190             removeNode(node);
1191             appendNode(node, element);
1192         }
1193         if (node == endNode)
1194             break;
1195         node = next;
1196     }
1197 }
1198
1199 void ApplyStyleCommand::addBlockStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *node)
1200 {
1201     // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
1202     // inline content.
1203     if (!node)
1204         return;
1205     
1206     HTMLElement *block = static_cast<HTMLElement *>(node->enclosingBlockFlowElement());
1207     if (!block)
1208         return;
1209         
1210     StyleChange styleChange(style, Position(block, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
1211     if (styleChange.cssStyle().length() > 0) {
1212         moveParagraphContentsToNewBlockIfNecessary(Position(node, 0));
1213         block = static_cast<HTMLElement *>(node->enclosingBlockFlowElement());
1214         String cssText = styleChange.cssStyle();
1215         CSSMutableStyleDeclaration *decl = block->inlineStyleDecl();
1216         if (decl)
1217             cssText += decl->cssText();
1218         setNodeAttribute(block, styleAttr, cssText);
1219     }
1220 }
1221
1222 void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *startNode, Node *endNode)
1223 {
1224     if (m_removeOnly)
1225         return;
1226         
1227     StyleChange styleChange(style, Position(startNode, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
1228     ExceptionCode ec = 0;
1229     
1230     //
1231     // Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
1232     //
1233     if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) {
1234         RefPtr<Element> fontElement = createFontElement(document());
1235         ASSERT(ec == 0);
1236         insertNodeBefore(fontElement.get(), startNode);
1237         if (styleChange.applyFontColor())
1238             fontElement->setAttribute(colorAttr, styleChange.fontColor());
1239         if (styleChange.applyFontFace())
1240             fontElement->setAttribute(faceAttr, styleChange.fontFace());
1241         if (styleChange.applyFontSize())
1242             fontElement->setAttribute(sizeAttr, styleChange.fontSize());
1243         surroundNodeRangeWithElement(startNode, endNode, fontElement.get());
1244     }
1245
1246     if (styleChange.cssStyle().length() > 0) {
1247         RefPtr<Element> styleElement = createStyleSpanElement(document());
1248         styleElement->setAttribute(styleAttr, styleChange.cssStyle());
1249         insertNodeBefore(styleElement.get(), startNode);
1250         surroundNodeRangeWithElement(startNode, endNode, styleElement.get());
1251     }
1252
1253     if (styleChange.applyBold()) {
1254         RefPtr<Element> boldElement = document()->createElementNS(xhtmlNamespaceURI, "b", ec);
1255         ASSERT(ec == 0);
1256         insertNodeBefore(boldElement.get(), startNode);
1257         surroundNodeRangeWithElement(startNode, endNode, boldElement.get());
1258     }
1259
1260     if (styleChange.applyItalic()) {
1261         RefPtr<Element> italicElement = document()->createElementNS(xhtmlNamespaceURI, "i", ec);
1262         ASSERT(ec == 0);
1263         insertNodeBefore(italicElement.get(), startNode);
1264         surroundNodeRangeWithElement(startNode, endNode, italicElement.get());
1265     }
1266     
1267     if (m_styledInlineElement) {
1268         RefPtr<Element> clonedElement = static_pointer_cast<Element>(m_styledInlineElement->cloneNode(false));
1269         insertNodeBefore(clonedElement.get(), startNode);
1270         surroundNodeRangeWithElement(startNode, endNode, clonedElement.get());
1271     }
1272 }
1273
1274 float ApplyStyleCommand::computedFontSize(const Node *node)
1275 {
1276     if (!node)
1277         return 0;
1278     
1279     Position pos(const_cast<Node *>(node), 0);
1280     RefPtr<CSSComputedStyleDeclaration> computedStyle = pos.computedStyle();
1281     if (!computedStyle)
1282         return 0;
1283
1284     RefPtr<CSSPrimitiveValue> value = static_pointer_cast<CSSPrimitiveValue>(computedStyle->getPropertyCSSValue(CSS_PROP_FONT_SIZE));
1285     if (!value)
1286         return 0;
1287
1288     return value->getFloatValue(CSSPrimitiveValue::CSS_PX);
1289 }
1290
1291 void ApplyStyleCommand::joinChildTextNodes(Node *node, const Position &start, const Position &end)
1292 {
1293     if (!node)
1294         return;
1295
1296     Position newStart = start;
1297     Position newEnd = end;
1298     
1299     Node *child = node->firstChild();
1300     while (child) {
1301         Node *next = child->nextSibling();
1302         if (child->isTextNode() && next && next->isTextNode()) {
1303             Text *childText = static_cast<Text *>(child);
1304             Text *nextText = static_cast<Text *>(next);
1305             if (next == start.node())
1306                 newStart = Position(childText, childText->length() + start.offset());
1307             if (next == end.node())
1308                 newEnd = Position(childText, childText->length() + end.offset());
1309             String textToMove = nextText->data();
1310             insertTextIntoNode(childText, childText->length(), textToMove);
1311             removeNode(next);
1312             // don't move child node pointer. it may want to merge with more text nodes.
1313         }
1314         else {
1315             child = child->nextSibling();
1316         }
1317     }
1318
1319     updateStartEnd(newStart, newEnd);
1320 }
1321
1322 }