d888aed6e797f8db0460d8bd80cdb32db9d7a1f2
[WebKit-https.git] / WebCore / editing / ApplyStyleCommand.cpp
1 /*
2  * Copyright (C) 2005, 2006, 2008, 2009 Apple 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 "CSSParser.h"
31 #include "CSSProperty.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSValueKeywords.h"
34 #include "Document.h"
35 #include "Editor.h"
36 #include "Frame.h"
37 #include "HTMLElement.h"
38 #include "HTMLInterchange.h"
39 #include "HTMLNames.h"
40 #include "NodeList.h"
41 #include "Range.h"
42 #include "RenderObject.h"
43 #include "Text.h"
44 #include "TextIterator.h"
45 #include "htmlediting.h"
46 #include "visible_units.h"
47 #include <wtf/StdLibExtras.h>
48
49 namespace WebCore {
50
51 using namespace HTMLNames;
52
53 class StyleChange {
54 public:
55     explicit StyleChange(CSSStyleDeclaration*, const Position&);
56
57     String cssStyle() const { return m_cssStyle; }
58     bool applyBold() const { return m_applyBold; }
59     bool applyItalic() const { return m_applyItalic; }
60     bool applySubscript() const { return m_applySubscript; }
61     bool applySuperscript() const { return m_applySuperscript; }
62     bool applyFontColor() const { return m_applyFontColor.length() > 0; }
63     bool applyFontFace() const { return m_applyFontFace.length() > 0; }
64     bool applyFontSize() const { return m_applyFontSize.length() > 0; }
65
66     String fontColor() { return m_applyFontColor; }
67     String fontFace() { return m_applyFontFace; }
68     String fontSize() { return m_applyFontSize; }
69
70 private:
71     void init(PassRefPtr<CSSStyleDeclaration>, const Position&);
72     bool checkForLegacyHTMLStyleChange(const CSSProperty*);
73     static bool currentlyHasStyle(const Position&, const CSSProperty*);
74     
75     String m_cssStyle;
76     bool m_applyBold;
77     bool m_applyItalic;
78     bool m_applySubscript;
79     bool m_applySuperscript;
80     String m_applyFontColor;
81     String m_applyFontFace;
82     String m_applyFontSize;
83 };
84
85
86 StyleChange::StyleChange(CSSStyleDeclaration* style, const Position& position)
87     : m_applyBold(false)
88     , m_applyItalic(false)
89     , m_applySubscript(false)
90     , m_applySuperscript(false)
91 {
92     init(style, position);
93 }
94
95 void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position& position)
96 {
97     Document* document = position.node() ? position.node()->document() : 0;
98     if (!document || !document->frame())
99         return;
100         
101     bool useHTMLFormattingTags = !document->frame()->editor()->shouldStyleWithCSS();
102             
103     RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
104     
105     String styleText("");
106
107     bool addedDirection = false;
108     CSSMutableStyleDeclaration::const_iterator end = mutableStyle->end();
109     for (CSSMutableStyleDeclaration::const_iterator it = mutableStyle->begin(); it != end; ++it) {
110         const CSSProperty *property = &*it;
111
112         // If position is empty or the position passed in already has the 
113         // style, just move on.
114         if (position.isNotNull() && currentlyHasStyle(position, property))
115             continue;
116         
117         // Changing the whitespace style in a tab span would collapse the tab into a space.
118         if (property->id() == CSSPropertyWhiteSpace && (isTabSpanTextNode(position.node()) || isTabSpanNode((position.node()))))
119             continue;
120         
121         // If needed, figure out if this change is a legacy HTML style change.
122         if (useHTMLFormattingTags && checkForLegacyHTMLStyleChange(property))
123             continue;
124
125         if (property->id() == CSSPropertyDirection) {
126             if (addedDirection)
127                 continue;
128             addedDirection = true;
129         }
130
131         // Add this property
132
133         if (property->id() == CSSPropertyWebkitTextDecorationsInEffect) {
134             // we have to special-case text decorations
135             // FIXME: Why?
136             CSSProperty alteredProperty(CSSPropertyTextDecoration, property->value(), property->isImportant());
137             styleText += alteredProperty.cssText();
138         } else
139             styleText += property->cssText();
140
141         if (!addedDirection && property->id() == CSSPropertyUnicodeBidi) {
142             styleText += "direction: " + style->getPropertyValue(CSSPropertyDirection) + "; ";
143             addedDirection = true;
144         }
145     }
146
147     // Save the result for later
148     m_cssStyle = styleText.stripWhiteSpace();
149 }
150
151 // This function is the mapping from CSS styles to styling tags (like font-weight: bold to <b>)
152 bool StyleChange::checkForLegacyHTMLStyleChange(const CSSProperty* property)
153 {
154     if (!property || !property->value())
155         return false;
156     
157     String valueText(property->value()->cssText());
158     switch (property->id()) {
159         case CSSPropertyFontWeight:
160             if (equalIgnoringCase(valueText, "bold")) {
161                 m_applyBold = true;
162                 return true;
163             }
164             break;
165         case CSSPropertyVerticalAlign:
166             if (equalIgnoringCase(valueText, "sub")) {
167                 m_applySubscript = true;
168                 return true;
169             }
170             if (equalIgnoringCase(valueText, "super")) {
171                 m_applySuperscript = true;
172                 return true;
173             }
174             break;
175         case CSSPropertyFontStyle:
176             if (equalIgnoringCase(valueText, "italic") || equalIgnoringCase(valueText, "oblique")) {
177                 m_applyItalic = true;
178                 return true;
179             }
180             break;
181         case CSSPropertyColor: {
182             RGBA32 rgba = 0;
183             CSSParser::parseColor(rgba, valueText);
184             Color color(rgba);
185             m_applyFontColor = color.name();
186             return true;
187         }
188         case CSSPropertyFontFamily:
189             m_applyFontFace = valueText;
190             return true;
191         case CSSPropertyFontSize:
192             if (property->value()->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
193                 CSSPrimitiveValue *value = static_cast<CSSPrimitiveValue *>(property->value());
194
195                 if (value->primitiveType() < CSSPrimitiveValue::CSS_PX || value->primitiveType() > CSSPrimitiveValue::CSS_PC)
196                     // Size keyword or relative unit.
197                     return false;
198
199                 float number = value->getFloatValue(CSSPrimitiveValue::CSS_PX);
200                 if (number <= 9)
201                     m_applyFontSize = "1";
202                 else if (number <= 10)
203                     m_applyFontSize = "2";
204                 else if (number <= 13)
205                     m_applyFontSize = "3";
206                 else if (number <= 16)
207                     m_applyFontSize = "4";
208                 else if (number <= 18)
209                     m_applyFontSize = "5";
210                 else if (number <= 24)
211                     m_applyFontSize = "6";
212                 else
213                     m_applyFontSize = "7";
214                 // Huge quirk in Microsft Entourage is that they understand CSS font-size, but also write 
215                 // out legacy 1-7 values in font tags (I guess for mailers that are not CSS-savvy at all, 
216                 // like Eudora). Yes, they write out *both*. We need to write out both as well. Return false.
217                 return false; 
218             }
219             else {
220                 // Can't make sense of the number. Put no font size.
221                 return true;
222             }
223     }
224     return false;
225 }
226
227 bool StyleChange::currentlyHasStyle(const Position &pos, const CSSProperty *property)
228 {
229     ASSERT(pos.isNotNull());
230     RefPtr<CSSComputedStyleDeclaration> style = pos.computedStyle();
231     RefPtr<CSSValue> value = style->getPropertyCSSValue(property->id(), DoNotUpdateLayout);
232     if (!value)
233         return false;
234     return equalIgnoringCase(value->cssText(), property->value()->cssText());
235 }
236
237 static String& styleSpanClassString()
238 {
239     DEFINE_STATIC_LOCAL(String, styleSpanClassString, ((AppleStyleSpanClass)));
240     return styleSpanClassString;
241 }
242
243 bool isStyleSpan(const Node *node)
244 {
245     if (!node || !node->isHTMLElement())
246         return false;
247
248     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
249     return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styleSpanClassString();
250 }
251
252 static bool isUnstyledStyleSpan(const Node* node)
253 {
254     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
255         return false;
256
257     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
258     CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
259     return (!inlineStyleDecl || inlineStyleDecl->length() == 0) && elem->getAttribute(classAttr) == styleSpanClassString();
260 }
261
262 static bool isSpanWithoutAttributesOrUnstyleStyleSpan(const Node* node)
263 {
264     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
265         return false;
266
267     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
268     NamedAttrMap* attributes = elem->attributes(true); // readonly
269     if (attributes->length() == 0)
270         return true;
271
272     return isUnstyledStyleSpan(node);
273 }
274
275 static bool isEmptyFontTag(const Node *node)
276 {
277     if (!node || !node->hasTagName(fontTag))
278         return false;
279
280     const Element *elem = static_cast<const Element *>(node);
281     NamedAttrMap *map = elem->attributes(true); // true for read-only
282     return (!map || map->length() == 1) && elem->getAttribute(classAttr) == styleSpanClassString();
283 }
284
285 static PassRefPtr<Element> createFontElement(Document* document)
286 {
287     RefPtr<Element> fontNode = createHTMLElement(document, fontTag);
288     fontNode->setAttribute(classAttr, styleSpanClassString());
289     return fontNode.release();
290 }
291
292 PassRefPtr<HTMLElement> createStyleSpanElement(Document* document)
293 {
294     RefPtr<HTMLElement> styleElement = createHTMLElement(document, spanTag);
295     styleElement->setAttribute(classAttr, styleSpanClassString());
296     return styleElement.release();
297 }
298
299 ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, EditAction editingAction, EPropertyLevel propertyLevel)
300     : CompositeEditCommand(document)
301     , m_style(style->makeMutable())
302     , m_editingAction(editingAction)
303     , m_propertyLevel(propertyLevel)
304     , m_start(endingSelection().start().downstream())
305     , m_end(endingSelection().end().upstream())
306     , m_useEndingSelection(true)
307     , m_styledInlineElement(0)
308     , m_removeOnly(false)
309 {
310 }
311
312 ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, const Position& start, const Position& end, EditAction editingAction, EPropertyLevel propertyLevel)
313     : CompositeEditCommand(document)
314     , m_style(style->makeMutable())
315     , m_editingAction(editingAction)
316     , m_propertyLevel(propertyLevel)
317     , m_start(start)
318     , m_end(end)
319     , m_useEndingSelection(false)
320     , m_styledInlineElement(0)
321     , m_removeOnly(false)
322 {
323 }
324
325 ApplyStyleCommand::ApplyStyleCommand(PassRefPtr<Element> element, bool removeOnly, EditAction editingAction)
326     : CompositeEditCommand(element->document())
327     , m_style(CSSMutableStyleDeclaration::create())
328     , m_editingAction(editingAction)
329     , m_propertyLevel(PropertyDefault)
330     , m_start(endingSelection().start().downstream())
331     , m_end(endingSelection().end().upstream())
332     , m_useEndingSelection(true)
333     , m_styledInlineElement(element)
334     , m_removeOnly(removeOnly)
335 {
336 }
337
338 void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position& newEnd)
339 {
340     ASSERT(Range::compareBoundaryPoints(newEnd, newStart) >= 0);
341
342     if (!m_useEndingSelection && (newStart != m_start || newEnd != m_end))
343         m_useEndingSelection = true;
344
345     setEndingSelection(VisibleSelection(newStart, newEnd, VP_DEFAULT_AFFINITY));
346     m_start = newStart;
347     m_end = newEnd;
348 }
349
350 Position ApplyStyleCommand::startPosition()
351 {
352     if (m_useEndingSelection)
353         return endingSelection().start();
354     
355     return m_start;
356 }
357
358 Position ApplyStyleCommand::endPosition()
359 {
360     if (m_useEndingSelection)
361         return endingSelection().end();
362     
363     return m_end;
364 }
365
366 void ApplyStyleCommand::doApply()
367 {
368     switch (m_propertyLevel) {
369         case PropertyDefault: {
370             // apply the block-centric properties of the style
371             RefPtr<CSSMutableStyleDeclaration> blockStyle = m_style->copyBlockProperties();
372             if (blockStyle->length())
373                 applyBlockStyle(blockStyle.get());
374             // apply any remaining styles to the inline elements
375             // NOTE: hopefully, this string comparison is the same as checking for a non-null diff
376             if (blockStyle->length() < m_style->length() || m_styledInlineElement) {
377                 RefPtr<CSSMutableStyleDeclaration> inlineStyle = m_style->copy();
378                 applyRelativeFontStyleChange(inlineStyle.get());
379                 blockStyle->diff(inlineStyle.get());
380                 applyInlineStyle(inlineStyle.get());
381             }
382             break;
383         }
384         case ForceBlockProperties:
385             // Force all properties to be applied as block styles.
386             applyBlockStyle(m_style.get());
387             break;
388     }
389 }
390
391 EditAction ApplyStyleCommand::editingAction() const
392 {
393     return m_editingAction;
394 }
395
396 void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclaration *style)
397 {
398     // update document layout once before removing styles
399     // so that we avoid the expense of updating before each and every call
400     // to check a computed style
401     updateLayout();
402
403     // get positions we want to use for applying style
404     Position start = startPosition();
405     Position end = endPosition();
406     if (Range::compareBoundaryPoints(end, start) < 0) {
407         Position swap = start;
408         start = end;
409         end = swap;
410     }
411         
412     VisiblePosition visibleStart(start);
413     VisiblePosition visibleEnd(end);
414     // Save and restore the selection endpoints using their indices in the document, since
415     // addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints.
416     // Calculate start and end indices from the start of the tree that they're in.
417     Node* scope = highestAncestor(visibleStart.deepEquivalent().node());
418     Position rangeStart(scope, 0);
419     RefPtr<Range> startRange = Range::create(document(), rangeStart, rangeCompliantEquivalent(visibleStart.deepEquivalent()));
420     RefPtr<Range> endRange = Range::create(document(), rangeStart, rangeCompliantEquivalent(visibleEnd.deepEquivalent()));
421     int startIndex = TextIterator::rangeLength(startRange.get(), true);
422     int endIndex = TextIterator::rangeLength(endRange.get(), true);
423     
424     VisiblePosition paragraphStart(startOfParagraph(visibleStart));
425     VisiblePosition nextParagraphStart(endOfParagraph(paragraphStart).next());
426     VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next());
427     while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) {
428         StyleChange styleChange(style, paragraphStart.deepEquivalent());
429         if (styleChange.cssStyle().length() > 0 || m_removeOnly) {
430             RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().node());
431             RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent());
432             if (newBlock)
433                 block = newBlock;
434             ASSERT(block->isHTMLElement());
435             if (block->isHTMLElement()) {
436                 removeCSSStyle(style, static_cast<HTMLElement*>(block.get()));
437                 if (!m_removeOnly)
438                     addBlockStyle(styleChange, static_cast<HTMLElement*>(block.get()));
439             }
440         }
441         paragraphStart = nextParagraphStart;
442         nextParagraphStart = endOfParagraph(paragraphStart).next();
443     }
444     
445     startRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), startIndex, 0, true);
446     endRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), endIndex, 0, true);
447     if (startRange && endRange)
448         updateStartEnd(startRange->startPosition(), endRange->startPosition());
449 }
450
451 #define NoFontDelta (0.0f)
452 #define MinimumFontSize (0.1f)
453
454 void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclaration *style)
455 {
456     RefPtr<CSSValue> value = style->getPropertyCSSValue(CSSPropertyFontSize);
457     if (value) {
458         // Explicit font size overrides any delta.
459         style->removeProperty(CSSPropertyWebkitFontSizeDelta);
460         return;
461     }
462
463     // Get the adjustment amount out of the style.
464     value = style->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
465     if (!value)
466         return;
467     float adjustment = NoFontDelta;
468     if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
469         CSSPrimitiveValue *primitiveValue = static_cast<CSSPrimitiveValue *>(value.get());
470         if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_PX) {
471             // Only PX handled now. If we handle more types in the future, perhaps
472             // a switch statement here would be more appropriate.
473             adjustment = primitiveValue->getFloatValue();
474         }
475     }
476     style->removeProperty(CSSPropertyWebkitFontSizeDelta);
477     if (adjustment == NoFontDelta)
478         return;
479     
480     Position start = startPosition();
481     Position end = endPosition();
482     if (Range::compareBoundaryPoints(end, start) < 0) {
483         Position swap = start;
484         start = end;
485         end = swap;
486     }
487     
488     // Join up any adjacent text nodes.
489     if (start.node()->isTextNode()) {
490         joinChildTextNodes(start.node()->parentNode(), start, end);
491         start = startPosition();
492         end = endPosition();
493     }
494     if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) {
495         joinChildTextNodes(end.node()->parentNode(), start, end);
496         start = startPosition();
497         end = endPosition();
498     }
499
500     // Split the start text nodes if needed to apply style.
501     bool splitStart = splitTextAtStartIfNeeded(start, end); 
502     if (splitStart) {
503         start = startPosition();
504         end = endPosition();
505     }
506     bool splitEnd = splitTextAtEndIfNeeded(start, end);
507     if (splitEnd) {
508         start = startPosition();
509         end = endPosition();
510     }
511
512     // Calculate loop end point.
513     // If the end node is before the start node (can only happen if the end node is
514     // an ancestor of the start node), we gather nodes up to the next sibling of the end node
515     Node *beyondEnd;
516     if (start.node()->isDescendantOf(end.node()))
517         beyondEnd = end.node()->traverseNextSibling();
518     else
519         beyondEnd = end.node()->traverseNextNode();
520     
521     start = start.upstream(); // Move upstream to ensure we do not add redundant spans.
522     Node *startNode = start.node();
523     if (startNode->isTextNode() && start.offset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters.
524         startNode = startNode->traverseNextNode();
525
526     // Store away font size before making any changes to the document.
527     // This ensures that changes to one node won't effect another.
528     HashMap<Node*, float> startingFontSizes;
529     for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode())
530         startingFontSizes.set(node, computedFontSize(node));
531
532     // These spans were added by us. If empty after font size changes, they can be removed.
533     Vector<RefPtr<HTMLElement> > unstyledSpans;
534     
535     Node* lastStyledNode = 0;
536     for (Node* node = startNode; node != beyondEnd; node = node->traverseNextNode()) {
537         RefPtr<HTMLElement> element;
538         if (node->isHTMLElement()) {
539             // Only work on fully selected nodes.
540             if (!nodeFullySelected(node, start, end))
541                 continue;
542             element = static_cast<HTMLElement*>(node);
543         } else if (node->isTextNode() && node->renderer() && node->parentNode() != lastStyledNode) {
544             // Last styled node was not parent node of this text node, but we wish to style this
545             // text node. To make this possible, add a style span to surround this text node.
546             RefPtr<HTMLElement> span = createStyleSpanElement(document());
547             surroundNodeRangeWithElement(node, node, span.get());
548             element = span.release();
549         }  else {
550             // Only handle HTML elements and text nodes.
551             continue;
552         }
553         lastStyledNode = node;
554
555         CSSMutableStyleDeclaration* inlineStyleDecl = element->getInlineStyleDecl();
556         float currentFontSize = computedFontSize(node);
557         float desiredFontSize = max(MinimumFontSize, startingFontSizes.get(node) + adjustment);
558         RefPtr<CSSValue> value = inlineStyleDecl->getPropertyCSSValue(CSSPropertyFontSize);
559         if (value) {
560             inlineStyleDecl->removeProperty(CSSPropertyFontSize, true);
561             currentFontSize = computedFontSize(node);
562         }
563         if (currentFontSize != desiredFontSize) {
564             inlineStyleDecl->setProperty(CSSPropertyFontSize, String::number(desiredFontSize) + "px", false, false);
565             setNodeAttribute(element.get(), styleAttr, inlineStyleDecl->cssText());
566         }
567         if (inlineStyleDecl->length() == 0) {
568             removeNodeAttribute(element.get(), styleAttr);
569             // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
570             if (isUnstyledStyleSpan(element.get()))
571                 unstyledSpans.append(element.release());
572         }
573     }
574
575     size_t size = unstyledSpans.size();
576     for (size_t i = 0; i < size; ++i)
577         removeNodePreservingChildren(unstyledSpans[i].get());
578 }
579
580 #undef NoFontDelta
581 #undef MinimumFontSize
582
583 static Node* dummySpanAncestorForNode(const Node* node)
584 {
585     while (node && !isStyleSpan(node))
586         node = node->parent();
587     
588     return node ? node->parent() : 0;
589 }
590
591 void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor)
592 {
593     if (!dummySpanAncestor)
594         return;
595
596     // Dummy spans are created when text node is split, so that style information
597     // can be propagated, which can result in more splitting. If a dummy span gets
598     // cloned/split, the new node is always a sibling of it. Therefore, we scan
599     // all the children of the dummy's parent
600     Node* next;
601     for (Node* node = dummySpanAncestor->firstChild(); node; node = next) {
602         next = node->nextSibling();
603         if (isUnstyledStyleSpan(node))
604             removeNodePreservingChildren(node);
605         node = next;
606     }
607 }
608
609 HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool before, RefPtr<CSSPrimitiveValue> allowedDirection)
610 {
611     // We are allowed to leave the highest ancestor with unicode-bidi unsplit if it is unicode-bidi: embed and direction: allowedDirection.
612     // In that case, we return the unsplit ancestor. Otherwise, we return 0.
613     Node* block = enclosingBlock(node);
614     if (!block)
615         return 0;
616
617     Node* highestAncestorWithUnicodeBidi = 0;
618     Node* nextHighestAncestorWithUnicodeBidi = 0;
619     RefPtr<CSSPrimitiveValue> highestAncestorUnicodeBidi;
620     for (Node* n = node->parent(); n != block; n = n->parent()) {
621         RefPtr<CSSValue> unicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
622         if (unicodeBidi) {
623             ASSERT(unicodeBidi->isPrimitiveValue());
624             if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() != CSSValueNormal) {
625                 highestAncestorUnicodeBidi = static_cast<CSSPrimitiveValue*>(unicodeBidi.get());
626                 nextHighestAncestorWithUnicodeBidi = highestAncestorWithUnicodeBidi;
627                 highestAncestorWithUnicodeBidi = n;
628             }
629         }
630     }
631
632     if (!highestAncestorWithUnicodeBidi)
633         return 0;
634
635     HTMLElement* unsplitAncestor = 0;
636
637     if (allowedDirection && highestAncestorUnicodeBidi->getIdent() != CSSValueBidiOverride) {
638         RefPtr<CSSValue> highestAncestorDirection = computedStyle(highestAncestorWithUnicodeBidi)->getPropertyCSSValue(CSSPropertyDirection);
639         ASSERT(highestAncestorDirection->isPrimitiveValue());
640         if (static_cast<CSSPrimitiveValue*>(highestAncestorDirection.get())->getIdent() == allowedDirection->getIdent() && highestAncestorWithUnicodeBidi->isHTMLElement()) {
641             if (!nextHighestAncestorWithUnicodeBidi)
642                 return static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi);
643
644             unsplitAncestor = static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi);
645             highestAncestorWithUnicodeBidi = nextHighestAncestorWithUnicodeBidi;
646         }
647     }
648
649     // Split every ancestor through highest ancestor with embedding.
650     Node* n = node;
651     while (true) {
652         Element* parent = static_cast<Element*>(n->parent());
653         if (before ? n->previousSibling() : n->nextSibling())
654             splitElement(parent, before ? n : n->nextSibling());
655         if (parent == highestAncestorWithUnicodeBidi)
656             break;
657         n = n->parent();
658     }
659     return unsplitAncestor;
660 }
661
662 void ApplyStyleCommand::removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsplitAncestor)
663 {
664     Node* block = enclosingBlock(node);
665     if (!block)
666         return;
667
668     Node* n = node->parent();
669     while (n != block && n != unsplitAncestor) {
670         Node* parent = n->parent();
671         if (!n->isStyledElement()) {
672             n = parent;
673             continue;
674         }
675
676         StyledElement* element = static_cast<StyledElement*>(n);
677         RefPtr<CSSValue> unicodeBidi = computedStyle(element)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
678         if (unicodeBidi) {
679             ASSERT(unicodeBidi->isPrimitiveValue());
680             if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() != CSSValueNormal) {
681                 // FIXME: This code should really consider the mapped attribute 'dir', the inline style declaration,
682                 // and all matching style rules in order to determine how to best set the unicode-bidi property to 'normal'.
683                 // For now, it assumes that if the 'dir' attribute is present, then removing it will suffice, and
684                 // otherwise it sets the property in the inline style declaration.
685                 if (element->hasAttribute(dirAttr)) {
686                     // FIXME: If this is a BDO element, we should probably just remove it if it has no
687                     // other attributes, like we (should) do with B and I elements.
688                     removeNodeAttribute(element, dirAttr);
689                 } else {
690                     RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
691                     inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal);
692                     inlineStyle->removeProperty(CSSPropertyDirection);
693                     setNodeAttribute(element, styleAttr, inlineStyle->cssText());
694                     // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
695                     if (isUnstyledStyleSpan(element))
696                         removeNodePreservingChildren(element);
697                 }
698             }
699         }
700         n = parent;
701     }
702 }
703
704 void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style)
705 {
706     Node* startDummySpanAncestor = 0;
707     Node* endDummySpanAncestor = 0;
708     
709     // update document layout once before removing styles
710     // so that we avoid the expense of updating before each and every call
711     // to check a computed style
712     updateLayout();
713
714     // adjust to the positions we want to use for applying style
715     Position start = startPosition();
716     Position end = endPosition();
717     if (Range::compareBoundaryPoints(end, start) < 0) {
718         Position swap = start;
719         start = end;
720         end = swap;
721     }
722
723     // split the start node and containing element if the selection starts inside of it
724     bool splitStart = splitTextElementAtStartIfNeeded(start, end); 
725     if (splitStart) {
726         start = startPosition();
727         end = endPosition();
728         startDummySpanAncestor = dummySpanAncestorForNode(start.node());
729     }
730
731     // split the end node and containing element if the selection ends inside of it
732     bool splitEnd = splitTextElementAtEndIfNeeded(start, end);
733     if (splitEnd) {
734         start = startPosition();
735         end = endPosition();
736         endDummySpanAncestor = dummySpanAncestorForNode(end.node());
737     }
738
739     RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
740     RefPtr<CSSValue> direction;
741     HTMLElement* startUnsplitAncestor = 0;
742     HTMLElement* endUnsplitAncestor = 0;
743     if (unicodeBidi) {
744         RefPtr<CSSPrimitiveValue> allowedDirection;
745         ASSERT(unicodeBidi->isPrimitiveValue());
746         if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() == CSSValueEmbed) {
747             // Leave alone an ancestor that provides the desired single level embedding, if there is one.
748             direction = style->getPropertyCSSValue(CSSPropertyDirection);
749             ASSERT(direction->isPrimitiveValue());
750             allowedDirection = static_cast<CSSPrimitiveValue*>(direction.get());
751         }
752         startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.node(), true, allowedDirection);
753         endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.node(), false, allowedDirection);
754         removeEmbeddingUpToEnclosingBlock(start.node(), startUnsplitAncestor);
755         removeEmbeddingUpToEnclosingBlock(end.node(), endUnsplitAncestor);
756     }
757
758     // Remove style from the selection.
759     // Use the upstream position of the start for removing style.
760     // This will ensure we remove all traces of the relevant styles from the selection
761     // and prevent us from adding redundant ones, as described in:
762     // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
763     Position removeStart = start.upstream();
764     Position embeddingRemoveStart = removeStart;
765     Position embeddingRemoveEnd = end;
766     if (unicodeBidi) {
767         // Avoid removing the dir attribute and the unicode-bidi and direction properties from the unsplit ancestors.
768         if (startUnsplitAncestor && nodeFullySelected(startUnsplitAncestor, removeStart, end))
769             embeddingRemoveStart = positionAfterNode(startUnsplitAncestor);
770         if (endUnsplitAncestor && nodeFullySelected(endUnsplitAncestor, removeStart, end))
771             embeddingRemoveEnd = positionBeforeNode(endUnsplitAncestor).downstream();
772     }
773
774     if (embeddingRemoveStart != removeStart || embeddingRemoveEnd != end) {
775         RefPtr<CSSMutableStyleDeclaration> embeddingStyle = CSSMutableStyleDeclaration::create();
776         embeddingStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed);
777         embeddingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
778         if (Range::compareBoundaryPoints(embeddingRemoveStart, embeddingRemoveEnd) <= 0)
779             removeInlineStyle(embeddingStyle, embeddingRemoveStart, embeddingRemoveEnd);
780
781         RefPtr<CSSMutableStyleDeclaration> styleWithoutEmbedding = style->copy();
782         styleWithoutEmbedding->removeProperty(CSSPropertyUnicodeBidi);
783         styleWithoutEmbedding->removeProperty(CSSPropertyDirection);
784         removeInlineStyle(styleWithoutEmbedding, removeStart, end);
785    } else
786         removeInlineStyle(style, removeStart, end);
787
788     start = startPosition();
789     end = endPosition();
790
791     if (splitStart) {
792         bool mergedStart = mergeStartWithPreviousIfIdentical(start, end);
793         if (mergedStart) {
794             start = startPosition();
795             end = endPosition();
796         }
797     }
798
799     if (splitEnd) {
800         mergeEndWithNextIfIdentical(start, end);
801         start = startPosition();
802         end = endPosition();
803     }
804
805     // update document layout once before running the rest of the function
806     // so that we avoid the expense of updating before each and every call
807     // to check a computed style
808     updateLayout();
809
810     Position embeddingApplyStart = start;
811     Position embeddingApplyEnd = end;
812     if (unicodeBidi) {
813         // Avoid applying the unicode-bidi and direction properties beneath ancestors that already have them.
814         Node* startEnclosingBlock = enclosingBlock(start.node());
815         for (Node* n = start.node(); n != startEnclosingBlock; n = n->parent()) {
816             if (n->isHTMLElement()) {
817                 RefPtr<CSSValue> ancestorUnicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
818                 if (ancestorUnicodeBidi) {
819                     ASSERT(ancestorUnicodeBidi->isPrimitiveValue());
820                     if (static_cast<CSSPrimitiveValue*>(ancestorUnicodeBidi.get())->getIdent() == CSSValueEmbed) {
821                         embeddingApplyStart = positionAfterNode(n);
822                         break;
823                     }
824                 }
825             }
826         }
827
828         Node* endEnclosingBlock = enclosingBlock(end.node());
829         for (Node* n = end.node(); n != endEnclosingBlock; n = n->parent()) {
830             if (n->isHTMLElement()) {
831                 RefPtr<CSSValue> ancestorUnicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
832                 if (ancestorUnicodeBidi) {
833                     ASSERT(ancestorUnicodeBidi->isPrimitiveValue());
834                     if (static_cast<CSSPrimitiveValue*>(ancestorUnicodeBidi.get())->getIdent() == CSSValueEmbed) {
835                         embeddingApplyEnd = positionBeforeNode(n);
836                         break;
837                     }
838                 }
839             }
840         }
841     }
842
843     if (embeddingApplyStart != start || embeddingApplyEnd != end) {
844         if (embeddingApplyStart.isNotNull() && embeddingApplyEnd.isNotNull()) {
845             RefPtr<CSSMutableStyleDeclaration> embeddingStyle = CSSMutableStyleDeclaration::create();
846             embeddingStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed);
847             embeddingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
848             applyInlineStyleToRange(embeddingStyle.get(), embeddingApplyStart, embeddingApplyEnd);
849         }
850
851         RefPtr<CSSMutableStyleDeclaration> styleWithoutEmbedding = style->copy();
852         styleWithoutEmbedding->removeProperty(CSSPropertyUnicodeBidi);
853         styleWithoutEmbedding->removeProperty(CSSPropertyDirection);
854         applyInlineStyleToRange(styleWithoutEmbedding.get(), start, end);
855    } else
856         applyInlineStyleToRange(style, start, end);
857
858     // Remove dummy style spans created by splitting text elements.
859     cleanupUnstyledAppleStyleSpans(startDummySpanAncestor);
860     if (endDummySpanAncestor != startDummySpanAncestor)
861         cleanupUnstyledAppleStyleSpans(endDummySpanAncestor);
862 }
863
864 void ApplyStyleCommand::applyInlineStyleToRange(CSSMutableStyleDeclaration* style, const Position& start, const Position& rangeEnd)
865 {
866     Node* node = start.node();
867     Position end = rangeEnd;
868
869     bool rangeIsEmpty = false;
870
871     if (start.offset() >= caretMaxOffset(start.node())) {
872         node = node->traverseNextNode();
873         Position newStart = Position(node, 0);
874         if (Range::compareBoundaryPoints(end, newStart) < 0)
875             rangeIsEmpty = true;
876     }
877
878     if (!rangeIsEmpty) {
879         // pastEndNode is the node after the last fully selected node.
880         Node* pastEndNode = end.node();
881         if (end.offset() >= caretMaxOffset(end.node()))
882             pastEndNode = end.node()->traverseNextSibling();
883         // FIXME: Callers should perform this operation on a Range that includes the br
884         // if they want style applied to the empty line.
885         if (start == end && start.node()->hasTagName(brTag))
886             pastEndNode = start.node()->traverseNextNode();
887         // Add the style to selected inline runs.
888         for (Node* next; node && node != pastEndNode; node = next) {
889             
890             next = node->traverseNextNode();
891             
892             if (!node->renderer() || !node->isContentEditable())
893                 continue;
894             
895             if (!node->isContentRichlyEditable() && node->isHTMLElement()) {
896                 // This is a plaintext-only region. Only proceed if it's fully selected.
897                 // pastEndNode is the node after the last fully selected node, so if it's inside node then
898                 // node isn't fully selected.
899                 if (pastEndNode->isDescendantOf(node))
900                     break;
901                 // Add to this element's inline style and skip over its contents.
902                 HTMLElement* element = static_cast<HTMLElement*>(node);
903                 RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
904                 inlineStyle->merge(style);
905                 setNodeAttribute(element, styleAttr, inlineStyle->cssText());
906                 next = node->traverseNextSibling();
907                 continue;
908             }
909         
910             if (isBlock(node))
911                 continue;
912                 
913             if (node->childNodeCount()) {
914                 if (editingIgnoresContent(node)) {
915                     next = node->traverseNextSibling();
916                     continue;
917                 }
918                 continue;
919             }
920             
921             Node* runStart = node;
922             // Find the end of the run.
923             Node* sibling = node->nextSibling();
924             while (sibling && sibling != pastEndNode && (!sibling->isElementNode() || sibling->hasTagName(brTag)) && !isBlock(sibling)) {
925                 node = sibling;
926                 sibling = node->nextSibling();
927             }
928             // Recompute next, since node has changed.
929             next = node->traverseNextNode();
930             // Apply the style to the run.
931             addInlineStyleIfNeeded(style, runStart, node);
932         }
933     }
934 }
935
936 // This function maps from styling tags to CSS styles.  Used for knowing which
937 // styling tags should be removed when toggling styles.
938 bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclaration* style, HTMLElement* elem)
939 {
940     CSSMutableStyleDeclaration::const_iterator end = style->end();
941     for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
942         switch ((*it).id()) {
943         case CSSPropertyFontWeight:
944             // IE inserts "strong" tags for execCommand("bold"), so we remove them, even though they're not strictly presentational
945             if (elem->hasLocalName(bTag) || elem->hasLocalName(strongTag))
946                 return true;
947             break;
948         case CSSPropertyVerticalAlign:
949             if (elem->hasLocalName(subTag) || elem->hasLocalName(supTag))
950                 return true;
951             break;
952         case CSSPropertyFontStyle:
953             // IE inserts "em" tags for execCommand("italic"), so we remove them, even though they're not strictly presentational
954             if (elem->hasLocalName(iTag) || elem->hasLocalName(emTag))
955                 return true;
956         }
957     }
958
959     return false;
960 }
961
962 void ApplyStyleCommand::removeHTMLStyleNode(HTMLElement *elem)
963 {
964     // This node can be removed.
965     // EDIT FIXME: This does not handle the case where the node
966     // has attributes. But how often do people add attributes to <B> tags? 
967     // Not so often I think.
968     ASSERT(elem);
969     removeNodePreservingChildren(elem);
970 }
971
972 void ApplyStyleCommand::removeHTMLFontStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem)
973 {
974     ASSERT(style);
975     ASSERT(elem);
976
977     if (!elem->hasLocalName(fontTag))
978         return;
979         
980     CSSMutableStyleDeclaration::const_iterator end = style->end();
981     for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
982         switch ((*it).id()) {
983             case CSSPropertyColor:
984                 removeNodeAttribute(elem, colorAttr);
985                 break;
986             case CSSPropertyFontFamily:
987                 removeNodeAttribute(elem, faceAttr);
988                 break;
989             case CSSPropertyFontSize:
990                 removeNodeAttribute(elem, sizeAttr);
991                 break;
992         }
993     }
994
995     if (isEmptyFontTag(elem))
996         removeNodePreservingChildren(elem);
997 }
998
999 void ApplyStyleCommand::removeHTMLBidiEmbeddingStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem)
1000 {
1001     ASSERT(style);
1002     ASSERT(elem);
1003
1004     if (!elem->hasAttribute(dirAttr))
1005         return;
1006
1007     if (!style->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->getPropertyCSSValue(CSSPropertyDirection))
1008         return;
1009
1010     removeNodeAttribute(elem, dirAttr);
1011
1012     // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
1013     if (isUnstyledStyleSpan(elem))
1014         removeNodePreservingChildren(elem);
1015 }
1016
1017 void ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclaration* style, HTMLElement* elem)
1018 {
1019     ASSERT(style);
1020     ASSERT(elem);
1021
1022     CSSMutableStyleDeclaration* decl = elem->inlineStyleDecl();
1023     if (!decl)
1024         return;
1025
1026     CSSMutableStyleDeclaration::const_iterator end = style->end();
1027     for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
1028         CSSPropertyID propertyID = static_cast<CSSPropertyID>((*it).id());
1029         RefPtr<CSSValue> value = decl->getPropertyCSSValue(propertyID);
1030         if (value && (propertyID != CSSPropertyWhiteSpace || !isTabSpanNode(elem))) {
1031             removeCSSProperty(decl, propertyID);
1032             if (propertyID == CSSPropertyUnicodeBidi && !decl->getPropertyValue(CSSPropertyDirection).isEmpty())
1033                 removeCSSProperty(decl, CSSPropertyDirection);
1034         }
1035     }
1036
1037     // No need to serialize <foo style=""> if we just removed the last css property
1038     if (decl->length() == 0)
1039         removeNodeAttribute(elem, styleAttr);
1040
1041     if (isSpanWithoutAttributesOrUnstyleStyleSpan(elem))
1042         removeNodePreservingChildren(elem);
1043 }
1044
1045 static bool hasTextDecorationProperty(Node *node)
1046 {
1047     if (!node->isElementNode())
1048         return false;
1049
1050     RefPtr<CSSValue> value = computedStyle(node)->getPropertyCSSValue(CSSPropertyTextDecoration, DoNotUpdateLayout);
1051     return value && !equalIgnoringCase(value->cssText(), "none");
1052 }
1053
1054 static Node* highestAncestorWithTextDecoration(Node *node)
1055 {
1056     Node *result = NULL;
1057
1058     for (Node *n = node; n; n = n->parentNode()) {
1059         if (hasTextDecorationProperty(n))
1060             result = n;
1061     }
1062
1063     return result;
1064 }
1065
1066 PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractTextDecorationStyle(Node* node)
1067 {
1068     ASSERT(node);
1069     ASSERT(node->isElementNode());
1070     
1071     // non-html elements not handled yet
1072     if (!node->isHTMLElement())
1073         return 0;
1074
1075     HTMLElement *element = static_cast<HTMLElement *>(node);
1076     RefPtr<CSSMutableStyleDeclaration> style = element->inlineStyleDecl();
1077     if (!style)
1078         return 0;
1079
1080     int properties[1] = { CSSPropertyTextDecoration };
1081     RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = style->copyPropertiesInSet(properties, 1);
1082
1083     RefPtr<CSSValue> property = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1084     if (property && !equalIgnoringCase(property->cssText(), "none"))
1085         removeCSSProperty(style.get(), CSSPropertyTextDecoration);
1086
1087     return textDecorationStyle.release();
1088 }
1089
1090 PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractAndNegateTextDecorationStyle(Node* node)
1091 {
1092     ASSERT(node);
1093     ASSERT(node->isElementNode());
1094     
1095     // non-html elements not handled yet
1096     if (!node->isHTMLElement())
1097         return 0;
1098
1099     RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node);
1100     ASSERT(nodeStyle);
1101
1102     int properties[1] = { CSSPropertyTextDecoration };
1103     RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = nodeStyle->copyPropertiesInSet(properties, 1);
1104
1105     RefPtr<CSSValue> property = nodeStyle->getPropertyCSSValue(CSSPropertyTextDecoration);
1106     if (property && !equalIgnoringCase(property->cssText(), "none")) {
1107         RefPtr<CSSMutableStyleDeclaration> newStyle = textDecorationStyle->copy();
1108         newStyle->setProperty(CSSPropertyTextDecoration, "none");
1109         applyTextDecorationStyle(node, newStyle.get());
1110     }
1111
1112     return textDecorationStyle.release();
1113 }
1114
1115 void ApplyStyleCommand::applyTextDecorationStyle(Node *node, CSSMutableStyleDeclaration *style)
1116 {
1117     ASSERT(node);
1118
1119     if (!style || !style->cssText().length())
1120         return;
1121
1122     if (node->isTextNode()) {
1123         RefPtr<HTMLElement> styleSpan = createStyleSpanElement(document());
1124         surroundNodeRangeWithElement(node, node, styleSpan.get());
1125         node = styleSpan.get();
1126     }
1127
1128     if (!node->isElementNode())
1129         return;
1130
1131     HTMLElement *element = static_cast<HTMLElement *>(node);
1132         
1133     StyleChange styleChange(style, Position(element, 0));
1134     if (styleChange.cssStyle().length() > 0) {
1135         String cssText = styleChange.cssStyle();
1136         CSSMutableStyleDeclaration *decl = element->inlineStyleDecl();
1137         if (decl)
1138             cssText += decl->cssText();
1139         setNodeAttribute(element, styleAttr, cssText);
1140     }
1141 }
1142
1143 void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node* node, bool force)
1144 {
1145     Node *highestAncestor = highestAncestorWithTextDecoration(node);
1146     
1147     if (highestAncestor) {
1148         Node *nextCurrent;
1149         Node *nextChild;
1150         for (Node *current = highestAncestor; current != node; current = nextCurrent) {
1151             ASSERT(current);
1152             
1153             nextCurrent = NULL;
1154             
1155             RefPtr<CSSMutableStyleDeclaration> decoration = force ? extractAndNegateTextDecorationStyle(current) : extractTextDecorationStyle(current);
1156
1157             for (Node *child = current->firstChild(); child; child = nextChild) {
1158                 nextChild = child->nextSibling();
1159
1160                 if (node == child) {
1161                     nextCurrent = child;
1162                 } else if (node->isDescendantOf(child)) {
1163                     applyTextDecorationStyle(child, decoration.get());
1164                     nextCurrent = child;
1165                 } else {
1166                     applyTextDecorationStyle(child, decoration.get());
1167                 }
1168             }
1169         }
1170     }
1171 }
1172
1173 void ApplyStyleCommand::pushDownTextDecorationStyleAtBoundaries(const Position &start, const Position &end)
1174 {
1175     // We need to work in two passes. First we push down any inline
1176     // styles that set text decoration. Then we look for any remaining
1177     // styles (caused by stylesheets) and explicitly negate text
1178     // decoration while pushing down.
1179
1180     pushDownTextDecorationStyleAroundNode(start.node(), false);
1181     updateLayout();
1182     pushDownTextDecorationStyleAroundNode(start.node(), true);
1183
1184     pushDownTextDecorationStyleAroundNode(end.node(), false);
1185     updateLayout();
1186     pushDownTextDecorationStyleAroundNode(end.node(), true);
1187 }
1188
1189 static int maxRangeOffset(Node *n)
1190 {
1191     if (n->offsetInCharacters())
1192         return n->maxCharacterOffset();
1193
1194     if (n->isElementNode())
1195         return n->childNodeCount();
1196
1197     return 1;
1198 }
1199
1200 void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration> style, const Position &start, const Position &end)
1201 {
1202     ASSERT(start.isNotNull());
1203     ASSERT(end.isNotNull());
1204     ASSERT(start.node()->inDocument());
1205     ASSERT(end.node()->inDocument());
1206     ASSERT(Range::compareBoundaryPoints(start, end) <= 0);
1207     
1208     RefPtr<CSSValue> textDecorationSpecialProperty = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1209
1210     if (textDecorationSpecialProperty) {
1211         pushDownTextDecorationStyleAtBoundaries(start.downstream(), end.upstream());
1212         style = style->copy();
1213         style->setProperty(CSSPropertyTextDecoration, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSSPropertyWebkitTextDecorationsInEffect));
1214     }
1215
1216     // The s and e variables store the positions used to set the ending selection after style removal
1217     // takes place. This will help callers to recognize when either the start node or the end node
1218     // are removed from the document during the work of this function.
1219     Position s = start;
1220     Position e = end;
1221
1222     Node *node = start.node();
1223     while (node) {
1224         Node *next = node->traverseNextNode();
1225         if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
1226             HTMLElement *elem = static_cast<HTMLElement *>(node);
1227             Node *prev = elem->traversePreviousNodePostOrder();
1228             Node *next = elem->traverseNextNode();
1229             if (m_styledInlineElement && elem->hasTagName(m_styledInlineElement->tagQName()))
1230                 removeNodePreservingChildren(elem);
1231             if (isHTMLStyleNode(style.get(), elem))
1232                 removeHTMLStyleNode(elem);
1233             else {
1234                 removeHTMLFontStyle(style.get(), elem);
1235                 removeHTMLBidiEmbeddingStyle(style.get(), elem);
1236                 removeCSSStyle(style.get(), elem);
1237             }
1238             if (!elem->inDocument()) {
1239                 if (s.node() == elem) {
1240                     // Since elem must have been fully selected, and it is at the start
1241                     // of the selection, it is clear we can set the new s offset to 0.
1242                     ASSERT(s.offset() <= caretMinOffset(s.node()));
1243                     s = Position(next, 0);
1244                 }
1245                 if (e.node() == elem) {
1246                     // Since elem must have been fully selected, and it is at the end
1247                     // of the selection, it is clear we can set the new e offset to
1248                     // the max range offset of prev.
1249                     ASSERT(e.offset() >= maxRangeOffset(e.node()));
1250                     e = Position(prev, maxRangeOffset(prev));
1251                 }
1252             }
1253         }
1254         if (node == end.node())
1255             break;
1256         node = next;
1257     }
1258     
1259     ASSERT(s.node()->inDocument());
1260     ASSERT(e.node()->inDocument());
1261     updateStartEnd(s, e);
1262 }
1263
1264 bool ApplyStyleCommand::nodeFullySelected(Node *node, const Position &start, const Position &end) const
1265 {
1266     ASSERT(node);
1267     ASSERT(node->isElementNode());
1268
1269     Position pos = Position(node, node->childNodeCount()).upstream();
1270     return Range::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 &&
1271         Range::compareBoundaryPoints(pos, end) <= 0;
1272 }
1273
1274 bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, const Position &end) const
1275 {
1276     ASSERT(node);
1277     ASSERT(node->isElementNode());
1278
1279     Position pos = Position(node, node->childNodeCount()).upstream();
1280     bool isFullyBeforeStart = Range::compareBoundaryPoints(pos, start) < 0;
1281     bool isFullyAfterEnd = Range::compareBoundaryPoints(node, 0, end.node(), end.offset()) > 0;
1282
1283     return isFullyBeforeStart || isFullyAfterEnd;
1284 }
1285
1286
1287 bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
1288 {
1289     if (start.node()->isTextNode() && start.offset() > caretMinOffset(start.node()) && start.offset() < caretMaxOffset(start.node())) {
1290         int endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
1291         Text *text = static_cast<Text *>(start.node());
1292         splitTextNode(text, start.offset());
1293         updateStartEnd(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment));
1294         return true;
1295     }
1296     return false;
1297 }
1298
1299 bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end)
1300 {
1301     if (end.node()->isTextNode() && end.offset() > caretMinOffset(end.node()) && end.offset() < caretMaxOffset(end.node())) {
1302         Text *text = static_cast<Text *>(end.node());
1303         splitTextNode(text, end.offset());
1304         
1305         Node *prevNode = text->previousSibling();
1306         ASSERT(prevNode);
1307         Node *startNode = start.node() == end.node() ? prevNode : start.node();
1308         ASSERT(startNode);
1309         updateStartEnd(Position(startNode, start.offset()), Position(prevNode, caretMaxOffset(prevNode)));
1310         return true;
1311     }
1312     return false;
1313 }
1314
1315 bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end)
1316 {
1317     if (start.node()->isTextNode() && start.offset() > caretMinOffset(start.node()) && start.offset() < caretMaxOffset(start.node())) {
1318         int endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
1319         Text *text = static_cast<Text *>(start.node());
1320         splitTextNodeContainingElement(text, start.offset());
1321
1322         updateStartEnd(Position(start.node()->parentNode(), start.node()->nodeIndex()), Position(end.node(), end.offset() - endOffsetAdjustment));
1323         return true;
1324     }
1325     return false;
1326 }
1327
1328 bool ApplyStyleCommand::splitTextElementAtEndIfNeeded(const Position &start, const Position &end)
1329 {
1330     if (end.node()->isTextNode() && end.offset() > caretMinOffset(end.node()) && end.offset() < caretMaxOffset(end.node())) {
1331         Text *text = static_cast<Text *>(end.node());
1332         splitTextNodeContainingElement(text, end.offset());
1333
1334         Node *prevNode = text->parent()->previousSibling()->lastChild();
1335         ASSERT(prevNode);
1336         Node *startNode = start.node() == end.node() ? prevNode : start.node();
1337         ASSERT(startNode);
1338         updateStartEnd(Position(startNode, start.offset()), Position(prevNode->parent(), prevNode->nodeIndex() + 1));
1339         return true;
1340     }
1341     return false;
1342 }
1343
1344 static bool areIdenticalElements(Node *first, Node *second)
1345 {
1346     // check that tag name and all attribute names and values are identical
1347
1348     if (!first->isElementNode())
1349         return false;
1350     
1351     if (!second->isElementNode())
1352         return false;
1353
1354     Element *firstElement = static_cast<Element *>(first);
1355     Element *secondElement = static_cast<Element *>(second);
1356     
1357     if (!firstElement->tagQName().matches(secondElement->tagQName()))
1358         return false;
1359
1360     NamedAttrMap *firstMap = firstElement->attributes();
1361     NamedAttrMap *secondMap = secondElement->attributes();
1362
1363     unsigned firstLength = firstMap->length();
1364
1365     if (firstLength != secondMap->length())
1366         return false;
1367
1368     for (unsigned i = 0; i < firstLength; i++) {
1369         Attribute *attribute = firstMap->attributeItem(i);
1370         Attribute *secondAttribute = secondMap->getAttributeItem(attribute->name());
1371
1372         if (!secondAttribute || attribute->value() != secondAttribute->value())
1373             return false;
1374     }
1375     
1376     return true;
1377 }
1378
1379 bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end)
1380 {
1381     Node *startNode = start.node();
1382     int startOffset = start.offset();
1383
1384     if (isAtomicNode(start.node())) {
1385         if (start.offset() != 0)
1386             return false;
1387
1388         // note: prior siblings could be unrendered elements. it's silly to miss the
1389         // merge opportunity just for that.
1390         if (start.node()->previousSibling())
1391             return false;
1392
1393         startNode = start.node()->parent();
1394         startOffset = 0;
1395     }
1396
1397     if (!startNode->isElementNode())
1398         return false;
1399
1400     if (startOffset != 0)
1401         return false;
1402
1403     Node *previousSibling = startNode->previousSibling();
1404
1405     if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
1406         Element *previousElement = static_cast<Element *>(previousSibling);
1407         Element *element = static_cast<Element *>(startNode);
1408         Node *startChild = element->firstChild();
1409         ASSERT(startChild);
1410         mergeIdenticalElements(previousElement, element);
1411
1412         int startOffsetAdjustment = startChild->nodeIndex();
1413         int endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0;
1414         updateStartEnd(Position(startNode, startOffsetAdjustment), Position(end.node(), end.offset() + endOffsetAdjustment)); 
1415         return true;
1416     }
1417
1418     return false;
1419 }
1420
1421 bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end)
1422 {
1423     Node *endNode = end.node();
1424     int endOffset = end.offset();
1425
1426     if (isAtomicNode(endNode)) {
1427         if (endOffset < caretMaxOffset(endNode))
1428             return false;
1429
1430         unsigned parentLastOffset = end.node()->parent()->childNodes()->length() - 1;
1431         if (end.node()->nextSibling())
1432             return false;
1433
1434         endNode = end.node()->parent();
1435         endOffset = parentLastOffset;
1436     }
1437
1438     if (!endNode->isElementNode() || endNode->hasTagName(brTag))
1439         return false;
1440
1441     Node *nextSibling = endNode->nextSibling();
1442
1443     if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
1444         Element *nextElement = static_cast<Element *>(nextSibling);
1445         Element *element = static_cast<Element *>(endNode);
1446         Node *nextChild = nextElement->firstChild();
1447
1448         mergeIdenticalElements(element, nextElement);
1449
1450         Node *startNode = start.node() == endNode ? nextElement : start.node();
1451         ASSERT(startNode);
1452
1453         int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
1454         updateStartEnd(Position(startNode, start.offset()), Position(nextElement, endOffset));
1455         return true;
1456     }
1457
1458     return false;
1459 }
1460
1461 void ApplyStyleCommand::surroundNodeRangeWithElement(Node* startNode, Node* endNode, PassRefPtr<Element> elementToInsert)
1462 {
1463     ASSERT(startNode);
1464     ASSERT(endNode);
1465     ASSERT(elementToInsert);
1466     RefPtr<Element> element = elementToInsert;
1467
1468     insertNodeBefore(element, startNode);
1469     
1470     Node* node = startNode;
1471     while (1) {
1472         Node* next = node->traverseNextNode();
1473         if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
1474             removeNode(node);
1475             appendNode(node, element);
1476         }
1477         if (node == endNode)
1478             break;
1479         node = next;
1480     }
1481     // FIXME: We should probably call updateStartEnd if the start or end was in the node
1482     // range so that the endingSelection() is canonicalized.  See the comments at the end of
1483     // VisibleSelection::validate().
1484 }
1485
1486 void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElement* block)
1487 {
1488     // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
1489     // inline content.
1490     if (!block)
1491         return;
1492         
1493     String cssText = styleChange.cssStyle();
1494     CSSMutableStyleDeclaration* decl = block->inlineStyleDecl();
1495     if (decl)
1496         cssText += decl->cssText();
1497     setNodeAttribute(block, styleAttr, cssText);
1498 }
1499
1500 static bool fontColorChangesComputedStyle(RenderStyle* computedStyle, StyleChange styleChange)
1501 {
1502     if (styleChange.applyFontColor()) {
1503         if (Color(styleChange.fontColor()) != computedStyle->color())
1504             return true;
1505     }
1506     return false;
1507 }
1508
1509 static bool fontSizeChangesComputedStyle(RenderStyle* computedStyle, StyleChange styleChange)
1510 {
1511     if (styleChange.applyFontSize()) {
1512         if (styleChange.fontSize().toInt() != computedStyle->fontSize())
1513             return true;
1514     }
1515     return false;
1516 }
1517
1518 static bool fontFaceChangesComputedStyle(RenderStyle* computedStyle, StyleChange styleChange)
1519 {
1520     if (styleChange.applyFontFace()) {
1521         if (computedStyle->fontDescription().family().family().string() != styleChange.fontFace())
1522             return true;
1523     }
1524     return false;
1525 }
1526
1527 void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *startNode, Node *endNode)
1528 {
1529     if (m_removeOnly)
1530         return;
1531
1532     StyleChange styleChange(style, Position(startNode, 0));
1533
1534     //
1535     // Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
1536     //
1537     if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) {
1538         RefPtr<Element> fontElement = createFontElement(document());
1539         RenderStyle* computedStyle = startNode->computedStyle();
1540
1541         // We only want to insert a font element if it will end up changing the style of the
1542         // text somehow. Otherwise it will be a garbage node that will create problems for us
1543         // most notably when we apply a blockquote style for a message reply.
1544         if (fontColorChangesComputedStyle(computedStyle, styleChange)
1545                 || fontFaceChangesComputedStyle(computedStyle, styleChange)
1546                 || fontSizeChangesComputedStyle(computedStyle, styleChange)) {
1547             if (styleChange.applyFontColor())
1548                 fontElement->setAttribute(colorAttr, styleChange.fontColor());
1549             if (styleChange.applyFontFace())
1550                 fontElement->setAttribute(faceAttr, styleChange.fontFace());
1551             if (styleChange.applyFontSize())
1552                 fontElement->setAttribute(sizeAttr, styleChange.fontSize());
1553             surroundNodeRangeWithElement(startNode, endNode, fontElement.get());
1554         }
1555     }
1556
1557     if (styleChange.cssStyle().length() > 0) {
1558         RefPtr<Element> styleElement = createStyleSpanElement(document());
1559         styleElement->setAttribute(styleAttr, styleChange.cssStyle());
1560         surroundNodeRangeWithElement(startNode, endNode, styleElement.release());
1561     }
1562
1563     if (styleChange.applyBold())
1564         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), bTag));
1565
1566     if (styleChange.applyItalic())
1567         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), iTag));
1568
1569     if (styleChange.applySubscript())
1570         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), subTag));
1571     else if (styleChange.applySuperscript())
1572         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), supTag));
1573
1574     if (m_styledInlineElement)
1575         surroundNodeRangeWithElement(startNode, endNode, m_styledInlineElement->cloneElement());
1576 }
1577
1578 float ApplyStyleCommand::computedFontSize(const Node *node)
1579 {
1580     if (!node)
1581         return 0;
1582     
1583     Position pos(const_cast<Node *>(node), 0);
1584     RefPtr<CSSComputedStyleDeclaration> computedStyle = pos.computedStyle();
1585     if (!computedStyle)
1586         return 0;
1587
1588     RefPtr<CSSPrimitiveValue> value = static_pointer_cast<CSSPrimitiveValue>(computedStyle->getPropertyCSSValue(CSSPropertyFontSize));
1589     if (!value)
1590         return 0;
1591
1592     return value->getFloatValue(CSSPrimitiveValue::CSS_PX);
1593 }
1594
1595 void ApplyStyleCommand::joinChildTextNodes(Node *node, const Position &start, const Position &end)
1596 {
1597     if (!node)
1598         return;
1599
1600     Position newStart = start;
1601     Position newEnd = end;
1602     
1603     Node *child = node->firstChild();
1604     while (child) {
1605         Node *next = child->nextSibling();
1606         if (child->isTextNode() && next && next->isTextNode()) {
1607             Text *childText = static_cast<Text *>(child);
1608             Text *nextText = static_cast<Text *>(next);
1609             if (next == start.node())
1610                 newStart = Position(childText, childText->length() + start.offset());
1611             if (next == end.node())
1612                 newEnd = Position(childText, childText->length() + end.offset());
1613             String textToMove = nextText->data();
1614             insertTextIntoNode(childText, childText->length(), textToMove);
1615             removeNode(next);
1616             // don't move child node pointer. it may want to merge with more text nodes.
1617         }
1618         else {
1619             child = child->nextSibling();
1620         }
1621     }
1622
1623     updateStartEnd(newStart, newEnd);
1624 }
1625
1626 }