diff options
author | Ben Murdoch <benm@google.com> | 2011-05-24 11:24:40 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-06-02 09:53:15 +0100 |
commit | 81bc750723a18f21cd17d1b173cd2a4dda9cea6e (patch) | |
tree | 7a9e5ed86ff429fd347a25153107221543909b19 /Source/WebCore/editing/EditingStyle.cpp | |
parent | 94088a6d336c1dd80a1e734af51e96abcbb689a7 (diff) | |
download | external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.zip external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.gz external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.bz2 |
Merge WebKit at r80534: Intial merge by Git
Change-Id: Ia7a83357124c9e1cdb1debf55d9661ec0bd09a61
Diffstat (limited to 'Source/WebCore/editing/EditingStyle.cpp')
-rw-r--r-- | Source/WebCore/editing/EditingStyle.cpp | 432 |
1 files changed, 424 insertions, 8 deletions
diff --git a/Source/WebCore/editing/EditingStyle.cpp b/Source/WebCore/editing/EditingStyle.cpp index a3f66be..de71fb7 100644 --- a/Source/WebCore/editing/EditingStyle.cpp +++ b/Source/WebCore/editing/EditingStyle.cpp @@ -31,11 +31,16 @@ #include "CSSComputedStyleDeclaration.h" #include "CSSMutableStyleDeclaration.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 { @@ -82,6 +87,175 @@ static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(Pass return copyEditingProperties(style.get()); } +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() @@ -101,7 +275,7 @@ EditingStyle::EditingStyle(const Position& position) : m_shouldUseFixedDefaultFontSize(false) , m_fontSizeDelta(NoFontDelta) { - init(position.node(), OnlyInheritableProperties); + init(position.deprecatedNode(), OnlyInheritableProperties); } EditingStyle::EditingStyle(const CSSStyleDeclaration* style) @@ -112,6 +286,14 @@ EditingStyle::EditingStyle(const CSSStyleDeclaration* style) extractFontSizeDelta(); } +EditingStyle::EditingStyle(int propertyID, const String& value) + : m_mutableStyle(0) + , m_shouldUseFixedDefaultFontSize(false) + , m_fontSizeDelta(NoFontDelta) +{ + setProperty(propertyID, value); +} + EditingStyle::~EditingStyle() { } @@ -144,6 +326,15 @@ void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyl 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); @@ -182,6 +373,9 @@ bool EditingStyle::isEmpty() const bool EditingStyle::textDirection(WritingDirection& writingDirection) const { + if (!m_mutableStyle) + return false; + RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); if (!unicodeBidi) return false; @@ -306,6 +500,181 @@ void EditingStyle::removeNonEditingProperties() 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; +} + void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection) { if (!m_mutableStyle) @@ -343,13 +712,60 @@ void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWrit } } -PassRefPtr<EditingStyle> editingStyleIncludingTypingStyle(const Position& position) +void EditingStyle::mergeTypingStyle(Document* document) { - RefPtr<EditingStyle> editingStyle = EditingStyle::create(position); - RefPtr<EditingStyle> typingStyle = position.node()->document()->frame()->selection()->typingStyle(); - if (typingStyle && typingStyle->style()) - editingStyle->style()->merge(copyEditingProperties(typingStyle->style()).get()); - return editingStyle; + 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()); + } +} + } |