2011-02-22 Ryosuke Niwa <rniwa@webkit.org>
[WebKit-https.git] / Source / 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 "CSSMutableStyleDeclaration.h"
31 #include "CSSParser.h"
32 #include "CSSProperty.h"
33 #include "CSSPropertyNames.h"
34 #include "CSSStyleSelector.h"
35 #include "CSSValueKeywords.h"
36 #include "CSSValueList.h"
37 #include "Document.h"
38 #include "EditingStyle.h"
39 #include "Editor.h"
40 #include "Frame.h"
41 #include "HTMLFontElement.h"
42 #include "HTMLInterchange.h"
43 #include "HTMLNames.h"
44 #include "NodeList.h"
45 #include "Range.h"
46 #include "RenderObject.h"
47 #include "Text.h"
48 #include "TextIterator.h"
49 #include "htmlediting.h"
50 #include "visible_units.h"
51 #include <wtf/StdLibExtras.h>
52
53 namespace WebCore {
54
55 using namespace HTMLNames;
56
57 static RGBA32 getRGBAFontColor(CSSStyleDeclaration* style)
58 {
59     RefPtr<CSSValue> colorValue = style->getPropertyCSSValue(CSSPropertyColor);
60     if (!colorValue)
61         return Color::transparent;
62
63     ASSERT(colorValue->isPrimitiveValue());
64
65     CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue.get());
66     RGBA32 rgba = 0;
67     if (primitiveColor->primitiveType() != CSSPrimitiveValue::CSS_RGBCOLOR) {
68         CSSParser::parseColor(rgba, colorValue->cssText());
69         // Need to take care of named color such as green and black
70         // This code should be removed after https://bugs.webkit.org/show_bug.cgi?id=28282 is fixed.
71     } else
72         rgba = primitiveColor->getRGBA32Value();
73
74     return rgba;
75 }
76
77 class StyleChange {
78 public:
79     explicit StyleChange(CSSStyleDeclaration*, const Position&);
80
81     String cssStyle() const { return m_cssStyle; }
82     bool applyBold() const { return m_applyBold; }
83     bool applyItalic() const { return m_applyItalic; }
84     bool applyUnderline() const { return m_applyUnderline; }
85     bool applyLineThrough() const { return m_applyLineThrough; }
86     bool applySubscript() const { return m_applySubscript; }
87     bool applySuperscript() const { return m_applySuperscript; }
88     bool applyFontColor() const { return m_applyFontColor.length() > 0; }
89     bool applyFontFace() const { return m_applyFontFace.length() > 0; }
90     bool applyFontSize() const { return m_applyFontSize.length() > 0; }
91
92     String fontColor() { return m_applyFontColor; }
93     String fontFace() { return m_applyFontFace; }
94     String fontSize() { return m_applyFontSize; }
95
96     bool operator==(const StyleChange& other)
97     {
98         return m_cssStyle == other.m_cssStyle
99             && m_applyBold == other.m_applyBold
100             && m_applyItalic == other.m_applyItalic
101             && m_applyUnderline == other.m_applyUnderline
102             && m_applyLineThrough == other.m_applyLineThrough
103             && m_applySubscript == other.m_applySubscript
104             && m_applySuperscript == other.m_applySuperscript
105             && m_applyFontColor == other.m_applyFontColor
106             && m_applyFontFace == other.m_applyFontFace
107             && m_applyFontSize == other.m_applyFontSize;
108     }
109     bool operator!=(const StyleChange& other)
110     {
111         return !(*this == other);
112     }
113 private:
114     void init(PassRefPtr<CSSStyleDeclaration>, const Position&);
115     void reconcileTextDecorationProperties(CSSMutableStyleDeclaration*);
116     void extractTextStyles(Document*, CSSMutableStyleDeclaration*, bool shouldUseFixedFontDefaultSize);
117
118     String m_cssStyle;
119     bool m_applyBold;
120     bool m_applyItalic;
121     bool m_applyUnderline;
122     bool m_applyLineThrough;
123     bool m_applySubscript;
124     bool m_applySuperscript;
125     String m_applyFontColor;
126     String m_applyFontFace;
127     String m_applyFontSize;
128 };
129
130
131 StyleChange::StyleChange(CSSStyleDeclaration* style, const Position& position)
132     : m_applyBold(false)
133     , m_applyItalic(false)
134     , m_applyUnderline(false)
135     , m_applyLineThrough(false)
136     , m_applySubscript(false)
137     , m_applySuperscript(false)
138 {
139     init(style, position);
140 }
141
142 void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position& position)
143 {
144     Document* document = position.anchorNode() ? position.anchorNode()->document() : 0;
145     if (!style || !document || !document->frame())
146         return;
147
148     RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
149     RefPtr<CSSMutableStyleDeclaration> mutableStyle = getPropertiesNotIn(style.get(), computedStyle.get());
150
151     reconcileTextDecorationProperties(mutableStyle.get());
152     if (!document->frame()->editor()->shouldStyleWithCSS())
153         extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize());
154
155     // Changing the whitespace style in a tab span would collapse the tab into a space.
156     if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
157         mutableStyle->removeProperty(CSSPropertyWhiteSpace);
158
159     // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
160     // FIXME: Shouldn't this be done in getPropertiesNotIn?
161     if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->getPropertyCSSValue(CSSPropertyDirection))
162         mutableStyle->setProperty(CSSPropertyDirection, style->getPropertyValue(CSSPropertyDirection));
163
164     // Save the result for later
165     m_cssStyle = mutableStyle->cssText().stripWhiteSpace();
166 }
167
168 void StyleChange::reconcileTextDecorationProperties(CSSMutableStyleDeclaration* style)
169 {    
170     RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
171     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
172     // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
173     ASSERT(!textDecorationsInEffect || !textDecoration);
174     if (textDecorationsInEffect) {
175         style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
176         style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
177         textDecoration = textDecorationsInEffect;
178     }
179
180     // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
181     if (textDecoration && !textDecoration->isValueList())
182         style->removeProperty(CSSPropertyTextDecoration);
183 }
184
185 static int getIdentifierValue(CSSStyleDeclaration* style, int propertyID)
186 {
187     if (!style)
188         return 0;
189
190     RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
191     if (!value || !value->isPrimitiveValue())
192         return 0;
193
194     return static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
195 }
196
197 static void setTextDecorationProperty(CSSMutableStyleDeclaration* style, const CSSValueList* newTextDecoration, int propertyID)
198 {
199     if (newTextDecoration->length())
200         style->setProperty(propertyID, newTextDecoration->cssText(), style->getPropertyPriority(propertyID));
201     else {
202         // text-decoration: none is redundant since it does not remove any text decorations.
203         ASSERT(!style->getPropertyPriority(propertyID));
204         style->removeProperty(propertyID);
205     }
206 }
207
208 static bool isCSSValueLength(CSSPrimitiveValue* value)
209 {
210     return value->primitiveType() >= CSSPrimitiveValue::CSS_PX && value->primitiveType() <= CSSPrimitiveValue::CSS_PC;
211 }
212
213 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
214 {
215     if (isCSSValueLength(value)) {
216         int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
217         int legacyFontSize = CSSStyleSelector::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
218         // Use legacy font size only if pixel value matches exactly to that of legacy font size.
219         int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
220         if (mode == AlwaysUseLegacyFontSize || CSSStyleSelector::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
221             return legacyFontSize;
222
223         return 0;
224     }
225
226     if (CSSValueXSmall <= value->getIdent() && value->getIdent() <= CSSValueWebkitXxxLarge)
227         return value->getIdent() - CSSValueXSmall + 1;
228
229     return 0;
230 }
231
232 void StyleChange::extractTextStyles(Document* document, CSSMutableStyleDeclaration* style, bool shouldUseFixedFontDefaultSize)
233 {
234     ASSERT(style);
235
236     if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
237         style->removeProperty(CSSPropertyFontWeight);
238         m_applyBold = true;
239     }
240
241     int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
242     if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
243         style->removeProperty(CSSPropertyFontStyle);
244         m_applyItalic = true;
245     }
246
247     // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
248     // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
249     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
250     if (textDecoration && textDecoration->isValueList()) {
251         DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
252         DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
253
254         RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
255         if (newTextDecoration->removeAll(underline.get()))
256             m_applyUnderline = true;
257         if (newTextDecoration->removeAll(lineThrough.get()))
258             m_applyLineThrough = true;
259
260         // If trimTextDecorations, delete underline and line-through
261         setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
262     }
263
264     int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
265     switch (verticalAlign) {
266     case CSSValueSub:
267         style->removeProperty(CSSPropertyVerticalAlign);
268         m_applySubscript = true;
269         break;
270     case CSSValueSuper:
271         style->removeProperty(CSSPropertyVerticalAlign);
272         m_applySuperscript = true;
273         break;
274     }
275
276     if (style->getPropertyCSSValue(CSSPropertyColor)) {
277         m_applyFontColor = Color(getRGBAFontColor(style)).serialized();
278         style->removeProperty(CSSPropertyColor);
279     }
280
281     m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
282     style->removeProperty(CSSPropertyFontFamily);
283
284     if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
285         if (!fontSize->isPrimitiveValue())
286             style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
287         else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(fontSize.get()),
288                 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
289             m_applyFontSize = String::number(legacyFontSize);
290             style->removeProperty(CSSPropertyFontSize);
291         }
292     }
293 }
294
295 static String& styleSpanClassString()
296 {
297     DEFINE_STATIC_LOCAL(String, styleSpanClassString, ((AppleStyleSpanClass)));
298     return styleSpanClassString;
299 }
300
301 bool isStyleSpan(const Node *node)
302 {
303     if (!node || !node->isHTMLElement())
304         return false;
305
306     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
307     return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styleSpanClassString();
308 }
309
310 static bool isUnstyledStyleSpan(const Node* node)
311 {
312     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
313         return false;
314
315     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
316     CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
317     return (!inlineStyleDecl || inlineStyleDecl->isEmpty()) && elem->getAttribute(classAttr) == styleSpanClassString();
318 }
319
320 static bool isSpanWithoutAttributesOrUnstyleStyleSpan(const Node* node)
321 {
322     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
323         return false;
324
325     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
326     NamedNodeMap* attributes = elem->attributes(true); // readonly
327     if (attributes->isEmpty())
328         return true;
329
330     return isUnstyledStyleSpan(node);
331 }
332
333 static bool isEmptyFontTag(const Node *node)
334 {
335     if (!node || !node->hasTagName(fontTag))
336         return false;
337
338     const Element *elem = static_cast<const Element *>(node);
339     NamedNodeMap *map = elem->attributes(true); // true for read-only
340     if (!map)
341         return true;
342     return map->isEmpty() || (map->length() == 1 && elem->getAttribute(classAttr) == styleSpanClassString());
343 }
344
345 static PassRefPtr<Element> createFontElement(Document* document)
346 {
347     RefPtr<Element> fontNode = createHTMLElement(document, fontTag);
348     fontNode->setAttribute(classAttr, styleSpanClassString());
349     return fontNode.release();
350 }
351
352 PassRefPtr<HTMLElement> createStyleSpanElement(Document* document)
353 {
354     RefPtr<HTMLElement> styleElement = createHTMLElement(document, spanTag);
355     styleElement->setAttribute(classAttr, styleSpanClassString());
356     return styleElement.release();
357 }
358
359 static void diffTextDecorations(CSSMutableStyleDeclaration* style, int propertID, CSSValue* refTextDecoration)
360 {
361     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
362     if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
363         return;
364
365     RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
366     CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration);
367
368     for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
369         newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
370
371     setTextDecorationProperty(style, newTextDecoration.get(), propertID);
372 }
373
374 static bool fontWeightIsBold(CSSStyleDeclaration* style)
375 {
376     ASSERT(style);
377     RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight);
378
379     if (!fontWeight)
380         return false;
381     if (!fontWeight->isPrimitiveValue())
382         return false;
383
384     // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
385     // Collapse all other values to either one of these two states for editing purposes.
386     switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) {
387         case CSSValue100:
388         case CSSValue200:
389         case CSSValue300:
390         case CSSValue400:
391         case CSSValue500:
392         case CSSValueNormal:
393             return false;
394         case CSSValueBold:
395         case CSSValue600:
396         case CSSValue700:
397         case CSSValue800:
398         case CSSValue900:
399             return true;
400     }
401
402     ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
403     return false; // Make compiler happy
404 }
405
406 static int getTextAlignment(CSSStyleDeclaration* style)
407 {
408     int textAlign = getIdentifierValue(style, CSSPropertyTextAlign);
409     switch (textAlign) {
410     case CSSValueCenter:
411     case CSSValueWebkitCenter:
412         return CSSValueCenter;
413     case CSSValueJustify:
414         return CSSValueJustify;
415     case CSSValueLeft:
416     case CSSValueWebkitLeft:
417         return CSSValueLeft;
418     case CSSValueRight:
419     case CSSValueWebkitRight:
420         return CSSValueRight;
421     }
422     return CSSValueInvalid;
423 }
424
425 RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
426 {
427     ASSERT(styleWithRedundantProperties);
428     ASSERT(baseStyle);
429     RefPtr<CSSMutableStyleDeclaration> result = styleWithRedundantProperties->copy();
430     baseStyle->diff(result.get());
431
432     RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
433     diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
434     diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
435
436     if (fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
437         result->removeProperty(CSSPropertyFontWeight);
438
439     if (getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
440         result->removeProperty(CSSPropertyColor);
441
442     if (getTextAlignment(result.get()) == getTextAlignment(baseStyle))
443         result->removeProperty(CSSPropertyTextAlign);        
444
445     return result;
446 }
447
448 ApplyStyleCommand::ApplyStyleCommand(Document* document, const EditingStyle* style, EditAction editingAction, EPropertyLevel propertyLevel)
449     : CompositeEditCommand(document)
450     , m_style(style->copy())
451     , m_editingAction(editingAction)
452     , m_propertyLevel(propertyLevel)
453     , m_start(endingSelection().start().downstream())
454     , m_end(endingSelection().end().upstream())
455     , m_useEndingSelection(true)
456     , m_styledInlineElement(0)
457     , m_removeOnly(false)
458     , m_isInlineElementToRemoveFunction(0)
459 {
460 }
461
462 ApplyStyleCommand::ApplyStyleCommand(Document* document, const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction, EPropertyLevel propertyLevel)
463     : CompositeEditCommand(document)
464     , m_style(style->copy())
465     , m_editingAction(editingAction)
466     , m_propertyLevel(propertyLevel)
467     , m_start(start)
468     , m_end(end)
469     , m_useEndingSelection(false)
470     , m_styledInlineElement(0)
471     , m_removeOnly(false)
472     , m_isInlineElementToRemoveFunction(0)
473 {
474 }
475
476 ApplyStyleCommand::ApplyStyleCommand(PassRefPtr<Element> element, bool removeOnly, EditAction editingAction)
477     : CompositeEditCommand(element->document())
478     , m_style(EditingStyle::create())
479     , m_editingAction(editingAction)
480     , m_propertyLevel(PropertyDefault)
481     , m_start(endingSelection().start().downstream())
482     , m_end(endingSelection().end().upstream())
483     , m_useEndingSelection(true)
484     , m_styledInlineElement(element)
485     , m_removeOnly(removeOnly)
486     , m_isInlineElementToRemoveFunction(0)
487 {
488 }
489
490 ApplyStyleCommand::ApplyStyleCommand(Document* document, const EditingStyle* style, IsInlineElementToRemoveFunction isInlineElementToRemoveFunction, EditAction editingAction)
491     : CompositeEditCommand(document)
492     , m_style(style->copy())
493     , m_editingAction(editingAction)
494     , m_propertyLevel(PropertyDefault)
495     , m_start(endingSelection().start().downstream())
496     , m_end(endingSelection().end().upstream())
497     , m_useEndingSelection(true)
498     , m_styledInlineElement(0)
499     , m_removeOnly(true)
500     , m_isInlineElementToRemoveFunction(isInlineElementToRemoveFunction)
501 {
502 }
503
504 void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position& newEnd)
505 {
506     ASSERT(comparePositions(newEnd, newStart) >= 0);
507
508     if (!m_useEndingSelection && (newStart != m_start || newEnd != m_end))
509         m_useEndingSelection = true;
510
511     setEndingSelection(VisibleSelection(newStart, newEnd, VP_DEFAULT_AFFINITY));
512     m_start = newStart;
513     m_end = newEnd;
514 }
515
516 Position ApplyStyleCommand::startPosition()
517 {
518     if (m_useEndingSelection)
519         return endingSelection().start();
520     
521     return m_start;
522 }
523
524 Position ApplyStyleCommand::endPosition()
525 {
526     if (m_useEndingSelection)
527         return endingSelection().end();
528     
529     return m_end;
530 }
531
532 void ApplyStyleCommand::doApply()
533 {
534     switch (m_propertyLevel) {
535     case PropertyDefault: {
536         // Apply the block-centric properties of the style.
537         RefPtr<EditingStyle> blockStyle = m_style->extractAndRemoveBlockProperties();
538         if (!blockStyle->isEmpty())
539             applyBlockStyle(blockStyle.get());
540         // Apply any remaining styles to the inline elements.
541         if (!m_style->isEmpty() || m_styledInlineElement || m_isInlineElementToRemoveFunction) {
542             applyRelativeFontStyleChange(m_style.get());
543             applyInlineStyle(m_style.get());
544         }
545         break;
546     }
547     case ForceBlockProperties:
548         // Force all properties to be applied as block styles.
549         applyBlockStyle(m_style.get());
550         break;
551     }
552 }
553
554 EditAction ApplyStyleCommand::editingAction() const
555 {
556     return m_editingAction;
557 }
558
559 void ApplyStyleCommand::applyBlockStyle(EditingStyle *style)
560 {
561     // update document layout once before removing styles
562     // so that we avoid the expense of updating before each and every call
563     // to check a computed style
564     updateLayout();
565
566     // get positions we want to use for applying style
567     Position start = startPosition();
568     Position end = endPosition();
569     if (comparePositions(end, start) < 0) {
570         Position swap = start;
571         start = end;
572         end = swap;
573     }
574
575     VisiblePosition visibleStart(start);
576     VisiblePosition visibleEnd(end);
577
578     if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || visibleEnd.isOrphan())
579         return;
580
581     // Save and restore the selection endpoints using their indices in the document, since
582     // addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints.
583     // Calculate start and end indices from the start of the tree that they're in.
584     Node* scope = highestAncestor(visibleStart.deepEquivalent().deprecatedNode());
585     RefPtr<Range> startRange = Range::create(document(), firstPositionInNode(scope), visibleStart.deepEquivalent().parentAnchoredEquivalent());
586     RefPtr<Range> endRange = Range::create(document(), firstPositionInNode(scope), visibleEnd.deepEquivalent().parentAnchoredEquivalent());
587     int startIndex = TextIterator::rangeLength(startRange.get(), true);
588     int endIndex = TextIterator::rangeLength(endRange.get(), true);
589
590     VisiblePosition paragraphStart(startOfParagraph(visibleStart));
591     VisiblePosition nextParagraphStart(endOfParagraph(paragraphStart).next());
592     VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next());
593     while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) {
594         StyleChange styleChange(style->style(), paragraphStart.deepEquivalent());
595         if (styleChange.cssStyle().length() || m_removeOnly) {
596             RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().deprecatedNode());
597             if (!m_removeOnly) {
598                 RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent());
599                 if (newBlock)
600                     block = newBlock;
601             }
602             ASSERT(block->isHTMLElement());
603             if (block->isHTMLElement()) {
604                 removeCSSStyle(style, toHTMLElement(block.get()));
605                 if (!m_removeOnly)
606                     addBlockStyle(styleChange, toHTMLElement(block.get()));
607             }
608
609             if (nextParagraphStart.isOrphan())
610                 nextParagraphStart = endOfParagraph(paragraphStart).next();
611         }
612
613         paragraphStart = nextParagraphStart;
614         nextParagraphStart = endOfParagraph(paragraphStart).next();
615     }
616     
617     startRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), startIndex, 0, true);
618     endRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), endIndex, 0, true);
619     if (startRange && endRange)
620         updateStartEnd(startRange->startPosition(), endRange->startPosition());
621 }
622
623 void ApplyStyleCommand::applyRelativeFontStyleChange(EditingStyle* style)
624 {
625     static const float MinimumFontSize = 0.1f;
626
627     if (!style || !style->hasFontSizeDelta())
628         return;
629
630     Position start = startPosition();
631     Position end = endPosition();
632     if (comparePositions(end, start) < 0) {
633         Position swap = start;
634         start = end;
635         end = swap;
636     }
637
638     // Join up any adjacent text nodes.
639     if (start.deprecatedNode()->isTextNode()) {
640         joinChildTextNodes(start.deprecatedNode()->parentNode(), start, end);
641         start = startPosition();
642         end = endPosition();
643     }
644     if (end.deprecatedNode()->isTextNode() && start.deprecatedNode()->parentNode() != end.deprecatedNode()->parentNode()) {
645         joinChildTextNodes(end.deprecatedNode()->parentNode(), start, end);
646         start = startPosition();
647         end = endPosition();
648     }
649
650     // Split the start text nodes if needed to apply style.
651     if (isValidCaretPositionInTextNode(start)) {
652         splitTextAtStart(start, end);
653         start = startPosition();
654         end = endPosition();
655     }
656
657     if (isValidCaretPositionInTextNode(end)) {
658         splitTextAtEnd(start, end);
659         start = startPosition();
660         end = endPosition();
661     }
662
663     // Calculate loop end point.
664     // If the end node is before the start node (can only happen if the end node is
665     // an ancestor of the start node), we gather nodes up to the next sibling of the end node
666     Node *beyondEnd;
667     if (start.deprecatedNode()->isDescendantOf(end.deprecatedNode()))
668         beyondEnd = end.deprecatedNode()->traverseNextSibling();
669     else
670         beyondEnd = end.deprecatedNode()->traverseNextNode();
671     
672     start = start.upstream(); // Move upstream to ensure we do not add redundant spans.
673     Node* startNode = start.deprecatedNode();
674     if (startNode->isTextNode() && start.deprecatedEditingOffset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters.
675         startNode = startNode->traverseNextNode();
676
677     // Store away font size before making any changes to the document.
678     // This ensures that changes to one node won't effect another.
679     HashMap<Node*, float> startingFontSizes;
680     for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode())
681         startingFontSizes.set(node, computedFontSize(node));
682
683     // These spans were added by us. If empty after font size changes, they can be removed.
684     Vector<RefPtr<HTMLElement> > unstyledSpans;
685     
686     Node* lastStyledNode = 0;
687     for (Node* node = startNode; node != beyondEnd; node = node->traverseNextNode()) {
688         RefPtr<HTMLElement> element;
689         if (node->isHTMLElement()) {
690             // Only work on fully selected nodes.
691             if (!nodeFullySelected(node, start, end))
692                 continue;
693             element = toHTMLElement(node);
694         } else if (node->isTextNode() && node->renderer() && node->parentNode() != lastStyledNode) {
695             // Last styled node was not parent node of this text node, but we wish to style this
696             // text node. To make this possible, add a style span to surround this text node.
697             RefPtr<HTMLElement> span = createStyleSpanElement(document());
698             surroundNodeRangeWithElement(node, node, span.get());
699             element = span.release();
700         }  else {
701             // Only handle HTML elements and text nodes.
702             continue;
703         }
704         lastStyledNode = node;
705
706         CSSMutableStyleDeclaration* inlineStyleDecl = element->getInlineStyleDecl();
707         float currentFontSize = computedFontSize(node);
708         float desiredFontSize = max(MinimumFontSize, startingFontSizes.get(node) + style->fontSizeDelta());
709         RefPtr<CSSValue> value = inlineStyleDecl->getPropertyCSSValue(CSSPropertyFontSize);
710         if (value) {
711             inlineStyleDecl->removeProperty(CSSPropertyFontSize, true);
712             currentFontSize = computedFontSize(node);
713         }
714         if (currentFontSize != desiredFontSize) {
715             inlineStyleDecl->setProperty(CSSPropertyFontSize, String::number(desiredFontSize) + "px", false, false);
716             setNodeAttribute(element.get(), styleAttr, inlineStyleDecl->cssText());
717         }
718         if (inlineStyleDecl->isEmpty()) {
719             removeNodeAttribute(element.get(), styleAttr);
720             // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
721             if (isUnstyledStyleSpan(element.get()))
722                 unstyledSpans.append(element.release());
723         }
724     }
725
726     size_t size = unstyledSpans.size();
727     for (size_t i = 0; i < size; ++i)
728         removeNodePreservingChildren(unstyledSpans[i].get());
729 }
730
731 static Node* dummySpanAncestorForNode(const Node* node)
732 {
733     while (node && !isStyleSpan(node))
734         node = node->parentNode();
735     
736     return node ? node->parentNode() : 0;
737 }
738
739 void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor)
740 {
741     if (!dummySpanAncestor)
742         return;
743
744     // Dummy spans are created when text node is split, so that style information
745     // can be propagated, which can result in more splitting. If a dummy span gets
746     // cloned/split, the new node is always a sibling of it. Therefore, we scan
747     // all the children of the dummy's parent
748     Node* next;
749     for (Node* node = dummySpanAncestor->firstChild(); node; node = next) {
750         next = node->nextSibling();
751         if (isUnstyledStyleSpan(node))
752             removeNodePreservingChildren(node);
753         node = next;
754     }
755 }
756
757 HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool before, WritingDirection allowedDirection)
758 {
759     // We are allowed to leave the highest ancestor with unicode-bidi unsplit if it is unicode-bidi: embed and direction: allowedDirection.
760     // In that case, we return the unsplit ancestor. Otherwise, we return 0.
761     Node* block = enclosingBlock(node);
762     if (!block)
763         return 0;
764
765     Node* highestAncestorWithUnicodeBidi = 0;
766     Node* nextHighestAncestorWithUnicodeBidi = 0;
767     int highestAncestorUnicodeBidi = 0;
768     for (Node* n = node->parentNode(); n != block; n = n->parentNode()) {
769         int unicodeBidi = getIdentifierValue(computedStyle(n).get(), CSSPropertyUnicodeBidi);
770         if (unicodeBidi && unicodeBidi != CSSValueNormal) {
771             highestAncestorUnicodeBidi = unicodeBidi;
772             nextHighestAncestorWithUnicodeBidi = highestAncestorWithUnicodeBidi;
773             highestAncestorWithUnicodeBidi = n;
774         }
775     }
776
777     if (!highestAncestorWithUnicodeBidi)
778         return 0;
779
780     HTMLElement* unsplitAncestor = 0;
781
782     WritingDirection highestAncestorDirection;
783     if (allowedDirection != NaturalWritingDirection
784         && highestAncestorUnicodeBidi != CSSValueBidiOverride
785         && highestAncestorWithUnicodeBidi->isHTMLElement()
786         && EditingStyle::create(highestAncestorWithUnicodeBidi, EditingStyle::AllProperties)->textDirection(highestAncestorDirection)
787         && highestAncestorDirection == allowedDirection) {
788         if (!nextHighestAncestorWithUnicodeBidi)
789             return toHTMLElement(highestAncestorWithUnicodeBidi);
790
791         unsplitAncestor = toHTMLElement(highestAncestorWithUnicodeBidi);
792         highestAncestorWithUnicodeBidi = nextHighestAncestorWithUnicodeBidi;
793     }
794
795     // Split every ancestor through highest ancestor with embedding.
796     Node* n = node;
797     while (true) {
798         Element* parent = static_cast<Element*>(n->parentNode());
799         if (before ? n->previousSibling() : n->nextSibling())
800             splitElement(parent, before ? n : n->nextSibling());
801         if (parent == highestAncestorWithUnicodeBidi)
802             break;
803         n = n->parentNode();
804     }
805     return unsplitAncestor;
806 }
807
808 void ApplyStyleCommand::removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsplitAncestor)
809 {
810     Node* block = enclosingBlock(node);
811     if (!block)
812         return;
813
814     Node* parent = 0;
815     for (Node* n = node->parentNode(); n != block && n != unsplitAncestor; n = parent) {
816         parent = n->parentNode();
817         if (!n->isStyledElement())
818             continue;
819
820         StyledElement* element = static_cast<StyledElement*>(n);
821         int unicodeBidi = getIdentifierValue(computedStyle(element).get(), CSSPropertyUnicodeBidi);
822         if (!unicodeBidi || unicodeBidi == CSSValueNormal)
823             continue;
824
825         // FIXME: This code should really consider the mapped attribute 'dir', the inline style declaration,
826         // and all matching style rules in order to determine how to best set the unicode-bidi property to 'normal'.
827         // For now, it assumes that if the 'dir' attribute is present, then removing it will suffice, and
828         // otherwise it sets the property in the inline style declaration.
829         if (element->hasAttribute(dirAttr)) {
830             // FIXME: If this is a BDO element, we should probably just remove it if it has no
831             // other attributes, like we (should) do with B and I elements.
832             removeNodeAttribute(element, dirAttr);
833         } else {
834             RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
835             inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal);
836             inlineStyle->removeProperty(CSSPropertyDirection);
837             setNodeAttribute(element, styleAttr, inlineStyle->cssText());
838             // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
839             if (isUnstyledStyleSpan(element))
840                 removeNodePreservingChildren(element);
841         }
842     }
843 }
844
845 static Node* highestEmbeddingAncestor(Node* startNode, Node* enclosingNode)
846 {
847     for (Node* n = startNode; n && n != enclosingNode; n = n->parentNode()) {
848         if (n->isHTMLElement() && getIdentifierValue(computedStyle(n).get(), CSSPropertyUnicodeBidi) == CSSValueEmbed)
849             return n;
850     }
851
852     return 0;
853 }
854
855 void ApplyStyleCommand::applyInlineStyle(EditingStyle* style)
856 {
857     Node* startDummySpanAncestor = 0;
858     Node* endDummySpanAncestor = 0;
859
860     style->collapseTextDecorationProperties();
861
862     // update document layout once before removing styles
863     // so that we avoid the expense of updating before each and every call
864     // to check a computed style
865     updateLayout();
866
867     // adjust to the positions we want to use for applying style
868     Position start = startPosition();
869     Position end = endPosition();
870     if (comparePositions(end, start) < 0) {
871         Position swap = start;
872         start = end;
873         end = swap;
874     }
875
876     // split the start node and containing element if the selection starts inside of it
877     bool splitStart = isValidCaretPositionInTextNode(start);
878     if (splitStart) {
879         if (shouldSplitTextElement(start.deprecatedNode()->parentElement(), style))
880             splitTextElementAtStart(start, end);
881         else
882             splitTextAtStart(start, end);
883         start = startPosition();
884         end = endPosition();
885         startDummySpanAncestor = dummySpanAncestorForNode(start.deprecatedNode());
886     }
887
888     // split the end node and containing element if the selection ends inside of it
889     bool splitEnd = isValidCaretPositionInTextNode(end);
890     if (splitEnd) {
891         if (shouldSplitTextElement(end.deprecatedNode()->parentElement(), style))
892             splitTextElementAtEnd(start, end);
893         else
894             splitTextAtEnd(start, end);
895         start = startPosition();
896         end = endPosition();
897         endDummySpanAncestor = dummySpanAncestorForNode(end.deprecatedNode());
898     }
899
900     // Remove style from the selection.
901     // Use the upstream position of the start for removing style.
902     // This will ensure we remove all traces of the relevant styles from the selection
903     // and prevent us from adding redundant ones, as described in:
904     // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
905     Position removeStart = start.upstream();
906     WritingDirection textDirection = NaturalWritingDirection;
907     bool hasTextDirection = style->textDirection(textDirection);
908     RefPtr<EditingStyle> styleWithoutEmbedding;
909     RefPtr<EditingStyle> embeddingStyle;
910     if (hasTextDirection) {
911         // Leave alone an ancestor that provides the desired single level embedding, if there is one.
912         HTMLElement* startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.deprecatedNode(), true, textDirection);
913         HTMLElement* endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.deprecatedNode(), false, textDirection);
914         removeEmbeddingUpToEnclosingBlock(start.deprecatedNode(), startUnsplitAncestor);
915         removeEmbeddingUpToEnclosingBlock(end.deprecatedNode(), endUnsplitAncestor);
916
917         // Avoid removing the dir attribute and the unicode-bidi and direction properties from the unsplit ancestors.
918         Position embeddingRemoveStart = removeStart;
919         if (startUnsplitAncestor && nodeFullySelected(startUnsplitAncestor, removeStart, end))
920             embeddingRemoveStart = positionInParentAfterNode(startUnsplitAncestor);
921
922         Position embeddingRemoveEnd = end;
923         if (endUnsplitAncestor && nodeFullySelected(endUnsplitAncestor, removeStart, end))
924             embeddingRemoveEnd = positionInParentBeforeNode(endUnsplitAncestor).downstream();
925
926         if (embeddingRemoveEnd != removeStart || embeddingRemoveEnd != end) {
927             styleWithoutEmbedding = style->copy();
928             embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirection();
929
930             if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0)
931                 removeInlineStyle(embeddingStyle.get(), embeddingRemoveStart, embeddingRemoveEnd);
932         }
933     }
934
935     removeInlineStyle(styleWithoutEmbedding ? styleWithoutEmbedding.get() : style, removeStart, end);
936     start = startPosition();
937     end = endPosition();
938     if (start.isNull() || start.isOrphan() || end.isNull() || end.isOrphan())
939         return;
940
941     if (splitStart && mergeStartWithPreviousIfIdentical(start, end)) {
942         start = startPosition();
943         end = endPosition();
944     }
945
946     if (splitEnd) {
947         mergeEndWithNextIfIdentical(start, end);
948         start = startPosition();
949         end = endPosition();
950     }
951
952     // update document layout once before running the rest of the function
953     // so that we avoid the expense of updating before each and every call
954     // to check a computed style
955     updateLayout();
956
957     RefPtr<EditingStyle> styleToApply = style;
958     if (hasTextDirection) {
959         // Avoid applying the unicode-bidi and direction properties beneath ancestors that already have them.
960         Node* embeddingStartNode = highestEmbeddingAncestor(start.deprecatedNode(), enclosingBlock(start.deprecatedNode()));
961         Node* embeddingEndNode = highestEmbeddingAncestor(end.deprecatedNode(), enclosingBlock(end.deprecatedNode()));
962
963         if (embeddingStartNode || embeddingEndNode) {
964             Position embeddingApplyStart = embeddingStartNode ? positionInParentAfterNode(embeddingStartNode) : start;
965             Position embeddingApplyEnd = embeddingEndNode ? positionInParentBeforeNode(embeddingEndNode) : end;
966             ASSERT(embeddingApplyStart.isNotNull() && embeddingApplyEnd.isNotNull());
967
968             if (!embeddingStyle) {
969                 styleWithoutEmbedding = style->copy();
970                 embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirection();
971             }
972             fixRangeAndApplyInlineStyle(embeddingStyle.get(), embeddingApplyStart, embeddingApplyEnd);
973
974             styleToApply = styleWithoutEmbedding;
975         }
976     }
977
978     fixRangeAndApplyInlineStyle(styleToApply.get(), start, end);
979
980     // Remove dummy style spans created by splitting text elements.
981     cleanupUnstyledAppleStyleSpans(startDummySpanAncestor);
982     if (endDummySpanAncestor != startDummySpanAncestor)
983         cleanupUnstyledAppleStyleSpans(endDummySpanAncestor);
984 }
985
986 void ApplyStyleCommand::fixRangeAndApplyInlineStyle(EditingStyle* style, const Position& start, const Position& end)
987 {
988     Node* startNode = start.deprecatedNode();
989
990     if (start.deprecatedEditingOffset() >= caretMaxOffset(start.deprecatedNode())) {
991         startNode = startNode->traverseNextNode();
992         if (!startNode || comparePositions(end, firstPositionInOrBeforeNode(startNode)) < 0)
993             return;
994     }
995
996     Node* pastEndNode = end.deprecatedNode();
997     if (end.deprecatedEditingOffset() >= caretMaxOffset(end.deprecatedNode()))
998         pastEndNode = end.deprecatedNode()->traverseNextSibling();
999
1000     // FIXME: Callers should perform this operation on a Range that includes the br
1001     // if they want style applied to the empty line.
1002     if (start == end && start.deprecatedNode()->hasTagName(brTag))
1003         pastEndNode = start.deprecatedNode()->traverseNextNode();
1004
1005     // Start from the highest fully selected ancestor so that we can modify the fully selected node.
1006     // e.g. When applying font-size: large on <font color="blue">hello</font>, we need to include the font element in our run
1007     // to generate <font color="blue" size="4">hello</font> instead of <font color="blue"><font size="4">hello</font></font>
1008     RefPtr<Range> range = Range::create(startNode->document(), start, end);
1009     Element* editableRoot = startNode->rootEditableElement();
1010     if (startNode != editableRoot) {
1011         while (editableRoot && startNode->parentNode() != editableRoot && isNodeVisiblyContainedWithin(startNode->parentNode(), range.get()))
1012             startNode = startNode->parentNode();
1013     }
1014
1015     applyInlineStyleToNodeRange(style, startNode, pastEndNode);
1016 }
1017
1018 static bool containsNonEditableRegion(Node* node)
1019 {
1020     if (!node->isContentEditable())
1021         return true;
1022
1023     Node* sibling = node->traverseNextSibling();
1024     for (Node* descendent = node->firstChild(); descendent && descendent != sibling; descendent = descendent->traverseNextNode()) {
1025         if (!descendent->isContentEditable())
1026             return true;
1027     }
1028
1029     return false;
1030 }
1031
1032 void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, Node* node, Node* pastEndNode)
1033 {
1034     if (m_removeOnly)
1035         return;
1036
1037     for (RefPtr<Node> next; node && node != pastEndNode; node = next.get()) {
1038         next = node->traverseNextNode();
1039
1040         if (!node->renderer() || !node->isContentEditable())
1041             continue;
1042         
1043         if (!node->isContentRichlyEditable() && node->isHTMLElement()) {
1044             // This is a plaintext-only region. Only proceed if it's fully selected.
1045             // pastEndNode is the node after the last fully selected node, so if it's inside node then
1046             // node isn't fully selected.
1047             if (pastEndNode && pastEndNode->isDescendantOf(node))
1048                 break;
1049             // Add to this element's inline style and skip over its contents.
1050             HTMLElement* element = toHTMLElement(node);
1051             RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
1052             inlineStyle->merge(style->style());
1053             setNodeAttribute(element, styleAttr, inlineStyle->cssText());
1054             next = node->traverseNextSibling();
1055             continue;
1056         }
1057         
1058         if (isBlock(node))
1059             continue;
1060         
1061         if (node->childNodeCount()) {
1062             if (node->contains(pastEndNode) || containsNonEditableRegion(node) || !node->parentNode()->isContentEditable())
1063                 continue;
1064             if (editingIgnoresContent(node)) {
1065                 next = node->traverseNextSibling();
1066                 continue;
1067             }
1068         }
1069
1070         RefPtr<Node> runStart = node;
1071         RefPtr<Node> runEnd = node;
1072         Node* sibling = node->nextSibling();
1073         while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode)
1074                && (!isBlock(sibling) || sibling->hasTagName(brTag))
1075                && !containsNonEditableRegion(sibling)) {
1076             runEnd = sibling;
1077             sibling = runEnd->nextSibling();
1078         }
1079         next = runEnd->traverseNextSibling();
1080
1081         if (!removeStyleFromRunBeforeApplyingStyle(style, runStart, runEnd))
1082             continue;
1083         addInlineStyleIfNeeded(style->style(), runStart.get(), runEnd.get(), AddStyledElement);
1084     }
1085 }
1086
1087 bool ApplyStyleCommand::isStyledInlineElementToRemove(Element* element) const
1088 {
1089     return (m_styledInlineElement && element->hasTagName(m_styledInlineElement->tagQName()))
1090         || (m_isInlineElementToRemoveFunction && m_isInlineElementToRemoveFunction(element));
1091 }
1092
1093 bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(EditingStyle* style, RefPtr<Node>& runStart, RefPtr<Node>& runEnd)
1094 {
1095     ASSERT(runStart && runEnd && runStart->parentNode() == runEnd->parentNode());
1096     RefPtr<Node> pastEndNode = runEnd->traverseNextSibling();
1097     bool needToApplyStyle = false;
1098     for (Node* node = runStart.get(); node && node != pastEndNode.get(); node = node->traverseNextNode()) {
1099         if (node->childNodeCount())
1100             continue;
1101         // We don't consider m_isInlineElementToRemoveFunction here because we never apply style when m_isInlineElementToRemoveFunction is specified
1102         if ((!style->isEmpty() && getPropertiesNotIn(style->style(), computedStyle(node).get())->length())
1103             || (m_styledInlineElement && !enclosingNodeWithTag(positionBeforeNode(node), m_styledInlineElement->tagQName()))) {
1104             needToApplyStyle = true;
1105             break;
1106         }
1107     }
1108     if (!needToApplyStyle)
1109         return false;
1110
1111     RefPtr<Node> next = runStart;
1112     for (RefPtr<Node> node = next; node && node->inDocument() && node != pastEndNode; node = next) {
1113         next = node->traverseNextNode();
1114         if (!node->isHTMLElement())
1115             continue;
1116
1117         RefPtr<Node> previousSibling = node->previousSibling();
1118         RefPtr<Node> nextSibling = node->nextSibling();
1119         RefPtr<ContainerNode> parent = node->parentNode();
1120         removeInlineStyleFromElement(style, toHTMLElement(node.get()), RemoveAlways);
1121         if (!node->inDocument()) {
1122             // FIXME: We might need to update the start and the end of current selection here but need a test.
1123             if (runStart == node)
1124                 runStart = previousSibling ? previousSibling->nextSibling() : parent->firstChild();
1125             if (runEnd == node)
1126                 runEnd = nextSibling ? nextSibling->previousSibling() : parent->lastChild();
1127         }
1128     }
1129
1130     return true;
1131 }
1132
1133 bool ApplyStyleCommand::removeInlineStyleFromElement(EditingStyle* style, PassRefPtr<HTMLElement> element, InlineStyleRemovalMode mode, CSSMutableStyleDeclaration* extractedStyle)
1134 {
1135     ASSERT(element);
1136
1137     if (!element->parentNode() || !element->parentNode()->isContentEditable())
1138         return false;
1139
1140     if (isStyledInlineElementToRemove(element.get())) {
1141         if (mode == RemoveNone)
1142             return true;
1143         ASSERT(extractedStyle);
1144         if (element->inlineStyleDecl())
1145             extractedStyle->merge(element->inlineStyleDecl());
1146         removeNodePreservingChildren(element);
1147         return true;
1148     }
1149
1150     if (!style->style())
1151         return false;
1152
1153     bool removed = false;
1154     if (removeImplicitlyStyledElement(style->style(), element.get(), mode, extractedStyle))
1155         removed = true;
1156
1157     if (!element->inDocument())
1158         return removed;
1159
1160     // If the node was converted to a span, the span may still contain relevant
1161     // styles which must be removed (e.g. <b style='font-weight: bold'>)
1162     if (removeCSSStyle(style, element.get(), mode, extractedStyle))
1163         removed = true;
1164
1165     return removed;
1166 }
1167     
1168 enum EPushDownType { ShouldBePushedDown, ShouldNotBePushedDown };
1169 struct HTMLEquivalent {
1170     int propertyID;
1171     bool isValueList;
1172     int primitiveId;
1173     const QualifiedName* element;
1174     const QualifiedName* attribute;
1175     PassRefPtr<CSSValue> (*attributeToCSSValue)(int propertyID, const String&);
1176     EPushDownType pushDownType;
1177 };
1178
1179 static PassRefPtr<CSSValue> stringToCSSValue(int propertyID, const String& value)
1180 {
1181     RefPtr<CSSMutableStyleDeclaration> dummyStyle;
1182     dummyStyle = CSSMutableStyleDeclaration::create();
1183     dummyStyle->setProperty(propertyID, value);
1184     return dummyStyle->getPropertyCSSValue(propertyID);
1185 }
1186
1187 static PassRefPtr<CSSValue> fontSizeToCSSValue(int propertyID, const String& value)
1188 {
1189     UNUSED_PARAM(propertyID);
1190     ASSERT(propertyID == CSSPropertyFontSize);
1191     int size;
1192     if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size))
1193         return 0;
1194     return CSSPrimitiveValue::createIdentifier(size);
1195 }
1196
1197 static const HTMLEquivalent HTMLEquivalents[] = {
1198     { CSSPropertyFontWeight, false, CSSValueBold, &bTag, 0, 0, ShouldBePushedDown },
1199     { CSSPropertyFontWeight, false, CSSValueBold, &strongTag, 0, 0, ShouldBePushedDown },
1200     { CSSPropertyVerticalAlign, false, CSSValueSub, &subTag, 0, 0, ShouldBePushedDown },
1201     { CSSPropertyVerticalAlign, false, CSSValueSuper, &supTag, 0, 0, ShouldBePushedDown },
1202     { CSSPropertyFontStyle, false, CSSValueItalic, &iTag, 0, 0, ShouldBePushedDown },
1203     { CSSPropertyFontStyle, false, CSSValueItalic, &emTag, 0, 0, ShouldBePushedDown },
1204
1205     // text-decorations should be CSSValueList
1206     { CSSPropertyTextDecoration, true, CSSValueUnderline, &uTag, 0, 0, ShouldBePushedDown },
1207     { CSSPropertyTextDecoration, true, CSSValueLineThrough, &sTag, 0, 0, ShouldBePushedDown },
1208     { CSSPropertyTextDecoration, true, CSSValueLineThrough, &strikeTag, 0, 0, ShouldBePushedDown },
1209
1210     // FIXME: font attributes should only be removed if values were different
1211     { CSSPropertyColor, false, CSSValueInvalid, &fontTag, &colorAttr, stringToCSSValue, ShouldBePushedDown },
1212     { CSSPropertyFontFamily, false, CSSValueInvalid, &fontTag, &faceAttr, stringToCSSValue, ShouldBePushedDown },
1213     { CSSPropertyFontSize, false, CSSValueInvalid, &fontTag, &sizeAttr, fontSizeToCSSValue, ShouldBePushedDown },
1214
1215     // unicode-bidi and direction are pushed down separately so don't push down with other styles.
1216     { CSSPropertyDirection, false, CSSValueInvalid, 0, &dirAttr, stringToCSSValue, ShouldNotBePushedDown },
1217     { CSSPropertyUnicodeBidi, false, CSSValueInvalid, 0, &dirAttr, stringToCSSValue, ShouldNotBePushedDown },
1218 };
1219
1220 bool ApplyStyleCommand::removeImplicitlyStyledElement(CSSMutableStyleDeclaration* style, HTMLElement* element, InlineStyleRemovalMode mode, CSSMutableStyleDeclaration* extractedStyle)
1221 {
1222     // Current implementation does not support stylePushedDown when mode == RemoveNone because of early exit.
1223     ASSERT(style);
1224     ASSERT(!extractedStyle || mode != RemoveNone);
1225     bool removed = false;
1226     for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) {
1227         const HTMLEquivalent& equivalent = HTMLEquivalents[i];
1228         ASSERT(equivalent.element || equivalent.attribute);
1229         if ((extractedStyle && equivalent.pushDownType == ShouldNotBePushedDown)
1230             || (equivalent.element && !element->hasTagName(*equivalent.element))
1231             || (equivalent.attribute && !element->hasAttribute(*equivalent.attribute)))
1232             continue;
1233
1234         RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(equivalent.propertyID);
1235         if (!styleValue)
1236             continue;
1237         RefPtr<CSSValue> mapValue;
1238         if (equivalent.attribute)
1239             mapValue = equivalent.attributeToCSSValue(equivalent.propertyID, element->getAttribute(*equivalent.attribute));
1240         else
1241             mapValue = CSSPrimitiveValue::createIdentifier(equivalent.primitiveId).get();
1242
1243         if (mode != RemoveAlways) {
1244             if (equivalent.isValueList && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(mapValue.get()))
1245                 continue; // If CSS value assumes CSSValueList, then only skip if the value was present in style to apply.
1246             else if (mapValue && styleValue->cssText() == mapValue->cssText())
1247                 continue; // If CSS value is primitive, then skip if they are equal.
1248         }
1249
1250         if (extractedStyle && mapValue)
1251             extractedStyle->setProperty(equivalent.propertyID, mapValue->cssText());
1252
1253         if (mode == RemoveNone)
1254             return true;
1255
1256         removed = true;
1257         if (!equivalent.attribute) {
1258             replaceWithSpanOrRemoveIfWithoutAttributes(element);
1259             break;
1260         }
1261         removeNodeAttribute(element, *equivalent.attribute);
1262         if (isEmptyFontTag(element) || isSpanWithoutAttributesOrUnstyleStyleSpan(element))
1263             removeNodePreservingChildren(element);
1264     }
1265     return removed;
1266 }
1267     
1268 void ApplyStyleCommand::replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*& elem)
1269 {
1270     bool removeNode = false;
1271
1272     // Similar to isSpanWithoutAttributesOrUnstyleStyleSpan, but does not look for Apple-style-span.
1273     NamedNodeMap* attributes = elem->attributes(true); // readonly
1274     if (!attributes || attributes->isEmpty())
1275         removeNode = true;
1276     else if (attributes->length() == 1 && elem->hasAttribute(styleAttr)) {
1277         // Remove the element even if it has just style='' (this might be redundantly checked later too)
1278         CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
1279         if (!inlineStyleDecl || inlineStyleDecl->isEmpty())
1280             removeNode = true;
1281     }
1282
1283     if (removeNode)
1284         removeNodePreservingChildren(elem);
1285     else {
1286         HTMLElement* newSpanElement = replaceElementWithSpanPreservingChildrenAndAttributes(elem);
1287         ASSERT(newSpanElement && newSpanElement->inDocument());
1288         elem = newSpanElement;
1289     }
1290 }
1291
1292 bool ApplyStyleCommand::removeCSSStyle(EditingStyle* style, HTMLElement* element, InlineStyleRemovalMode mode, CSSMutableStyleDeclaration* extractedStyle)
1293 {
1294     ASSERT(style);
1295     ASSERT(element);
1296     
1297     if (mode == RemoveNone)
1298         return style->conflictsWithInlineStyleOfElement(element);
1299
1300     Vector<CSSPropertyID> properties;
1301     if (!style->conflictsWithInlineStyleOfElement(element, properties))
1302         return false;
1303
1304     CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
1305     ASSERT(inlineStyle);
1306     for (size_t i = 0; i < properties.size(); i++) {
1307         CSSPropertyID id = properties[i];
1308         if (extractedStyle) {
1309             ExceptionCode ec = 0;
1310             extractedStyle->setProperty(id, inlineStyle->getPropertyValue(id), inlineStyle->getPropertyPriority(id), ec);
1311         }
1312         removeCSSProperty(element, id);
1313     }
1314
1315     // No need to serialize <foo style=""> if we just removed the last css property
1316     if (inlineStyle->isEmpty())
1317         removeNodeAttribute(element, styleAttr);
1318
1319     if (isSpanWithoutAttributesOrUnstyleStyleSpan(element))
1320         removeNodePreservingChildren(element);
1321
1322     return true;
1323 }
1324
1325 HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(EditingStyle* style, Node* node)
1326 {
1327     if (!node)
1328         return 0;
1329
1330     HTMLElement* result = 0;
1331     Node* unsplittableElement = unsplittableElementForPosition(firstPositionInOrBeforeNode(node));
1332
1333     for (Node *n = node; n; n = n->parentNode()) {
1334         if (n->isHTMLElement() && shouldRemoveInlineStyleFromElement(style, toHTMLElement(n)))
1335             result = toHTMLElement(n);
1336         // Should stop at the editable root (cannot cross editing boundary) and
1337         // also stop at the unsplittable element to be consistent with other UAs
1338         if (n == unsplittableElement)
1339             break;
1340     }
1341
1342     return result;
1343 }
1344
1345 void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, CSSMutableStyleDeclaration* style)
1346 {
1347     ASSERT(node);
1348
1349     if (!style || !style->length() || !node->renderer())
1350         return;
1351
1352     RefPtr<CSSMutableStyleDeclaration> newInlineStyle = style;
1353     if (node->isHTMLElement()) {
1354         HTMLElement* element = toHTMLElement(node);
1355         CSSMutableStyleDeclaration* existingInlineStyle = element->inlineStyleDecl();
1356
1357         // Avoid overriding existing styles of node
1358         if (existingInlineStyle) {
1359             newInlineStyle = existingInlineStyle->copy();
1360             CSSMutableStyleDeclaration::const_iterator end = style->end();
1361             for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
1362                 ExceptionCode ec;
1363                 if (!existingInlineStyle->getPropertyCSSValue(it->id()))
1364                     newInlineStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
1365
1366                 // text-decorations adds up
1367                 if (it->id() == CSSPropertyTextDecoration && it->value()->isValueList()) {
1368                     RefPtr<CSSValue> textDecoration = newInlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration);
1369                     if (textDecoration && textDecoration->isValueList()) {
1370                         CSSValueList* textDecorationOfInlineStyle = static_cast<CSSValueList*>(textDecoration.get());
1371                         CSSValueList* textDecorationOfStyleApplied = static_cast<CSSValueList*>(it->value());
1372
1373                         DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1374                         DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1375                         
1376                         if (textDecorationOfStyleApplied->hasValue(underline.get()) && !textDecorationOfInlineStyle->hasValue(underline.get()))
1377                             textDecorationOfInlineStyle->append(underline.get());
1378
1379                         if (textDecorationOfStyleApplied->hasValue(lineThrough.get()) && !textDecorationOfInlineStyle->hasValue(lineThrough.get()))
1380                             textDecorationOfInlineStyle->append(lineThrough.get());
1381                     }
1382                 }
1383             }
1384         }
1385     }
1386
1387     // Since addInlineStyleIfNeeded can't add styles to block-flow render objects, add style attribute instead.
1388     // FIXME: applyInlineStyleToRange should be used here instead.
1389     if ((node->renderer()->isBlockFlow() || node->childNodeCount()) && node->isHTMLElement()) {
1390         setNodeAttribute(toHTMLElement(node), styleAttr, newInlineStyle->cssText());
1391         return;
1392     }
1393
1394     if (node->renderer()->isText() && static_cast<RenderText*>(node->renderer())->isAllCollapsibleWhitespace())
1395         return;
1396
1397     // We can't wrap node with the styled element here because new styled element will never be removed if we did.
1398     // If we modified the child pointer in pushDownInlineStyleAroundNode to point to new style element
1399     // then we fall into an infinite loop where we keep removing and adding styled element wrapping node.
1400     addInlineStyleIfNeeded(newInlineStyle.get(), node, node, DoNotAddStyledElement);
1401 }
1402
1403 void ApplyStyleCommand::pushDownInlineStyleAroundNode(EditingStyle* style, Node* targetNode)
1404 {
1405     HTMLElement* highestAncestor = highestAncestorWithConflictingInlineStyle(style, targetNode);
1406     if (!highestAncestor)
1407         return;
1408
1409     // The outer loop is traversing the tree vertically from highestAncestor to targetNode
1410     Node* current = highestAncestor;
1411     // Along the way, styled elements that contain targetNode are removed and accumulated into elementsToPushDown.
1412     // Each child of the removed element, exclusing ancestors of targetNode, is then wrapped by clones of elements in elementsToPushDown.
1413     Vector<RefPtr<Element> > elementsToPushDown;
1414     while (current != targetNode) {
1415         ASSERT(current);
1416         ASSERT(current->isHTMLElement());
1417         ASSERT(current->contains(targetNode));
1418         Node* child = current->firstChild();
1419         Node* lastChild = current->lastChild();
1420         RefPtr<StyledElement> styledElement;
1421         if (current->isStyledElement() && isStyledInlineElementToRemove(static_cast<Element*>(current))) {
1422             styledElement = static_cast<StyledElement*>(current);
1423             elementsToPushDown.append(styledElement);
1424         }
1425         RefPtr<CSSMutableStyleDeclaration> styleToPushDown = CSSMutableStyleDeclaration::create();
1426         removeInlineStyleFromElement(style, toHTMLElement(current), RemoveIfNeeded, styleToPushDown.get());
1427
1428         // The inner loop will go through children on each level
1429         // FIXME: we should aggregate inline child elements together so that we don't wrap each child separately.
1430         while (child) {
1431             Node* nextChild = child->nextSibling();
1432
1433             if (!child->contains(targetNode) && elementsToPushDown.size()) {
1434                 for (size_t i = 0; i < elementsToPushDown.size(); i++) {
1435                     RefPtr<Element> wrapper = elementsToPushDown[i]->cloneElementWithoutChildren();
1436                     ExceptionCode ec = 0;
1437                     wrapper->removeAttribute(styleAttr, ec);
1438                     ASSERT(!ec);
1439                     surroundNodeRangeWithElement(child, child, wrapper);
1440                 }
1441             }
1442
1443             // Apply text decoration to all nodes containing targetNode and their siblings but NOT to targetNode
1444             // But if we've removed styledElement then go ahead and always apply the style.
1445             if (child != targetNode || styledElement)
1446                 applyInlineStyleToPushDown(child, styleToPushDown.get());
1447
1448             // We found the next node for the outer loop (contains targetNode)
1449             // When reached targetNode, stop the outer loop upon the completion of the current inner loop
1450             if (child == targetNode || child->contains(targetNode))
1451                 current = child;
1452
1453             if (child == lastChild || child->contains(lastChild))
1454                 break;
1455             child = nextChild;
1456         }
1457     }
1458 }
1459
1460 void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &start, const Position &end)
1461 {
1462     ASSERT(start.isNotNull());
1463     ASSERT(end.isNotNull());
1464     ASSERT(start.anchorNode()->inDocument());
1465     ASSERT(end.anchorNode()->inDocument());
1466     ASSERT(comparePositions(start, end) <= 0);
1467
1468     Position pushDownStart = start.downstream();
1469     // If the pushDownStart is at the end of a text node, then this node is not fully selected.
1470     // Move it to the next deep quivalent position to avoid removing the style from this node.
1471     // e.g. if pushDownStart was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead.
1472     Node* pushDownStartContainer = pushDownStart.containerNode();
1473     if (pushDownStartContainer && pushDownStartContainer->isTextNode()
1474         && pushDownStart.computeOffsetInContainerNode() == pushDownStartContainer->maxCharacterOffset())
1475         pushDownStart = nextVisuallyDistinctCandidate(pushDownStart);
1476     Position pushDownEnd = end.upstream();
1477
1478     pushDownInlineStyleAroundNode(style, pushDownStart.deprecatedNode());
1479     pushDownInlineStyleAroundNode(style, pushDownEnd.deprecatedNode());
1480
1481     // The s and e variables store the positions used to set the ending selection after style removal
1482     // takes place. This will help callers to recognize when either the start node or the end node
1483     // are removed from the document during the work of this function.
1484     // If pushDownInlineStyleAroundNode has pruned start.deprecatedNode() or end.deprecatedNode(),
1485     // use pushDownStart or pushDownEnd instead, which pushDownInlineStyleAroundNode won't prune.
1486     Position s = start.isNull() || start.isOrphan() ? pushDownStart : start;
1487     Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end;
1488
1489     Node* node = start.deprecatedNode();
1490     while (node) {
1491         RefPtr<Node> next = node->traverseNextNode();
1492         if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
1493             RefPtr<HTMLElement> elem = toHTMLElement(node);
1494             RefPtr<Node> prev = elem->traversePreviousNodePostOrder();
1495             RefPtr<Node> next = elem->traverseNextNode();
1496             RefPtr<CSSMutableStyleDeclaration> styleToPushDown;
1497             PassRefPtr<Node> childNode = 0;
1498             if (isStyledInlineElementToRemove(elem.get())) {
1499                 styleToPushDown = CSSMutableStyleDeclaration::create();
1500                 childNode = elem->firstChild();
1501             }
1502
1503             removeInlineStyleFromElement(style, elem.get(), RemoveIfNeeded, styleToPushDown.get());
1504             if (!elem->inDocument()) {
1505                 if (s.deprecatedNode() == elem) {
1506                     // Since elem must have been fully selected, and it is at the start
1507                     // of the selection, it is clear we can set the new s offset to 0.
1508                     ASSERT(s.anchorType() == Position::PositionIsBeforeAnchor || s.offsetInContainerNode() <= 0);
1509                     s = firstPositionInOrBeforeNode(next.get());
1510                 }
1511                 if (e.deprecatedNode() == elem) {
1512                     // Since elem must have been fully selected, and it is at the end
1513                     // of the selection, it is clear we can set the new e offset to
1514                     // the max range offset of prev.
1515                     ASSERT(s.anchorType() == Position::PositionIsAfterAnchor
1516                            || s.offsetInContainerNode() >= lastOffsetInNode(s.containerNode()));
1517                     e = lastPositionInOrAfterNode(prev.get());
1518                 }
1519             }
1520
1521             if (styleToPushDown) {
1522                 for (; childNode; childNode = childNode->nextSibling())
1523                     applyInlineStyleToPushDown(childNode.get(), styleToPushDown.get());
1524             }
1525         }
1526         if (node == end.deprecatedNode())
1527             break;
1528         node = next.get();
1529     }
1530
1531     updateStartEnd(s, e);
1532 }
1533
1534 bool ApplyStyleCommand::nodeFullySelected(Node *node, const Position &start, const Position &end) const
1535 {
1536     ASSERT(node);
1537     ASSERT(node->isElementNode());
1538
1539     return comparePositions(firstPositionInOrBeforeNode(node), start) >= 0
1540         && comparePositions(lastPositionInOrAfterNode(node).upstream(), end) <= 0;
1541 }
1542
1543 bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, const Position &end) const
1544 {
1545     ASSERT(node);
1546     ASSERT(node->isElementNode());
1547
1548     bool isFullyBeforeStart = comparePositions(lastPositionInOrAfterNode(node).upstream(), start) < 0;
1549     bool isFullyAfterEnd = comparePositions(firstPositionInOrBeforeNode(node), end) > 0;
1550
1551     return isFullyBeforeStart || isFullyAfterEnd;
1552 }
1553
1554 void ApplyStyleCommand::splitTextAtStart(const Position& start, const Position& end)
1555 {
1556     ASSERT(start.anchorType() == Position::PositionIsOffsetInAnchor);
1557
1558     Position newEnd;
1559     if (end.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode())
1560         newEnd = Position(end.containerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor);
1561     else
1562         newEnd = end;
1563
1564     Text* text = static_cast<Text*>(start.deprecatedNode());
1565     splitTextNode(text, start.offsetInContainerNode());
1566     updateStartEnd(firstPositionInNode(start.deprecatedNode()), newEnd);
1567 }
1568
1569 void ApplyStyleCommand::splitTextAtEnd(const Position& start, const Position& end)
1570 {
1571     ASSERT(end.anchorType() == Position::PositionIsOffsetInAnchor);
1572
1573     bool shouldUpdateStart = start.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode();
1574     Text* text = static_cast<Text *>(end.deprecatedNode());
1575     splitTextNode(text, end.offsetInContainerNode());
1576
1577     Node* prevNode = text->previousSibling();
1578     ASSERT(prevNode);
1579     Position newStart = shouldUpdateStart ? Position(prevNode, start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor) : start;
1580     updateStartEnd(newStart, lastPositionInNode(prevNode));
1581 }
1582
1583 void ApplyStyleCommand::splitTextElementAtStart(const Position& start, const Position& end)
1584 {
1585     ASSERT(start.anchorType() == Position::PositionIsOffsetInAnchor);
1586
1587     Position newEnd;
1588     if (end.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode())
1589         newEnd = Position(end.containerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor);
1590     else
1591         newEnd = end;
1592
1593     Text* text = static_cast<Text*>(start.deprecatedNode());
1594     splitTextNodeContainingElement(text, start.deprecatedEditingOffset());
1595     updateStartEnd(Position(start.deprecatedNode()->parentNode(), start.deprecatedNode()->nodeIndex(), Position::PositionIsOffsetInAnchor), newEnd);
1596 }
1597
1598 void ApplyStyleCommand::splitTextElementAtEnd(const Position& start, const Position& end)
1599 {
1600     ASSERT(end.anchorType() == Position::PositionIsOffsetInAnchor);
1601
1602     bool shouldUpdateStart = start.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode();
1603     Text* text = static_cast<Text*>(end.deprecatedNode());
1604     splitTextNodeContainingElement(text, end.deprecatedEditingOffset());
1605
1606     Node* prevNode = text->parentNode()->previousSibling()->lastChild();
1607     ASSERT(prevNode);
1608     Position newStart = shouldUpdateStart ? Position(prevNode, start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor) : start;
1609     updateStartEnd(newStart, Position(prevNode->parentNode(), prevNode->nodeIndex() + 1, Position::PositionIsOffsetInAnchor));
1610 }
1611
1612 bool ApplyStyleCommand::shouldSplitTextElement(Element* element, EditingStyle* style)
1613 {
1614     if (!element || !element->isHTMLElement())
1615         return false;
1616
1617     return shouldRemoveInlineStyleFromElement(style, toHTMLElement(element));
1618 }
1619
1620 bool ApplyStyleCommand::isValidCaretPositionInTextNode(const Position& position)
1621 {
1622     Node* node = position.containerNode();
1623     if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node->isTextNode())
1624         return false;
1625     int offsetInText = position.offsetInContainerNode();
1626     return offsetInText > caretMinOffset(node) && offsetInText < caretMaxOffset(node);
1627 }
1628
1629 static bool areIdenticalElements(Node *first, Node *second)
1630 {
1631     // check that tag name and all attribute names and values are identical
1632
1633     if (!first->isElementNode())
1634         return false;
1635     
1636     if (!second->isElementNode())
1637         return false;
1638
1639     Element *firstElement = static_cast<Element *>(first);
1640     Element *secondElement = static_cast<Element *>(second);
1641     
1642     if (!firstElement->tagQName().matches(secondElement->tagQName()))
1643         return false;
1644
1645     NamedNodeMap *firstMap = firstElement->attributes();
1646     NamedNodeMap *secondMap = secondElement->attributes();
1647
1648     unsigned firstLength = firstMap->length();
1649
1650     if (firstLength != secondMap->length())
1651         return false;
1652
1653     for (unsigned i = 0; i < firstLength; i++) {
1654         Attribute *attribute = firstMap->attributeItem(i);
1655         Attribute *secondAttribute = secondMap->getAttributeItem(attribute->name());
1656
1657         if (!secondAttribute || attribute->value() != secondAttribute->value())
1658             return false;
1659     }
1660     
1661     return true;
1662 }
1663
1664 bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position& start, const Position& end)
1665 {
1666     Node* startNode = start.containerNode();
1667     int startOffset = start.computeOffsetInContainerNode();
1668     if (startOffset)
1669         return false;
1670
1671     if (isAtomicNode(startNode)) {
1672         // note: prior siblings could be unrendered elements. it's silly to miss the
1673         // merge opportunity just for that.
1674         if (startNode->previousSibling())
1675             return false;
1676
1677         startNode = startNode->parentNode();
1678         startOffset = 0;
1679     }
1680
1681     if (!startNode->isElementNode())
1682         return false;
1683
1684     Node* previousSibling = startNode->previousSibling();
1685
1686     if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
1687         Element* previousElement = static_cast<Element*>(previousSibling);
1688         Element* element = static_cast<Element*>(startNode);
1689         Node* startChild = element->firstChild();
1690         ASSERT(startChild);
1691         mergeIdenticalElements(previousElement, element);
1692
1693         int startOffsetAdjustment = startChild->nodeIndex();
1694         int endOffsetAdjustment = startNode == end.deprecatedNode() ? startOffsetAdjustment : 0;
1695         updateStartEnd(Position(startNode, startOffsetAdjustment, Position::PositionIsOffsetInAnchor),
1696                        Position(end.deprecatedNode(), end.deprecatedEditingOffset() + endOffsetAdjustment, Position::PositionIsOffsetInAnchor)); 
1697         return true;
1698     }
1699
1700     return false;
1701 }
1702
1703 bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position& start, const Position& end)
1704 {
1705     Node* endNode = end.containerNode();
1706     int endOffset = end.computeOffsetInContainerNode();
1707
1708     if (isAtomicNode(endNode)) {
1709         if (endOffset < lastOffsetInNode(endNode))
1710             return false;
1711
1712         unsigned parentLastOffset = end.deprecatedNode()->parentNode()->childNodes()->length() - 1;
1713         if (end.deprecatedNode()->nextSibling())
1714             return false;
1715
1716         endNode = end.deprecatedNode()->parentNode();
1717         endOffset = parentLastOffset;
1718     }
1719
1720     if (!endNode->isElementNode() || endNode->hasTagName(brTag))
1721         return false;
1722
1723     Node* nextSibling = endNode->nextSibling();
1724     if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
1725         Element* nextElement = static_cast<Element *>(nextSibling);
1726         Element* element = static_cast<Element *>(endNode);
1727         Node* nextChild = nextElement->firstChild();
1728
1729         mergeIdenticalElements(element, nextElement);
1730
1731         bool shouldUpdateStart = start.containerNode() == endNode;
1732         int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
1733         updateStartEnd(shouldUpdateStart ? Position(nextElement, start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor) : start,
1734                        Position(nextElement, endOffset, Position::PositionIsOffsetInAnchor));
1735         return true;
1736     }
1737
1738     return false;
1739 }
1740
1741 void ApplyStyleCommand::surroundNodeRangeWithElement(PassRefPtr<Node> passedStartNode, PassRefPtr<Node> endNode, PassRefPtr<Element> elementToInsert)
1742 {
1743     ASSERT(passedStartNode);
1744     ASSERT(endNode);
1745     ASSERT(elementToInsert);
1746     RefPtr<Node> startNode = passedStartNode;
1747     RefPtr<Element> element = elementToInsert;
1748
1749     insertNodeBefore(element, startNode);
1750
1751     RefPtr<Node> node = startNode;
1752     while (node) {
1753         RefPtr<Node> next = node->nextSibling();
1754         removeNode(node);
1755         appendNode(node, element);
1756         if (node == endNode)
1757             break;
1758         node = next;
1759     }
1760
1761     RefPtr<Node> nextSibling = element->nextSibling();
1762     RefPtr<Node> previousSibling = element->previousSibling();
1763     if (nextSibling && nextSibling->isElementNode() && nextSibling->isContentEditable()
1764         && areIdenticalElements(element.get(), static_cast<Element*>(nextSibling.get())))
1765         mergeIdenticalElements(element.get(), static_cast<Element*>(nextSibling.get()));
1766
1767     if (previousSibling && previousSibling->isElementNode() && previousSibling->isContentEditable()) {
1768         Node* mergedElement = previousSibling->nextSibling();
1769         if (mergedElement->isElementNode() && mergedElement->isContentEditable()
1770             && areIdenticalElements(static_cast<Element*>(previousSibling.get()), static_cast<Element*>(mergedElement)))
1771             mergeIdenticalElements(static_cast<Element*>(previousSibling.get()), static_cast<Element*>(mergedElement));
1772     }
1773
1774     // FIXME: We should probably call updateStartEnd if the start or end was in the node
1775     // range so that the endingSelection() is canonicalized.  See the comments at the end of
1776     // VisibleSelection::validate().
1777 }
1778
1779 void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElement* block)
1780 {
1781     // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
1782     // inline content.
1783     if (!block)
1784         return;
1785         
1786     String cssText = styleChange.cssStyle();
1787     CSSMutableStyleDeclaration* decl = block->inlineStyleDecl();
1788     if (decl)
1789         cssText += decl->cssText();
1790     setNodeAttribute(block, styleAttr, cssText);
1791 }
1792
1793 void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, PassRefPtr<Node> passedStart, PassRefPtr<Node> passedEnd, EAddStyledElement addStyledElement)
1794 {
1795     if (!passedStart || !passedEnd || !passedStart->inDocument() || !passedEnd->inDocument())
1796         return;
1797     RefPtr<Node> startNode = passedStart;
1798     RefPtr<Node> endNode = passedEnd;
1799
1800     // It's okay to obtain the style at the startNode because we've removed all relevant styles from the current run.
1801     RefPtr<HTMLElement> dummyElement;
1802     Position positionForStyleComparison;
1803     if (!startNode->isElementNode()) {
1804         dummyElement = createStyleSpanElement(document());
1805         insertNodeAt(dummyElement, positionBeforeNode(startNode.get()));
1806         positionForStyleComparison = positionBeforeNode(dummyElement.get());
1807     } else
1808         positionForStyleComparison = firstPositionInNode(startNode.get());
1809
1810     StyleChange styleChange(style, positionForStyleComparison);
1811
1812     if (dummyElement)
1813         removeNode(dummyElement);
1814
1815     // Find appropriate font and span elements top-down.
1816     HTMLElement* fontContainer = 0;
1817     HTMLElement* styleContainer = 0;
1818     for (Node* container = startNode.get(); container && startNode == endNode; container = container->firstChild()) {
1819         if (container->isHTMLElement() && container->hasTagName(fontTag))
1820             fontContainer = toHTMLElement(container);
1821         bool styleContainerIsNotSpan = !styleContainer || !styleContainer->hasTagName(spanTag);
1822         if (container->isHTMLElement() && (container->hasTagName(spanTag) || (styleContainerIsNotSpan && container->childNodeCount())))
1823             styleContainer = toHTMLElement(container);
1824         if (!container->firstChild())
1825             break;
1826         startNode = container->firstChild();
1827         endNode = container->lastChild();
1828     }
1829
1830     // Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
1831     if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) {
1832         if (fontContainer) {
1833             if (styleChange.applyFontColor())
1834                 setNodeAttribute(fontContainer, colorAttr, styleChange.fontColor());
1835             if (styleChange.applyFontFace())
1836                 setNodeAttribute(fontContainer, faceAttr, styleChange.fontFace());
1837             if (styleChange.applyFontSize())
1838                 setNodeAttribute(fontContainer, sizeAttr, styleChange.fontSize());
1839         } else {
1840             RefPtr<Element> fontElement = createFontElement(document());
1841             if (styleChange.applyFontColor())
1842                 fontElement->setAttribute(colorAttr, styleChange.fontColor());
1843             if (styleChange.applyFontFace())
1844                 fontElement->setAttribute(faceAttr, styleChange.fontFace());
1845             if (styleChange.applyFontSize())
1846                 fontElement->setAttribute(sizeAttr, styleChange.fontSize());
1847             surroundNodeRangeWithElement(startNode, endNode, fontElement.get());
1848         }
1849     }
1850
1851     if (styleChange.cssStyle().length()) {
1852         if (styleContainer) {
1853             CSSMutableStyleDeclaration* existingStyle = toHTMLElement(styleContainer)->inlineStyleDecl();
1854             if (existingStyle)
1855                 setNodeAttribute(styleContainer, styleAttr, existingStyle->cssText() + styleChange.cssStyle());
1856             else
1857                 setNodeAttribute(styleContainer, styleAttr, styleChange.cssStyle());
1858         } else {
1859             RefPtr<Element> styleElement = createStyleSpanElement(document());
1860             styleElement->setAttribute(styleAttr, styleChange.cssStyle());
1861             surroundNodeRangeWithElement(startNode, endNode, styleElement.release());
1862         }
1863     }
1864
1865     if (styleChange.applyBold())
1866         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), bTag));
1867
1868     if (styleChange.applyItalic())
1869         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), iTag));
1870
1871     if (styleChange.applyUnderline())
1872         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), uTag));
1873
1874     if (styleChange.applyLineThrough())
1875         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), strikeTag));
1876
1877     if (styleChange.applySubscript())
1878         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), subTag));
1879     else if (styleChange.applySuperscript())
1880         surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), supTag));
1881
1882     if (m_styledInlineElement && addStyledElement == AddStyledElement)
1883         surroundNodeRangeWithElement(startNode, endNode, m_styledInlineElement->cloneElementWithoutChildren());
1884 }
1885
1886 float ApplyStyleCommand::computedFontSize(Node* node)
1887 {
1888     if (!node)
1889         return 0;
1890
1891     RefPtr<CSSComputedStyleDeclaration> style = computedStyle(node);
1892     if (!style)
1893         return 0;
1894
1895     RefPtr<CSSPrimitiveValue> value = static_pointer_cast<CSSPrimitiveValue>(style->getPropertyCSSValue(CSSPropertyFontSize));
1896     if (!value)
1897         return 0;
1898
1899     return value->getFloatValue(CSSPrimitiveValue::CSS_PX);
1900 }
1901
1902 void ApplyStyleCommand::joinChildTextNodes(Node* node, const Position& start, const Position& end)
1903 {
1904     if (!node)
1905         return;
1906
1907     Position newStart = start;
1908     Position newEnd = end;
1909
1910     Node* child = node->firstChild();
1911     while (child) {
1912         Node* next = child->nextSibling();
1913         if (child->isTextNode() && next && next->isTextNode()) {
1914             Text* childText = static_cast<Text *>(child);
1915             Text* nextText = static_cast<Text *>(next);
1916             if (start.anchorType() == Position::PositionIsOffsetInAnchor && next == start.containerNode())
1917                 newStart = Position(childText, childText->length() + start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor);
1918             if (end.anchorType() == Position::PositionIsOffsetInAnchor && next == end.containerNode())
1919                 newEnd = Position(childText, childText->length() + end.offsetInContainerNode(), Position::PositionIsOffsetInAnchor);
1920             String textToMove = nextText->data();
1921             insertTextIntoNode(childText, childText->length(), textToMove);
1922             removeNode(next);
1923             // don't move child node pointer. it may want to merge with more text nodes.
1924         }
1925         else {
1926             child = child->nextSibling();
1927         }
1928     }
1929
1930     updateStartEnd(newStart, newEnd);
1931 }
1932
1933 }