summaryrefslogtreecommitdiffstats
path: root/WebCore/editing
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-08-11 14:44:44 +0100
committerBen Murdoch <benm@google.com>2010-08-12 19:15:41 +0100
commitdd8bb3de4f353a81954234999f1fea748aee2ea9 (patch)
tree729b52bf09294f0d6c67cd5ea80aee1b727b7bd8 /WebCore/editing
parentf3d41ba51d86bf719c7a65ab5297aea3c17e2d98 (diff)
downloadexternal_webkit-dd8bb3de4f353a81954234999f1fea748aee2ea9.zip
external_webkit-dd8bb3de4f353a81954234999f1fea748aee2ea9.tar.gz
external_webkit-dd8bb3de4f353a81954234999f1fea748aee2ea9.tar.bz2
Merge WebKit at r65072 : Initial merge by git.
Change-Id: Ibcf418498376b2660aacb7f8d46ea7085ef91585
Diffstat (limited to 'WebCore/editing')
-rw-r--r--WebCore/editing/ApplyStyleCommand.cpp297
-rw-r--r--WebCore/editing/ApplyStyleCommand.h8
-rw-r--r--WebCore/editing/CompositeEditCommand.cpp4
-rw-r--r--WebCore/editing/CompositeEditCommand.h3
-rw-r--r--WebCore/editing/Editor.cpp60
-rw-r--r--WebCore/editing/Editor.h7
-rw-r--r--WebCore/editing/HTMLInterchange.h3
-rw-r--r--WebCore/editing/RemoveCSSPropertyCommand.cpp16
-rw-r--r--WebCore/editing/RemoveCSSPropertyCommand.h9
-rw-r--r--WebCore/editing/TextIterator.cpp155
-rw-r--r--WebCore/editing/TextIterator.h25
-rw-r--r--WebCore/editing/htmlediting.h1
-rw-r--r--WebCore/editing/markup.cpp167
-rw-r--r--WebCore/editing/markup.h1
-rw-r--r--WebCore/editing/visible_units.cpp32
15 files changed, 427 insertions, 361 deletions
diff --git a/WebCore/editing/ApplyStyleCommand.cpp b/WebCore/editing/ApplyStyleCommand.cpp
index d865468..a9d1509 100644
--- a/WebCore/editing/ApplyStyleCommand.cpp
+++ b/WebCore/editing/ApplyStyleCommand.cpp
@@ -51,6 +51,26 @@ namespace WebCore {
using namespace HTMLNames;
+static RGBA32 getRGBAFontColor(CSSStyleDeclaration* style)
+{
+ RefPtr<CSSValue> colorValue = style->getPropertyCSSValue(CSSPropertyColor);
+ if (!colorValue)
+ return Color::transparent;
+
+ ASSERT(colorValue->isPrimitiveValue());
+
+ CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue.get());
+ RGBA32 rgba = 0;
+ if (primitiveColor->primitiveType() != CSSPrimitiveValue::CSS_RGBCOLOR) {
+ CSSParser::parseColor(rgba, colorValue->cssText());
+ // Need to take care of named color such as green and black
+ // This code should be removed after https://bugs.webkit.org/show_bug.cgi?id=28282 is fixed.
+ } else
+ rgba = primitiveColor->getRGBA32Value();
+
+ return rgba;
+}
+
class StyleChange {
public:
explicit StyleChange(CSSStyleDeclaration*, const Position&);
@@ -142,7 +162,7 @@ void StyleChange::reconcileTextDecorationProperties(CSSMutableStyleDeclaration*
style->removeProperty(CSSPropertyTextDecoration);
}
-static int getIdentifierValue(CSSMutableStyleDeclaration* style, int propertyID)
+static int getIdentifierValue(CSSStyleDeclaration* style, int propertyID)
{
if (!style)
return 0;
@@ -209,17 +229,8 @@ void StyleChange::extractTextStyles(CSSMutableStyleDeclaration* style)
break;
}
- if (RefPtr<CSSValue> colorValue = style->getPropertyCSSValue(CSSPropertyColor)) {
- ASSERT(colorValue->isPrimitiveValue());
- CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue.get());
- RGBA32 rgba = 0;
- if (primitiveColor->primitiveType() != CSSPrimitiveValue::CSS_RGBCOLOR) {
- CSSParser::parseColor(rgba, colorValue->cssText());
- // Need to take care of named color such as green and black
- // This code should be removed after https://bugs.webkit.org/show_bug.cgi?id=28282 is fixed.
- } else
- rgba = primitiveColor->getRGBA32Value();
- m_applyFontColor = Color(rgba).name();
+ if (style->getPropertyCSSValue(CSSPropertyColor)) {
+ m_applyFontColor = Color(getRGBAFontColor(style)).name();
style->removeProperty(CSSPropertyColor);
}
@@ -382,6 +393,9 @@ RefPtr<CSSMutableStyleDeclaration> getPropertiesNotInComputedStyle(CSSStyleDecla
if (fontWeightIsBold(result.get()) == fontWeightIsBold(computedStyle))
result->removeProperty(CSSPropertyFontWeight);
+ if (getRGBAFontColor(result.get()) == getRGBAFontColor(computedStyle))
+ result->removeProperty(CSSPropertyColor);
+
return result;
}
@@ -477,7 +491,7 @@ void removeStylesAddedByNode(CSSMutableStyleDeclaration* editingStyle, Node* nod
parentStyle->diff(style.get());
style->diff(editingStyle);
}
-
+
ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, EditAction editingAction, EPropertyLevel propertyLevel)
: CompositeEditCommand(document)
, m_style(style->makeMutable())
@@ -793,7 +807,7 @@ void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor)
}
}
-HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool before, RefPtr<CSSPrimitiveValue> allowedDirection)
+HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool before, int allowedDirection)
{
// We are allowed to leave the highest ancestor with unicode-bidi unsplit if it is unicode-bidi: embed and direction: allowedDirection.
// In that case, we return the unsplit ancestor. Otherwise, we return 0.
@@ -803,16 +817,13 @@ HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool b
Node* highestAncestorWithUnicodeBidi = 0;
Node* nextHighestAncestorWithUnicodeBidi = 0;
- RefPtr<CSSPrimitiveValue> highestAncestorUnicodeBidi;
+ int highestAncestorUnicodeBidi = 0;
for (Node* n = node->parent(); n != block; n = n->parent()) {
- RefPtr<CSSValue> unicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
- if (unicodeBidi) {
- ASSERT(unicodeBidi->isPrimitiveValue());
- if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() != CSSValueNormal) {
- highestAncestorUnicodeBidi = static_cast<CSSPrimitiveValue*>(unicodeBidi.get());
- nextHighestAncestorWithUnicodeBidi = highestAncestorWithUnicodeBidi;
- highestAncestorWithUnicodeBidi = n;
- }
+ int unicodeBidi = getIdentifierValue(computedStyle(n).get(), CSSPropertyUnicodeBidi);
+ if (unicodeBidi && unicodeBidi != CSSValueNormal) {
+ highestAncestorUnicodeBidi = unicodeBidi;
+ nextHighestAncestorWithUnicodeBidi = highestAncestorWithUnicodeBidi;
+ highestAncestorWithUnicodeBidi = n;
}
}
@@ -821,16 +832,14 @@ HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool b
HTMLElement* unsplitAncestor = 0;
- if (allowedDirection && highestAncestorUnicodeBidi->getIdent() != CSSValueBidiOverride) {
- RefPtr<CSSValue> highestAncestorDirection = computedStyle(highestAncestorWithUnicodeBidi)->getPropertyCSSValue(CSSPropertyDirection);
- ASSERT(highestAncestorDirection->isPrimitiveValue());
- if (static_cast<CSSPrimitiveValue*>(highestAncestorDirection.get())->getIdent() == allowedDirection->getIdent() && highestAncestorWithUnicodeBidi->isHTMLElement()) {
- if (!nextHighestAncestorWithUnicodeBidi)
- return static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi);
+ if (allowedDirection && highestAncestorUnicodeBidi != CSSValueBidiOverride
+ && getIdentifierValue(computedStyle(highestAncestorWithUnicodeBidi).get(), CSSPropertyDirection) == allowedDirection
+ && highestAncestorWithUnicodeBidi->isHTMLElement()) {
+ if (!nextHighestAncestorWithUnicodeBidi)
+ return static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi);
- unsplitAncestor = static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi);
- highestAncestorWithUnicodeBidi = nextHighestAncestorWithUnicodeBidi;
- }
+ unsplitAncestor = static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi);
+ highestAncestorWithUnicodeBidi = nextHighestAncestorWithUnicodeBidi;
}
// Split every ancestor through highest ancestor with embedding.
@@ -852,39 +861,34 @@ void ApplyStyleCommand::removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsp
if (!block)
return;
- Node* n = node->parent();
- while (n != block && n != unsplitAncestor) {
- Node* parent = n->parent();
- if (!n->isStyledElement()) {
- n = parent;
+ Node* parent = 0;
+ for (Node* n = node->parent(); n != block && n != unsplitAncestor; n = parent) {
+ parent = n->parent();
+ if (!n->isStyledElement())
continue;
- }
StyledElement* element = static_cast<StyledElement*>(n);
- RefPtr<CSSValue> unicodeBidi = computedStyle(element)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
- if (unicodeBidi) {
- ASSERT(unicodeBidi->isPrimitiveValue());
- if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() != CSSValueNormal) {
- // FIXME: This code should really consider the mapped attribute 'dir', the inline style declaration,
- // and all matching style rules in order to determine how to best set the unicode-bidi property to 'normal'.
- // For now, it assumes that if the 'dir' attribute is present, then removing it will suffice, and
- // otherwise it sets the property in the inline style declaration.
- if (element->hasAttribute(dirAttr)) {
- // FIXME: If this is a BDO element, we should probably just remove it if it has no
- // other attributes, like we (should) do with B and I elements.
- removeNodeAttribute(element, dirAttr);
- } else {
- RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
- inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal);
- inlineStyle->removeProperty(CSSPropertyDirection);
- setNodeAttribute(element, styleAttr, inlineStyle->cssText());
- // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan? Need a test.
- if (isUnstyledStyleSpan(element))
- removeNodePreservingChildren(element);
- }
- }
+ int unicodeBidi = getIdentifierValue(computedStyle(element).get(), CSSPropertyUnicodeBidi);
+ if (!unicodeBidi || unicodeBidi == CSSValueNormal)
+ continue;
+
+ // FIXME: This code should really consider the mapped attribute 'dir', the inline style declaration,
+ // and all matching style rules in order to determine how to best set the unicode-bidi property to 'normal'.
+ // For now, it assumes that if the 'dir' attribute is present, then removing it will suffice, and
+ // otherwise it sets the property in the inline style declaration.
+ if (element->hasAttribute(dirAttr)) {
+ // FIXME: If this is a BDO element, we should probably just remove it if it has no
+ // other attributes, like we (should) do with B and I elements.
+ removeNodeAttribute(element, dirAttr);
+ } else {
+ RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
+ inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal);
+ inlineStyle->removeProperty(CSSPropertyDirection);
+ setNodeAttribute(element, styleAttr, inlineStyle->cssText());
+ // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan? Need a test.
+ if (isUnstyledStyleSpan(element))
+ removeNodePreservingChildren(element);
}
- n = parent;
}
}
@@ -931,21 +935,16 @@ void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style)
endDummySpanAncestor = dummySpanAncestorForNode(end.node());
}
- RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
- RefPtr<CSSValue> direction;
+ int unicodeBidi = getIdentifierValue(style, CSSPropertyUnicodeBidi);
+ int direction = 0;
HTMLElement* startUnsplitAncestor = 0;
HTMLElement* endUnsplitAncestor = 0;
if (unicodeBidi) {
- RefPtr<CSSPrimitiveValue> allowedDirection;
- ASSERT(unicodeBidi->isPrimitiveValue());
- if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() == CSSValueEmbed) {
- // Leave alone an ancestor that provides the desired single level embedding, if there is one.
- direction = style->getPropertyCSSValue(CSSPropertyDirection);
- ASSERT(direction->isPrimitiveValue());
- allowedDirection = static_cast<CSSPrimitiveValue*>(direction.get());
- }
- startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.node(), true, allowedDirection);
- endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.node(), false, allowedDirection);
+ // Leave alone an ancestor that provides the desired single level embedding, if there is one.
+ if (unicodeBidi == CSSValueEmbed)
+ direction = getIdentifierValue(style, CSSPropertyDirection);
+ startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.node(), true, direction);
+ endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.node(), false, direction);
removeEmbeddingUpToEnclosingBlock(start.node(), startUnsplitAncestor);
removeEmbeddingUpToEnclosingBlock(end.node(), endUnsplitAncestor);
}
@@ -969,7 +968,7 @@ void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style)
if (embeddingRemoveStart != removeStart || embeddingRemoveEnd != end) {
RefPtr<CSSMutableStyleDeclaration> embeddingStyle = CSSMutableStyleDeclaration::create();
embeddingStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed);
- embeddingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
+ embeddingStyle->setProperty(CSSPropertyDirection, direction);
if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0)
removeInlineStyle(embeddingStyle, embeddingRemoveStart, embeddingRemoveEnd);
@@ -1008,29 +1007,17 @@ void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style)
// Avoid applying the unicode-bidi and direction properties beneath ancestors that already have them.
Node* startEnclosingBlock = enclosingBlock(start.node());
for (Node* n = start.node(); n != startEnclosingBlock; n = n->parent()) {
- if (n->isHTMLElement()) {
- RefPtr<CSSValue> ancestorUnicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
- if (ancestorUnicodeBidi) {
- ASSERT(ancestorUnicodeBidi->isPrimitiveValue());
- if (static_cast<CSSPrimitiveValue*>(ancestorUnicodeBidi.get())->getIdent() == CSSValueEmbed) {
- embeddingApplyStart = positionInParentAfterNode(n);
- break;
- }
- }
+ if (n->isHTMLElement() && getIdentifierValue(computedStyle(n).get(), CSSPropertyUnicodeBidi) == CSSValueEmbed) {
+ embeddingApplyStart = positionInParentAfterNode(n);
+ break;
}
}
Node* endEnclosingBlock = enclosingBlock(end.node());
for (Node* n = end.node(); n != endEnclosingBlock; n = n->parent()) {
- if (n->isHTMLElement()) {
- RefPtr<CSSValue> ancestorUnicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
- if (ancestorUnicodeBidi) {
- ASSERT(ancestorUnicodeBidi->isPrimitiveValue());
- if (static_cast<CSSPrimitiveValue*>(ancestorUnicodeBidi.get())->getIdent() == CSSValueEmbed) {
- embeddingApplyEnd = positionInParentBeforeNode(n);
- break;
- }
- }
+ if (n->isHTMLElement() && getIdentifierValue(computedStyle(n).get(), CSSPropertyUnicodeBidi) == CSSValueEmbed) {
+ embeddingApplyEnd = positionInParentBeforeNode(n);
+ break;
}
}
}
@@ -1039,7 +1026,7 @@ void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style)
if (embeddingApplyStart.isNotNull() && embeddingApplyEnd.isNotNull()) {
RefPtr<CSSMutableStyleDeclaration> embeddingStyle = CSSMutableStyleDeclaration::create();
embeddingStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed);
- embeddingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
+ embeddingStyle->setProperty(CSSPropertyDirection, direction);
applyInlineStyleToRange(embeddingStyle.get(), embeddingApplyStart, embeddingApplyEnd);
}
@@ -1318,9 +1305,9 @@ bool ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclaration* style, HTMLEl
removed = true;
if (mode == RemoveNone)
return true;
- removeCSSProperty(decl, propertyID);
+ removeCSSProperty(elem, propertyID);
if (propertyID == CSSPropertyUnicodeBidi && !decl->getPropertyValue(CSSPropertyDirection).isEmpty())
- removeCSSProperty(decl, CSSPropertyDirection);
+ removeCSSProperty(elem, CSSPropertyDirection);
}
}
@@ -1380,32 +1367,7 @@ PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractTextDecorationS
RefPtr<CSSValue> property = style->getPropertyCSSValue(CSSPropertyTextDecoration);
if (property && !equalIgnoringCase(property->cssText(), "none"))
- removeCSSProperty(style.get(), CSSPropertyTextDecoration);
-
- return textDecorationStyle.release();
-}
-
-PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractAndNegateTextDecorationStyle(Node* node)
-{
- ASSERT(node);
- ASSERT(node->isElementNode());
-
- // non-html elements not handled yet
- if (!node->isHTMLElement())
- return 0;
-
- RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node);
- ASSERT(nodeStyle);
-
- int properties[1] = { CSSPropertyTextDecoration };
- RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = nodeStyle->copyPropertiesInSet(properties, 1);
-
- RefPtr<CSSValue> property = nodeStyle->getPropertyCSSValue(CSSPropertyTextDecoration);
- if (property && !equalIgnoringCase(property->cssText(), "none")) {
- RefPtr<CSSMutableStyleDeclaration> newStyle = textDecorationStyle->copy();
- newStyle->setProperty(CSSPropertyTextDecoration, "none");
- applyTextDecorationStyle(node, newStyle.get());
- }
+ removeCSSProperty(element, CSSPropertyTextDecoration);
return textDecorationStyle.release();
}
@@ -1443,7 +1405,7 @@ void ApplyStyleCommand::applyTextDecorationStyle(Node *node, CSSMutableStyleDecl
surroundNodeRangeWithElement(node, node, createHTMLElement(document(), sTag));
}
-void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node* targetNode, bool forceNegate)
+void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node* targetNode)
{
ASSERT(targetNode);
Node* highestAncestor = highestAncestorWithTextDecoration(targetNode);
@@ -1455,7 +1417,7 @@ void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node* targetNode,
while (current != targetNode) {
ASSERT(current);
ASSERT(current->contains(targetNode));
- RefPtr<CSSMutableStyleDeclaration> decoration = forceNegate ? extractAndNegateTextDecorationStyle(current) : extractTextDecorationStyle(current);
+ RefPtr<CSSMutableStyleDeclaration> decoration = extractTextDecorationStyle(current);
// The inner loop will go through children on each level
Node* child = current->firstChild();
@@ -1476,34 +1438,6 @@ void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node* targetNode,
}
}
-void ApplyStyleCommand::pushDownTextDecorationStyleAtBoundaries(const Position &start, const Position &end)
-{
- // We need to work in two passes. First we push down any inline
- // styles that set text decoration. Then we look for any remaining
- // styles (caused by stylesheets) and explicitly negate text
- // decoration while pushing down.
-
- pushDownTextDecorationStyleAroundNode(start.node(), false);
- updateLayout();
- pushDownTextDecorationStyleAroundNode(start.node(), true);
-
- pushDownTextDecorationStyleAroundNode(end.node(), false);
- updateLayout();
- pushDownTextDecorationStyleAroundNode(end.node(), true);
-}
-
-// FIXME: Why does this exist? Callers should either use lastOffsetForEditing or lastOffsetInNode
-static int maxRangeOffset(Node *n)
-{
- if (n->offsetInCharacters())
- return n->maxCharacterOffset();
-
- if (n->isElementNode())
- return n->childNodeCount();
-
- return 1;
-}
-
void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration> style, const Position &start, const Position &end)
{
ASSERT(start.isNotNull());
@@ -1515,7 +1449,8 @@ void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration>
RefPtr<CSSValue> textDecorationSpecialProperty = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
if (textDecorationSpecialProperty) {
- pushDownTextDecorationStyleAtBoundaries(start.downstream(), end.upstream());
+ pushDownTextDecorationStyleAroundNode(start.downstream().node());
+ pushDownTextDecorationStyleAroundNode(end.upstream().node());
style = style->copy();
style->setProperty(CSSPropertyTextDecoration, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSSPropertyWebkitTextDecorationsInEffect));
}
@@ -1545,8 +1480,8 @@ void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration>
// Since elem must have been fully selected, and it is at the end
// of the selection, it is clear we can set the new e offset to
// the max range offset of prev.
- ASSERT(e.deprecatedEditingOffset() >= maxRangeOffset(e.node()));
- e = Position(prev, maxRangeOffset(prev));
+ ASSERT(e.deprecatedEditingOffset() >= lastOffsetForEditing(e.node()));
+ e = Position(prev, lastOffsetForEditing(prev));
}
}
}
@@ -1808,33 +1743,6 @@ void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElemen
setNodeAttribute(block, styleAttr, cssText);
}
-static bool fontColorChangesComputedStyle(const Color& computedStyleColor, StyleChange styleChange)
-{
- if (styleChange.applyFontColor()) {
- if (Color(styleChange.fontColor()) != computedStyleColor)
- return true;
- }
- return false;
-}
-
-static bool fontSizeChangesComputedStyle(RenderStyle* computedStyle, StyleChange styleChange)
-{
- if (styleChange.applyFontSize()) {
- if (styleChange.fontSize().toInt() != computedStyle->fontSize())
- return true;
- }
- return false;
-}
-
-static bool fontFaceChangesComputedStyle(RenderStyle* computedStyle, StyleChange styleChange)
-{
- if (styleChange.applyFontFace()) {
- if (computedStyle->fontDescription().family().family().string() != styleChange.fontFace())
- return true;
- }
- return false;
-}
-
void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *startNode, Node *endNode)
{
if (m_removeOnly)
@@ -1842,27 +1750,18 @@ void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style
StyleChange styleChange(style, Position(startNode, 0));
- //
// Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
- //
if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) {
RefPtr<Element> fontElement = createFontElement(document());
- RenderStyle* computedStyle = startNode->computedStyle();
-
- // We only want to insert a font element if it will end up changing the style of the
- // text somehow. Otherwise it will be a garbage node that will create problems for us
- // most notably when we apply a blockquote style for a message reply.
- if (fontColorChangesComputedStyle(computedStyle->color(), styleChange)
- || fontFaceChangesComputedStyle(computedStyle, styleChange)
- || fontSizeChangesComputedStyle(computedStyle, styleChange)) {
- if (styleChange.applyFontColor())
- fontElement->setAttribute(colorAttr, styleChange.fontColor());
- if (styleChange.applyFontFace())
- fontElement->setAttribute(faceAttr, styleChange.fontFace());
- if (styleChange.applyFontSize())
- fontElement->setAttribute(sizeAttr, styleChange.fontSize());
- surroundNodeRangeWithElement(startNode, endNode, fontElement.get());
- }
+
+ if (styleChange.applyFontColor())
+ fontElement->setAttribute(colorAttr, styleChange.fontColor());
+ if (styleChange.applyFontFace())
+ fontElement->setAttribute(faceAttr, styleChange.fontFace());
+ if (styleChange.applyFontSize())
+ fontElement->setAttribute(sizeAttr, styleChange.fontSize());
+
+ surroundNodeRangeWithElement(startNode, endNode, fontElement.get());
}
if (styleChange.cssStyle().length()) {
diff --git a/WebCore/editing/ApplyStyleCommand.h b/WebCore/editing/ApplyStyleCommand.h
index 0aa8110..86c24da 100644
--- a/WebCore/editing/ApplyStyleCommand.h
+++ b/WebCore/editing/ApplyStyleCommand.h
@@ -82,11 +82,9 @@ private:
bool nodeFullySelected(Node*, const Position& start, const Position& end) const;
bool nodeFullyUnselected(Node*, const Position& start, const Position& end) const;
PassRefPtr<CSSMutableStyleDeclaration> extractTextDecorationStyle(Node*);
- PassRefPtr<CSSMutableStyleDeclaration> extractAndNegateTextDecorationStyle(Node*);
void applyTextDecorationStyle(Node*, CSSMutableStyleDeclaration *style);
- void pushDownTextDecorationStyleAroundNode(Node*, bool forceNegate);
- void pushDownTextDecorationStyleAtBoundaries(const Position& start, const Position& end);
-
+ void pushDownTextDecorationStyleAroundNode(Node*);
+
// style-application helpers
void applyBlockStyle(CSSMutableStyleDeclaration*);
void applyRelativeFontStyleChange(CSSMutableStyleDeclaration*);
@@ -108,7 +106,7 @@ private:
float computedFontSize(const Node*);
void joinChildTextNodes(Node*, const Position& start, const Position& end);
- HTMLElement* splitAncestorsWithUnicodeBidi(Node*, bool before, RefPtr<CSSPrimitiveValue> allowedDirection);
+ HTMLElement* splitAncestorsWithUnicodeBidi(Node*, bool before, int allowedDirection);
void removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsplitAncestor);
void updateStartEnd(const Position& newStart, const Position& newEnd);
diff --git a/WebCore/editing/CompositeEditCommand.cpp b/WebCore/editing/CompositeEditCommand.cpp
index c5a8959..5ec87d6 100644
--- a/WebCore/editing/CompositeEditCommand.cpp
+++ b/WebCore/editing/CompositeEditCommand.cpp
@@ -370,9 +370,9 @@ void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bo
applyCommandToComposite(DeleteSelectionCommand::create(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements));
}
-void CompositeEditCommand::removeCSSProperty(PassRefPtr<CSSMutableStyleDeclaration> style, CSSPropertyID property)
+void CompositeEditCommand::removeCSSProperty(PassRefPtr<StyledElement> element, CSSPropertyID property)
{
- applyCommandToComposite(RemoveCSSPropertyCommand::create(document(), style, property));
+ applyCommandToComposite(RemoveCSSPropertyCommand::create(document(), element, property));
}
void CompositeEditCommand::removeNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute)
diff --git a/WebCore/editing/CompositeEditCommand.h b/WebCore/editing/CompositeEditCommand.h
index 0cceaaa..9d69925 100644
--- a/WebCore/editing/CompositeEditCommand.h
+++ b/WebCore/editing/CompositeEditCommand.h
@@ -34,6 +34,7 @@ namespace WebCore {
class CSSStyleDeclaration;
class HTMLElement;
+class StyledElement;
class Text;
class CompositeEditCommand : public EditCommand {
@@ -68,7 +69,7 @@ protected:
void rebalanceWhitespace();
void rebalanceWhitespaceAt(const Position&);
void prepareWhitespaceAtPositionForSplit(Position&);
- void removeCSSProperty(PassRefPtr<CSSMutableStyleDeclaration>, CSSPropertyID);
+ void removeCSSProperty(PassRefPtr<StyledElement>, CSSPropertyID);
void removeNodeAttribute(PassRefPtr<Element>, const QualifiedName& attribute);
void removeChildrenInRange(PassRefPtr<Node>, unsigned from, unsigned to);
virtual void removeNode(PassRefPtr<Node>);
diff --git a/WebCore/editing/Editor.cpp b/WebCore/editing/Editor.cpp
index 44d8d5f..7a1ad4d 100644
--- a/WebCore/editing/Editor.cpp
+++ b/WebCore/editing/Editor.cpp
@@ -66,6 +66,7 @@
#include "Settings.h"
#include "Sound.h"
#include "Text.h"
+#include "TextEvent.h"
#include "TextIterator.h"
#include "TypingCommand.h"
#include "UserTypingGestureIndicator.h"
@@ -127,6 +128,26 @@ void Editor::handleInputMethodKeydown(KeyboardEvent* event)
c->handleInputMethodKeydown(event);
}
+bool Editor::handleTextEvent(TextEvent* event)
+{
+ if (event->isPaste()) {
+ if (event->pastingFragment())
+ replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle());
+ else
+ replaceSelectionWithText(event->data(), false, event->shouldSmartReplace());
+ return true;
+ }
+
+ String data = event->data();
+ if (data == "\n") {
+ if (event->isLineBreak())
+ return insertLineBreak();
+ return insertParagraphSeparator();
+ }
+
+ return insertTextWithoutSendingTextEvent(data, false, event);
+}
+
bool Editor::canEdit() const
{
return m_frame->selection()->isContentEditable();
@@ -282,11 +303,29 @@ void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete));
}
+void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace)
+{
+ Node* target = findEventTargetFromSelection();
+ if (!target)
+ return;
+ ExceptionCode ec = 0;
+ target->dispatchEvent(TextEvent::createForPlainTextPaste(m_frame->domWindow(), pastingText, smartReplace), ec);
+}
+
+void Editor::pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment, bool smartReplace, bool matchStyle)
+{
+ Node* target = findEventTargetFromSelection();
+ if (!target)
+ return;
+ ExceptionCode ec = 0;
+ target->dispatchEvent(TextEvent::createForFragmentPaste(m_frame->domWindow(), pastingFragment, smartReplace, matchStyle), ec);
+}
+
void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard)
{
String text = pasteboard->plainText(m_frame);
if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
- replaceSelectionWithText(text, false, canSmartReplaceWithPasteboard(pasteboard));
+ pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard));
}
void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
@@ -295,7 +334,7 @@ void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
bool chosePlainText;
RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText);
if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
- replaceSelectionWithFragment(fragment, false, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
+ pasteAsFragment(fragment, canSmartReplaceWithPasteboard(pasteboard), chosePlainText);
}
bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard)
@@ -706,12 +745,9 @@ void Editor::clearLastEditCommand()
// the event handler NOT setting the return value to false
bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
{
- Node* target = m_frame->selection()->start().element();
- if (!target)
- target = m_frame->document()->body();
+ Node* target = findEventTargetFromSelection();
if (!target)
return true;
- target = target->shadowAncestorNode();
RefPtr<Clipboard> clipboard = newGeneralClipboard(policy, m_frame);
@@ -726,6 +762,16 @@ bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPoli
return !noDefaultProcessing;
}
+Node* Editor::findEventTargetFromSelection() const
+{
+ Node* target = m_frame->selection()->start().element();
+ if (!target)
+ target = m_frame->document()->body();
+ if (!target)
+ return 0;
+ return target->shadowAncestorNode();
+}
+
void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
{
switch (m_frame->selection()->selectionType()) {
@@ -2359,7 +2405,7 @@ bool Editor::spellCheckingEnabledInFocusedNode() const
const Node* node = frame()->document()->focusedNode();
while (node) {
if (node->isElementNode()) {
- const WebCore::AtomicString& value = static_cast<const Element*>(node)->getAttribute(spellcheckAttr);
+ const WTF::AtomicString& value = static_cast<const Element*>(node)->getAttribute(spellcheckAttr);
if (equalIgnoringCase(value, "true"))
return true;
if (equalIgnoringCase(value, "false"))
diff --git a/WebCore/editing/Editor.h b/WebCore/editing/Editor.h
index db249e7..13e6df5 100644
--- a/WebCore/editing/Editor.h
+++ b/WebCore/editing/Editor.h
@@ -49,6 +49,7 @@ class KillRing;
class Pasteboard;
class SimpleFontData;
class Text;
+class TextEvent;
struct CompositionUnderline {
CompositionUnderline()
@@ -77,6 +78,7 @@ public:
void handleKeyboardEvent(KeyboardEvent*);
void handleInputMethodKeydown(KeyboardEvent*);
+ bool handleTextEvent(TextEvent*);
bool canEdit() const;
bool canEditRichly() const;
@@ -293,6 +295,10 @@ public:
PassRefPtr<Range> nextVisibleRange(Range*, const String&, bool forward, bool caseFlag, bool wrapFlag);
void addToKillRing(Range*, bool prepend);
+
+ void pasteAsFragment(PassRefPtr<DocumentFragment>, bool smartReplace, bool matchStyle);
+ void pasteAsPlainText(const String&, bool smartReplace);
+
private:
Frame* m_frame;
OwnPtr<DeleteButtonController> m_deleteButtonController;
@@ -326,6 +332,7 @@ private:
PassRefPtr<Range> lastVisibleRange(const String&, bool caseFlag);
void changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle);
+ Node* findEventTargetFromSelection() const;
};
inline void Editor::setStartNewKillRingSequence(bool flag)
diff --git a/WebCore/editing/HTMLInterchange.h b/WebCore/editing/HTMLInterchange.h
index 3b68efb..4029ea2 100644
--- a/WebCore/editing/HTMLInterchange.h
+++ b/WebCore/editing/HTMLInterchange.h
@@ -26,9 +26,10 @@
#ifndef HTMLInterchange_h
#define HTMLInterchange_h
+#include <wtf/Forward.h>
+
namespace WebCore {
-class String;
class Text;
#define AppleInterchangeNewline "Apple-interchange-newline"
diff --git a/WebCore/editing/RemoveCSSPropertyCommand.cpp b/WebCore/editing/RemoveCSSPropertyCommand.cpp
index 17870a7..8b37db8 100644
--- a/WebCore/editing/RemoveCSSPropertyCommand.cpp
+++ b/WebCore/editing/RemoveCSSPropertyCommand.cpp
@@ -31,25 +31,27 @@
namespace WebCore {
-RemoveCSSPropertyCommand::RemoveCSSPropertyCommand(Document* document, PassRefPtr<CSSMutableStyleDeclaration> style, CSSPropertyID property)
+RemoveCSSPropertyCommand::RemoveCSSPropertyCommand(Document* document, PassRefPtr<StyledElement> element, CSSPropertyID property)
: SimpleEditCommand(document)
- , m_style(style)
+ , m_element(element)
, m_property(property)
, m_important(false)
{
- ASSERT(m_style);
+ ASSERT(m_element);
}
void RemoveCSSPropertyCommand::doApply()
{
- m_oldValue = m_style->getPropertyValue(m_property);
- m_important = m_style->getPropertyPriority(m_property);
- m_style->removeProperty(m_property);
+ CSSMutableStyleDeclaration* style = m_element->inlineStyleDecl();
+ m_oldValue = style->getPropertyValue(m_property);
+ m_important = style->getPropertyPriority(m_property);
+ style->removeProperty(m_property);
}
void RemoveCSSPropertyCommand::doUnapply()
{
- m_style->setProperty(m_property, m_oldValue, m_important);
+ CSSMutableStyleDeclaration* style = m_element->inlineStyleDecl();
+ style->setProperty(m_property, m_oldValue, m_important);
}
} // namespace WebCore
diff --git a/WebCore/editing/RemoveCSSPropertyCommand.h b/WebCore/editing/RemoveCSSPropertyCommand.h
index 836f9d7..46e0498 100644
--- a/WebCore/editing/RemoveCSSPropertyCommand.h
+++ b/WebCore/editing/RemoveCSSPropertyCommand.h
@@ -29,23 +29,24 @@
#include "EditCommand.h"
#include "CSSMutableStyleDeclaration.h"
#include "CSSPropertyNames.h"
+#include "StyledElement.h"
namespace WebCore {
class RemoveCSSPropertyCommand : public SimpleEditCommand {
public:
- static PassRefPtr<RemoveCSSPropertyCommand> create(Document* document, PassRefPtr<CSSMutableStyleDeclaration> style, CSSPropertyID property)
+ static PassRefPtr<RemoveCSSPropertyCommand> create(Document* document, PassRefPtr<StyledElement> element, CSSPropertyID property)
{
- return adoptRef(new RemoveCSSPropertyCommand(document, style, property));
+ return adoptRef(new RemoveCSSPropertyCommand(document, element, property));
}
private:
- RemoveCSSPropertyCommand(Document*, PassRefPtr<CSSMutableStyleDeclaration>, CSSPropertyID property);
+ RemoveCSSPropertyCommand(Document*, PassRefPtr<StyledElement>, CSSPropertyID property);
virtual void doApply();
virtual void doUnapply();
- RefPtr<CSSMutableStyleDeclaration> m_style;
+ RefPtr<StyledElement> m_element;
CSSPropertyID m_property;
String m_oldValue;
bool m_important;
diff --git a/WebCore/editing/TextIterator.cpp b/WebCore/editing/TextIterator.cpp
index f3bd9df..9589bff 100644
--- a/WebCore/editing/TextIterator.cpp
+++ b/WebCore/editing/TextIterator.cpp
@@ -37,6 +37,7 @@
#include "RenderTableCell.h"
#include "RenderTableRow.h"
#include "RenderTextControl.h"
+#include "RenderTextFragment.h"
#include "VisiblePosition.h"
#include "visible_units.h"
@@ -253,10 +254,13 @@ TextIterator::TextIterator()
, m_positionNode(0)
, m_textCharacters(0)
, m_textLength(0)
+ , m_remainingTextBox(0)
+ , m_firstLetterText(0)
, m_lastCharacter(0)
, m_emitsCharactersBetweenAllVisiblePositions(false)
, m_entersTextControls(false)
, m_emitsTextWithoutTranscoding(false)
+ , m_handledFirstLetter(false)
{
}
@@ -268,10 +272,16 @@ TextIterator::TextIterator(const Range* r, TextIteratorBehavior behavior)
, m_positionNode(0)
, m_textCharacters(0)
, m_textLength(0)
+ , m_remainingTextBox(0)
+ , m_firstLetterText(0)
, m_emitsCharactersBetweenAllVisiblePositions(behavior & TextIteratorEmitsCharactersBetweenAllVisiblePositions)
, m_entersTextControls(behavior & TextIteratorEntersTextControls)
, m_emitsTextWithoutTranscoding(behavior & TextIteratorEmitsTextsWithoutTranscoding)
+ , m_handledFirstLetter(false)
{
+ // FIXME: should support TextIteratorEndsAtEditingBoundary http://webkit.org/b/43609
+ ASSERT(behavior != TextIteratorEndsAtEditingBoundary);
+
if (!r)
return;
@@ -344,6 +354,12 @@ void TextIterator::advance()
return;
}
+ if (!m_textBox && m_remainingTextBox) {
+ m_textBox = m_remainingTextBox;
+ m_remainingTextBox = 0;
+ m_firstLetterText = 0;
+ m_offset = 0;
+ }
// handle remembered text box
if (m_textBox) {
handleTextBox();
@@ -417,6 +433,8 @@ void TextIterator::advance()
pushFullyClippedState(m_fullyClippedStack, m_node);
m_handledNode = false;
m_handledChildren = false;
+ m_handledFirstLetter = false;
+ m_firstLetterText = 0;
// how would this ever be?
if (m_positionNode)
@@ -435,8 +453,6 @@ bool TextIterator::handleTextNode()
return false;
RenderText* renderer = toRenderText(m_node->renderer());
- if (renderer->style()->visibility() != VISIBLE)
- return false;
m_lastTextNode = m_node;
String str = renderer->text();
@@ -444,10 +460,22 @@ bool TextIterator::handleTextNode()
// handle pre-formatted text
if (!renderer->style()->collapseWhiteSpace()) {
int runStart = m_offset;
- if (m_lastTextNodeEndedWithCollapsedSpace) {
+ if (m_lastTextNodeEndedWithCollapsedSpace && hasVisibleTextNode(renderer)) {
emitCharacter(' ', m_node, 0, runStart, runStart);
return false;
}
+ if (!m_handledFirstLetter && renderer->isTextFragment()) {
+ handleTextNodeFirstLetter(static_cast<RenderTextFragment*>(renderer));
+ if (m_firstLetterText) {
+ String firstLetter = m_firstLetterText->text();
+ emitText(m_node, m_firstLetterText, m_offset, firstLetter.length());
+ m_firstLetterText = 0;
+ m_textBox = 0;
+ return false;
+ }
+ }
+ if (renderer->style()->visibility() != VISIBLE)
+ return false;
int strLength = str.length();
int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX;
int runEnd = min(strLength, end);
@@ -460,6 +488,15 @@ bool TextIterator::handleTextNode()
}
if (!renderer->firstTextBox() && str.length() > 0) {
+ if (!m_handledFirstLetter && renderer->isTextFragment()) {
+ handleTextNodeFirstLetter(static_cast<RenderTextFragment*>(renderer));
+ if (m_firstLetterText) {
+ handleTextBox();
+ return false;
+ }
+ }
+ if (renderer->style()->visibility() != VISIBLE)
+ return false;
m_lastTextNodeEndedWithCollapsedSpace = true; // entire block is collapsed space
return true;
}
@@ -475,13 +512,19 @@ bool TextIterator::handleTextNode()
}
m_textBox = renderer->containsReversedText() ? (m_sortedTextBoxes.isEmpty() ? 0 : m_sortedTextBoxes[0]) : renderer->firstTextBox();
+ if (!m_handledFirstLetter && renderer->isTextFragment() && !m_offset)
+ handleTextNodeFirstLetter(static_cast<RenderTextFragment*>(renderer));
handleTextBox();
return true;
}
void TextIterator::handleTextBox()
{
- RenderText* renderer = toRenderText(m_node->renderer());
+ RenderText* renderer = m_firstLetterText ? m_firstLetterText : toRenderText(m_node->renderer());
+ if (renderer->style()->visibility() != VISIBLE) {
+ m_textBox = 0;
+ return;
+ }
String str = renderer->text();
int start = m_offset;
int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX;
@@ -527,7 +570,7 @@ void TextIterator::handleTextBox()
subrunEnd = runEnd;
m_offset = subrunEnd;
- emitText(m_node, runStart, subrunEnd);
+ emitText(m_node, renderer, runStart, subrunEnd);
}
// If we are doing a subrun that doesn't go to the end of the text box,
@@ -549,6 +592,33 @@ void TextIterator::handleTextBox()
if (renderer->containsReversedText())
++m_sortedTextBoxesPosition;
}
+ if (!m_textBox && m_remainingTextBox) {
+ m_textBox = m_remainingTextBox;
+ m_remainingTextBox = 0;
+ m_firstLetterText = 0;
+ m_offset = 0;
+ handleTextBox();
+ }
+}
+
+void TextIterator::handleTextNodeFirstLetter(RenderTextFragment* renderer)
+{
+ if (renderer->firstLetter()) {
+ RenderObject* r = renderer->firstLetter();
+ if (r->style()->visibility() != VISIBLE)
+ return;
+ for (RenderObject *currChild = r->firstChild(); currChild; currChild->nextSibling()) {
+ if (currChild->isText()) {
+ RenderText* firstLetter = toRenderText(currChild);
+ m_handledFirstLetter = true;
+ m_remainingTextBox = m_textBox;
+ m_textBox = firstLetter->firstTextBox();
+ m_firstLetterText = firstLetter;
+ return;
+ }
+ }
+ }
+ m_handledFirstLetter = true;
}
bool TextIterator::handleReplacedElement()
@@ -597,6 +667,18 @@ bool TextIterator::handleReplacedElement()
return true;
}
+bool TextIterator::hasVisibleTextNode(RenderText* renderer)
+{
+ if (renderer->style()->visibility() == VISIBLE)
+ return true;
+ if (renderer->isTextFragment()) {
+ RenderTextFragment* fragment = static_cast<RenderTextFragment*>(renderer);
+ if (fragment->firstLetter() && fragment->firstLetter()->style()->visibility() == VISIBLE)
+ return true;
+ }
+ return false;
+}
+
static bool shouldEmitTabBeforeNode(Node* node)
{
RenderObject* r = node->renderer();
@@ -888,9 +970,9 @@ void TextIterator::emitCharacter(UChar c, Node* textNode, Node* offsetBaseNode,
m_lastCharacter = c;
}
-void TextIterator::emitText(Node* textNode, int textStartOffset, int textEndOffset)
+void TextIterator::emitText(Node* textNode, RenderObject* renderObject, int textStartOffset, int textEndOffset)
{
- RenderText* renderer = toRenderText(m_node->renderer());
+ RenderText* renderer = toRenderText(renderObject);
m_text = m_emitsTextWithoutTranscoding ? renderer->textWithoutTranscoding() : renderer->text();
ASSERT(m_text.characters());
@@ -906,6 +988,11 @@ void TextIterator::emitText(Node* textNode, int textStartOffset, int textEndOffs
m_hasEmitted = true;
}
+void TextIterator::emitText(Node* textNode, int textStartOffset, int textEndOffset)
+{
+ emitText(textNode, m_node->renderer(), textStartOffset, textEndOffset);
+}
+
PassRefPtr<Range> TextIterator::range() const
{
// use the current run information, if we have it
@@ -944,13 +1031,19 @@ Node* TextIterator::node() const
// --------
SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator()
- : m_positionNode(0)
+ : m_behavior(TextIteratorDefaultBehavior)
+ , m_node(0)
+ , m_positionNode(0)
{
}
-SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r)
- : m_positionNode(0)
+SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r, TextIteratorBehavior behavior)
+ : m_behavior(behavior)
+ , m_node(0)
+ , m_positionNode(0)
{
+ ASSERT(m_behavior == TextIteratorDefaultBehavior || m_behavior == TextIteratorEndsAtEditingBoundary);
+
if (!r)
return;
@@ -974,7 +1067,7 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r)
}
}
- m_node = endNode;
+ setCurrentNode(endNode);
setUpFullyClippedStack(m_fullyClippedStack, m_node);
m_offset = endOffset;
m_handledNode = false;
@@ -1038,12 +1131,9 @@ void SimplifiedBackwardsTextIterator::advance()
}
}
// Exit all other containers.
- next = m_node->previousSibling();
- while (!next) {
- Node* parentNode = parentCrossingShadowBoundaries(m_node);
- if (!parentNode)
+ while (!m_node->previousSibling()) {
+ if (!setCurrentNode(parentCrossingShadowBoundaries(m_node)))
break;
- m_node = parentNode;
m_fullyClippedStack.pop();
exitNode();
if (m_positionNode) {
@@ -1051,14 +1141,17 @@ void SimplifiedBackwardsTextIterator::advance()
m_handledChildren = true;
return;
}
- next = m_node->previousSibling();
}
+
+ next = m_node->previousSibling();
m_fullyClippedStack.pop();
}
- m_node = next;
- if (m_node)
+ if (m_node && setCurrentNode(next))
pushFullyClippedState(m_fullyClippedStack, m_node);
+ else
+ clearCurrentNode();
+
// For the purpose of word boundary detection,
// we should iterate all visible text and trailing (collapsed) whitespaces.
m_offset = m_node ? maxOffsetIncludingCollapsedSpaces(m_node) : 0;
@@ -1137,6 +1230,26 @@ void SimplifiedBackwardsTextIterator::emitCharacter(UChar c, Node* node, int sta
m_lastCharacter = c;
}
+bool SimplifiedBackwardsTextIterator::crossesEditingBoundary(Node* node) const
+{
+ return m_node && m_node->isContentEditable() != node->isContentEditable();
+}
+
+bool SimplifiedBackwardsTextIterator::setCurrentNode(Node* node)
+{
+ if (!node)
+ return false;
+ if (m_behavior == TextIteratorEndsAtEditingBoundary && crossesEditingBoundary(node))
+ return false;
+ m_node = node;
+ return true;
+}
+
+void SimplifiedBackwardsTextIterator::clearCurrentNode()
+{
+ m_node = 0;
+}
+
PassRefPtr<Range> SimplifiedBackwardsTextIterator::range() const
{
if (m_positionNode)
@@ -1262,11 +1375,11 @@ BackwardsCharacterIterator::BackwardsCharacterIterator()
{
}
-BackwardsCharacterIterator::BackwardsCharacterIterator(const Range* range)
+BackwardsCharacterIterator::BackwardsCharacterIterator(const Range* range, TextIteratorBehavior behavior)
: m_offset(0)
, m_runOffset(0)
, m_atBreak(true)
- , m_textIterator(range)
+ , m_textIterator(range, behavior)
{
while (!atEnd() && !m_textIterator.length())
m_textIterator.advance();
diff --git a/WebCore/editing/TextIterator.h b/WebCore/editing/TextIterator.h
index ba381b9..805e060 100644
--- a/WebCore/editing/TextIterator.h
+++ b/WebCore/editing/TextIterator.h
@@ -32,6 +32,9 @@
namespace WebCore {
+class RenderText;
+class RenderTextFragment;
+
// FIXME: Can't really answer this question correctly without knowing the white-space mode.
// FIXME: Move this somewhere else in the editing directory. It doesn't belong here.
inline bool isCollapsibleWhitespace(UChar c)
@@ -73,6 +76,7 @@ enum TextIteratorBehavior {
TextIteratorEmitsCharactersBetweenAllVisiblePositions = 1 << 0,
TextIteratorEntersTextControls = 1 << 1,
TextIteratorEmitsTextsWithoutTranscoding = 1 << 2,
+ TextIteratorEndsAtEditingBoundary = 1 << 3
};
class TextIterator {
@@ -102,7 +106,10 @@ private:
bool handleReplacedElement();
bool handleNonTextNode();
void handleTextBox();
+ void handleTextNodeFirstLetter(RenderTextFragment*);
+ bool hasVisibleTextNode(RenderText*);
void emitCharacter(UChar, Node* textNode, Node* offsetBaseNode, int textStartOffset, int textEndOffset);
+ void emitText(Node* textNode, RenderObject* renderObject, int textStartOffset, int textEndOffset);
void emitText(Node* textNode, int textStartOffset, int textEndOffset);
// Current position, not necessarily of the text being returned, but position
@@ -134,6 +141,11 @@ private:
// are false and 0, we go back to normal iterating.
bool m_needsAnotherNewline;
InlineTextBox* m_textBox;
+ // Used when iteration over :first-letter text to save pointer to
+ // remaining text box.
+ InlineTextBox* m_remainingTextBox;
+ // Used to point to RenderText object for :first-letter.
+ RenderText *m_firstLetterText;
// Used to do the whitespace collapsing logic.
Node* m_lastTextNode;
@@ -159,6 +171,8 @@ private:
// Used when we want texts for copying, pasting, and transposing.
bool m_emitsTextWithoutTranscoding;
+ // Used when deciding text fragment created by :first-letter should be looked into.
+ bool m_handledFirstLetter;
};
// Iterates through the DOM range, returning all the text, and 0-length boundaries
@@ -167,7 +181,7 @@ private:
class SimplifiedBackwardsTextIterator {
public:
SimplifiedBackwardsTextIterator();
- explicit SimplifiedBackwardsTextIterator(const Range*);
+ explicit SimplifiedBackwardsTextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
bool atEnd() const { return !m_positionNode; }
void advance();
@@ -183,7 +197,11 @@ private:
bool handleReplacedElement();
bool handleNonTextNode();
void emitCharacter(UChar, Node*, int startOffset, int endOffset);
-
+ bool crossesEditingBoundary(Node*) const;
+ bool setCurrentNode(Node*);
+ void clearCurrentNode();
+
+ TextIteratorBehavior m_behavior;
// Current position, not necessarily of the text being returned, but position
// as we walk through the DOM tree.
Node* m_node;
@@ -247,7 +265,7 @@ private:
class BackwardsCharacterIterator {
public:
BackwardsCharacterIterator();
- explicit BackwardsCharacterIterator(const Range*);
+ explicit BackwardsCharacterIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
void advance(int);
@@ -256,6 +274,7 @@ public:
PassRefPtr<Range> range() const;
private:
+ TextIteratorBehavior m_behavior;
int m_offset;
int m_runOffset;
bool m_atBreak;
diff --git a/WebCore/editing/htmlediting.h b/WebCore/editing/htmlediting.h
index e60ada9..8612440 100644
--- a/WebCore/editing/htmlediting.h
+++ b/WebCore/editing/htmlediting.h
@@ -39,7 +39,6 @@ class HTMLElement;
class Node;
class Position;
class Range;
-class String;
class VisiblePosition;
class VisibleSelection;
diff --git a/WebCore/editing/markup.cpp b/WebCore/editing/markup.cpp
index 22e700a..26989c3 100644
--- a/WebCore/editing/markup.cpp
+++ b/WebCore/editing/markup.cpp
@@ -778,7 +778,7 @@ public:
DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\""));
DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>"));
Vector<UChar> openTag;
- WebCore::append(openTag, isBlock ? divStyle : styleSpanOpen);
+ WTF::append(openTag, isBlock ? divStyle : styleSpanOpen);
appendAttributeValue(openTag, style->cssText(), document->isHTMLDocument());
openTag.append('\"');
openTag.append('>');
@@ -805,10 +805,10 @@ public:
result.reserveInitialCapacity(length);
for (size_t i = preCount; i > 0; --i)
- WebCore::append(result, preMarkups[i - 1]);
+ WTF::append(result, preMarkups[i - 1]);
for (size_t i = 0; i < postCount; ++i)
- WebCore::append(result, postMarkups[i]);
+ WTF::append(result, postMarkups[i]);
return String::adopt(result);
}
@@ -818,6 +818,84 @@ private:
Vector<String> postMarkups;
};
+static Node* serializeNodes(MarkupAccumulatorWrapper& accumulator, Node* startNode, Node* pastEnd, Vector<Node*>* nodes, const Range* range, EAnnotateForInterchange annotate, EAbsoluteURLs absoluteURLs)
+{
+ Vector<Node*> ancestorsToClose;
+ Node* next;
+ Node* lastClosed = 0;
+ for (Node* n = startNode; n != pastEnd; n = next) {
+ // According to <rdar://problem/5730668>, it is possible for n to blow
+ // past pastEnd and become null here. This shouldn't be possible.
+ // This null check will prevent crashes (but create too much markup)
+ // and the ASSERT will hopefully lead us to understanding the problem.
+ ASSERT(n);
+ if (!n)
+ break;
+
+ next = n->traverseNextNode();
+ bool openedTag = false;
+
+ if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd)
+ // Don't write out empty block containers that aren't fully selected.
+ continue;
+
+ if (!n->renderer() && !enclosingNodeWithTag(Position(n, 0), selectTag)) {
+ next = n->traverseNextSibling();
+ // Don't skip over pastEnd.
+ if (pastEnd && pastEnd->isDescendantOf(n))
+ next = pastEnd;
+ } else {
+ // Add the node to the markup if we're not skipping the descendants
+ accumulator.insertOpenTag(n, range, annotate, absoluteURLs);
+ if (nodes)
+ nodes->append(n);
+
+ // If node has no children, close the tag now.
+ if (!n->childNodeCount()) {
+ accumulator.insertEndTag(n);
+ lastClosed = n;
+ } else {
+ openedTag = true;
+ ancestorsToClose.append(n);
+ }
+ }
+
+ // If we didn't insert open tag and there's no more siblings or we're at the end of the traversal, take care of ancestors.
+ // FIXME: What happens if we just inserted open tag and reached the end?
+ if (!openedTag && (!n->nextSibling() || next == pastEnd)) {
+ // Close up the ancestors.
+ while (!ancestorsToClose.isEmpty()) {
+ Node* ancestor = ancestorsToClose.last();
+ if (next != pastEnd && next->isDescendantOf(ancestor))
+ break;
+ // Not at the end of the range, close ancestors up to sibling of next node.
+ accumulator.insertEndTag(ancestor);
+ lastClosed = ancestor;
+ ancestorsToClose.removeLast();
+ }
+
+ // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors.
+ Node* nextParent = next ? next->parentNode() : 0;
+ if (next != pastEnd && n != nextParent) {
+ Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n;
+ for (Node *parent = lastAncestorClosedOrSelf->parent(); parent && parent != nextParent; parent = parent->parentNode()) {
+ // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered:
+ if (!parent->renderer())
+ continue;
+ // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
+ ASSERT(startNode->isDescendantOf(parent));
+ accumulator.wrapWithNode(parent, range, annotate, absoluteURLs);
+ if (nodes)
+ nodes->append(parent);
+ lastClosed = parent;
+ }
+ }
+ }
+ }
+
+ return lastClosed;
+}
+
// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange?
// FIXME: At least, annotation and style info should probably not be included in range.markupString()
String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterchange annotate, bool convertBlocksToInlines, EAbsoluteURLs absoluteURLs)
@@ -856,9 +934,7 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc
MarkupAccumulatorWrapper accumulator;
Node* pastEnd = updatedRange->pastLastNode();
- Node* lastClosed = 0;
- Vector<Node*> ancestorsToClose;
-
+
Node* startNode = updatedRange->firstNode();
VisiblePosition visibleStart(updatedRange->startPosition(), VP_DEFAULT_AFFINITY);
VisiblePosition visibleEnd(updatedRange->endPosition(), VP_DEFAULT_AFFINITY);
@@ -879,84 +955,7 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc
}
}
- Node* next;
- for (Node* n = startNode; n != pastEnd; n = next) {
- // According to <rdar://problem/5730668>, it is possible for n to blow
- // past pastEnd and become null here. This shouldn't be possible.
- // This null check will prevent crashes (but create too much markup)
- // and the ASSERT will hopefully lead us to understanding the problem.
- ASSERT(n);
- if (!n)
- break;
-
- next = n->traverseNextNode();
- bool skipDescendants = false;
- bool addMarkupForNode = true;
-
- if (!n->renderer() && !enclosingNodeWithTag(Position(n, 0), selectTag)) {
- skipDescendants = true;
- addMarkupForNode = false;
- next = n->traverseNextSibling();
- // Don't skip over pastEnd.
- if (pastEnd && pastEnd->isDescendantOf(n))
- next = pastEnd;
- }
-
- if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd)
- // Don't write out empty block containers that aren't fully selected.
- continue;
-
- // Add the node to the markup.
- if (addMarkupForNode) {
-
- accumulator.insertOpenTag(n, updatedRange.get(), annotate, absoluteURLs);
- if (nodes)
- nodes->append(n);
- }
-
- if (n->firstChild() == 0 || skipDescendants) {
- // Node has no children, or we are skipping it's descendants, add its close tag now.
- if (addMarkupForNode) {
- accumulator.insertEndTag(n);
- lastClosed = n;
- }
-
- // Check if the node is the last leaf of a tree.
- if (!n->nextSibling() || next == pastEnd) {
- if (!ancestorsToClose.isEmpty()) {
- // Close up the ancestors.
- do {
- Node *ancestor = ancestorsToClose.last();
- if (next != pastEnd && next->isDescendantOf(ancestor))
- break;
- // Not at the end of the range, close ancestors up to sibling of next node.
- accumulator.insertEndTag(ancestor);
- lastClosed = ancestor;
- ancestorsToClose.removeLast();
- } while (!ancestorsToClose.isEmpty());
- }
-
- // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors.
- Node* nextParent = next ? next->parentNode() : 0;
- if (next != pastEnd && n != nextParent) {
- Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n;
- for (Node *parent = lastAncestorClosedOrSelf->parent(); parent != 0 && parent != nextParent; parent = parent->parentNode()) {
- // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered:
- if (!parent->renderer())
- continue;
- // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
- ASSERT(startNode->isDescendantOf(parent));
- accumulator.wrapWithNode(parent, updatedRange.get(), annotate, absoluteURLs);
- if (nodes)
- nodes->append(parent);
- lastClosed = parent;
- }
- }
- }
- } else if (addMarkupForNode && !skipDescendants)
- // We added markup for this node, and we're descending into it. Set it to close eventually.
- ancestorsToClose.append(n);
- }
+ Node* lastClosed = serializeNodes(accumulator, startNode, pastEnd, nodes, range, annotate, absoluteURLs);
// Include ancestors that aren't completely inside the range but are required to retain
// the structure and appearance of the copied markup.
diff --git a/WebCore/editing/markup.h b/WebCore/editing/markup.h
index 5428097..dbf8b80 100644
--- a/WebCore/editing/markup.h
+++ b/WebCore/editing/markup.h
@@ -38,7 +38,6 @@ namespace WebCore {
class KURL;
class Node;
class Range;
- class String;
enum EChildrenOnly { IncludeNode, ChildrenOnly };
enum EAbsoluteURLs { DoNotResolveURLs, AbsoluteURLs };
diff --git a/WebCore/editing/visible_units.cpp b/WebCore/editing/visible_units.cpp
index dd48406..f84fec0 100644
--- a/WebCore/editing/visible_units.cpp
+++ b/WebCore/editing/visible_units.cpp
@@ -75,20 +75,11 @@ typedef unsigned (*BoundarySearchFunction)(const UChar*, unsigned length, unsign
static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
{
Position pos = c.deepEquivalent();
- Node *n = pos.node();
- if (!n)
- return VisiblePosition();
- Document *d = n->document();
- Node *de = d->documentElement();
- if (!de)
- return VisiblePosition();
- Node *boundary = n->enclosingBlockFlowElement();
+ Node* boundary = pos.parentEditingBoundary();
if (!boundary)
return VisiblePosition();
- bool isContentEditable = boundary->isContentEditable();
- while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable())
- boundary = boundary->parentNode();
+ Document* d = boundary->document();
Position start = rangeCompliantEquivalent(Position(boundary, 0));
Position end = rangeCompliantEquivalent(pos);
RefPtr<Range> searchRange = Range::create(d);
@@ -121,7 +112,7 @@ static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearch
if (ec)
return VisiblePosition();
- SimplifiedBackwardsTextIterator it(searchRange.get());
+ SimplifiedBackwardsTextIterator it(searchRange.get(), TextIteratorEndsAtEditingBoundary);
unsigned next = 0;
bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
bool needMoreContext = false;
@@ -156,7 +147,7 @@ static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearch
pos = Position(node, next);
else {
// Use the character iterator to translate the next value into a DOM position.
- BackwardsCharacterIterator charIt(searchRange.get());
+ BackwardsCharacterIterator charIt(searchRange.get(), TextIteratorEndsAtEditingBoundary);
charIt.advance(string.size() - suffixLength - next);
pos = charIt.range()->endPosition();
}
@@ -168,20 +159,11 @@ static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearch
static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
{
Position pos = c.deepEquivalent();
- Node *n = pos.node();
- if (!n)
- return VisiblePosition();
- Document *d = n->document();
- Node *de = d->documentElement();
- if (!de)
- return VisiblePosition();
- Node *boundary = n->enclosingBlockFlowElement();
+ Node* boundary = pos.parentEditingBoundary();
if (!boundary)
return VisiblePosition();
- bool isContentEditable = boundary->isContentEditable();
- while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable())
- boundary = boundary->parentNode();
+ Document* d = boundary->document();
RefPtr<Range> searchRange(d->createRange());
Position start(rangeCompliantEquivalent(pos));
@@ -1019,7 +1001,7 @@ VisiblePosition startOfDocument(const VisiblePosition &c)
VisiblePosition endOfDocument(const Node* node)
{
- if (!node || !node->document())
+ if (!node || !node->document() || !node->document()->documentElement())
return VisiblePosition();
Element* doc = node->document()->documentElement();