2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "EditingStyle.h"
30 #include "ApplyStyleCommand.h"
31 #include "CSSComputedStyleDeclaration.h"
32 #include "CSSMutableStyleDeclaration.h"
33 #include "CSSValueKeywords.h"
34 #include "CSSValueList.h"
36 #include "HTMLFontElement.h"
37 #include "HTMLNames.h"
40 #include "RenderStyle.h"
41 #include "SelectionController.h"
42 #include "StyledElement.h"
43 #include "htmlediting.h"
47 // Editing style properties must be preserved during editing operation.
48 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
49 // FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties
50 static const int editingStyleProperties[] = {
51 // CSS inheritable properties
52 CSSPropertyBorderCollapse,
54 CSSPropertyFontFamily,
57 CSSPropertyFontVariant,
58 CSSPropertyFontWeight,
59 CSSPropertyLetterSpacing,
60 CSSPropertyLineHeight,
63 CSSPropertyTextIndent,
64 CSSPropertyTextTransform,
65 CSSPropertyWhiteSpace,
67 CSSPropertyWordSpacing,
68 CSSPropertyWebkitBorderHorizontalSpacing,
69 CSSPropertyWebkitBorderVerticalSpacing,
70 CSSPropertyWebkitTextDecorationsInEffect,
71 CSSPropertyWebkitTextFillColor,
72 CSSPropertyWebkitTextSizeAdjust,
73 CSSPropertyWebkitTextStrokeColor,
74 CSSPropertyWebkitTextStrokeWidth,
76 size_t numEditingStyleProperties = WTF_ARRAY_LENGTH(editingStyleProperties);
78 static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style)
80 return style->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties);
83 static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style)
86 return CSSMutableStyleDeclaration::create();
87 return copyEditingProperties(style.get());
90 class HTMLElementEquivalent {
92 static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName)
94 return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
97 virtual ~HTMLElementEquivalent() { }
98 virtual bool matches(Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
99 virtual bool hasAttribute() const { return false; }
100 bool propertyExistsInStyle(CSSStyleDeclaration* style) const { return style->getPropertyCSSValue(m_propertyID); }
101 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
102 virtual void addToStyle(Element*, EditingStyle*) const;
105 HTMLElementEquivalent(CSSPropertyID);
106 HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
107 HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName);
108 const int m_propertyID;
109 const RefPtr<CSSPrimitiveValue> m_primitiveValue;
110 const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global.
113 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
119 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName)
121 , m_tagName(&tagName)
125 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName)
127 , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue))
128 , m_tagName(&tagName)
130 ASSERT(primitiveValue != CSSValueInvalid);
133 bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
135 RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
136 return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent();
139 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
141 style->setProperty(m_propertyID, m_primitiveValue->cssText());
144 class HTMLTextDecorationEquivalent : public HTMLElementEquivalent {
146 static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName)
148 return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
150 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
153 HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName);
156 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName)
157 : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
161 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
163 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
164 return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get());
167 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
169 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName)
171 return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
173 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
175 return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName));
178 bool matches(Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); }
179 virtual bool hasAttribute() const { return true; }
180 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
181 virtual void addToStyle(Element*, EditingStyle*) const;
182 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
183 inline const QualifiedName& attributeName() const { return m_attrName; }
186 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
187 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
188 const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
191 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName)
192 : HTMLElementEquivalent(id, tagName)
193 , m_attrName(attrName)
197 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
198 : HTMLElementEquivalent(id)
199 , m_attrName(attrName)
203 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
205 RefPtr<CSSValue> value = attributeValueAsCSSValue(element);
206 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
208 // FIXME: This is very inefficient way of comparing values
209 // but we can't string compare attribute value and CSS property value.
210 return value && styleValue && value->cssText() == styleValue->cssText();
213 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
215 if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element))
216 style->setProperty(m_propertyID, value->cssText());
219 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
222 if (!element->hasAttribute(m_attrName))
225 RefPtr<CSSMutableStyleDeclaration> dummyStyle;
226 dummyStyle = CSSMutableStyleDeclaration::create();
227 dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName));
228 return dummyStyle->getPropertyCSSValue(m_propertyID);
231 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
233 static PassOwnPtr<HTMLFontSizeEquivalent> create()
235 return adoptPtr(new HTMLFontSizeEquivalent());
237 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
240 HTMLFontSizeEquivalent();
243 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
244 : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
248 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
251 if (!element->hasAttribute(m_attrName))
254 if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size))
256 return CSSPrimitiveValue::createIdentifier(size);
259 float EditingStyle::NoFontDelta = 0.0f;
261 EditingStyle::EditingStyle()
262 : m_shouldUseFixedDefaultFontSize(false)
263 , m_fontSizeDelta(NoFontDelta)
267 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
268 : m_shouldUseFixedDefaultFontSize(false)
269 , m_fontSizeDelta(NoFontDelta)
271 init(node, propertiesToInclude);
274 EditingStyle::EditingStyle(const Position& position)
275 : m_shouldUseFixedDefaultFontSize(false)
276 , m_fontSizeDelta(NoFontDelta)
278 init(position.deprecatedNode(), OnlyInheritableProperties);
281 EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
282 : m_mutableStyle(style->copy())
283 , m_shouldUseFixedDefaultFontSize(false)
284 , m_fontSizeDelta(NoFontDelta)
286 extractFontSizeDelta();
289 EditingStyle::EditingStyle(int propertyID, const String& value)
291 , m_shouldUseFixedDefaultFontSize(false)
292 , m_fontSizeDelta(NoFontDelta)
294 setProperty(propertyID, value);
297 EditingStyle::~EditingStyle()
301 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
303 if (isTabSpanTextNode(node))
304 node = tabSpanNode(node)->parentNode();
305 else if (isTabSpanNode(node))
306 node = node->parentNode();
308 RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node);
309 m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition);
311 if (node && node->computedStyle()) {
312 RenderStyle* renderStyle = node->computedStyle();
313 removeTextFillAndStrokeColorsIfNeeded(renderStyle);
314 replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
317 m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
318 extractFontSizeDelta();
321 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
323 // If a node's text fill color is invalid, then its children use
324 // their font-color as their text fill color (they don't
325 // inherit it). Likewise for stroke color.
326 ExceptionCode ec = 0;
327 if (!renderStyle->textFillColor().isValid())
328 m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec);
329 if (!renderStyle->textStrokeColor().isValid())
330 m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
334 void EditingStyle::setProperty(int propertyID, const String& value, bool important)
337 m_mutableStyle = CSSMutableStyleDeclaration::create();
340 m_mutableStyle->setProperty(propertyID, value, important, ec);
343 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
346 if (renderStyle->fontDescription().keywordSize())
347 m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
350 void EditingStyle::extractFontSizeDelta()
352 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
353 // Explicit font size overrides any delta.
354 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
358 // Get the adjustment amount out of the style.
359 RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
360 if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE)
363 CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
365 // Only PX handled now. If we handle more types in the future, perhaps
366 // a switch statement here would be more appropriate.
367 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX)
370 m_fontSizeDelta = primitiveValue->getFloatValue();
371 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
374 bool EditingStyle::isEmpty() const
376 return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
379 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
384 RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
385 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
388 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
389 if (unicodeBidiValue == CSSValueEmbed) {
390 RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
391 if (!direction || !direction->isPrimitiveValue())
394 writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
399 if (unicodeBidiValue == CSSValueNormal) {
400 writingDirection = NaturalWritingDirection;
407 void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style)
409 m_mutableStyle = style;
410 // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
411 // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
412 m_shouldUseFixedDefaultFontSize = false;
413 extractFontSizeDelta();
416 void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style)
418 if (!style || !style->length())
421 m_mutableStyle = CSSMutableStyleDeclaration::create();
422 m_mutableStyle->merge(style);
423 extractFontSizeDelta();
426 void EditingStyle::clear()
428 m_mutableStyle.clear();
429 m_shouldUseFixedDefaultFontSize = false;
430 m_fontSizeDelta = NoFontDelta;
433 PassRefPtr<EditingStyle> EditingStyle::copy() const
435 RefPtr<EditingStyle> copy = EditingStyle::create();
437 copy->m_mutableStyle = m_mutableStyle->copy();
438 copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
439 copy->m_fontSizeDelta = m_fontSizeDelta;
443 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
445 RefPtr<EditingStyle> blockProperties = EditingStyle::create();
447 return blockProperties;
449 blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
450 m_mutableStyle->removeBlockProperties();
452 return blockProperties;
455 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
457 RefPtr<EditingStyle> textDirection = EditingStyle::create();
458 textDirection->m_mutableStyle = CSSMutableStyleDeclaration::create();
459 textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->getPropertyPriority(CSSPropertyUnicodeBidi));
460 textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
461 m_mutableStyle->getPropertyPriority(CSSPropertyDirection));
463 m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
464 m_mutableStyle->removeProperty(CSSPropertyDirection);
466 return textDirection;
469 void EditingStyle::removeBlockProperties()
474 m_mutableStyle->removeBlockProperties();
477 void EditingStyle::removeStyleAddedByNode(Node* node)
479 if (!node || !node->parentNode())
481 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
482 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
483 parentStyle->diff(nodeStyle.get());
484 nodeStyle->diff(m_mutableStyle.get());
487 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
489 if (!node || !node->parentNode() || !m_mutableStyle)
491 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
492 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
493 parentStyle->diff(nodeStyle.get());
495 CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end();
496 for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it)
497 m_mutableStyle->removeProperty(it->id());
500 void EditingStyle::removeNonEditingProperties()
503 m_mutableStyle = copyEditingProperties(m_mutableStyle.get());
506 void EditingStyle::collapseTextDecorationProperties()
511 RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
512 if (!textDecorationsInEffect)
515 m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
516 m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
519 // CSS properties that create a visual difference only when applied to text.
520 static const int textOnlyProperties[] = {
521 CSSPropertyTextDecoration,
522 CSSPropertyWebkitTextDecorationsInEffect,
523 CSSPropertyFontStyle,
524 CSSPropertyFontWeight,
528 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
530 RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
532 if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
533 difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
535 if (!difference->length())
537 if (difference->length() == m_mutableStyle->length())
538 return FalseTriState;
540 return MixedTriState;
543 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
546 ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
548 CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
549 if (!m_mutableStyle || !inlineStyle)
552 if (!conflictingProperties) {
553 CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
554 for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
555 CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
557 // We don't override whitespace property of a tab span because that would collapse the tab into a space.
558 if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
561 if (inlineStyle->getPropertyCSSValue(propertyID))
568 CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
569 for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
570 CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
571 if ((propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) || !inlineStyle->getPropertyCSSValue(propertyID))
574 if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
576 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
577 conflictingProperties->append(CSSPropertyDirection);
580 conflictingProperties->append(propertyID);
582 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
585 return !conflictingProperties->isEmpty();
588 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
593 static const HTMLElementEquivalent* HTMLEquivalents[] = {
594 HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag).leakPtr(),
595 HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag).leakPtr(),
596 HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag).leakPtr(),
597 HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag).leakPtr(),
598 HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag).leakPtr(),
599 HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag).leakPtr(),
601 HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag).leakPtr(),
602 HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag).leakPtr(),
603 HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag).leakPtr(),
606 for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) {
607 const HTMLElementEquivalent* equivalent = HTMLEquivalents[i];
608 if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
609 && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
611 equivalent->addToStyle(element, extractedStyle);
618 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
620 DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
622 if (!HTMLAttributeEquivalents.size()) {
623 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
624 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
625 HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
627 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
628 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
631 return HTMLAttributeEquivalents;
634 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
640 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
641 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
642 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
643 && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
650 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
651 EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
654 // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
655 ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
659 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
660 bool removed = false;
661 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
662 const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
664 // unicode-bidi and direction are pushed down separately so don't push down with other styles.
665 if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
668 if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
669 || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
673 equivalent->addToStyle(element, extractedStyle);
674 conflictingAttributes.append(equivalent->attributeName());
681 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
683 return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length();
686 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
691 // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
692 // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
693 // which one of editingStyleAtPosition or computedStyle is called.
694 RefPtr<EditingStyle> style = EditingStyle::create(position);
696 RefPtr<CSSValue> unicodeBidi;
697 RefPtr<CSSValue> direction;
698 if (shouldPreserveWritingDirection == PreserveWritingDirection) {
699 unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
700 direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
703 style->m_mutableStyle->diff(m_mutableStyle.get());
705 // if alpha value is zero, we don't add the background color.
706 RefPtr<CSSValue> backgroundColor = m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
707 if (backgroundColor && backgroundColor->isPrimitiveValue()
708 && !alphaChannel(static_cast<CSSPrimitiveValue*>(backgroundColor.get())->getRGBA32Value())) {
710 m_mutableStyle->removeProperty(CSSPropertyBackgroundColor, ec);
713 if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
714 m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
715 if (direction && direction->isPrimitiveValue())
716 m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
720 void EditingStyle::mergeTypingStyle(Document* document)
724 RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle();
725 if (!typingStyle || typingStyle == this)
728 mergeStyle(typingStyle->style());
731 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element)
734 mergeStyle(element->inlineStyleDecl());
737 void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style)
742 if (!m_mutableStyle) {
743 m_mutableStyle = style->copy();
747 CSSMutableStyleDeclaration::const_iterator end = style->end();
748 for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
749 RefPtr<CSSValue> value;
750 if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) {
751 value = m_mutableStyle->getPropertyCSSValue(it->id());
752 if (value && !value->isValueList())
758 m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
762 CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value());
763 CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get());
765 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
766 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
768 if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get()))
769 textDecorations->append(underline.get());
771 if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get()))
772 textDecorations->append(lineThrough.get());