| /* |
| * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc. |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "EditingStyle.h" |
| |
| #include "ApplyStyleCommand.h" |
| #include "CSSComputedStyleDeclaration.h" |
| #include "CSSMutableStyleDeclaration.h" |
| #include "CSSParser.h" |
| #include "CSSStyleSelector.h" |
| #include "CSSValueKeywords.h" |
| #include "CSSValueList.h" |
| #include "Frame.h" |
| #include "HTMLFontElement.h" |
| #include "HTMLNames.h" |
| #include "Node.h" |
| #include "Position.h" |
| #include "RenderStyle.h" |
| #include "SelectionController.h" |
| #include "StyledElement.h" |
| #include "htmlediting.h" |
| |
| namespace WebCore { |
| |
| // Editing style properties must be preserved during editing operation. |
| // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph. |
| // FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties |
| static const int editingStyleProperties[] = { |
| // CSS inheritable properties |
| CSSPropertyBorderCollapse, |
| CSSPropertyColor, |
| CSSPropertyFontFamily, |
| CSSPropertyFontSize, |
| CSSPropertyFontStyle, |
| CSSPropertyFontVariant, |
| CSSPropertyFontWeight, |
| CSSPropertyLetterSpacing, |
| CSSPropertyLineHeight, |
| CSSPropertyOrphans, |
| CSSPropertyTextAlign, |
| CSSPropertyTextIndent, |
| CSSPropertyTextTransform, |
| CSSPropertyWhiteSpace, |
| CSSPropertyWidows, |
| CSSPropertyWordSpacing, |
| CSSPropertyWebkitBorderHorizontalSpacing, |
| CSSPropertyWebkitBorderVerticalSpacing, |
| CSSPropertyWebkitTextDecorationsInEffect, |
| CSSPropertyWebkitTextFillColor, |
| CSSPropertyWebkitTextSizeAdjust, |
| CSSPropertyWebkitTextStrokeColor, |
| CSSPropertyWebkitTextStrokeWidth, |
| }; |
| size_t numEditingStyleProperties = WTF_ARRAY_LENGTH(editingStyleProperties); |
| |
| static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style) |
| { |
| return style->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties); |
| } |
| |
| static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style) |
| { |
| if (!style) |
| return CSSMutableStyleDeclaration::create(); |
| return copyEditingProperties(style.get()); |
| } |
| |
| static RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle); |
| |
| class HTMLElementEquivalent { |
| public: |
| static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName) |
| { |
| return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName)); |
| } |
| |
| virtual ~HTMLElementEquivalent() { } |
| virtual bool matches(Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); } |
| virtual bool hasAttribute() const { return false; } |
| bool propertyExistsInStyle(CSSStyleDeclaration* style) const { return style->getPropertyCSSValue(m_propertyID); } |
| virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; |
| virtual void addToStyle(Element*, EditingStyle*) const; |
| |
| protected: |
| HTMLElementEquivalent(CSSPropertyID); |
| HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName); |
| HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName); |
| const int m_propertyID; |
| const RefPtr<CSSPrimitiveValue> m_primitiveValue; |
| const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global. |
| }; |
| |
| HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id) |
| : m_propertyID(id) |
| , m_tagName(0) |
| { |
| } |
| |
| HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName) |
| : m_propertyID(id) |
| , m_tagName(&tagName) |
| { |
| } |
| |
| HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName) |
| : m_propertyID(id) |
| , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue)) |
| , m_tagName(&tagName) |
| { |
| ASSERT(primitiveValue != CSSValueInvalid); |
| } |
| |
| bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const |
| { |
| RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID); |
| return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent(); |
| } |
| |
| void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const |
| { |
| style->setProperty(m_propertyID, m_primitiveValue->cssText()); |
| } |
| |
| class HTMLTextDecorationEquivalent : public HTMLElementEquivalent { |
| public: |
| static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName) |
| { |
| return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName)); |
| } |
| virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; |
| |
| private: |
| HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName); |
| }; |
| |
| HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName) |
| : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName) |
| { |
| } |
| |
| bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const |
| { |
| RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID); |
| return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get()); |
| } |
| |
| class HTMLAttributeEquivalent : public HTMLElementEquivalent { |
| public: |
| static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName) |
| { |
| return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName)); |
| } |
| static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName) |
| { |
| return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName)); |
| } |
| |
| bool matches(Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); } |
| virtual bool hasAttribute() const { return true; } |
| virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; |
| virtual void addToStyle(Element*, EditingStyle*) const; |
| virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const; |
| inline const QualifiedName& attributeName() const { return m_attrName; } |
| |
| protected: |
| HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName); |
| HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName); |
| const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global. |
| }; |
| |
| HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName) |
| : HTMLElementEquivalent(id, tagName) |
| , m_attrName(attrName) |
| { |
| } |
| |
| HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName) |
| : HTMLElementEquivalent(id) |
| , m_attrName(attrName) |
| { |
| } |
| |
| bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const |
| { |
| RefPtr<CSSValue> value = attributeValueAsCSSValue(element); |
| RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID); |
| |
| // FIXME: This is very inefficient way of comparing values |
| // but we can't string compare attribute value and CSS property value. |
| return value && styleValue && value->cssText() == styleValue->cssText(); |
| } |
| |
| void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const |
| { |
| if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element)) |
| style->setProperty(m_propertyID, value->cssText()); |
| } |
| |
| PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const |
| { |
| ASSERT(element); |
| if (!element->hasAttribute(m_attrName)) |
| return 0; |
| |
| RefPtr<CSSMutableStyleDeclaration> dummyStyle; |
| dummyStyle = CSSMutableStyleDeclaration::create(); |
| dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName)); |
| return dummyStyle->getPropertyCSSValue(m_propertyID); |
| } |
| |
| class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent { |
| public: |
| static PassOwnPtr<HTMLFontSizeEquivalent> create() |
| { |
| return adoptPtr(new HTMLFontSizeEquivalent()); |
| } |
| virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const; |
| |
| private: |
| HTMLFontSizeEquivalent(); |
| }; |
| |
| HTMLFontSizeEquivalent::HTMLFontSizeEquivalent() |
| : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr) |
| { |
| } |
| |
| PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const |
| { |
| ASSERT(element); |
| if (!element->hasAttribute(m_attrName)) |
| return 0; |
| int size; |
| if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size)) |
| return 0; |
| return CSSPrimitiveValue::createIdentifier(size); |
| } |
| |
| float EditingStyle::NoFontDelta = 0.0f; |
| |
| EditingStyle::EditingStyle() |
| : m_shouldUseFixedDefaultFontSize(false) |
| , m_fontSizeDelta(NoFontDelta) |
| { |
| } |
| |
| EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude) |
| : m_shouldUseFixedDefaultFontSize(false) |
| , m_fontSizeDelta(NoFontDelta) |
| { |
| init(node, propertiesToInclude); |
| } |
| |
| EditingStyle::EditingStyle(const Position& position) |
| : m_shouldUseFixedDefaultFontSize(false) |
| , m_fontSizeDelta(NoFontDelta) |
| { |
| init(position.deprecatedNode(), OnlyInheritableProperties); |
| } |
| |
| EditingStyle::EditingStyle(const CSSStyleDeclaration* style) |
| : m_mutableStyle(style->copy()) |
| , m_shouldUseFixedDefaultFontSize(false) |
| , m_fontSizeDelta(NoFontDelta) |
| { |
| extractFontSizeDelta(); |
| } |
| |
| EditingStyle::EditingStyle(int propertyID, const String& value) |
| : m_mutableStyle(0) |
| , m_shouldUseFixedDefaultFontSize(false) |
| , m_fontSizeDelta(NoFontDelta) |
| { |
| setProperty(propertyID, value); |
| } |
| |
| EditingStyle::~EditingStyle() |
| { |
| } |
| |
| void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude) |
| { |
| if (isTabSpanTextNode(node)) |
| node = tabSpanNode(node)->parentNode(); |
| else if (isTabSpanNode(node)) |
| node = node->parentNode(); |
| |
| RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node); |
| m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition); |
| |
| if (node && node->computedStyle()) { |
| RenderStyle* renderStyle = node->computedStyle(); |
| removeTextFillAndStrokeColorsIfNeeded(renderStyle); |
| replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get()); |
| } |
| |
| m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize(); |
| extractFontSizeDelta(); |
| } |
| |
| void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle) |
| { |
| // If a node's text fill color is invalid, then its children use |
| // their font-color as their text fill color (they don't |
| // inherit it). Likewise for stroke color. |
| ExceptionCode ec = 0; |
| if (!renderStyle->textFillColor().isValid()) |
| m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec); |
| if (!renderStyle->textStrokeColor().isValid()) |
| m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec); |
| ASSERT(!ec); |
| } |
| |
| void EditingStyle::setProperty(int propertyID, const String& value, bool important) |
| { |
| if (!m_mutableStyle) |
| m_mutableStyle = CSSMutableStyleDeclaration::create(); |
| |
| ExceptionCode ec; |
| m_mutableStyle->setProperty(propertyID, value, important, ec); |
| } |
| |
| void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle) |
| { |
| ASSERT(renderStyle); |
| if (renderStyle->fontDescription().keywordSize()) |
| m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText()); |
| } |
| |
| void EditingStyle::extractFontSizeDelta() |
| { |
| if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) { |
| // Explicit font size overrides any delta. |
| m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); |
| return; |
| } |
| |
| // Get the adjustment amount out of the style. |
| RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta); |
| if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE) |
| return; |
| |
| CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get()); |
| |
| // Only PX handled now. If we handle more types in the future, perhaps |
| // a switch statement here would be more appropriate. |
| if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX) |
| return; |
| |
| m_fontSizeDelta = primitiveValue->getFloatValue(); |
| m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); |
| } |
| |
| bool EditingStyle::isEmpty() const |
| { |
| return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta; |
| } |
| |
| bool EditingStyle::textDirection(WritingDirection& writingDirection) const |
| { |
| if (!m_mutableStyle) |
| return false; |
| |
| RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); |
| if (!unicodeBidi || !unicodeBidi->isPrimitiveValue()) |
| return false; |
| |
| int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent(); |
| if (unicodeBidiValue == CSSValueEmbed) { |
| RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); |
| if (!direction || !direction->isPrimitiveValue()) |
| return false; |
| |
| writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection; |
| |
| return true; |
| } |
| |
| if (unicodeBidiValue == CSSValueNormal) { |
| writingDirection = NaturalWritingDirection; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style) |
| { |
| m_mutableStyle = style; |
| // FIXME: We should be able to figure out whether or not font is fixed width for mutable style. |
| // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here. |
| m_shouldUseFixedDefaultFontSize = false; |
| extractFontSizeDelta(); |
| } |
| |
| void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style) |
| { |
| if (!style || !style->length()) |
| return; |
| if (!m_mutableStyle) |
| m_mutableStyle = CSSMutableStyleDeclaration::create(); |
| m_mutableStyle->merge(style); |
| extractFontSizeDelta(); |
| } |
| |
| void EditingStyle::clear() |
| { |
| m_mutableStyle.clear(); |
| m_shouldUseFixedDefaultFontSize = false; |
| m_fontSizeDelta = NoFontDelta; |
| } |
| |
| PassRefPtr<EditingStyle> EditingStyle::copy() const |
| { |
| RefPtr<EditingStyle> copy = EditingStyle::create(); |
| if (m_mutableStyle) |
| copy->m_mutableStyle = m_mutableStyle->copy(); |
| copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize; |
| copy->m_fontSizeDelta = m_fontSizeDelta; |
| return copy; |
| } |
| |
| PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties() |
| { |
| RefPtr<EditingStyle> blockProperties = EditingStyle::create(); |
| if (!m_mutableStyle) |
| return blockProperties; |
| |
| blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties(); |
| m_mutableStyle->removeBlockProperties(); |
| |
| return blockProperties; |
| } |
| |
| PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection() |
| { |
| RefPtr<EditingStyle> textDirection = EditingStyle::create(); |
| textDirection->m_mutableStyle = CSSMutableStyleDeclaration::create(); |
| textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->getPropertyPriority(CSSPropertyUnicodeBidi)); |
| textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection), |
| m_mutableStyle->getPropertyPriority(CSSPropertyDirection)); |
| |
| m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi); |
| m_mutableStyle->removeProperty(CSSPropertyDirection); |
| |
| return textDirection; |
| } |
| |
| void EditingStyle::removeBlockProperties() |
| { |
| if (!m_mutableStyle) |
| return; |
| |
| m_mutableStyle->removeBlockProperties(); |
| } |
| |
| void EditingStyle::removeStyleAddedByNode(Node* node) |
| { |
| if (!node || !node->parentNode()) |
| return; |
| RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode())); |
| RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node)); |
| parentStyle->diff(nodeStyle.get()); |
| nodeStyle->diff(m_mutableStyle.get()); |
| } |
| |
| void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node) |
| { |
| if (!node || !node->parentNode() || !m_mutableStyle) |
| return; |
| RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode())); |
| RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node)); |
| parentStyle->diff(nodeStyle.get()); |
| |
| CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end(); |
| for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it) |
| m_mutableStyle->removeProperty(it->id()); |
| } |
| |
| void EditingStyle::removeNonEditingProperties() |
| { |
| if (m_mutableStyle) |
| m_mutableStyle = copyEditingProperties(m_mutableStyle.get()); |
| } |
| |
| void EditingStyle::collapseTextDecorationProperties() |
| { |
| if (!m_mutableStyle) |
| return; |
| |
| RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); |
| if (!textDecorationsInEffect) |
| return; |
| |
| m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration)); |
| m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); |
| } |
| |
| // CSS properties that create a visual difference only when applied to text. |
| static const int textOnlyProperties[] = { |
| CSSPropertyTextDecoration, |
| CSSPropertyWebkitTextDecorationsInEffect, |
| CSSPropertyFontStyle, |
| CSSPropertyFontWeight, |
| CSSPropertyColor, |
| }; |
| |
| TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const |
| { |
| RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare); |
| |
| if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties) |
| difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties)); |
| |
| if (!difference->length()) |
| return TrueTriState; |
| if (difference->length() == m_mutableStyle->length()) |
| return FalseTriState; |
| |
| return MixedTriState; |
| } |
| |
| bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const |
| { |
| ASSERT(element); |
| ASSERT(!conflictingProperties || conflictingProperties->isEmpty()); |
| |
| CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl(); |
| if (!m_mutableStyle || !inlineStyle) |
| return false; |
| |
| if (!conflictingProperties) { |
| CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end(); |
| for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) { |
| CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id()); |
| |
| // We don't override whitespace property of a tab span because that would collapse the tab into a space. |
| if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) |
| continue; |
| |
| if (inlineStyle->getPropertyCSSValue(propertyID)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end(); |
| for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) { |
| CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id()); |
| if ((propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) || !inlineStyle->getPropertyCSSValue(propertyID)) |
| continue; |
| |
| if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) { |
| if (extractedStyle) |
| extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID)); |
| conflictingProperties->append(CSSPropertyDirection); |
| } |
| |
| conflictingProperties->append(propertyID); |
| if (extractedStyle) |
| extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID)); |
| } |
| |
| return !conflictingProperties->isEmpty(); |
| } |
| |
| bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const |
| { |
| if (!m_mutableStyle) |
| return false; |
| |
| static const HTMLElementEquivalent* HTMLEquivalents[] = { |
| HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag).leakPtr(), |
| HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag).leakPtr(), |
| HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag).leakPtr(), |
| HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag).leakPtr(), |
| HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag).leakPtr(), |
| HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag).leakPtr(), |
| |
| HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag).leakPtr(), |
| HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag).leakPtr(), |
| HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag).leakPtr(), |
| }; |
| |
| for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) { |
| const HTMLElementEquivalent* equivalent = HTMLEquivalents[i]; |
| if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get()) |
| && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) { |
| if (extractedStyle) |
| equivalent->addToStyle(element, extractedStyle); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents() |
| { |
| DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ()); |
| |
| if (!HTMLAttributeEquivalents.size()) { |
| HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr)); |
| HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr)); |
| HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create()); |
| |
| HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr)); |
| HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr)); |
| } |
| |
| return HTMLAttributeEquivalents; |
| } |
| |
| bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const |
| { |
| ASSERT(element); |
| if (!m_mutableStyle) |
| return false; |
| |
| const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); |
| for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { |
| if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get()) |
| && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get())) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection, |
| EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const |
| { |
| ASSERT(element); |
| // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties |
| ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection); |
| if (!m_mutableStyle) |
| return false; |
| |
| const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); |
| bool removed = false; |
| for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { |
| const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get(); |
| |
| // unicode-bidi and direction are pushed down separately so don't push down with other styles. |
| if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr) |
| continue; |
| |
| if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get()) |
| || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) |
| continue; |
| |
| if (extractedStyle) |
| equivalent->addToStyle(element, extractedStyle); |
| conflictingAttributes.append(equivalent->attributeName()); |
| removed = true; |
| } |
| |
| return removed; |
| } |
| |
| bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const |
| { |
| return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length(); |
| } |
| |
| void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection) |
| { |
| if (!m_mutableStyle) |
| return; |
| |
| // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style. |
| // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate |
| // which one of editingStyleAtPosition or computedStyle is called. |
| RefPtr<EditingStyle> style = EditingStyle::create(position); |
| |
| RefPtr<CSSValue> unicodeBidi; |
| RefPtr<CSSValue> direction; |
| if (shouldPreserveWritingDirection == PreserveWritingDirection) { |
| unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); |
| direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); |
| } |
| |
| style->m_mutableStyle->diff(m_mutableStyle.get()); |
| |
| // if alpha value is zero, we don't add the background color. |
| RefPtr<CSSValue> backgroundColor = m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); |
| if (backgroundColor && backgroundColor->isPrimitiveValue() |
| && !alphaChannel(static_cast<CSSPrimitiveValue*>(backgroundColor.get())->getRGBA32Value())) { |
| ExceptionCode ec; |
| m_mutableStyle->removeProperty(CSSPropertyBackgroundColor, ec); |
| } |
| |
| if (unicodeBidi && unicodeBidi->isPrimitiveValue()) { |
| m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent()); |
| if (direction && direction->isPrimitiveValue()) |
| m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent()); |
| } |
| } |
| |
| void EditingStyle::mergeTypingStyle(Document* document) |
| { |
| ASSERT(document); |
| |
| RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle(); |
| if (!typingStyle || typingStyle == this) |
| return; |
| |
| mergeStyle(typingStyle->style()); |
| } |
| |
| void EditingStyle::mergeInlineStyleOfElement(StyledElement* element) |
| { |
| ASSERT(element); |
| mergeStyle(element->inlineStyleDecl()); |
| } |
| |
| void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style) |
| { |
| if (!style) |
| return; |
| |
| if (!m_mutableStyle) { |
| m_mutableStyle = style->copy(); |
| return; |
| } |
| |
| CSSMutableStyleDeclaration::const_iterator end = style->end(); |
| for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) { |
| RefPtr<CSSValue> value; |
| if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) { |
| value = m_mutableStyle->getPropertyCSSValue(it->id()); |
| if (value && !value->isValueList()) |
| value = 0; |
| } |
| |
| if (!value) { |
| ExceptionCode ec; |
| m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec); |
| continue; |
| } |
| |
| CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value()); |
| CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get()); |
| |
| DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); |
| DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); |
| |
| if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get())) |
| textDecorations->append(underline.get()); |
| |
| if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get())) |
| textDecorations->append(lineThrough.get()); |
| } |
| } |
| |
| static void reconcileTextDecorationProperties(CSSMutableStyleDeclaration* style) |
| { |
| RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); |
| RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration); |
| // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense. |
| ASSERT(!textDecorationsInEffect || !textDecoration); |
| if (textDecorationsInEffect) { |
| style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText()); |
| style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); |
| textDecoration = textDecorationsInEffect; |
| } |
| |
| // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none". |
| if (textDecoration && !textDecoration->isValueList()) |
| style->removeProperty(CSSPropertyTextDecoration); |
| } |
| |
| StyleChange::StyleChange(EditingStyle* style, const Position& position) |
| : m_applyBold(false) |
| , m_applyItalic(false) |
| , m_applyUnderline(false) |
| , m_applyLineThrough(false) |
| , m_applySubscript(false) |
| , m_applySuperscript(false) |
| { |
| Document* document = position.anchorNode() ? position.anchorNode()->document() : 0; |
| if (!style || !style->style() || !document || !document->frame()) |
| return; |
| |
| RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle(); |
| RefPtr<CSSMutableStyleDeclaration> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get()); |
| |
| reconcileTextDecorationProperties(mutableStyle.get()); |
| if (!document->frame()->editor()->shouldStyleWithCSS()) |
| extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize()); |
| |
| // Changing the whitespace style in a tab span would collapse the tab into a space. |
| if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode()))) |
| mutableStyle->removeProperty(CSSPropertyWhiteSpace); |
| |
| // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle. |
| // FIXME: Shouldn't this be done in getPropertiesNotIn? |
| if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection)) |
| mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection)); |
| |
| // Save the result for later |
| m_cssStyle = mutableStyle->cssText().stripWhiteSpace(); |
| } |
| |
| static void setTextDecorationProperty(CSSMutableStyleDeclaration* style, const CSSValueList* newTextDecoration, int propertyID) |
| { |
| if (newTextDecoration->length()) |
| style->setProperty(propertyID, newTextDecoration->cssText(), style->getPropertyPriority(propertyID)); |
| else { |
| // text-decoration: none is redundant since it does not remove any text decorations. |
| ASSERT(!style->getPropertyPriority(propertyID)); |
| style->removeProperty(propertyID); |
| } |
| } |
| |
| static RGBA32 getRGBAFontColor(CSSStyleDeclaration* style) |
| { |
| RefPtr<CSSValue> colorValue = style->getPropertyCSSValue(CSSPropertyColor); |
| if (!colorValue || !colorValue->isPrimitiveValue()) |
| return Color::transparent; |
| |
| CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue.get()); |
| if (primitiveColor->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR) |
| return primitiveColor->getRGBA32Value(); |
| |
| // Need to take care of named color such as green and black |
| // This code should be removed after https://bugs.webkit.org/show_bug.cgi?id=28282 is fixed. |
| RGBA32 rgba = 0; |
| CSSParser::parseColor(rgba, colorValue->cssText()); |
| return rgba; |
| } |
| |
| void StyleChange::extractTextStyles(Document* document, CSSMutableStyleDeclaration* style, bool shouldUseFixedFontDefaultSize) |
| { |
| ASSERT(style); |
| |
| if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) { |
| style->removeProperty(CSSPropertyFontWeight); |
| m_applyBold = true; |
| } |
| |
| int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle); |
| if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) { |
| style->removeProperty(CSSPropertyFontStyle); |
| m_applyItalic = true; |
| } |
| |
| // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect |
| // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList. |
| RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration); |
| if (textDecoration && textDecoration->isValueList()) { |
| DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); |
| DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); |
| |
| RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy(); |
| if (newTextDecoration->removeAll(underline.get())) |
| m_applyUnderline = true; |
| if (newTextDecoration->removeAll(lineThrough.get())) |
| m_applyLineThrough = true; |
| |
| // If trimTextDecorations, delete underline and line-through |
| setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration); |
| } |
| |
| int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign); |
| switch (verticalAlign) { |
| case CSSValueSub: |
| style->removeProperty(CSSPropertyVerticalAlign); |
| m_applySubscript = true; |
| break; |
| case CSSValueSuper: |
| style->removeProperty(CSSPropertyVerticalAlign); |
| m_applySuperscript = true; |
| break; |
| } |
| |
| if (style->getPropertyCSSValue(CSSPropertyColor)) { |
| m_applyFontColor = Color(getRGBAFontColor(style)).serialized(); |
| style->removeProperty(CSSPropertyColor); |
| } |
| |
| m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily); |
| style->removeProperty(CSSPropertyFontFamily); |
| |
| if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) { |
| if (!fontSize->isPrimitiveValue()) |
| style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size. |
| else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(fontSize.get()), |
| shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) { |
| m_applyFontSize = String::number(legacyFontSize); |
| style->removeProperty(CSSPropertyFontSize); |
| } |
| } |
| } |
| |
| static void diffTextDecorations(CSSMutableStyleDeclaration* style, int propertID, CSSValue* refTextDecoration) |
| { |
| RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID); |
| if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList()) |
| return; |
| |
| RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy(); |
| CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration); |
| |
| for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++) |
| newTextDecoration->removeAll(valuesInRefTextDecoration->item(i)); |
| |
| setTextDecorationProperty(style, newTextDecoration.get(), propertID); |
| } |
| |
| static bool fontWeightIsBold(CSSStyleDeclaration* style) |
| { |
| ASSERT(style); |
| RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight); |
| |
| if (!fontWeight) |
| return false; |
| if (!fontWeight->isPrimitiveValue()) |
| return false; |
| |
| // Because b tag can only bold text, there are only two states in plain html: bold and not bold. |
| // Collapse all other values to either one of these two states for editing purposes. |
| switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) { |
| case CSSValue100: |
| case CSSValue200: |
| case CSSValue300: |
| case CSSValue400: |
| case CSSValue500: |
| case CSSValueNormal: |
| return false; |
| case CSSValueBold: |
| case CSSValue600: |
| case CSSValue700: |
| case CSSValue800: |
| case CSSValue900: |
| return true; |
| } |
| |
| ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter |
| return false; // Make compiler happy |
| } |
| |
| static int getTextAlignment(CSSStyleDeclaration* style) |
| { |
| int textAlign = getIdentifierValue(style, CSSPropertyTextAlign); |
| switch (textAlign) { |
| case CSSValueCenter: |
| case CSSValueWebkitCenter: |
| return CSSValueCenter; |
| case CSSValueJustify: |
| return CSSValueJustify; |
| case CSSValueLeft: |
| case CSSValueWebkitLeft: |
| return CSSValueLeft; |
| case CSSValueRight: |
| case CSSValueWebkitRight: |
| return CSSValueRight; |
| } |
| return CSSValueInvalid; |
| } |
| |
| RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle) |
| { |
| ASSERT(styleWithRedundantProperties); |
| ASSERT(baseStyle); |
| RefPtr<CSSMutableStyleDeclaration> result = styleWithRedundantProperties->copy(); |
| baseStyle->diff(result.get()); |
| |
| RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); |
| diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get()); |
| diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get()); |
| |
| if (fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle)) |
| result->removeProperty(CSSPropertyFontWeight); |
| |
| if (getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle)) |
| result->removeProperty(CSSPropertyColor); |
| |
| if (getTextAlignment(result.get()) == getTextAlignment(baseStyle)) |
| result->removeProperty(CSSPropertyTextAlign); |
| |
| return result; |
| } |
| |
| |
| int getIdentifierValue(CSSStyleDeclaration* style, int propertyID) |
| { |
| if (!style) |
| return 0; |
| |
| RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); |
| if (!value || !value->isPrimitiveValue()) |
| return 0; |
| |
| return static_cast<CSSPrimitiveValue*>(value.get())->getIdent(); |
| } |
| |
| static bool isCSSValueLength(CSSPrimitiveValue* value) |
| { |
| return value->primitiveType() >= CSSPrimitiveValue::CSS_PX && value->primitiveType() <= CSSPrimitiveValue::CSS_PC; |
| } |
| |
| int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode) |
| { |
| if (isCSSValueLength(value)) { |
| int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX); |
| int legacyFontSize = CSSStyleSelector::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize); |
| // Use legacy font size only if pixel value matches exactly to that of legacy font size. |
| int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall; |
| if (mode == AlwaysUseLegacyFontSize || CSSStyleSelector::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize) |
| return legacyFontSize; |
| |
| return 0; |
| } |
| |
| if (CSSValueXSmall <= value->getIdent() && value->getIdent() <= CSSValueWebkitXxxLarge) |
| return value->getIdent() - CSSValueXSmall + 1; |
| |
| return 0; |
| } |
| |
| } |