summaryrefslogtreecommitdiffstats
path: root/WebCore/editing/ApplyStyleCommand.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/editing/ApplyStyleCommand.cpp')
-rw-r--r--WebCore/editing/ApplyStyleCommand.cpp142
1 files changed, 89 insertions, 53 deletions
diff --git a/WebCore/editing/ApplyStyleCommand.cpp b/WebCore/editing/ApplyStyleCommand.cpp
index a9d1509..bfbfab8 100644
--- a/WebCore/editing/ApplyStyleCommand.cpp
+++ b/WebCore/editing/ApplyStyleCommand.cpp
@@ -1311,6 +1311,9 @@ bool ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclaration* style, HTMLEl
}
}
+ if (mode == RemoveNone)
+ return removed;
+
// No need to serialize <foo style=""> if we just removed the last css property
if (decl->isEmpty())
removeNodeAttribute(elem, styleAttr);
@@ -1321,24 +1324,17 @@ bool ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclaration* style, HTMLEl
return removed;
}
-static bool hasTextDecorationProperty(Node *node)
+HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(CSSMutableStyleDeclaration* style, Node* node)
{
- if (!node->isElementNode())
- return false;
-
- RefPtr<CSSValue> value = computedStyle(node)->getPropertyCSSValue(CSSPropertyTextDecoration, DoNotUpdateLayout);
- return value && !equalIgnoringCase(value->cssText(), "none");
-}
+ if (!node)
+ return 0;
-static Node* highestAncestorWithTextDecoration(Node *node)
-{
- ASSERT(node);
- Node* result = 0;
+ HTMLElement* result = 0;
Node* unsplittableElement = unsplittableElementForPosition(Position(node, 0));
for (Node *n = node; n; n = n->parentNode()) {
- if (hasTextDecorationProperty(n))
- result = n;
+ if (n->isHTMLElement() && shouldRemoveInlineStyleFromElement(style, static_cast<HTMLElement*>(n)))
+ result = static_cast<HTMLElement*>(n);
// Should stop at the editable root (cannot cross editing boundary) and
// also stop at the unsplittable element to be consistent with other UAs
if (n == unsplittableElement)
@@ -1348,7 +1344,7 @@ static Node* highestAncestorWithTextDecoration(Node *node)
return result;
}
-PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractTextDecorationStyle(Node* node)
+PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractInlineStyleToPushDown(Node* node, const Vector<int>& properties)
{
ASSERT(node);
ASSERT(node->isElementNode());
@@ -1362,72 +1358,108 @@ PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractTextDecorationS
if (!style)
return 0;
- int properties[1] = { CSSPropertyTextDecoration };
- RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = style->copyPropertiesInSet(properties, 1);
+ style = style->copyPropertiesInSet(properties.data(), properties.size());
+
+ for (size_t i = 0; i < properties.size(); i++) {
+ RefPtr<CSSValue> property = style->getPropertyCSSValue(properties[i]);
+ if (property)
+ removeCSSProperty(element, static_cast<CSSPropertyID>(properties[i]));
+ }
- RefPtr<CSSValue> property = style->getPropertyCSSValue(CSSPropertyTextDecoration);
- if (property && !equalIgnoringCase(property->cssText(), "none"))
- removeCSSProperty(element, CSSPropertyTextDecoration);
+ if (element->inlineStyleDecl() && element->inlineStyleDecl()->isEmpty())
+ removeNodeAttribute(element, styleAttr);
- return textDecorationStyle.release();
+ if (isSpanWithoutAttributesOrUnstyleStyleSpan(element))
+ removeNodePreservingChildren(element);
+
+ return style.release();
}
-void ApplyStyleCommand::applyTextDecorationStyle(Node *node, CSSMutableStyleDeclaration *style)
+void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, CSSMutableStyleDeclaration* style)
{
ASSERT(node);
- if (!style || style->cssText().isEmpty())
+ if (!style || !style->length() || !node->renderer())
return;
- StyleChange styleChange(style, Position(node, 0));
- if (styleChange.cssStyle().length()) {
- if (node->isTextNode()) {
- RefPtr<HTMLElement> styleSpan = createStyleSpanElement(document());
- surroundNodeRangeWithElement(node, node, styleSpan.get());
- node = styleSpan.get();
- }
+ // Since addInlineStyleIfNeeded can't add styles to block-flow render objects, add style attribute instead.
+ // FIXME: applyInlineStyleToRange should be used here instead.
+ if ((node->renderer()->isBlockFlow() || node->childNodeCount()) && node->isHTMLElement()) {
+ HTMLElement* element = static_cast<HTMLElement*>(node);
+ CSSMutableStyleDeclaration* existingInlineStyle = element->inlineStyleDecl();
+
+ // Avoid overriding existing styles of node
+ if (existingInlineStyle) {
+ RefPtr<CSSMutableStyleDeclaration> newInlineStyle = existingInlineStyle->copy();
+ CSSMutableStyleDeclaration::const_iterator end = style->end();
+ for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
+ ExceptionCode ec;
+ if (!existingInlineStyle->getPropertyCSSValue(it->id()))
+ newInlineStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
+
+ // text-decorations adds up
+ if (it->id() == CSSPropertyTextDecoration) {
+ ASSERT(it->value()->isValueList());
+ RefPtr<CSSValue> textDecoration = newInlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration);
+ if (textDecoration) {
+ ASSERT(textDecoration->isValueList());
+ CSSValueList* textDecorationOfInlineStyle = static_cast<CSSValueList*>(textDecoration.get());
+ CSSValueList* textDecorationOfStyleApplied = static_cast<CSSValueList*>(it->value());
+
+ DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
+ DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
+
+ if (textDecorationOfStyleApplied->hasValue(underline.get()) && !textDecorationOfInlineStyle->hasValue(underline.get()))
+ textDecorationOfInlineStyle->append(underline.get());
+
+ if (textDecorationOfStyleApplied->hasValue(lineThrough.get()) && !textDecorationOfInlineStyle->hasValue(lineThrough.get()))
+ textDecorationOfInlineStyle->append(lineThrough.get());
+ }
+ }
+ }
- if (!node->isElementNode())
- return;
+ setNodeAttribute(element, styleAttr, newInlineStyle->cssText());
+ } else
+ setNodeAttribute(element, styleAttr, style->cssText());
- HTMLElement *element = static_cast<HTMLElement *>(node);
- String cssText = styleChange.cssStyle();
- CSSMutableStyleDeclaration *decl = element->inlineStyleDecl();
- if (decl)
- cssText += decl->cssText();
- setNodeAttribute(element, styleAttr, cssText);
+ return;
}
- if (styleChange.applyUnderline())
- surroundNodeRangeWithElement(node, node, createHTMLElement(document(), uTag));
+ if (node->renderer()->isText() && static_cast<RenderText*>(node->renderer())->isAllCollapsibleWhitespace())
+ return;
- if (styleChange.applyLineThrough())
- surroundNodeRangeWithElement(node, node, createHTMLElement(document(), sTag));
+ // FIXME: addInlineStyleIfNeeded may override the style of node
+ addInlineStyleIfNeeded(style, node, node);
}
-void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node* targetNode)
+void ApplyStyleCommand::pushDownInlineStyleAroundNode(CSSMutableStyleDeclaration* style, Node* targetNode)
{
- ASSERT(targetNode);
- Node* highestAncestor = highestAncestorWithTextDecoration(targetNode);
+ HTMLElement* highestAncestor = highestAncestorWithConflictingInlineStyle(style, targetNode);
if (!highestAncestor)
return;
+ Vector<int> properties;
+ CSSMutableStyleDeclaration::const_iterator end = style->end();
+ for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it)
+ properties.append(it->id());
+
// The outer loop is traversing the tree vertically from highestAncestor to targetNode
Node* current = highestAncestor;
while (current != targetNode) {
ASSERT(current);
+ ASSERT(current->isHTMLElement());
ASSERT(current->contains(targetNode));
- RefPtr<CSSMutableStyleDeclaration> decoration = extractTextDecorationStyle(current);
+ Node* child = current->firstChild();
+ RefPtr<CSSMutableStyleDeclaration> styleToPushDown = extractInlineStyleToPushDown(current, properties);
// The inner loop will go through children on each level
- Node* child = current->firstChild();
while (child) {
Node* nextChild = child->nextSibling();
// Apply text decoration to all nodes containing targetNode and their siblings but NOT to targetNode
if (child != targetNode)
- applyTextDecorationStyle(child, decoration.get());
-
+ applyInlineStyleToPushDown(child, styleToPushDown.get());
+
// We found the next node for the outer loop (contains targetNode)
// When reached targetNode, stop the outer loop upon the completion of the current inner loop
if (child == targetNode || child->contains(targetNode))
@@ -1447,19 +1479,23 @@ void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration>
ASSERT(comparePositions(start, end) <= 0);
RefPtr<CSSValue> textDecorationSpecialProperty = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
-
if (textDecorationSpecialProperty) {
- pushDownTextDecorationStyleAroundNode(start.downstream().node());
- pushDownTextDecorationStyleAroundNode(end.upstream().node());
style = style->copy();
style->setProperty(CSSPropertyTextDecoration, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSSPropertyWebkitTextDecorationsInEffect));
}
+ Position pushDownStart = start.downstream();
+ Position pushDownEnd = end.upstream();
+ pushDownInlineStyleAroundNode(style.get(), pushDownStart.node());
+ pushDownInlineStyleAroundNode(style.get(), pushDownEnd.node());
+
// The s and e variables store the positions used to set the ending selection after style removal
// takes place. This will help callers to recognize when either the start node or the end node
// are removed from the document during the work of this function.
- Position s = start;
- Position e = end;
+ // If pushDownInlineStyleAroundNode has pruned start.node() or end.node(),
+ // use pushDownStart or pushDownEnd instead, which pushDownInlineStyleAroundNode won't prune.
+ Position s = start.isNull() || start.isOrphan() ? pushDownStart : start;
+ Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end;
Node* node = start.node();
while (node) {