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