diff options
Diffstat (limited to 'Source/WebCore/editing/EditingStyle.cpp')
-rw-r--r-- | Source/WebCore/editing/EditingStyle.cpp | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/Source/WebCore/editing/EditingStyle.cpp b/Source/WebCore/editing/EditingStyle.cpp new file mode 100644 index 0000000..8caf4b6 --- /dev/null +++ b/Source/WebCore/editing/EditingStyle.cpp @@ -0,0 +1,341 @@ +/* + * 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 "CSSValueKeywords.h" +#include "Frame.h" +#include "Node.h" +#include "Position.h" +#include "RenderStyle.h" +#include "SelectionController.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()); +} + +float EditingStyle::NoFontDelta = 0.0f; + +EditingStyle::EditingStyle() + : m_shouldUseFixedDefaultFontSize(false) + , m_fontSizeDelta(NoFontDelta) +{ +} + +EditingStyle::EditingStyle(Node* node) + : m_shouldUseFixedDefaultFontSize(false) + , m_fontSizeDelta(NoFontDelta) +{ + init(node); +} + +EditingStyle::EditingStyle(const Position& position) + : m_shouldUseFixedDefaultFontSize(false) + , m_fontSizeDelta(NoFontDelta) +{ + init(position.node()); +} + +EditingStyle::EditingStyle(const CSSStyleDeclaration* style) + : m_mutableStyle(style->copy()) + , m_shouldUseFixedDefaultFontSize(false) + , m_fontSizeDelta(NoFontDelta) +{ + extractFontSizeDelta(); +} + +EditingStyle::~EditingStyle() +{ +} + +void EditingStyle::init(Node* node) +{ + RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node); + m_mutableStyle = 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::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 +{ + RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); + if (!unicodeBidi) + return false; + + ASSERT(unicodeBidi->isPrimitiveValue()); + int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent(); + if (unicodeBidiValue == CSSValueEmbed) { + RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); + ASSERT(!direction || direction->isPrimitiveValue()); + if (!direction) + 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; +} + +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::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) { + ASSERT(unicodeBidi->isPrimitiveValue()); + m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent()); + if (direction) { + ASSERT(direction->isPrimitiveValue()); + m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent()); + } + } +} + +PassRefPtr<EditingStyle> editingStyleIncludingTypingStyle(const Position& position) +{ + 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; +} + +} |