2010-09-14 Ryosuke Niwa <rniwa@webkit.org>
[WebKit-https.git] / WebCore / editing / ApplyStyleCommand.cpp
index 69dc619..b2e93ec 100644 (file)
@@ -1071,6 +1071,20 @@ void ApplyStyleCommand::fixRangeAndApplyInlineStyle(CSSMutableStyleDeclaration*
     applyInlineStyleToNodeRange(style, startNode, pastEndNode);
 }
 
+static bool containsNonEditableRegion(Node* node)
+{
+    if (!node->isContentEditable())
+        return true;
+
+    Node* sibling = node->traverseNextSibling();
+    for (Node* descendent = node->firstChild(); descendent && descendent != sibling; descendent = descendent->traverseNextNode()) {
+        if (!descendent->isContentEditable())
+            return true;
+    }
+
+    return false;
+}
+
 void ApplyStyleCommand::applyInlineStyleToNodeRange(CSSMutableStyleDeclaration* style, Node* node, Node* pastEndNode)
 {
     for (Node* next; node && node != pastEndNode; node = next) {
@@ -1098,32 +1112,62 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(CSSMutableStyleDeclaration*
             continue;
         
         if (node->childNodeCount()) {
-            if (editingIgnoresContent(node))
+            if (node->contains(pastEndNode) || containsNonEditableRegion(node) || !node->parentNode()->isContentEditable())
+                continue;
+            if (editingIgnoresContent(node)) {
                 next = node->traverseNextSibling();
-            continue;
+                continue;
+            }
         }
 
         Node* runEnd = node;
         Node* sibling = node->nextSibling();
-        StyleChange startChange(style, Position(node, 0));
         while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode)
                && (!isBlock(sibling) || sibling->hasTagName(brTag))
-               && StyleChange(style, Position(sibling, 0)) == startChange) {
+               && !containsNonEditableRegion(sibling)) {
             runEnd = sibling;
             sibling = runEnd->nextSibling();
         }
         next = runEnd->traverseNextSibling();
+
+        if (!removeStyleFromRunBeforeApplyingStyle(style, node, runEnd))
+            continue;
         addInlineStyleIfNeeded(style, node, runEnd, m_removeOnly ? DoNotAddStyledElement : AddStyledElement);
     }
 }
 
+bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDeclaration* style, Node*& runStart, Node*& runEnd)
+{
+    Node* pastEndNode = runEnd->traverseNextSibling();
+    Node* next;
+    for (Node* node = runStart; node && node != pastEndNode; node = next) {
+        next = node->traverseNextNode();
+        if (!node->isHTMLElement())
+            continue;
+        
+        Node* previousSibling = node->previousSibling();
+        Node* nextSibling = node->nextSibling();
+        Node* parent = node->parentNode();
+        removeInlineStyleFromElement(style, static_cast<HTMLElement*>(node), RemoveAlways);
+        if (!node->inDocument()) {
+            // FIXME: We might need to update the start and the end of current selection here but need a test.
+            if (runStart == node)
+                runStart = previousSibling ? previousSibling->nextSibling() : parent->firstChild();
+            if (runEnd == node)
+                runEnd = nextSibling ? nextSibling->previousSibling() : parent->lastChild();
+        }
+    }
+
+    return true;
+}
+
 bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration* style, HTMLElement* element, InlineStyleRemovalMode mode)
 {
     ASSERT(style);
     ASSERT(element);
 
     if (m_styledInlineElement && element->hasTagName(m_styledInlineElement->tagQName())) {
-        if (mode == RemoveAttributesAndElements)
+        if (mode != RemoveNone)
             removeNodePreservingChildren(element);
         return true;
     }
@@ -1220,10 +1264,12 @@ bool ApplyStyleCommand::removeImplicitlyStyledElement(CSSMutableStyleDeclaration
         else
             mapValue = CSSPrimitiveValue::createIdentifier(equivalent.primitiveId).get();
 
-        if (equivalent.isValueList && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(mapValue.get()))
-            continue; // If CSS value assumes CSSValueList, then only skip if the value was present in style to apply.
-        else if (mapValue && styleValue->cssText() == mapValue->cssText())
-            continue; // If CSS value is primitive, then skip if they are equal.
+        if (mode != RemoveAlways) {
+            if (equivalent.isValueList && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(mapValue.get()))
+                continue; // If CSS value assumes CSSValueList, then only skip if the value was present in style to apply.
+            else if (mapValue && styleValue->cssText() == mapValue->cssText())
+                continue; // If CSS value is primitive, then skip if they are equal.
+        }
 
         if (extractedStyle)
             extractedStyle->setProperty(equivalent.propertyID, mapValue->cssText());
@@ -1342,7 +1388,7 @@ PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractInlineStyleToPu
 
     if (!style) {
         style = CSSMutableStyleDeclaration::create();
-        removeImplicitlyStyledElement(styleToApply, element, RemoveAttributesAndElements, style.get());
+        removeImplicitlyStyledElement(styleToApply, element, RemoveIfNeeded, style.get());
         return style.release();
     }
 
@@ -1364,7 +1410,7 @@ PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractInlineStyleToPu
     if (isSpanWithoutAttributesOrUnstyleStyleSpan(element))
         removeNodePreservingChildren(element);
 
-    removeImplicitlyStyledElement(styleToApply, element, RemoveAttributesAndElements, style.get());
+    removeImplicitlyStyledElement(styleToApply, element, RemoveIfNeeded, style.get());
 
     return style.release();
 }