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 "CSSParser.h"
34 #include "CSSStyleRule.h"
35 #include "CSSStyleSelector.h"
36 #include "CSSValueKeywords.h"
37 #include "CSSValueList.h"
39 #include "FrameSelection.h"
40 #include "HTMLFontElement.h"
41 #include "HTMLInterchange.h"
42 #include "HTMLNames.h"
45 #include "QualifiedName.h"
46 #include "RenderStyle.h"
47 #include "StyledElement.h"
48 #include "htmlediting.h"
49 #include <wtf/HashSet.h>
53 // Editing style properties must be preserved during editing operation.
54 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
55 static const int editingInheritableProperties[] = {
56 // CSS inheritable properties
58 CSSPropertyFontFamily,
61 CSSPropertyFontVariant,
62 CSSPropertyFontWeight,
63 CSSPropertyLetterSpacing,
64 CSSPropertyLineHeight,
67 CSSPropertyTextIndent,
68 CSSPropertyTextTransform,
69 CSSPropertyWhiteSpace,
71 CSSPropertyWordSpacing,
72 CSSPropertyWebkitTextDecorationsInEffect,
73 CSSPropertyWebkitTextFillColor,
74 CSSPropertyWebkitTextSizeAdjust,
75 CSSPropertyWebkitTextStrokeColor,
76 CSSPropertyWebkitTextStrokeWidth,
78 size_t numEditingInheritableProperties = WTF_ARRAY_LENGTH(editingInheritableProperties);
80 static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style)
82 return style->copyPropertiesInSet(editingInheritableProperties, numEditingInheritableProperties);
85 static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style)
88 return CSSMutableStyleDeclaration::create();
89 return copyEditingProperties(style.get());
92 static RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle);
94 class HTMLElementEquivalent {
96 static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName)
98 return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
101 virtual ~HTMLElementEquivalent() { }
102 virtual bool matches(const Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
103 virtual bool hasAttribute() const { return false; }
104 virtual bool propertyExistsInStyle(CSSStyleDeclaration* style) const { return style->getPropertyCSSValue(m_propertyID); }
105 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
106 virtual void addToStyle(Element*, EditingStyle*) const;
109 HTMLElementEquivalent(CSSPropertyID);
110 HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
111 HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName);
112 const int m_propertyID;
113 const RefPtr<CSSPrimitiveValue> m_primitiveValue;
114 const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global.
117 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
123 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName)
125 , m_tagName(&tagName)
129 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName)
131 , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue))
132 , m_tagName(&tagName)
134 ASSERT(primitiveValue != CSSValueInvalid);
137 bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
139 RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
140 return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent();
143 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
145 style->setProperty(m_propertyID, m_primitiveValue->cssText());
148 class HTMLTextDecorationEquivalent : public HTMLElementEquivalent {
150 static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName)
152 return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
154 virtual bool propertyExistsInStyle(CSSStyleDeclaration*) const;
155 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
158 HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName);
161 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName)
162 : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
163 // m_propertyID is used in HTMLElementEquivalent::addToStyle
167 bool HTMLTextDecorationEquivalent::propertyExistsInStyle(CSSStyleDeclaration* style) const
169 return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect) || style->getPropertyCSSValue(CSSPropertyTextDecoration);
172 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
174 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
176 styleValue = style->getPropertyCSSValue(CSSPropertyTextDecoration);
177 return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get());
180 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
182 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName)
184 return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
186 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
188 return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName));
191 bool matches(const Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); }
192 virtual bool hasAttribute() const { return true; }
193 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
194 virtual void addToStyle(Element*, EditingStyle*) const;
195 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
196 inline const QualifiedName& attributeName() const { return m_attrName; }
199 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
200 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
201 const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
204 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName)
205 : HTMLElementEquivalent(id, tagName)
206 , m_attrName(attrName)
210 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
211 : HTMLElementEquivalent(id)
212 , m_attrName(attrName)
216 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
218 RefPtr<CSSValue> value = attributeValueAsCSSValue(element);
219 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
221 // FIXME: This is very inefficient way of comparing values
222 // but we can't string compare attribute value and CSS property value.
223 return value && styleValue && value->cssText() == styleValue->cssText();
226 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
228 if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element))
229 style->setProperty(m_propertyID, value->cssText());
232 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
235 if (!element->hasAttribute(m_attrName))
238 RefPtr<CSSMutableStyleDeclaration> dummyStyle;
239 dummyStyle = CSSMutableStyleDeclaration::create();
240 dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName));
241 return dummyStyle->getPropertyCSSValue(m_propertyID);
244 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
246 static PassOwnPtr<HTMLFontSizeEquivalent> create()
248 return adoptPtr(new HTMLFontSizeEquivalent());
250 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
253 HTMLFontSizeEquivalent();
256 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
257 : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
261 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
264 if (!element->hasAttribute(m_attrName))
267 if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size))
269 return CSSPrimitiveValue::createIdentifier(size);
272 float EditingStyle::NoFontDelta = 0.0f;
274 EditingStyle::EditingStyle()
275 : m_shouldUseFixedDefaultFontSize(false)
276 , m_fontSizeDelta(NoFontDelta)
280 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
281 : m_shouldUseFixedDefaultFontSize(false)
282 , m_fontSizeDelta(NoFontDelta)
284 init(node, propertiesToInclude);
287 EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude)
288 : m_shouldUseFixedDefaultFontSize(false)
289 , m_fontSizeDelta(NoFontDelta)
291 init(position.deprecatedNode(), propertiesToInclude);
294 EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
295 : m_mutableStyle(style ? style->copy() : 0)
296 , m_shouldUseFixedDefaultFontSize(false)
297 , m_fontSizeDelta(NoFontDelta)
299 extractFontSizeDelta();
302 EditingStyle::EditingStyle(int propertyID, const String& value)
304 , m_shouldUseFixedDefaultFontSize(false)
305 , m_fontSizeDelta(NoFontDelta)
307 setProperty(propertyID, value);
310 EditingStyle::~EditingStyle()
314 static RGBA32 cssValueToRGBA(CSSValue* colorValue)
316 if (!colorValue || !colorValue->isPrimitiveValue())
317 return Color::transparent;
319 CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue);
320 if (primitiveColor->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR)
321 return primitiveColor->getRGBA32Value();
324 CSSParser::parseColor(rgba, colorValue->cssText());
328 static inline RGBA32 getRGBAFontColor(CSSStyleDeclaration* style)
330 return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyColor).get());
333 static inline RGBA32 rgbaBackgroundColorInEffect(Node* node)
335 return cssValueToRGBA(backgroundColorInEffect(node).get());
338 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
340 if (isTabSpanTextNode(node))
341 node = tabSpanNode(node)->parentNode();
342 else if (isTabSpanNode(node))
343 node = node->parentNode();
345 RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node);
346 m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition);
348 if (propertiesToInclude == EditingInheritablePropertiesAndBackgroundColorInEffect) {
349 if (RefPtr<CSSValue> value = backgroundColorInEffect(node))
350 m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText());
353 if (node && node->computedStyle()) {
354 RenderStyle* renderStyle = node->computedStyle();
355 removeTextFillAndStrokeColorsIfNeeded(renderStyle);
356 replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
359 m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
360 extractFontSizeDelta();
363 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
365 // If a node's text fill color is invalid, then its children use
366 // their font-color as their text fill color (they don't
367 // inherit it). Likewise for stroke color.
368 ExceptionCode ec = 0;
369 if (!renderStyle->textFillColor().isValid())
370 m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec);
371 if (!renderStyle->textStrokeColor().isValid())
372 m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
376 void EditingStyle::setProperty(int propertyID, const String& value, bool important)
379 m_mutableStyle = CSSMutableStyleDeclaration::create();
382 m_mutableStyle->setProperty(propertyID, value, important, ec);
385 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
388 if (renderStyle->fontDescription().keywordSize())
389 m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
392 void EditingStyle::extractFontSizeDelta()
397 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
398 // Explicit font size overrides any delta.
399 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
403 // Get the adjustment amount out of the style.
404 RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
405 if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE)
408 CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
410 // Only PX handled now. If we handle more types in the future, perhaps
411 // a switch statement here would be more appropriate.
412 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX)
415 m_fontSizeDelta = primitiveValue->getFloatValue();
416 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
419 bool EditingStyle::isEmpty() const
421 return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
424 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
429 RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
430 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
433 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
434 if (unicodeBidiValue == CSSValueEmbed) {
435 RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
436 if (!direction || !direction->isPrimitiveValue())
439 writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
444 if (unicodeBidiValue == CSSValueNormal) {
445 writingDirection = NaturalWritingDirection;
452 void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style)
454 m_mutableStyle = style;
455 // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
456 // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
457 m_shouldUseFixedDefaultFontSize = false;
458 extractFontSizeDelta();
461 void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style)
463 if (!style || !style->length())
466 m_mutableStyle = CSSMutableStyleDeclaration::create();
467 m_mutableStyle->merge(style);
468 extractFontSizeDelta();
471 void EditingStyle::clear()
473 m_mutableStyle.clear();
474 m_shouldUseFixedDefaultFontSize = false;
475 m_fontSizeDelta = NoFontDelta;
478 PassRefPtr<EditingStyle> EditingStyle::copy() const
480 RefPtr<EditingStyle> copy = EditingStyle::create();
482 copy->m_mutableStyle = m_mutableStyle->copy();
483 copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
484 copy->m_fontSizeDelta = m_fontSizeDelta;
488 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
490 RefPtr<EditingStyle> blockProperties = EditingStyle::create();
492 return blockProperties;
494 blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
495 m_mutableStyle->removeBlockProperties();
497 return blockProperties;
500 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
502 RefPtr<EditingStyle> textDirection = EditingStyle::create();
503 textDirection->m_mutableStyle = CSSMutableStyleDeclaration::create();
504 textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->getPropertyPriority(CSSPropertyUnicodeBidi));
505 textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
506 m_mutableStyle->getPropertyPriority(CSSPropertyDirection));
508 m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
509 m_mutableStyle->removeProperty(CSSPropertyDirection);
511 return textDirection;
514 void EditingStyle::removeBlockProperties()
519 m_mutableStyle->removeBlockProperties();
522 void EditingStyle::removeStyleAddedByNode(Node* node)
524 if (!node || !node->parentNode())
526 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
527 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
528 parentStyle->diff(nodeStyle.get());
529 nodeStyle->diff(m_mutableStyle.get());
532 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
534 if (!node || !node->parentNode() || !m_mutableStyle)
536 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
537 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
538 parentStyle->diff(nodeStyle.get());
540 CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end();
541 for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it)
542 m_mutableStyle->removeProperty(it->id());
545 void EditingStyle::removeNonEditingProperties()
548 m_mutableStyle = copyEditingProperties(m_mutableStyle.get());
551 void EditingStyle::collapseTextDecorationProperties()
556 RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
557 if (!textDecorationsInEffect)
560 if (textDecorationsInEffect->isValueList())
561 m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
563 m_mutableStyle->removeProperty(CSSPropertyTextDecoration);
564 m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
567 // CSS properties that create a visual difference only when applied to text.
568 static const int textOnlyProperties[] = {
569 CSSPropertyTextDecoration,
570 CSSPropertyWebkitTextDecorationsInEffect,
571 CSSPropertyFontStyle,
572 CSSPropertyFontWeight,
576 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
578 // FIXME: take care of background-color in effect
579 RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
581 if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
582 difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
584 if (!difference->length())
586 if (difference->length() == m_mutableStyle->length())
587 return FalseTriState;
589 return MixedTriState;
592 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
595 ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
597 CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
598 if (!m_mutableStyle || !inlineStyle)
601 CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
602 for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
603 CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
605 // We don't override whitespace property of a tab span because that would collapse the tab into a space.
606 if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
609 if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration)) {
610 if (!conflictingProperties)
612 conflictingProperties->append(CSSPropertyTextDecoration);
614 extractedStyle->setProperty(CSSPropertyTextDecoration, inlineStyle->getPropertyValue(CSSPropertyTextDecoration), inlineStyle->getPropertyPriority(CSSPropertyTextDecoration));
618 if (!inlineStyle->getPropertyCSSValue(propertyID))
621 if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
622 if (!conflictingProperties)
624 conflictingProperties->append(CSSPropertyDirection);
626 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
629 if (!conflictingProperties)
632 conflictingProperties->append(propertyID);
635 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
638 return conflictingProperties && !conflictingProperties->isEmpty();
641 static const Vector<OwnPtr<HTMLElementEquivalent> >& htmlElementEquivalents()
643 DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLElementEquivalent> >, HTMLElementEquivalents, ());
645 if (!HTMLElementEquivalents.size()) {
646 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
647 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
648 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
649 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
650 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
651 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
653 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag));
654 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag));
655 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag));
658 return HTMLElementEquivalents;
662 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
667 const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
668 for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) {
669 const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get();
670 if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
671 && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
673 equivalent->addToStyle(element, extractedStyle);
680 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
682 DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
684 if (!HTMLAttributeEquivalents.size()) {
685 // elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
686 // of exactly one element except dirAttr.
687 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
688 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
689 HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
691 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
692 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
695 return HTMLAttributeEquivalents;
698 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
704 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
705 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
706 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
707 && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
714 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
715 EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
718 // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
719 ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
723 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
724 bool removed = false;
725 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
726 const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
728 // unicode-bidi and direction are pushed down separately so don't push down with other styles.
729 if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
732 if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
733 || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
737 equivalent->addToStyle(element, extractedStyle);
738 conflictingAttributes.append(equivalent->attributeName());
745 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
747 return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length();
750 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
752 bool elementIsSpanOrElementEquivalent = false;
753 if (element->hasTagName(HTMLNames::spanTag))
754 elementIsSpanOrElementEquivalent = true;
756 const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
758 for (i = 0; i < HTMLElementEquivalents.size(); ++i) {
759 if (HTMLElementEquivalents[i]->matches(element)) {
760 elementIsSpanOrElementEquivalent = true;
766 const NamedNodeMap* attributeMap = element->attributeMap();
767 if (!attributeMap || attributeMap->isEmpty())
768 return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
770 unsigned matchedAttributes = 0;
771 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
772 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
773 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
777 if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
778 return false; // element is not a span, a html element equivalent, or font element.
780 if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
783 if (element->hasAttribute(HTMLNames::styleAttr)) {
784 if (CSSMutableStyleDeclaration* style = element->inlineStyleDecl()) {
785 CSSMutableStyleDeclaration::const_iterator end = style->end();
786 for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
787 bool matched = false;
788 for (size_t i = 0; i < numEditingInheritableProperties; ++i) {
789 if (editingInheritableProperties[i] == it->id()) {
794 if (!matched && it->id() != CSSPropertyBackgroundColor)
801 // font with color attribute, span with style attribute, etc...
802 ASSERT(matchedAttributes <= attributeMap->length());
803 return matchedAttributes >= attributeMap->length();
806 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
811 // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
812 // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
813 // which one of editingStyleAtPosition or computedStyle is called.
814 RefPtr<EditingStyle> style = EditingStyle::create(position, EditingInheritablePropertiesAndBackgroundColorInEffect);
816 RefPtr<CSSValue> unicodeBidi;
817 RefPtr<CSSValue> direction;
818 if (shouldPreserveWritingDirection == PreserveWritingDirection) {
819 unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
820 direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
823 style->m_mutableStyle->diff(m_mutableStyle.get());
824 if (getRGBAFontColor(m_mutableStyle.get()) == getRGBAFontColor(style->m_mutableStyle.get()))
825 m_mutableStyle->removeProperty(CSSPropertyColor);
827 if (hasTransparentBackgroundColor(m_mutableStyle.get())
828 || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
829 m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
831 if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
832 m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
833 if (direction && direction->isPrimitiveValue())
834 m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
838 void EditingStyle::mergeTypingStyle(Document* document)
842 RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle();
843 if (!typingStyle || typingStyle == this)
846 mergeStyle(typingStyle->style());
849 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element)
852 mergeStyle(element->inlineStyleDecl());
855 void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style)
860 if (!m_mutableStyle) {
861 m_mutableStyle = style->copy();
865 CSSMutableStyleDeclaration::const_iterator end = style->end();
866 for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
867 RefPtr<CSSValue> value;
868 if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) {
869 value = m_mutableStyle->getPropertyCSSValue(it->id());
870 if (value && !value->isValueList())
876 m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
880 CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value());
881 CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get());
883 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
884 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
886 if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get()))
887 textDecorations->append(underline.get());
889 if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get()))
890 textDecorations->append(lineThrough.get());
894 static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
896 RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
897 RefPtr<CSSRuleList> matchedRules = element->document()->styleSelector()->styleRulesForElement(element, rulesToInclude);
899 for (unsigned i = 0; i < matchedRules->length(); i++) {
900 if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) {
901 RefPtr<CSSMutableStyleDeclaration> s = static_cast<CSSStyleRule*>(matchedRules->item(i))->style();
902 style->merge(s.get(), true);
907 return style.release();
910 void EditingStyle::mergeStyleFromRules(StyledElement* element)
912 RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(element,
913 CSSStyleSelector::AuthorCSSRules | CSSStyleSelector::CrossOriginCSSRules);
914 // Styles from the inline style declaration, held in the variable "style", take precedence
915 // over those from matched rules.
917 styleFromMatchedRules->merge(m_mutableStyle.get());
920 m_mutableStyle = styleFromMatchedRules;
923 void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement* element)
925 mergeStyleFromRules(element);
927 // The property value, if it's a percentage, may not reflect the actual computed value.
928 // For example: style="height: 1%; overflow: visible;" in quirksmode
929 // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
930 RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = computedStyle(element);
931 RefPtr<CSSMutableStyleDeclaration> fromComputedStyle = CSSMutableStyleDeclaration::create();
933 CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
934 for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
935 const CSSProperty& property = *it;
936 CSSValue* value = property.value();
937 if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE)
938 if (static_cast<CSSPrimitiveValue*>(value)->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
939 if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id()))
940 fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue));
943 m_mutableStyle->merge(fromComputedStyle.get());
946 void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node* context)
952 // 1. Remove style from matched rules because style remain without repeating it in inline style declaration
953 RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(element, CSSStyleSelector::AllButEmptyCSSRules);
954 if (styleFromMatchedRules && styleFromMatchedRules->length())
955 m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), styleFromMatchedRules.get());
957 // 2. Remove style present in context and not overriden by matched rules.
958 RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingInheritablePropertiesAndBackgroundColorInEffect);
959 if (computedStyle->m_mutableStyle) {
960 computedStyle->removePropertiesInElementDefaultStyle(element);
961 m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle.get());
964 // 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
965 // These rules are added by serialization code to wrap text nodes.
966 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) {
967 if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyDisplay) == CSSValueInline)
968 m_mutableStyle->removeProperty(CSSPropertyDisplay);
969 if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyFloat) == CSSValueNone)
970 m_mutableStyle->removeProperty(CSSPropertyFloat);
974 void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
976 if (!m_mutableStyle || !m_mutableStyle->length())
979 RefPtr<CSSMutableStyleDeclaration> defaultStyle = styleFromMatchedRulesForElement(element, CSSStyleSelector::UAAndUserCSSRules);
981 Vector<int> propertiesToRemove(defaultStyle->length());
983 CSSMutableStyleDeclaration::const_iterator end = defaultStyle->end();
984 for (CSSMutableStyleDeclaration::const_iterator it = defaultStyle->begin(); it != end; ++it, ++i)
985 propertiesToRemove[i] = it->id();
987 m_mutableStyle->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
990 void EditingStyle::forceInline()
993 m_mutableStyle = CSSMutableStyleDeclaration::create();
994 const bool propertyIsImportant = true;
995 m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
999 static void reconcileTextDecorationProperties(CSSMutableStyleDeclaration* style)
1001 RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1002 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1003 // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
1004 ASSERT(!textDecorationsInEffect || !textDecoration);
1005 if (textDecorationsInEffect) {
1006 style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
1007 style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
1008 textDecoration = textDecorationsInEffect;
1011 // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
1012 if (textDecoration && !textDecoration->isValueList())
1013 style->removeProperty(CSSPropertyTextDecoration);
1016 StyleChange::StyleChange(EditingStyle* style, const Position& position)
1017 : m_applyBold(false)
1018 , m_applyItalic(false)
1019 , m_applyUnderline(false)
1020 , m_applyLineThrough(false)
1021 , m_applySubscript(false)
1022 , m_applySuperscript(false)
1024 Document* document = position.anchorNode() ? position.anchorNode()->document() : 0;
1025 if (!style || !style->style() || !document || !document->frame())
1028 RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
1029 // FIXME: take care of background-color in effect
1030 RefPtr<CSSMutableStyleDeclaration> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get());
1032 reconcileTextDecorationProperties(mutableStyle.get());
1033 if (!document->frame()->editor()->shouldStyleWithCSS())
1034 extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize());
1036 // Changing the whitespace style in a tab span would collapse the tab into a space.
1037 if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
1038 mutableStyle->removeProperty(CSSPropertyWhiteSpace);
1040 // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1041 // FIXME: Shouldn't this be done in getPropertiesNotIn?
1042 if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
1043 mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
1045 // Save the result for later
1046 m_cssStyle = mutableStyle->cssText().stripWhiteSpace();
1049 static void setTextDecorationProperty(CSSMutableStyleDeclaration* style, const CSSValueList* newTextDecoration, int propertyID)
1051 if (newTextDecoration->length())
1052 style->setProperty(propertyID, newTextDecoration->cssText(), style->getPropertyPriority(propertyID));
1054 // text-decoration: none is redundant since it does not remove any text decorations.
1055 ASSERT(!style->getPropertyPriority(propertyID));
1056 style->removeProperty(propertyID);
1060 void StyleChange::extractTextStyles(Document* document, CSSMutableStyleDeclaration* style, bool shouldUseFixedFontDefaultSize)
1064 if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
1065 style->removeProperty(CSSPropertyFontWeight);
1069 int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
1070 if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
1071 style->removeProperty(CSSPropertyFontStyle);
1072 m_applyItalic = true;
1075 // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1076 // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1077 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1078 if (textDecoration && textDecoration->isValueList()) {
1079 DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1080 DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1082 RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
1083 if (newTextDecoration->removeAll(underline.get()))
1084 m_applyUnderline = true;
1085 if (newTextDecoration->removeAll(lineThrough.get()))
1086 m_applyLineThrough = true;
1088 // If trimTextDecorations, delete underline and line-through
1089 setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
1092 int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
1093 switch (verticalAlign) {
1095 style->removeProperty(CSSPropertyVerticalAlign);
1096 m_applySubscript = true;
1099 style->removeProperty(CSSPropertyVerticalAlign);
1100 m_applySuperscript = true;
1104 if (style->getPropertyCSSValue(CSSPropertyColor)) {
1105 m_applyFontColor = Color(getRGBAFontColor(style)).serialized();
1106 style->removeProperty(CSSPropertyColor);
1109 m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
1110 style->removeProperty(CSSPropertyFontFamily);
1112 if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
1113 if (!fontSize->isPrimitiveValue())
1114 style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
1115 else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(fontSize.get()),
1116 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
1117 m_applyFontSize = String::number(legacyFontSize);
1118 style->removeProperty(CSSPropertyFontSize);
1123 static void diffTextDecorations(CSSMutableStyleDeclaration* style, int propertID, CSSValue* refTextDecoration)
1125 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
1126 if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
1129 RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
1130 CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration);
1132 for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
1133 newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
1135 setTextDecorationProperty(style, newTextDecoration.get(), propertID);
1138 static bool fontWeightIsBold(CSSStyleDeclaration* style)
1141 RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight);
1145 if (!fontWeight->isPrimitiveValue())
1148 // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1149 // Collapse all other values to either one of these two states for editing purposes.
1150 switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) {
1156 case CSSValueNormal:
1166 ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1167 return false; // Make compiler happy
1170 static int getTextAlignment(CSSStyleDeclaration* style)
1172 int textAlign = getIdentifierValue(style, CSSPropertyTextAlign);
1173 switch (textAlign) {
1174 case CSSValueCenter:
1175 case CSSValueWebkitCenter:
1176 return CSSValueCenter;
1177 case CSSValueJustify:
1178 return CSSValueJustify;
1180 case CSSValueWebkitLeft:
1181 return CSSValueLeft;
1183 case CSSValueWebkitRight:
1184 return CSSValueRight;
1186 return CSSValueInvalid;
1189 RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
1191 ASSERT(styleWithRedundantProperties);
1193 RefPtr<CSSMutableStyleDeclaration> result = styleWithRedundantProperties->copy();
1195 baseStyle->diff(result.get());
1197 RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1198 diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
1199 diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1201 if (baseStyle->getPropertyCSSValue(CSSPropertyFontSize) && fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
1202 result->removeProperty(CSSPropertyFontWeight);
1204 if (baseStyle->getPropertyCSSValue(CSSPropertyColor) && getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
1205 result->removeProperty(CSSPropertyColor);
1207 if (baseStyle->getPropertyCSSValue(CSSPropertyTextAlign) && getTextAlignment(result.get()) == getTextAlignment(baseStyle))
1208 result->removeProperty(CSSPropertyTextAlign);
1213 int getIdentifierValue(CSSStyleDeclaration* style, int propertyID)
1218 RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
1219 if (!value || !value->isPrimitiveValue())
1222 return static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
1225 static bool isCSSValueLength(CSSPrimitiveValue* value)
1227 return value->primitiveType() >= CSSPrimitiveValue::CSS_PX && value->primitiveType() <= CSSPrimitiveValue::CSS_PC;
1230 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
1232 if (isCSSValueLength(value)) {
1233 int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1234 int legacyFontSize = CSSStyleSelector::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
1235 // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1236 int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
1237 if (mode == AlwaysUseLegacyFontSize || CSSStyleSelector::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
1238 return legacyFontSize;
1243 if (CSSValueXSmall <= value->getIdent() && value->getIdent() <= CSSValueWebkitXxxLarge)
1244 return value->getIdent() - CSSValueXSmall + 1;
1249 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style)
1251 RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1255 if (!cssValue->isPrimitiveValue())
1257 CSSPrimitiveValue* value = static_cast<CSSPrimitiveValue*>(cssValue.get());
1259 if (value->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR)
1260 return !alphaChannel(value->getRGBA32Value());
1262 return value->getIdent() == CSSValueTransparent;
1265 PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
1267 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1268 RefPtr<CSSComputedStyleDeclaration> ancestorStyle = computedStyle(ancestor);
1269 if (!hasTransparentBackgroundColor(ancestorStyle.get()))
1270 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);