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