OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
54 namespace WebCore { | 54 namespace WebCore { |
55 | 55 |
56 using namespace HTMLNames; | 56 using namespace HTMLNames; |
57 | 57 |
58 static String& styleSpanClassString() | 58 static String& styleSpanClassString() |
59 { | 59 { |
60 DEFINE_STATIC_LOCAL(String, styleSpanClassString, ((AppleStyleSpanClass))); | 60 DEFINE_STATIC_LOCAL(String, styleSpanClassString, ((AppleStyleSpanClass))); |
61 return styleSpanClassString; | 61 return styleSpanClassString; |
62 } | 62 } |
63 | 63 |
64 bool isLegacyAppleStyleSpan(const Node *node) | 64 bool isStyleSpan(const Node *node) |
65 { | 65 { |
66 if (!node || !node->isHTMLElement()) | 66 if (!node || !node->isHTMLElement()) |
67 return false; | 67 return false; |
68 | 68 |
69 const HTMLElement* elem = static_cast<const HTMLElement*>(node); | 69 const HTMLElement* elem = static_cast<const HTMLElement*>(node); |
70 return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styl
eSpanClassString(); | 70 return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styl
eSpanClassString(); |
71 } | 71 } |
72 | 72 |
73 enum ShouldStyleAttributeBeEmpty { AllowNonEmptyStyleAttribute, StyleAttributeSh
ouldBeEmpty }; | 73 enum ShouldStyleAttributeBeEmpty { AllowNonEmptyStyleAttribute, StyleAttributeSh
ouldBeEmpty }; |
74 static bool hasNoAttributeOrOnlyStyleAttribute(const StyledElement* element, Sho
uldStyleAttributeBeEmpty shouldStyleAttributeBeEmpty) | 74 static bool hasNoAttributeOrOnlyStyleAttribute(const StyledElement* element, Sho
uldStyleAttributeBeEmpty shouldStyleAttributeBeEmpty) |
(...skipping 14 matching lines...) Expand all Loading... |
89 return matchedAttributes == map->length(); | 89 return matchedAttributes == map->length(); |
90 } | 90 } |
91 | 91 |
92 bool isStyleSpanOrSpanWithOnlyStyleAttribute(const Element* element) | 92 bool isStyleSpanOrSpanWithOnlyStyleAttribute(const Element* element) |
93 { | 93 { |
94 if (!element || !element->hasTagName(spanTag)) | 94 if (!element || !element->hasTagName(spanTag)) |
95 return false; | 95 return false; |
96 return hasNoAttributeOrOnlyStyleAttribute(toHTMLElement(element), AllowNonEm
ptyStyleAttribute); | 96 return hasNoAttributeOrOnlyStyleAttribute(toHTMLElement(element), AllowNonEm
ptyStyleAttribute); |
97 } | 97 } |
98 | 98 |
99 static inline bool isSpanWithoutAttributesOrUnstyledStyleSpan(const Node* node) | 99 static inline bool isUnstyledStyleSpan(const Node* node) |
| 100 { |
| 101 return isStyleSpan(node) && hasNoAttributeOrOnlyStyleAttribute(toHTMLElement
(node), StyleAttributeShouldBeEmpty); |
| 102 } |
| 103 |
| 104 static inline bool isSpanWithoutAttributesOrUnstyleStyleSpan(const Node* node) |
100 { | 105 { |
101 if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag)) | 106 if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag)) |
102 return false; | 107 return false; |
103 return hasNoAttributeOrOnlyStyleAttribute(toHTMLElement(node), StyleAttribut
eShouldBeEmpty); | 108 return hasNoAttributeOrOnlyStyleAttribute(toHTMLElement(node), StyleAttribut
eShouldBeEmpty); |
104 } | 109 } |
105 | 110 |
106 static bool isEmptyFontTag(const Node *node) | 111 static bool isEmptyFontTag(const Node *node) |
107 { | 112 { |
108 if (!node || !node->hasTagName(fontTag)) | 113 if (!node || !node->hasTagName(fontTag)) |
109 return false; | 114 return false; |
110 | 115 |
111 const Element *elem = static_cast<const Element *>(node); | 116 const Element *elem = static_cast<const Element *>(node); |
112 NamedNodeMap *map = elem->attributes(true); // true for read-only | 117 NamedNodeMap *map = elem->attributes(true); // true for read-only |
113 if (!map) | 118 if (!map) |
114 return true; | 119 return true; |
115 return map->isEmpty() || (map->length() == 1 && elem->getAttribute(classAttr
) == styleSpanClassString()); | 120 return map->isEmpty() || (map->length() == 1 && elem->getAttribute(classAttr
) == styleSpanClassString()); |
116 } | 121 } |
117 | 122 |
118 static PassRefPtr<Element> createFontElement(Document* document) | 123 static PassRefPtr<Element> createFontElement(Document* document) |
119 { | 124 { |
120 RefPtr<Element> fontNode = createHTMLElement(document, fontTag); | 125 RefPtr<Element> fontNode = createHTMLElement(document, fontTag); |
| 126 fontNode->setAttribute(classAttr, styleSpanClassString()); |
121 return fontNode.release(); | 127 return fontNode.release(); |
122 } | 128 } |
123 | 129 |
124 PassRefPtr<HTMLElement> createStyleSpanElement(Document* document) | 130 PassRefPtr<HTMLElement> createStyleSpanElement(Document* document) |
125 { | 131 { |
126 RefPtr<HTMLElement> styleElement = createHTMLElement(document, spanTag); | 132 RefPtr<HTMLElement> styleElement = createHTMLElement(document, spanTag); |
| 133 styleElement->setAttribute(classAttr, styleSpanClassString()); |
127 return styleElement.release(); | 134 return styleElement.release(); |
128 } | 135 } |
129 | 136 |
130 ApplyStyleCommand::ApplyStyleCommand(Document* document, const EditingStyle* sty
le, EditAction editingAction, EPropertyLevel propertyLevel) | 137 ApplyStyleCommand::ApplyStyleCommand(Document* document, const EditingStyle* sty
le, EditAction editingAction, EPropertyLevel propertyLevel) |
131 : CompositeEditCommand(document) | 138 : CompositeEditCommand(document) |
132 , m_style(style->copy()) | 139 , m_style(style->copy()) |
133 , m_editingAction(editingAction) | 140 , m_editingAction(editingAction) |
134 , m_propertyLevel(propertyLevel) | 141 , m_propertyLevel(propertyLevel) |
135 , m_start(endingSelection().start().downstream()) | 142 , m_start(endingSelection().start().downstream()) |
136 , m_end(endingSelection().end().upstream()) | 143 , m_end(endingSelection().end().upstream()) |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 if (value) { | 399 if (value) { |
393 inlineStyleDecl->removeProperty(CSSPropertyFontSize, true); | 400 inlineStyleDecl->removeProperty(CSSPropertyFontSize, true); |
394 currentFontSize = computedFontSize(node); | 401 currentFontSize = computedFontSize(node); |
395 } | 402 } |
396 if (currentFontSize != desiredFontSize) { | 403 if (currentFontSize != desiredFontSize) { |
397 inlineStyleDecl->setProperty(CSSPropertyFontSize, String::number(des
iredFontSize) + "px", false, false); | 404 inlineStyleDecl->setProperty(CSSPropertyFontSize, String::number(des
iredFontSize) + "px", false, false); |
398 setNodeAttribute(element.get(), styleAttr, inlineStyleDecl->cssText(
)); | 405 setNodeAttribute(element.get(), styleAttr, inlineStyleDecl->cssText(
)); |
399 } | 406 } |
400 if (inlineStyleDecl->isEmpty()) { | 407 if (inlineStyleDecl->isEmpty()) { |
401 removeNodeAttribute(element.get(), styleAttr); | 408 removeNodeAttribute(element.get(), styleAttr); |
402 if (isSpanWithoutAttributesOrUnstyledStyleSpan(element.get())) | 409 // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?
Need a test. |
| 410 if (isUnstyledStyleSpan(element.get())) |
403 unstyledSpans.append(element.release()); | 411 unstyledSpans.append(element.release()); |
404 } | 412 } |
405 } | 413 } |
406 | 414 |
407 size_t size = unstyledSpans.size(); | 415 size_t size = unstyledSpans.size(); |
408 for (size_t i = 0; i < size; ++i) | 416 for (size_t i = 0; i < size; ++i) |
409 removeNodePreservingChildren(unstyledSpans[i].get()); | 417 removeNodePreservingChildren(unstyledSpans[i].get()); |
410 } | 418 } |
411 | 419 |
412 static Node* dummySpanAncestorForNode(const Node* node) | 420 static Node* dummySpanAncestorForNode(const Node* node) |
413 { | 421 { |
414 while (node && (!node->isElementNode() || !isStyleSpanOrSpanWithOnlyStyleAtt
ribute(toElement(node)))) | 422 while (node && !isStyleSpan(node)) |
415 node = node->parentNode(); | 423 node = node->parentNode(); |
416 | 424 |
417 return node ? node->parentNode() : 0; | 425 return node ? node->parentNode() : 0; |
418 } | 426 } |
419 | 427 |
420 void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor) | 428 void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor) |
421 { | 429 { |
422 if (!dummySpanAncestor) | 430 if (!dummySpanAncestor) |
423 return; | 431 return; |
424 | 432 |
425 // Dummy spans are created when text node is split, so that style informatio
n | 433 // Dummy spans are created when text node is split, so that style informatio
n |
426 // can be propagated, which can result in more splitting. If a dummy span ge
ts | 434 // can be propagated, which can result in more splitting. If a dummy span ge
ts |
427 // cloned/split, the new node is always a sibling of it. Therefore, we scan | 435 // cloned/split, the new node is always a sibling of it. Therefore, we scan |
428 // all the children of the dummy's parent | 436 // all the children of the dummy's parent |
429 Node* next; | 437 Node* next; |
430 for (Node* node = dummySpanAncestor->firstChild(); node; node = next) { | 438 for (Node* node = dummySpanAncestor->firstChild(); node; node = next) { |
431 next = node->nextSibling(); | 439 next = node->nextSibling(); |
432 if (isSpanWithoutAttributesOrUnstyledStyleSpan(node)) | 440 if (isUnstyledStyleSpan(node)) |
433 removeNodePreservingChildren(node); | 441 removeNodePreservingChildren(node); |
434 node = next; | 442 node = next; |
435 } | 443 } |
436 } | 444 } |
437 | 445 |
438 HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool b
efore, WritingDirection allowedDirection) | 446 HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool b
efore, WritingDirection allowedDirection) |
439 { | 447 { |
440 // We are allowed to leave the highest ancestor with unicode-bidi unsplit if
it is unicode-bidi: embed and direction: allowedDirection. | 448 // We are allowed to leave the highest ancestor with unicode-bidi unsplit if
it is unicode-bidi: embed and direction: allowedDirection. |
441 // In that case, we return the unsplit ancestor. Otherwise, we return 0. | 449 // In that case, we return the unsplit ancestor. Otherwise, we return 0. |
442 Node* block = enclosingBlock(node); | 450 Node* block = enclosingBlock(node); |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
509 // otherwise it sets the property in the inline style declaration. | 517 // otherwise it sets the property in the inline style declaration. |
510 if (element->hasAttribute(dirAttr)) { | 518 if (element->hasAttribute(dirAttr)) { |
511 // FIXME: If this is a BDO element, we should probably just remove i
t if it has no | 519 // FIXME: If this is a BDO element, we should probably just remove i
t if it has no |
512 // other attributes, like we (should) do with B and I elements. | 520 // other attributes, like we (should) do with B and I elements. |
513 removeNodeAttribute(element, dirAttr); | 521 removeNodeAttribute(element, dirAttr); |
514 } else { | 522 } else { |
515 RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineS
tyleDecl()->copy(); | 523 RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineS
tyleDecl()->copy(); |
516 inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal); | 524 inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal); |
517 inlineStyle->removeProperty(CSSPropertyDirection); | 525 inlineStyle->removeProperty(CSSPropertyDirection); |
518 setNodeAttribute(element, styleAttr, inlineStyle->cssText()); | 526 setNodeAttribute(element, styleAttr, inlineStyle->cssText()); |
519 if (isSpanWithoutAttributesOrUnstyledStyleSpan(element)) | 527 // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?
Need a test. |
| 528 if (isUnstyledStyleSpan(element)) |
520 removeNodePreservingChildren(element); | 529 removeNodePreservingChildren(element); |
521 } | 530 } |
522 } | 531 } |
523 } | 532 } |
524 | 533 |
525 static Node* highestEmbeddingAncestor(Node* startNode, Node* enclosingNode) | 534 static Node* highestEmbeddingAncestor(Node* startNode, Node* enclosingNode) |
526 { | 535 { |
527 for (Node* n = startNode; n && n != enclosingNode; n = n->parentNode()) { | 536 for (Node* n = startNode; n && n != enclosingNode; n = n->parentNode()) { |
528 if (n->isHTMLElement() && getIdentifierValue(computedStyle(n).get(), CSS
PropertyUnicodeBidi) == CSSValueEmbed) | 537 if (n->isHTMLElement() && getIdentifierValue(computedStyle(n).get(), CSS
PropertyUnicodeBidi) == CSSValueEmbed) |
529 return n; | 538 return n; |
(...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
866 | 875 |
867 // unicode-bidi and direction are pushed down separately so don't push down
with other styles | 876 // unicode-bidi and direction are pushed down separately so don't push down
with other styles |
868 Vector<QualifiedName> attributes; | 877 Vector<QualifiedName> attributes; |
869 if (!style->extractConflictingImplicitStyleOfAttributes(element, extractedSt
yle ? EditingStyle::PreserveWritingDirection : EditingStyle::DoNotPreserveWritin
gDirection, | 878 if (!style->extractConflictingImplicitStyleOfAttributes(element, extractedSt
yle ? EditingStyle::PreserveWritingDirection : EditingStyle::DoNotPreserveWritin
gDirection, |
870 extractedStyle, attributes, mode == RemoveAlways ? EditingStyle::Extract
MatchingStyle : EditingStyle::DoNotExtractMatchingStyle)) | 879 extractedStyle, attributes, mode == RemoveAlways ? EditingStyle::Extract
MatchingStyle : EditingStyle::DoNotExtractMatchingStyle)) |
871 return false; | 880 return false; |
872 | 881 |
873 for (size_t i = 0; i < attributes.size(); i++) | 882 for (size_t i = 0; i < attributes.size(); i++) |
874 removeNodeAttribute(element, attributes[i]); | 883 removeNodeAttribute(element, attributes[i]); |
875 | 884 |
876 if (isEmptyFontTag(element) || isSpanWithoutAttributesOrUnstyledStyleSpan(el
ement)) | 885 if (isEmptyFontTag(element) || isSpanWithoutAttributesOrUnstyleStyleSpan(ele
ment)) |
877 removeNodePreservingChildren(element); | 886 removeNodePreservingChildren(element); |
878 | 887 |
879 return true; | 888 return true; |
880 } | 889 } |
881 | 890 |
882 bool ApplyStyleCommand::removeCSSStyle(EditingStyle* style, HTMLElement* element
, InlineStyleRemovalMode mode, EditingStyle* extractedStyle) | 891 bool ApplyStyleCommand::removeCSSStyle(EditingStyle* style, HTMLElement* element
, InlineStyleRemovalMode mode, EditingStyle* extractedStyle) |
883 { | 892 { |
884 ASSERT(style); | 893 ASSERT(style); |
885 ASSERT(element); | 894 ASSERT(element); |
886 | 895 |
887 if (mode == RemoveNone) | 896 if (mode == RemoveNone) |
888 return style->conflictsWithInlineStyleOfElement(element); | 897 return style->conflictsWithInlineStyleOfElement(element); |
889 | 898 |
890 Vector<CSSPropertyID> properties; | 899 Vector<CSSPropertyID> properties; |
891 if (!style->conflictsWithInlineStyleOfElement(element, extractedStyle, prope
rties)) | 900 if (!style->conflictsWithInlineStyleOfElement(element, extractedStyle, prope
rties)) |
892 return false; | 901 return false; |
893 | 902 |
894 CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl(); | 903 CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl(); |
895 ASSERT(inlineStyle); | 904 ASSERT(inlineStyle); |
896 // FIXME: We should use a mass-removal function here but we don't have an un
doable one yet. | 905 // FIXME: We should use a mass-removal function here but we don't have an un
doable one yet. |
897 for (size_t i = 0; i < properties.size(); i++) | 906 for (size_t i = 0; i < properties.size(); i++) |
898 removeCSSProperty(element, properties[i]); | 907 removeCSSProperty(element, properties[i]); |
899 | 908 |
900 // No need to serialize <foo style=""> if we just removed the last css prope
rty | 909 // No need to serialize <foo style=""> if we just removed the last css prope
rty |
901 if (inlineStyle->isEmpty()) | 910 if (inlineStyle->isEmpty()) |
902 removeNodeAttribute(element, styleAttr); | 911 removeNodeAttribute(element, styleAttr); |
903 | 912 |
904 if (isSpanWithoutAttributesOrUnstyledStyleSpan(element)) | 913 if (isSpanWithoutAttributesOrUnstyleStyleSpan(element)) |
905 removeNodePreservingChildren(element); | 914 removeNodePreservingChildren(element); |
906 | 915 |
907 return true; | 916 return true; |
908 } | 917 } |
909 | 918 |
910 HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(Editin
gStyle* style, Node* node) | 919 HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(Editin
gStyle* style, Node* node) |
911 { | 920 { |
912 if (!node) | 921 if (!node) |
913 return 0; | 922 return 0; |
914 | 923 |
(...skipping 573 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1488 } | 1497 } |
1489 else { | 1498 else { |
1490 child = child->nextSibling(); | 1499 child = child->nextSibling(); |
1491 } | 1500 } |
1492 } | 1501 } |
1493 | 1502 |
1494 updateStartEnd(newStart, newEnd); | 1503 updateStartEnd(newStart, newEnd); |
1495 } | 1504 } |
1496 | 1505 |
1497 } | 1506 } |
OLD | NEW |