summaryrefslogtreecommitdiffstats
path: root/WebCore/editing
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/editing')
-rw-r--r--WebCore/editing/ApplyStyleCommand.cpp182
-rw-r--r--WebCore/editing/ApplyStyleCommand.h15
-rw-r--r--WebCore/editing/CompositeEditCommand.cpp20
-rw-r--r--WebCore/editing/CorrectionPanelInfo.h61
-rw-r--r--WebCore/editing/DeleteSelectionCommand.cpp29
-rw-r--r--WebCore/editing/DeleteSelectionCommand.h6
-rw-r--r--WebCore/editing/EditingAllInOne.cpp1
-rw-r--r--WebCore/editing/EditingBehaviorTypes.h3
-rw-r--r--WebCore/editing/EditingStyle.cpp221
-rw-r--r--WebCore/editing/EditingStyle.h93
-rw-r--r--WebCore/editing/Editor.cpp286
-rw-r--r--WebCore/editing/Editor.h21
-rw-r--r--WebCore/editing/EditorCommand.cpp18
-rw-r--r--WebCore/editing/InsertLineBreakCommand.cpp4
-rw-r--r--WebCore/editing/InsertParagraphSeparatorCommand.cpp16
-rw-r--r--WebCore/editing/InsertParagraphSeparatorCommand.h8
-rw-r--r--WebCore/editing/InsertTextCommand.cpp6
-rw-r--r--WebCore/editing/RemoveFormatCommand.cpp15
-rw-r--r--WebCore/editing/ReplaceSelectionCommand.cpp92
-rw-r--r--WebCore/editing/ReplaceSelectionCommand.h3
-rw-r--r--WebCore/editing/SelectionController.cpp6
-rw-r--r--WebCore/editing/SelectionController.h11
-rw-r--r--WebCore/editing/TypingCommand.cpp4
-rw-r--r--WebCore/editing/markup.cpp44
24 files changed, 738 insertions, 427 deletions
diff --git a/WebCore/editing/ApplyStyleCommand.cpp b/WebCore/editing/ApplyStyleCommand.cpp
index 8862da7..4e1c733 100644
--- a/WebCore/editing/ApplyStyleCommand.cpp
+++ b/WebCore/editing/ApplyStyleCommand.cpp
@@ -36,7 +36,6 @@
#include "Document.h"
#include "Editor.h"
#include "Frame.h"
-#include "HTMLElement.h"
#include "HTMLFontElement.h"
#include "HTMLInterchange.h"
#include "HTMLNames.h"
@@ -430,104 +429,6 @@ RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* style
return result;
}
-// Editing style properties must be preserved during editing operation.
-// e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
-// FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties
-static const int editingStyleProperties[] = {
- // CSS inheritable properties
- CSSPropertyBorderCollapse,
- CSSPropertyColor,
- CSSPropertyFontFamily,
- CSSPropertyFontSize,
- CSSPropertyFontStyle,
- CSSPropertyFontVariant,
- CSSPropertyFontWeight,
- CSSPropertyLetterSpacing,
- CSSPropertyLineHeight,
- CSSPropertyOrphans,
- CSSPropertyTextAlign,
- CSSPropertyTextIndent,
- CSSPropertyTextTransform,
- CSSPropertyWhiteSpace,
- CSSPropertyWidows,
- CSSPropertyWordSpacing,
- CSSPropertyWebkitBorderHorizontalSpacing,
- CSSPropertyWebkitBorderVerticalSpacing,
- CSSPropertyWebkitTextDecorationsInEffect,
- CSSPropertyWebkitTextFillColor,
- CSSPropertyWebkitTextSizeAdjust,
- CSSPropertyWebkitTextStrokeColor,
- CSSPropertyWebkitTextStrokeWidth,
-};
-size_t numEditingStyleProperties = sizeof(editingStyleProperties)/sizeof(editingStyleProperties[0]);
-
-RefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::removeNonEditingProperties(CSSStyleDeclaration* style)
-{
- return style->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties);
-}
-
-PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::editingStyleAtPosition(Position pos, ShouldIncludeTypingStyle shouldIncludeTypingStyle)
-{
- RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = pos.computedStyle();
- RefPtr<CSSMutableStyleDeclaration> style;
- if (!computedStyleAtPosition)
- style = CSSMutableStyleDeclaration::create();
- else
- style = removeNonEditingProperties(computedStyleAtPosition.get());
-
- if (style && pos.node() && pos.node()->computedStyle()) {
- RenderStyle* renderStyle = pos.node()->computedStyle();
- // If a node's text fill color is invalid, then its children use
- // their font-color as their text fill color (they don't
- // inherit it). Likewise for stroke color.
- ExceptionCode ec = 0;
- if (!renderStyle->textFillColor().isValid())
- style->removeProperty(CSSPropertyWebkitTextFillColor, ec);
- if (!renderStyle->textStrokeColor().isValid())
- style->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
- ASSERT(ec == 0);
- if (renderStyle->fontDescription().keywordSize())
- style->setProperty(CSSPropertyFontSize, computedStyleAtPosition->getFontSizeCSSValuePreferringKeyword()->cssText());
- }
-
- if (shouldIncludeTypingStyle == IncludeTypingStyle) {
- CSSMutableStyleDeclaration* typingStyle = pos.node()->document()->frame()->selection()->typingStyle();
- if (typingStyle)
- style->merge(typingStyle);
- }
-
- return style.release();
-}
-
-void prepareEditingStyleToApplyAt(CSSMutableStyleDeclaration* editingStyle, Position pos)
-{
- // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
- // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
- // which one of editingStyleAtPosition or computedStyle is called.
- RefPtr<CSSMutableStyleDeclaration> style = ApplyStyleCommand::editingStyleAtPosition(pos);
- style->diff(editingStyle);
-
- // if alpha value is zero, we don't add the background color.
- RefPtr<CSSValue> backgroundColor = editingStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
- if (backgroundColor && backgroundColor->isPrimitiveValue()) {
- CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(backgroundColor.get());
- Color color = Color(primitiveValue->getRGBA32Value());
- ExceptionCode ec;
- if (color.alpha() == 0)
- editingStyle->removeProperty(CSSPropertyBackgroundColor, ec);
- }
-}
-
-void removeStylesAddedByNode(CSSMutableStyleDeclaration* editingStyle, Node* node)
-{
- ASSERT(node);
- ASSERT(node->parentNode());
- RefPtr<CSSMutableStyleDeclaration> parentStyle = ApplyStyleCommand::editingStyleAtPosition(Position(node->parentNode(), 0));
- RefPtr<CSSMutableStyleDeclaration> style = ApplyStyleCommand::editingStyleAtPosition(Position(node, 0));
- parentStyle->diff(style.get());
- style->diff(editingStyle);
-}
-
ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, EditAction editingAction, EPropertyLevel propertyLevel)
: CompositeEditCommand(document)
, m_style(style->makeMutable())
@@ -1046,6 +947,8 @@ void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style)
removeInlineStyle(styleWithoutEmbedding ? styleWithoutEmbedding.get() : style, removeStart, end);
start = startPosition();
end = endPosition();
+ if (start.isNull() || start.isOrphan() || end.isNull() || end.isOrphan())
+ return;
if (splitStart) {
if (mergeStartWithPreviousIfIdentical(start, end)) {
@@ -1150,9 +1053,9 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(CSSMutableStyleDeclaration*
if (m_removeOnly)
return;
- for (Node* next; node && node != pastEndNode; node = next) {
+ for (RefPtr<Node> next; node && node != pastEndNode; node = next.get()) {
next = node->traverseNextNode();
-
+
if (!node->renderer() || !node->isContentEditable())
continue;
@@ -1183,7 +1086,8 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(CSSMutableStyleDeclaration*
}
}
- Node* runEnd = node;
+ RefPtr<Node> runStart = node;
+ RefPtr<Node> runEnd = node;
Node* sibling = node->nextSibling();
while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode)
&& (!isBlock(sibling) || sibling->hasTagName(brTag))
@@ -1193,9 +1097,9 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(CSSMutableStyleDeclaration*
}
next = runEnd->traverseNextSibling();
- if (!removeStyleFromRunBeforeApplyingStyle(style, node, runEnd))
+ if (!removeStyleFromRunBeforeApplyingStyle(style, runStart, runEnd))
continue;
- addInlineStyleIfNeeded(style, node, runEnd, AddStyledElement);
+ addInlineStyleIfNeeded(style, runStart.get(), runEnd.get(), AddStyledElement);
}
}
@@ -1205,12 +1109,12 @@ bool ApplyStyleCommand::isStyledInlineElementToRemove(Element* element) const
|| (m_isInlineElementToRemoveFunction && m_isInlineElementToRemoveFunction(element));
}
-bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDeclaration* style, Node*& runStart, Node*& runEnd)
+bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDeclaration* style, RefPtr<Node>& runStart, RefPtr<Node>& runEnd)
{
ASSERT(runStart && runEnd && runStart->parentNode() == runEnd->parentNode());
- Node* pastEndNode = runEnd->traverseNextSibling();
+ RefPtr<Node> pastEndNode = runEnd->traverseNextSibling();
bool needToApplyStyle = false;
- for (Node* node = runStart; node && node != pastEndNode; node = node->traverseNextNode()) {
+ for (Node* node = runStart.get(); node && node != pastEndNode.get(); node = node->traverseNextNode()) {
if (node->childNodeCount())
continue;
// We don't consider m_isInlineElementToRemoveFunction here because we never apply style when m_isInlineElementToRemoveFunction is specified
@@ -1223,16 +1127,16 @@ bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDec
if (!needToApplyStyle)
return false;
- Node* next;
- for (Node* node = runStart; node && node != pastEndNode; node = next) {
+ RefPtr<Node> next = runStart;
+ for (RefPtr<Node> node = next; node && node->inDocument() && node != pastEndNode; node = next) {
next = node->traverseNextNode();
if (!node->isHTMLElement())
continue;
-
- Node* previousSibling = node->previousSibling();
- Node* nextSibling = node->nextSibling();
- ContainerNode* parent = node->parentNode();
- removeInlineStyleFromElement(style, static_cast<HTMLElement*>(node), RemoveAlways);
+
+ RefPtr<Node> previousSibling = node->previousSibling();
+ RefPtr<Node> nextSibling = node->nextSibling();
+ RefPtr<ContainerNode> parent = node->parentNode();
+ removeInlineStyleFromElement(style, static_cast<HTMLElement*>(node.get()), 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)
@@ -1245,7 +1149,7 @@ bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDec
return true;
}
-bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration* style, HTMLElement* element, InlineStyleRemovalMode mode, CSSMutableStyleDeclaration* extractedStyle)
+bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration* style, PassRefPtr<HTMLElement> element, InlineStyleRemovalMode mode, CSSMutableStyleDeclaration* extractedStyle)
{
ASSERT(style);
ASSERT(element);
@@ -1253,7 +1157,7 @@ bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration*
if (!element->parentNode() || !element->parentNode()->isContentEditable())
return false;
- if (isStyledInlineElementToRemove(element)) {
+ if (isStyledInlineElementToRemove(element.get())) {
if (mode == RemoveNone)
return true;
ASSERT(extractedStyle);
@@ -1264,7 +1168,7 @@ bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration*
}
bool removed = false;
- if (removeImplicitlyStyledElement(style, element, mode, extractedStyle))
+ if (removeImplicitlyStyledElement(style, element.get(), mode, extractedStyle))
removed = true;
if (!element->inDocument())
@@ -1272,7 +1176,7 @@ bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration*
// If the node was converted to a span, the span may still contain relevant
// styles which must be removed (e.g. <b style='font-weight: bold'>)
- if (removeCSSStyle(style, element, mode, extractedStyle))
+ if (removeCSSStyle(style, element.get(), mode, extractedStyle))
removed = true;
return removed;
@@ -1658,9 +1562,7 @@ void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration>
break;
node = next.get();
}
-
- ASSERT(s.node()->inDocument());
- ASSERT(e.node()->inDocument());
+
updateStartEnd(s, e);
}
@@ -1859,18 +1761,19 @@ bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const
return false;
}
-void ApplyStyleCommand::surroundNodeRangeWithElement(Node* startNode, Node* endNode, PassRefPtr<Element> elementToInsert)
+void ApplyStyleCommand::surroundNodeRangeWithElement(PassRefPtr<Node> passedStartNode, PassRefPtr<Node> endNode, PassRefPtr<Element> elementToInsert)
{
- ASSERT(startNode);
+ ASSERT(passedStartNode);
ASSERT(endNode);
ASSERT(elementToInsert);
+ RefPtr<Node> startNode = passedStartNode;
RefPtr<Element> element = elementToInsert;
insertNodeBefore(element, startNode);
-
- Node* node = startNode;
- while (1) {
- Node* next = node->nextSibling();
+
+ RefPtr<Node> node = startNode;
+ while (node) {
+ RefPtr<Node> next = node->nextSibling();
removeNode(node);
appendNode(node, element);
if (node == endNode)
@@ -1878,17 +1781,17 @@ void ApplyStyleCommand::surroundNodeRangeWithElement(Node* startNode, Node* endN
node = next;
}
- Node* nextSibling = element->nextSibling();
- Node* previousSibling = element->previousSibling();
+ RefPtr<Node> nextSibling = element->nextSibling();
+ RefPtr<Node> previousSibling = element->previousSibling();
if (nextSibling && nextSibling->isElementNode() && nextSibling->isContentEditable()
- && areIdenticalElements(element.get(), static_cast<Element*>(nextSibling)))
- mergeIdenticalElements(element, static_cast<Element*>(nextSibling));
+ && areIdenticalElements(element.get(), static_cast<Element*>(nextSibling.get())))
+ mergeIdenticalElements(element.get(), static_cast<Element*>(nextSibling.get()));
if (previousSibling && previousSibling->isElementNode() && previousSibling->isContentEditable()) {
Node* mergedElement = previousSibling->nextSibling();
if (mergedElement->isElementNode() && mergedElement->isContentEditable()
- && areIdenticalElements(static_cast<Element*>(previousSibling), static_cast<Element*>(mergedElement)))
- mergeIdenticalElements(static_cast<Element*>(previousSibling), static_cast<Element*>(mergedElement));
+ && areIdenticalElements(static_cast<Element*>(previousSibling.get()), static_cast<Element*>(mergedElement)))
+ mergeIdenticalElements(static_cast<Element*>(previousSibling.get()), static_cast<Element*>(mergedElement));
}
// FIXME: We should probably call updateStartEnd if the start or end was in the node
@@ -1910,17 +1813,22 @@ void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElemen
setNodeAttribute(block, styleAttr, cssText);
}
-void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *startNode, Node *endNode, EAddStyledElement addStyledElement)
+void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, PassRefPtr<Node> passedStart, PassRefPtr<Node> passedEnd, EAddStyledElement addStyledElement)
{
+ if (!passedStart || !passedEnd || !passedStart->inDocument() || !passedEnd->inDocument())
+ return;
+ RefPtr<Node> startNode = passedStart;
+ RefPtr<Node> endNode = passedEnd;
+
// It's okay to obtain the style at the startNode because we've removed all relevant styles from the current run.
RefPtr<HTMLElement> dummyElement;
Position positionForStyleComparison;
if (!startNode->isElementNode()) {
dummyElement = createStyleSpanElement(document());
- insertNodeAt(dummyElement, positionBeforeNode(startNode));
+ insertNodeAt(dummyElement, positionBeforeNode(startNode.get()));
positionForStyleComparison = positionBeforeNode(dummyElement.get());
} else
- positionForStyleComparison = firstPositionInNode(startNode);
+ positionForStyleComparison = firstPositionInNode(startNode.get());
StyleChange styleChange(style, positionForStyleComparison);
@@ -1930,7 +1838,7 @@ void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style
// Find appropriate font and span elements top-down.
HTMLElement* fontContainer = 0;
HTMLElement* styleContainer = 0;
- for (Node* container = startNode; container && startNode == endNode; container = container->firstChild()) {
+ for (Node* container = startNode.get(); container && startNode == endNode; container = container->firstChild()) {
if (container->isHTMLElement() && container->hasTagName(fontTag))
fontContainer = static_cast<HTMLElement*>(container);
bool styleContainerIsNotSpan = !styleContainer || !styleContainer->hasTagName(spanTag);
diff --git a/WebCore/editing/ApplyStyleCommand.h b/WebCore/editing/ApplyStyleCommand.h
index 16c5b68..018148f 100644
--- a/WebCore/editing/ApplyStyleCommand.h
+++ b/WebCore/editing/ApplyStyleCommand.h
@@ -27,6 +27,7 @@
#define ApplyStyleCommand_h
#include "CompositeEditCommand.h"
+#include "HTMLElement.h"
namespace WebCore {
@@ -63,9 +64,6 @@ public:
return adoptRef(new ApplyStyleCommand(document, style, isInlineElementToRemoveFunction, action));
}
- static RefPtr<CSSMutableStyleDeclaration> removeNonEditingProperties(CSSStyleDeclaration* style);
- static PassRefPtr<CSSMutableStyleDeclaration> editingStyleAtPosition(Position pos, ShouldIncludeTypingStyle shouldIncludeTypingStyle = IgnoreTypingStyle);
-
private:
ApplyStyleCommand(Document*, CSSStyleDeclaration*, EditAction, EPropertyLevel);
ApplyStyleCommand(Document*, CSSStyleDeclaration*, const Position& start, const Position& end, EditAction, EPropertyLevel);
@@ -79,8 +77,8 @@ private:
// style-removal helpers
bool isStyledInlineElementToRemove(Element*) const;
- bool removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDeclaration* style, Node*& runStart, Node*& runEnd);
- bool removeInlineStyleFromElement(CSSMutableStyleDeclaration*, HTMLElement*, InlineStyleRemovalMode = RemoveIfNeeded, CSSMutableStyleDeclaration* extractedStyle = 0);
+ bool removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDeclaration* style, RefPtr<Node>& runStart, RefPtr<Node>& runEnd);
+ bool removeInlineStyleFromElement(CSSMutableStyleDeclaration*, PassRefPtr<HTMLElement>, InlineStyleRemovalMode = RemoveIfNeeded, CSSMutableStyleDeclaration* extractedStyle = 0);
inline bool shouldRemoveInlineStyleFromElement(CSSMutableStyleDeclaration* style, HTMLElement* element) {return removeInlineStyleFromElement(style, element, RemoveNone);}
bool removeImplicitlyStyledElement(CSSMutableStyleDeclaration*, HTMLElement*, InlineStyleRemovalMode, CSSMutableStyleDeclaration* extractedStyle);
void replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*&);
@@ -99,7 +97,7 @@ private:
void fixRangeAndApplyInlineStyle(CSSMutableStyleDeclaration*, const Position& start, const Position& end);
void applyInlineStyleToNodeRange(CSSMutableStyleDeclaration*, Node* startNode, Node* pastEndNode);
void addBlockStyle(const StyleChange&, HTMLElement*);
- void addInlineStyleIfNeeded(CSSMutableStyleDeclaration*, Node* start, Node* end, EAddStyledElement addStyledElement = AddStyledElement);
+ void addInlineStyleIfNeeded(CSSMutableStyleDeclaration*, PassRefPtr<Node> start, PassRefPtr<Node> end, EAddStyledElement addStyledElement = AddStyledElement);
void splitTextAtStart(const Position& start, const Position& end);
void splitTextAtEnd(const Position& start, const Position& end);
void splitTextElementAtStart(const Position& start, const Position& end);
@@ -110,7 +108,7 @@ private:
bool mergeEndWithNextIfIdentical(const Position& start, const Position& end);
void cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor);
- void surroundNodeRangeWithElement(Node* start, Node* end, PassRefPtr<Element>);
+ void surroundNodeRangeWithElement(PassRefPtr<Node> start, PassRefPtr<Node> end, PassRefPtr<Element>);
float computedFontSize(const Node*);
void joinChildTextNodes(Node*, const Position& start, const Position& end);
@@ -136,9 +134,6 @@ bool isStyleSpan(const Node*);
PassRefPtr<HTMLElement> createStyleSpanElement(Document*);
RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle);
-void prepareEditingStyleToApplyAt(CSSMutableStyleDeclaration*, Position);
-void removeStylesAddedByNode(CSSMutableStyleDeclaration*, Node*);
-
} // namespace WebCore
#endif
diff --git a/WebCore/editing/CompositeEditCommand.cpp b/WebCore/editing/CompositeEditCommand.cpp
index 6f47fb4..5579b25 100644
--- a/WebCore/editing/CompositeEditCommand.cpp
+++ b/WebCore/editing/CompositeEditCommand.cpp
@@ -206,6 +206,8 @@ void CompositeEditCommand::removeChildrenInRange(PassRefPtr<Node> node, unsigned
void CompositeEditCommand::removeNode(PassRefPtr<Node> node)
{
+ if (!node || !node->parentNode())
+ return;
applyCommandToComposite(RemoveNodeCommand::create(node));
}
@@ -935,9 +937,9 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
// A non-empty paragraph's style is moved when we copy and move it. We don't move
// anything if we're given an empty paragraph, but an empty paragraph can have style
// too, <div><b><br></b></div> for example. Save it so that we can preserve it later.
- RefPtr<CSSMutableStyleDeclaration> styleInEmptyParagraph;
+ RefPtr<EditingStyle> styleInEmptyParagraph;
if (startOfParagraphToMove == endOfParagraphToMove && preserveStyle) {
- styleInEmptyParagraph = ApplyStyleCommand::editingStyleAtPosition(startOfParagraphToMove.deepEquivalent(), IncludeTypingStyle);
+ styleInEmptyParagraph = editingStyleIncludingTypingStyle(startOfParagraphToMove.deepEquivalent());
// The moved paragraph should assume the block style of the destination.
styleInEmptyParagraph->removeBlockProperties();
}
@@ -981,8 +983,8 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
// If the selection is in an empty paragraph, restore styles from the old empty paragraph to the new empty paragraph.
bool selectionIsEmptyParagraph = endingSelection().isCaret() && isStartOfParagraph(endingSelection().visibleStart()) && isEndOfParagraph(endingSelection().visibleStart());
if (styleInEmptyParagraph && selectionIsEmptyParagraph)
- applyStyle(styleInEmptyParagraph.get());
-
+ applyStyle(styleInEmptyParagraph->style());
+
if (preserveSelection && startIndex != -1) {
// Fragment creation (using createMarkup) incorrectly uses regular
// spaces instead of nbsps for some spaces that were rendered (11475), which
@@ -1003,7 +1005,7 @@ bool CompositeEditCommand::breakOutOfEmptyListItem()
if (!emptyListItem)
return false;
- RefPtr<CSSMutableStyleDeclaration> style = ApplyStyleCommand::editingStyleAtPosition(endingSelection().start(), IncludeTypingStyle);
+ RefPtr<EditingStyle> style = editingStyleIncludingTypingStyle(endingSelection().start());
ContainerNode* listNode = emptyListItem->parentNode();
// FIXME: Can't we do something better when the immediate parent wasn't a list node?
@@ -1052,10 +1054,10 @@ bool CompositeEditCommand::breakOutOfEmptyListItem()
appendBlockPlaceholder(newBlock);
setEndingSelection(VisibleSelection(Position(newBlock.get(), 0), DOWNSTREAM));
- prepareEditingStyleToApplyAt(style.get(), endingSelection().start());
- if (style->length())
- applyStyle(style.get());
-
+ style->prepareToApplyAt(endingSelection().start());
+ if (!style->isEmpty())
+ applyStyle(style->style());
+
return true;
}
diff --git a/WebCore/editing/CorrectionPanelInfo.h b/WebCore/editing/CorrectionPanelInfo.h
new file mode 100644
index 0000000..2caac17
--- /dev/null
+++ b/WebCore/editing/CorrectionPanelInfo.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CorrectionPanelInfo_h
+#define CorrectionPanelInfo_h
+
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+// Some platforms provide UI for suggesting autocorrection.
+#define SUPPORT_AUTOCORRECTION_PANEL 1
+// Some platforms use spelling and autocorrection markers to provide visual cue.
+// On such platform, if word with marker is edited, we need to remove the marker.
+#define REMOVE_MARKERS_UPON_EDITING 1
+#else
+#define SUPPORT_AUTOCORRECTION_PANEL 0
+#define REMOVE_MARKERS_UPON_EDITING 0
+#endif // #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+
+#include "Range.h"
+
+namespace WebCore {
+
+struct CorrectionPanelInfo {
+ enum PanelType {
+ PanelTypeCorrection = 0,
+ PanelTypeReversion
+ };
+
+ RefPtr<Range> m_rangeToBeReplaced;
+ String m_replacedString;
+ String m_replacementString;
+ PanelType m_panelType;
+ bool m_isActive;
+};
+
+enum CorrectionWasRejectedOrNot { CorrectionWasNotRejected, CorrectionWasRejected };
+
+} // namespace WebCore
+
+#endif // CorrectionPanelInfo_h
diff --git a/WebCore/editing/DeleteSelectionCommand.cpp b/WebCore/editing/DeleteSelectionCommand.cpp
index 1f56da7..56deac3 100644
--- a/WebCore/editing/DeleteSelectionCommand.cpp
+++ b/WebCore/editing/DeleteSelectionCommand.cpp
@@ -26,25 +26,18 @@
#include "config.h"
#include "DeleteSelectionCommand.h"
-#include "CSSMutableStyleDeclaration.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "Editor.h"
#include "EditorClient.h"
#include "Element.h"
#include "Frame.h"
-#include "Logging.h"
-#include "CSSComputedStyleDeclaration.h"
#include "htmlediting.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
-#include "markup.h"
#include "RenderTableCell.h"
-#include "ReplaceSelectionCommand.h"
#include "Text.h"
-#include "TextIterator.h"
#include "visible_units.h"
-#include "ApplyStyleCommand.h"
namespace WebCore {
@@ -272,15 +265,6 @@ void DeleteSelectionCommand::initializePositionData()
m_endBlock = enclosingNodeOfType(rangeCompliantEquivalent(m_upstreamEnd), &isBlock, false);
}
-static void removeEnclosingAnchorStyle(CSSMutableStyleDeclaration* style, const Position& position)
-{
- Node* enclosingAnchor = enclosingAnchorElement(position);
- if (!enclosingAnchor || !enclosingAnchor->parentNode())
- return;
-
- removeStylesAddedByNode(style, enclosingAnchor);
-}
-
void DeleteSelectionCommand::saveTypingStyleState()
{
// A common case is deleting characters that are all from the same text node. In
@@ -294,14 +278,13 @@ void DeleteSelectionCommand::saveTypingStyleState()
return;
// Figure out the typing style in effect before the delete is done.
- m_typingStyle = ApplyStyleCommand::editingStyleAtPosition(positionBeforeTabSpan(m_selectionToDelete.start()));
-
- removeEnclosingAnchorStyle(m_typingStyle.get(), m_selectionToDelete.start());
+ m_typingStyle = EditingStyle::create(positionBeforeTabSpan(m_selectionToDelete.start()));
+ m_typingStyle->removeStyleAddedByNode(enclosingAnchorElement(m_selectionToDelete.start()));
// If we're deleting into a Mail blockquote, save the style at end() instead of start()
// We'll use this later in computeTypingStyleAfterDelete if we end up outside of a Mail blockquote
if (nearestMailBlockquote(m_selectionToDelete.start().node()))
- m_deleteIntoBlockquoteStyle = ApplyStyleCommand::editingStyleAtPosition(m_selectionToDelete.end());
+ m_deleteIntoBlockquoteStyle = EditingStyle::create(m_selectionToDelete.end());
else
m_deleteIntoBlockquoteStyle = 0;
}
@@ -693,8 +676,8 @@ void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
m_typingStyle = m_deleteIntoBlockquoteStyle;
m_deleteIntoBlockquoteStyle = 0;
- prepareEditingStyleToApplyAt(m_typingStyle.get(), m_endingPosition);
- if (!m_typingStyle->length())
+ m_typingStyle->prepareToApplyAt(m_endingPosition);
+ if (m_typingStyle->isEmpty())
m_typingStyle = 0;
VisiblePosition visibleEnd(m_endingPosition);
if (m_typingStyle &&
@@ -707,7 +690,7 @@ void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
// then move it back (which will clear typing style).
setEndingSelection(visibleEnd);
- applyStyle(m_typingStyle.get(), EditActionUnspecified);
+ applyStyle(m_typingStyle->style(), EditActionUnspecified);
// applyStyle can destroy the placeholder that was at m_endingPosition if it needs to
// move it, but it will set an endingSelection() at [movedPlaceholder, 0] if it does so.
m_endingPosition = endingSelection().start();
diff --git a/WebCore/editing/DeleteSelectionCommand.h b/WebCore/editing/DeleteSelectionCommand.h
index 20f52f4..7b46935 100644
--- a/WebCore/editing/DeleteSelectionCommand.h
+++ b/WebCore/editing/DeleteSelectionCommand.h
@@ -30,6 +30,8 @@
namespace WebCore {
+class EditingStyle;
+
class DeleteSelectionCommand : public CompositeEditCommand {
public:
static PassRefPtr<DeleteSelectionCommand> create(Document* document, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false)
@@ -86,8 +88,8 @@ private:
Position m_trailingWhitespace;
RefPtr<Node> m_startBlock;
RefPtr<Node> m_endBlock;
- RefPtr<CSSMutableStyleDeclaration> m_typingStyle;
- RefPtr<CSSMutableStyleDeclaration> m_deleteIntoBlockquoteStyle;
+ RefPtr<EditingStyle> m_typingStyle;
+ RefPtr<EditingStyle> m_deleteIntoBlockquoteStyle;
RefPtr<Node> m_startRoot;
RefPtr<Node> m_endRoot;
RefPtr<Node> m_startTableRow;
diff --git a/WebCore/editing/EditingAllInOne.cpp b/WebCore/editing/EditingAllInOne.cpp
index 81483f8..e93840a 100644
--- a/WebCore/editing/EditingAllInOne.cpp
+++ b/WebCore/editing/EditingAllInOne.cpp
@@ -36,6 +36,7 @@
#include <DeleteFromTextNodeCommand.cpp>
#include <DeleteSelectionCommand.cpp>
#include <EditCommand.cpp>
+#include <EditingStyle.cpp>
#include <Editor.cpp>
#include <EditorCommand.cpp>
#include <FormatBlockCommand.cpp>
diff --git a/WebCore/editing/EditingBehaviorTypes.h b/WebCore/editing/EditingBehaviorTypes.h
index 26ba18e..11345da 100644
--- a/WebCore/editing/EditingBehaviorTypes.h
+++ b/WebCore/editing/EditingBehaviorTypes.h
@@ -38,7 +38,8 @@ namespace WebCore {
// if possible in the future.
enum EditingBehaviorType {
EditingMacBehavior,
- EditingWindowsBehavior
+ EditingWindowsBehavior,
+ EditingUnixBehavior
};
} // WebCore namespace
diff --git a/WebCore/editing/EditingStyle.cpp b/WebCore/editing/EditingStyle.cpp
new file mode 100644
index 0000000..9da337f
--- /dev/null
+++ b/WebCore/editing/EditingStyle.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "EditingStyle.h"
+
+#include "ApplyStyleCommand.h"
+#include "CSSComputedStyleDeclaration.h"
+#include "CSSMutableStyleDeclaration.h"
+#include "Frame.h"
+#include "RenderStyle.h"
+#include "SelectionController.h"
+
+namespace WebCore {
+
+// Editing style properties must be preserved during editing operation.
+// e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
+// FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties
+static const int editingStyleProperties[] = {
+ // CSS inheritable properties
+ CSSPropertyBorderCollapse,
+ CSSPropertyColor,
+ CSSPropertyFontFamily,
+ CSSPropertyFontSize,
+ CSSPropertyFontStyle,
+ CSSPropertyFontVariant,
+ CSSPropertyFontWeight,
+ CSSPropertyLetterSpacing,
+ CSSPropertyLineHeight,
+ CSSPropertyOrphans,
+ CSSPropertyTextAlign,
+ CSSPropertyTextIndent,
+ CSSPropertyTextTransform,
+ CSSPropertyWhiteSpace,
+ CSSPropertyWidows,
+ CSSPropertyWordSpacing,
+ CSSPropertyWebkitBorderHorizontalSpacing,
+ CSSPropertyWebkitBorderVerticalSpacing,
+ CSSPropertyWebkitTextDecorationsInEffect,
+ CSSPropertyWebkitTextFillColor,
+ CSSPropertyWebkitTextSizeAdjust,
+ CSSPropertyWebkitTextStrokeColor,
+ CSSPropertyWebkitTextStrokeWidth,
+};
+size_t numEditingStyleProperties = sizeof(editingStyleProperties) / sizeof(editingStyleProperties[0]);
+
+static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style)
+{
+ return style->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties);
+}
+
+static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style)
+{
+ if (!style)
+ return CSSMutableStyleDeclaration::create();
+ return copyEditingProperties(style.get());
+}
+
+EditingStyle::EditingStyle()
+ : m_shouldUseFixedDefaultFontSize(false)
+{
+}
+
+EditingStyle::EditingStyle(Node* node)
+ : m_shouldUseFixedDefaultFontSize(false)
+{
+ init(node);
+}
+
+EditingStyle::EditingStyle(const Position& position)
+ : m_shouldUseFixedDefaultFontSize(false)
+{
+ init(position.node());
+}
+
+EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
+ : m_mutableStyle(style->copy())
+ , m_shouldUseFixedDefaultFontSize(false)
+{
+}
+
+void EditingStyle::init(Node* node)
+{
+ RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node);
+ m_mutableStyle = editingStyleFromComputedStyle(computedStyleAtPosition);
+
+ if (node && node->computedStyle()) {
+ RenderStyle* renderStyle = node->computedStyle();
+ removeTextFillAndStrokeColorsIfNeeded(renderStyle);
+ replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
+ }
+
+ m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
+}
+
+void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
+{
+ // If a node's text fill color is invalid, then its children use
+ // their font-color as their text fill color (they don't
+ // inherit it). Likewise for stroke color.
+ ExceptionCode ec = 0;
+ if (!renderStyle->textFillColor().isValid())
+ m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec);
+ if (!renderStyle->textStrokeColor().isValid())
+ m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
+ ASSERT(!ec);
+}
+
+void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
+{
+ ASSERT(renderStyle);
+ if (renderStyle->fontDescription().keywordSize())
+ m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
+}
+
+bool EditingStyle::isEmpty() const
+{
+ return !m_mutableStyle || m_mutableStyle->isEmpty();
+}
+
+void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style)
+{
+ m_mutableStyle = style;
+ // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
+ // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
+ m_shouldUseFixedDefaultFontSize = false;
+}
+
+void EditingStyle::clear()
+{
+ m_mutableStyle.clear();
+ m_shouldUseFixedDefaultFontSize = false;
+}
+
+void EditingStyle::removeBlockProperties()
+{
+ if (!m_mutableStyle)
+ return;
+
+ m_mutableStyle->removeBlockProperties();
+}
+
+void EditingStyle::removeStyleAddedByNode(Node* node)
+{
+ if (!node || !node->parentNode())
+ return;
+ RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
+ RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
+ parentStyle->diff(nodeStyle.get());
+ nodeStyle->diff(m_mutableStyle.get());
+}
+
+void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
+{
+ if (!node || !node->parentNode())
+ return;
+ RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
+ RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
+ parentStyle->diff(nodeStyle.get());
+
+ CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end();
+ for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it)
+ m_mutableStyle->removeProperty(it->id());
+}
+
+void EditingStyle::removeNonEditingProperties()
+{
+ if (m_mutableStyle)
+ m_mutableStyle = copyEditingProperties(m_mutableStyle.get());
+}
+
+void EditingStyle::prepareToApplyAt(const Position& position)
+{
+ // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
+ // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
+ // which one of editingStyleAtPosition or computedStyle is called.
+ RefPtr<EditingStyle> style = EditingStyle::create(position);
+ style->m_mutableStyle->diff(m_mutableStyle.get());
+
+ // if alpha value is zero, we don't add the background color.
+ RefPtr<CSSValue> backgroundColor = m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
+ if (backgroundColor && backgroundColor->isPrimitiveValue()
+ && !alphaChannel(static_cast<CSSPrimitiveValue*>(backgroundColor.get())->getRGBA32Value())) {
+ ExceptionCode ec;
+ m_mutableStyle->removeProperty(CSSPropertyBackgroundColor, ec);
+ }
+}
+
+PassRefPtr<EditingStyle> editingStyleIncludingTypingStyle(const Position& position)
+{
+ RefPtr<EditingStyle> editingStyle = EditingStyle::create(position);
+ RefPtr<CSSMutableStyleDeclaration> typingStyle = position.node()->document()->frame()->selection()->typingStyle();
+ if (typingStyle)
+ editingStyle->style()->merge(copyEditingProperties(typingStyle.get()).get());
+ return editingStyle;
+}
+
+}
diff --git a/WebCore/editing/EditingStyle.h b/WebCore/editing/EditingStyle.h
new file mode 100644
index 0000000..6b4c60c
--- /dev/null
+++ b/WebCore/editing/EditingStyle.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef EditingStyle_h
+#define EditingStyle_h
+
+#include "CSSMutableStyleDeclaration.h"
+#include "Document.h"
+#include "Position.h"
+
+namespace WebCore {
+
+class CSSStyleDeclaration;
+class CSSComputedStyleDeclaration;
+
+class EditingStyle : public RefCounted<EditingStyle> {
+public:
+
+ static PassRefPtr<EditingStyle> create()
+ {
+ return adoptRef(new EditingStyle());
+ }
+
+ static PassRefPtr<EditingStyle> create(Node* node)
+ {
+ return adoptRef(new EditingStyle(node));
+ }
+
+ static PassRefPtr<EditingStyle> create(const Position& position)
+ {
+ return adoptRef(new EditingStyle(position));
+ }
+
+ static PassRefPtr<EditingStyle> create(const CSSStyleDeclaration* style)
+ {
+ return adoptRef(new EditingStyle(style));
+ }
+
+ CSSMutableStyleDeclaration* style() { return m_mutableStyle.get(); }
+ bool isEmpty() const;
+ void setStyle(PassRefPtr<CSSMutableStyleDeclaration>);
+ void clear();
+ void removeBlockProperties();
+ void removeStyleAddedByNode(Node* node);
+ void removeStyleConflictingWithStyleOfNode(Node* node);
+ void removeNonEditingProperties();
+ void prepareToApplyAt(const Position&);
+
+private:
+ EditingStyle();
+ EditingStyle(Node*);
+ EditingStyle(const Position&);
+ EditingStyle(const CSSStyleDeclaration*);
+ void init(Node*);
+ void removeTextFillAndStrokeColorsIfNeeded(RenderStyle*);
+ void replaceFontSizeByKeywordIfPossible(RenderStyle*, CSSComputedStyleDeclaration*);
+
+ RefPtr<CSSMutableStyleDeclaration> m_mutableStyle;
+ bool m_shouldUseFixedDefaultFontSize;
+};
+
+PassRefPtr<EditingStyle> editingStyleIncludingTypingStyle(const Position&);
+
+} // namespace WebCore
+
+#endif // EditingStyle_h
diff --git a/WebCore/editing/Editor.cpp b/WebCore/editing/Editor.cpp
index c74d765..327aa5f 100644
--- a/WebCore/editing/Editor.cpp
+++ b/WebCore/editing/Editor.cpp
@@ -457,9 +457,53 @@ bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
void Editor::respondToChangedSelection(const VisibleSelection& oldSelection)
{
+#if SUPPORT_AUTOCORRECTION_PANEL
+ VisibleSelection currentSelection(frame()->selection()->selection());
+ if (currentSelection != oldSelection) {
+ stopCorrectionPanelTimer();
+ dismissCorrectionPanel(CorrectionWasNotRejected);
+ }
+#endif // SUPPORT_AUTOCORRECTION_PANEL
+
if (client())
client()->respondToChangedSelection();
m_deleteButtonController->respondToChangedSelection(oldSelection);
+
+#if SUPPORT_AUTOCORRECTION_PANEL
+ // When user moves caret to the end of autocorrected word and pauses, we show the panel
+ // containing the original pre-correction word so that user can quickly revert the
+ // undesired autocorrection. Here, we start correction panel timer once we confirm that
+ // the new caret position is at the end of a word.
+ if (!currentSelection.isCaret() || currentSelection == oldSelection)
+ return;
+
+ VisiblePosition selectionPosition = currentSelection.start();
+ VisiblePosition endPositionOfWord = endOfWord(selectionPosition, LeftWordIfOnBoundary);
+ if (selectionPosition != endPositionOfWord)
+ return;
+
+ Position position = endPositionOfWord.deepEquivalent();
+ if (position.anchorType() != Position::PositionIsOffsetInAnchor)
+ return;
+
+ Node* node = position.containerNode();
+ int endOffset = position.offsetInContainerNode();
+ Vector<DocumentMarker> markers = node->document()->markers()->markersForNode(node);
+ size_t markerCount = markers.size();
+ for (size_t i = 0; i < markerCount; ++i) {
+ const DocumentMarker& marker = markers[i];
+ if (marker.type == DocumentMarker::CorrectionIndicator && static_cast<int>(marker.endOffset) == endOffset) {
+ RefPtr<Range> wordRange = Range::create(frame()->document(), node, marker.startOffset, node, marker.endOffset);
+ String currentWord = plainText(wordRange.get());
+ if (currentWord.length() > 0 && marker.description.length() > 0) {
+ m_correctionPanelInfo.m_rangeToBeReplaced = wordRange;
+ m_correctionPanelInfo.m_replacementString = marker.description;
+ startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeReversion);
+ }
+ break;
+ }
+ }
+#endif // SUPPORT_AUTOCORRECTION_PANEL
}
void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
@@ -564,7 +608,8 @@ WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbe
}
if (m_frame->selection()->isCaret()) {
- if (CSSMutableStyleDeclaration* typingStyle = m_frame->selection()->typingStyle()) {
+ RefPtr<CSSMutableStyleDeclaration> typingStyle = m_frame->selection()->typingStyle();
+ if (typingStyle) {
RefPtr<CSSValue> unicodeBidi = typingStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
if (unicodeBidi) {
ASSERT(unicodeBidi->isPrimitiveValue());
@@ -998,10 +1043,13 @@ static void dispatchEditableContentChangedEvents(const EditCommand& command)
void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
{
+ // We may start reversion panel timer in respondToChangedSelection().
+ // So we stop the timer for current panel before calling changeSelectionAfterCommand() later in this method.
+ stopCorrectionPanelTimer();
m_frame->document()->updateLayout();
-
+
dispatchEditableContentChangedEvents(*cmd);
-
+
VisibleSelection newSelection(cmd->endingSelection());
// Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
changeSelectionAfterCommand(newSelection, false, false);
@@ -1020,7 +1068,6 @@ void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
client()->registerCommandForUndo(m_lastEditCommand);
}
respondToChangedContents(newSelection);
- stopCorrectionPanelTimer();
}
void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
@@ -1068,9 +1115,8 @@ Editor::Editor(Frame* frame)
Editor::~Editor()
{
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
- if (client())
- client()->dismissCorrectionPanel(true);
+#if SUPPORT_AUTOCORRECTION_PANEL
+ dismissCorrectionPanel(CorrectionWasNotRejected);
#endif
}
@@ -1671,7 +1717,6 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// repeated "check spelling" commands work.
VisibleSelection selection(frame()->selection()->selection());
RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
- TextCheckingHelper checker(client(), spellingSearchRange);
bool startedWithSelection = false;
if (selection.start().node()) {
@@ -1738,7 +1783,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
bool isSpelling = true;
int foundOffset = 0;
GrammarDetail grammarDetail;
- String foundItem = checker.findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
+ String foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
if (isSpelling) {
misspelledWord = foundItem;
misspellingOffset = foundOffset;
@@ -1748,7 +1793,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
}
#else
RefPtr<Range> firstMisspellingRange;
- String misspelledWord = checker.findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
+ String misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
String badGrammarPhrase;
#ifndef BUILDING_ON_TIGER
@@ -1765,7 +1810,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
}
if (isGrammarCheckingEnabled())
- badGrammarPhrase = checker.findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
+ badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
#endif
#endif
@@ -1778,7 +1823,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
grammarSearchRange = spellingSearchRange->cloneRange(ec);
- foundItem = checker.findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
+ foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
if (isSpelling) {
misspelledWord = foundItem;
misspellingOffset = foundOffset;
@@ -1787,7 +1832,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
grammarPhraseOffset = foundOffset;
}
#else
- misspelledWord = checker.findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
+ misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
#ifndef BUILDING_ON_TIGER
grammarSearchRange = spellingSearchRange->cloneRange(ec);
@@ -1797,8 +1842,9 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
chars.advance(misspellingOffset);
grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
}
+
if (isGrammarCheckingEnabled())
- badGrammarPhrase = checker.findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
+ badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
#endif
#endif
}
@@ -1967,47 +2013,10 @@ void Editor::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelecti
void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p)
{
#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
-#if !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
// Apply pending autocorrection before next round of spell checking.
- bool didApplyCorrection = false;
- if (m_rangeToBeReplacedByCorrection) {
- ExceptionCode ec = 0;
- RefPtr<Range> paragraphRangeContainingCorrection = m_rangeToBeReplacedByCorrection->cloneRange(ec);
- if (!ec) {
- setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(m_rangeToBeReplacedByCorrection->startPosition()));
- setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(m_rangeToBeReplacedByCorrection->endPosition()));
- // After we replace the word at range m_rangeToBeReplacedByCorrection, we need to add
- // autocorrection underline at that range. However, once the replacement took place, the
- // value of m_rangeToBeReplacedByCorrection is not valid anymore. So before we carry out
- // the replacement, we need to store the start position of m_rangeToBeReplacedByCorrection
- // relative to the start position of the containing paragraph. We use correctionStartOffsetInParagraph
- // to store this value. In order to obtain this offset, we need to first create a range
- // which spans from the start of paragraph to the start position of m_rangeToBeReplacedByCorrection.
- RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer(ec)->document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition());
- if (!ec) {
- Position startPositionOfRangeToBeReplaced = m_rangeToBeReplacedByCorrection->startPosition();
- correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfRangeToBeReplaced.containerNode(), startPositionOfRangeToBeReplaced.computeOffsetInContainerNode(), ec);
- if (!ec) {
- // Take note of the location of autocorrection so that we can add marker after the replacement took place.
- int correctionStartOffsetInParagraph = TextIterator::rangeLength(correctionStartOffsetInParagraphAsRange.get());
- Position caretPosition = m_frame->selection()->selection().end();
- RefPtr<Range> rangeToBeReplaced = m_rangeToBeReplacedByCorrection->cloneRange(ec);
- VisibleSelection selectionToReplace(rangeToBeReplaced.get(), DOWNSTREAM);
- if (m_frame->selection()->shouldChangeSelection(selectionToReplace)) {
- m_frame->selection()->setSelection(selectionToReplace);
- replaceSelectionWithText(m_correctionReplacementString, false, false);
- caretPosition.moveToOffset(caretPosition.offsetInContainerNode() + m_correctionReplacementString.length() - m_stringToBeReplacedByCorrection.length());
- RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionReplacementString.length());
- replacementRange->startContainer()->document()->markers()->addMarker(replacementRange.get(), DocumentMarker::Replacement, m_correctionReplacementString);
- replacementRange->startContainer()->document()->markers()->addMarker(replacementRange.get(), DocumentMarker::CorrectionIndicator);
- m_frame->selection()->moveTo(caretPosition, false);
- didApplyCorrection = true;
- }
- }
- }
- }
- m_rangeToBeReplacedByCorrection.clear();
- }
+ applyCorrectionPanelInfo(true);
+ m_correctionPanelInfo.m_rangeToBeReplaced.clear();
#endif
TextCheckingOptions textCheckingOptions = 0;
@@ -2095,7 +2104,7 @@ void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, boo
if (!editableNode || !editableNode->isContentEditable())
return;
- if (!isSpellCheckingEnabledInFocusedNode())
+ if (!isSpellCheckingEnabledFor(editableNode))
return;
// Get the spell checker if it is available
@@ -2115,11 +2124,8 @@ void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, boo
}
}
-bool Editor::isSpellCheckingEnabledInFocusedNode() const
+bool Editor::isSpellCheckingEnabledFor(Node* node) const
{
- // Ascend the DOM tree to find a "spellcheck" attribute.
- // When we find a "spellcheck" attribute, retrieve its value and return false if its value is "false".
- const Node* node = frame()->document()->focusedNode();
if (!node)
return false;
const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement();
@@ -2128,6 +2134,11 @@ bool Editor::isSpellCheckingEnabledInFocusedNode() const
return focusedElement->isSpellCheckingEnabled();
}
+bool Editor::isSpellCheckingEnabledInFocusedNode() const
+{
+ return isSpellCheckingEnabledFor(m_frame->selection()->start().node());
+}
+
void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
{
markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
@@ -2170,7 +2181,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh
if (!editableNode || !editableNode->isContentEditable())
return;
- if (!isSpellCheckingEnabledInFocusedNode())
+ if (!isSpellCheckingEnabledFor(editableNode))
return;
// Expand the range to encompass entire paragraphs, since text checking needs that much context.
@@ -2187,16 +2198,15 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh
bool adjustSelectionForParagraphBoundaries = false;
String paragraphString;
RefPtr<Range> paragraphRange;
- TextCheckingHelper checker(client(), grammarRange);
if (shouldMarkGrammar) {
// The spelling range should be contained in the paragraph-aligned extension of the grammar range.
- paragraphRange = checker.paragraphAlignedRange(grammarRangeStartOffset, paragraphString);
+ paragraphRange = TextCheckingHelper(client(), grammarRange).paragraphAlignedRange(grammarRangeStartOffset, paragraphString);
RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), spellingRange->startPosition());
spellingRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
grammarRangeEndOffset = grammarRangeStartOffset + TextIterator::rangeLength(grammarRange);
} else {
- paragraphRange = checker.paragraphAlignedRange(spellingRangeStartOffset, paragraphString);
+ paragraphRange = TextCheckingHelper(client(), spellingRange).paragraphAlignedRange(spellingRangeStartOffset, paragraphString);
}
spellingRangeEndOffset = spellingRangeStartOffset + TextIterator::rangeLength(spellingRange);
paragraphLength = paragraphString.length();
@@ -2242,7 +2252,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh
}
client()->checkTextOfParagraph(paragraphString.characters(), paragraphLength, checkingTypes, results);
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
// If this checking is only for showing correction panel, we shouldn't bother to mark misspellings.
if (shouldShowCorrectionPanel)
shouldMarkSpelling = false;
@@ -2327,7 +2337,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh
} else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
if (result->type == TextCheckingTypeCorrection)
replacedString = plainText(rangeToReplace.get());
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
if (shouldShowCorrectionPanel && resultLocation + resultLength == spellingRangeEndOffset && result->type == TextCheckingTypeCorrection) {
// We only show the correction panel on the last word.
Vector<FloatQuad> textQuads;
@@ -2336,10 +2346,11 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh
FloatRect totalBoundingBox;
for (Vector<FloatQuad>::const_iterator it = textQuads.begin(); it < end; ++it)
totalBoundingBox.unite(it->boundingBox());
- m_rangeToBeReplacedByCorrection = rangeToReplace;
- m_stringToBeReplacedByCorrection = replacedString;
- m_correctionReplacementString = result->replacement;
- client()->showCorrectionPanel(totalBoundingBox, m_stringToBeReplacedByCorrection, result->replacement, this);
+ m_correctionPanelInfo.m_rangeToBeReplaced = rangeToReplace;
+ m_correctionPanelInfo.m_replacedString = replacedString;
+ m_correctionPanelInfo.m_replacementString = result->replacement;
+ m_correctionPanelInfo.m_isActive = true;
+ client()->showCorrectionPanel(m_correctionPanelInfo.m_panelType, totalBoundingBox, m_correctionPanelInfo.m_replacedString, result->replacement, this);
doReplacement = false;
}
#endif
@@ -2353,7 +2364,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh
// Add a marker so that corrections can easily be undone and won't be re-corrected.
RefPtr<Range> replacedRange = TextIterator::subrange(paragraphRange.get(), resultLocation, replacementLength);
replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::Replacement, replacedString);
- replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::CorrectionIndicator);
+ replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::CorrectionIndicator, replacedString);
}
}
}
@@ -2416,60 +2427,99 @@ void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelec
void Editor::correctionPanelTimerFired(Timer<Editor>*)
{
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
- VisibleSelection selection(frame()->selection()->selection());
- VisiblePosition start(selection.start(), selection.affinity());
- VisiblePosition p = startOfWord(start, LeftWordIfOnBoundary);
- VisibleSelection adjacentWords = VisibleSelection(p, start);
- markAllMisspellingsAndBadGrammarInRanges(MarkSpelling | ShowCorrectionPanel, adjacentWords.toNormalizedRange().get(), 0);
+#if SUPPORT_AUTOCORRECTION_PANEL
+ if (m_correctionPanelInfo.m_panelType == CorrectionPanelInfo::PanelTypeCorrection) {
+ VisibleSelection selection(frame()->selection()->selection());
+ VisiblePosition start(selection.start(), selection.affinity());
+ VisiblePosition p = startOfWord(start, LeftWordIfOnBoundary);
+ VisibleSelection adjacentWords = VisibleSelection(p, start);
+ markAllMisspellingsAndBadGrammarInRanges(MarkSpelling | ShowCorrectionPanel, adjacentWords.toNormalizedRange().get(), 0);
+ } else {
+ String currentWord = plainText(m_correctionPanelInfo.m_rangeToBeReplaced.get());
+ Vector<FloatQuad> textQuads;
+ m_correctionPanelInfo.m_rangeToBeReplaced->getBorderAndTextQuads(textQuads);
+ Vector<FloatQuad>::const_iterator end = textQuads.end();
+ FloatRect totalBoundingBox;
+ for (Vector<FloatQuad>::const_iterator it = textQuads.begin(); it < end; ++it)
+ totalBoundingBox.unite(it->boundingBox());
+ m_correctionPanelInfo.m_isActive = true;
+ m_correctionPanelInfo.m_replacedString = currentWord;
+ client()->showCorrectionPanel(m_correctionPanelInfo.m_panelType, totalBoundingBox, m_correctionPanelInfo.m_replacedString, m_correctionPanelInfo.m_replacementString, this);
+ }
#endif
}
void Editor::handleRejectedCorrection()
{
- Range* replacedRange = m_rangeToBeReplacedByCorrection.get();
+ Range* replacedRange = m_correctionPanelInfo.m_rangeToBeReplaced.get();
if (!replacedRange || m_frame->document() != replacedRange->ownerDocument())
return;
- replacedRange->startContainer()->document()->markers()->addMarker(replacedRange, DocumentMarker::RejectedCorrection, m_stringToBeReplacedByCorrection);
- m_rangeToBeReplacedByCorrection.clear();
+ if (m_correctionPanelInfo.m_panelType == CorrectionPanelInfo::PanelTypeCorrection)
+ replacedRange->startContainer()->document()->markers()->addMarker(replacedRange, DocumentMarker::RejectedCorrection, m_correctionPanelInfo.m_replacedString);
+ else {
+ m_correctionPanelInfo.m_isActive = false;
+ applyCorrectionPanelInfo(false);
+ }
+ m_correctionPanelInfo.m_rangeToBeReplaced.clear();
}
-void Editor::startCorrectionPanelTimer()
+void Editor::startCorrectionPanelTimer(CorrectionPanelInfo::PanelType type)
{
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
- static const double correctionPanelTimerInterval = 0.3;
+#if SUPPORT_AUTOCORRECTION_PANEL
+ const double correctionPanelTimerInterval = 0.3;
if (isAutomaticSpellingCorrectionEnabled()) {
- m_rangeToBeReplacedByCorrection.clear();
+ if (type == CorrectionPanelInfo::PanelTypeCorrection)
+ // If type is PanelTypeReversion, then the new range has been set. So we shouldn't clear it.
+ m_correctionPanelInfo.m_rangeToBeReplaced.clear();
+ m_correctionPanelInfo.m_panelType = type;
m_correctionPanelTimer.startOneShot(correctionPanelTimerInterval);
}
+#else
+ UNUSED_PARAM(type);
#endif
}
void Editor::stopCorrectionPanelTimer()
{
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
m_correctionPanelTimer.stop();
#endif
}
void Editor::handleCancelOperation()
{
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
+ if (!m_correctionPanelInfo.m_isActive)
+ return;
+ m_correctionPanelInfo.m_isActive = false;
if (client())
- client()->dismissCorrectionPanel(false);
+ client()->dismissCorrectionPanel(CorrectionWasRejected);
#endif
}
bool Editor::isShowingCorrectionPanel()
{
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
if (client())
return client()->isShowingCorrectionPanel();
#endif
return false;
}
+void Editor::dismissCorrectionPanel(CorrectionWasRejectedOrNot correctionWasRejectedOrNot)
+{
+#if SUPPORT_AUTOCORRECTION_PANEL
+ if (!m_correctionPanelInfo.m_isActive)
+ return;
+ m_correctionPanelInfo.m_isActive = false;
+ m_correctionPanelInfo.m_rangeToBeReplaced.clear();
+ if (client())
+ client()->dismissCorrectionPanel(correctionWasRejectedOrNot);
+#else
+ UNUSED_PARAM(correctionWasRejectedOrNot);
+#endif
+}
void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemoveIfSelectionAtWordBoundary)
{
// We want to remove the markers from a word if an editing command will change the word. This can happen in one of
@@ -2536,6 +2586,8 @@ void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemove
Vector<RangeMarkerPair, 16> markersToRemove;
for (TextIterator textIterator(wordRange.get()); !textIterator.atEnd(); textIterator.advance()) {
Node* node = textIterator.node();
+ if (!node)
+ continue;
if (node == startOfFirstWord.deepEquivalent().containerNode() || node == endOfLastWord.deepEquivalent().containerNode()) {
// First word and last word can belong to the same node
bool processFirstWord = node == startOfFirstWord.deepEquivalent().containerNode() && document->markers()->hasMarkers(rangeOfFirstWord.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator);
@@ -2565,6 +2617,54 @@ void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemove
document->markers()->removeMarkers(pairIterator->first.get(), pairIterator->second);
}
+void Editor::applyCorrectionPanelInfo(bool addCorrectionIndicatorMarker)
+{
+ if (!m_correctionPanelInfo.m_rangeToBeReplaced)
+ return;
+
+ ExceptionCode ec = 0;
+ RefPtr<Range> paragraphRangeContainingCorrection = m_correctionPanelInfo.m_rangeToBeReplaced->cloneRange(ec);
+ if (ec)
+ return;
+
+ setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(m_correctionPanelInfo.m_rangeToBeReplaced->startPosition()));
+ setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(m_correctionPanelInfo.m_rangeToBeReplaced->endPosition()));
+
+ // After we replace the word at range m_rangeToBeReplaced, we need to add markers to that range.
+ // However, once the replacement took place, the value of m_rangeToBeReplaced is not valid anymore.
+ // So before we carry out the replacement, we need to store the start position of m_rangeToBeReplaced
+ // relative to the start position of the containing paragraph. We use correctionStartOffsetInParagraph
+ // to store this value. In order to obtain this offset, we need to first create a range
+ // which spans from the start of paragraph to the start position of m_rangeToBeReplaced.
+ RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer(ec)->document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition());
+ if (ec)
+ return;
+
+ Position startPositionOfRangeToBeReplaced = m_correctionPanelInfo.m_rangeToBeReplaced->startPosition();
+ correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfRangeToBeReplaced.containerNode(), startPositionOfRangeToBeReplaced.computeOffsetInContainerNode(), ec);
+ if (ec)
+ return;
+
+ // Take note of the location of autocorrection so that we can add marker after the replacement took place.
+ int correctionStartOffsetInParagraph = TextIterator::rangeLength(correctionStartOffsetInParagraphAsRange.get());
+ Position caretPosition = m_frame->selection()->selection().end();
+
+ // Clone the range, since the caller of this method may want to keep the original range around.
+ RefPtr<Range> rangeToBeReplaced = m_correctionPanelInfo.m_rangeToBeReplaced->cloneRange(ec);
+ VisibleSelection selectionToReplace(rangeToBeReplaced.get(), DOWNSTREAM);
+ if (m_frame->selection()->shouldChangeSelection(selectionToReplace)) {
+ m_frame->selection()->setSelection(selectionToReplace);
+ replaceSelectionWithText(m_correctionPanelInfo.m_replacementString, false, false);
+ caretPosition.moveToOffset(caretPosition.offsetInContainerNode() + m_correctionPanelInfo.m_replacementString.length() - m_correctionPanelInfo.m_replacedString.length());
+ setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(caretPosition));
+ RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionPanelInfo.m_replacementString.length());
+ replacementRange->startContainer()->document()->markers()->addMarker(replacementRange.get(), DocumentMarker::Replacement, m_correctionPanelInfo.m_replacementString);
+ if (addCorrectionIndicatorMarker)
+ replacementRange->startContainer()->document()->markers()->addMarker(replacementRange.get(), DocumentMarker::CorrectionIndicator, m_correctionPanelInfo.m_replacedString);
+ m_frame->selection()->moveTo(caretPosition, false);
+ }
+}
+
PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
{
Document* document = m_frame->documentAtPoint(windowPoint);
@@ -2831,7 +2931,7 @@ void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, b
if (newSelection.start().isOrphan() || newSelection.end().isOrphan())
return;
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
// Check to see if the command introduced paragraph separator. If it did, we remove existing autocorrection underlines.
// This is in consistency with the behavior in AppKit
if (!inSameParagraph(m_frame->selection()->selection().visibleStart(), newSelection.visibleEnd()))
@@ -2954,7 +3054,7 @@ void Editor::computeAndSetTypingStyle(CSSStyleDeclaration* style, EditAction edi
applyCommand(ApplyStyleCommand::create(m_frame->document(), blockStyle.get(), editingAction));
// Set the remaining style as the typing style.
- m_frame->selection()->setTypingStyle(mutableStyle.release());
+ m_frame->selection()->setTypingStyle(EditingStyle::create(mutableStyle.get()));
}
PassRefPtr<CSSMutableStyleDeclaration> Editor::selectionComputedStyle(bool& shouldUseFixedFontDefaultSize) const
@@ -2985,10 +3085,10 @@ PassRefPtr<CSSMutableStyleDeclaration> Editor::selectionComputedStyle(bool& shou
if (!m_frame->selection()->typingStyle())
return mutableStyle;
- RefPtr<CSSMutableStyleDeclaration> typingStyle = m_frame->selection()->typingStyle()->copy();
- ApplyStyleCommand::removeNonEditingProperties(typingStyle.get());
- prepareEditingStyleToApplyAt(typingStyle.get(), position);
- mutableStyle->merge(typingStyle.get());
+ RefPtr<EditingStyle> typingStyle = EditingStyle::create(m_frame->selection()->typingStyle());
+ typingStyle->removeNonEditingProperties();
+ typingStyle->prepareToApplyAt(position);
+ mutableStyle->merge(typingStyle->style());
return mutableStyle;
}
diff --git a/WebCore/editing/Editor.h b/WebCore/editing/Editor.h
index 110e3f9..24cdaf1 100644
--- a/WebCore/editing/Editor.h
+++ b/WebCore/editing/Editor.h
@@ -28,23 +28,13 @@
#include "ClipboardAccessPolicy.h"
#include "Color.h"
+#include "CorrectionPanelInfo.h"
#include "EditAction.h"
#include "EditingBehavior.h"
#include "EditorDeleteAction.h"
#include "EditorInsertAction.h"
#include "SelectionController.h"
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
-// Some platforms provide UI for suggesting autocorrection.
-#define SUPPORT_AUTOCORRECTION_PANEL 1
-// Some platforms use spelling and autocorrection markers to provide visual cue.
-// On such platform, if word with marker is edited, we need to remove the marker.
-#define REMOVE_MARKERS_UPON_EDITING 1
-#else
-#define SUPPORT_AUTOCORRECTION_PANEL 0
-#define REMOVE_MARKERS_UPON_EDITING 0
-#endif /* #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) */
-
#if PLATFORM(MAC) && !defined(__OBJC__)
class NSDictionary;
typedef int NSWritingDirection;
@@ -219,6 +209,7 @@ public:
Vector<String> guessesForUngrammaticalSelection();
Vector<String> guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical);
bool isSpellCheckingEnabledInFocusedNode() const;
+ bool isSpellCheckingEnabledFor(Node*) const;
void markMisspellingsAfterTypingToPosition(const VisiblePosition&);
void markMisspellings(const VisibleSelection&, RefPtr<Range>& firstMisspellingRange);
void markBadGrammar(const VisibleSelection&);
@@ -322,7 +313,7 @@ public:
void addToKillRing(Range*, bool prepend);
void handleCancelOperation();
- void startCorrectionPanelTimer();
+ void startCorrectionPanelTimer(CorrectionPanelInfo::PanelType);
void handleRejectedCorrection();
bool isShowingCorrectionPanel();
@@ -388,9 +379,7 @@ private:
bool m_shouldStartNewKillRingSequence;
bool m_shouldStyleWithCSS;
OwnPtr<KillRing> m_killRing;
- RefPtr<Range> m_rangeToBeReplacedByCorrection;
- String m_stringToBeReplacedByCorrection;
- String m_correctionReplacementString;
+ CorrectionPanelInfo m_correctionPanelInfo;
Timer<Editor> m_correctionPanelTimer;
VisibleSelection m_mark;
bool m_areMarkedTextMatchesHighlighted;
@@ -417,6 +406,8 @@ private:
void correctionPanelTimerFired(Timer<Editor>*);
Node* findEventTargetFromSelection() const;
void stopCorrectionPanelTimer();
+ void dismissCorrectionPanel(CorrectionWasRejectedOrNot);
+ void applyCorrectionPanelInfo(bool addCorrectionIndicatorMarker);
};
inline void Editor::setStartNewKillRingSequence(bool flag)
diff --git a/WebCore/editing/EditorCommand.cpp b/WebCore/editing/EditorCommand.cpp
index 85becd5..e3ea37e 100644
--- a/WebCore/editing/EditorCommand.cpp
+++ b/WebCore/editing/EditorCommand.cpp
@@ -618,8 +618,7 @@ static bool executeMoveBackwardAndModifySelection(Frame* frame, Event*, EditorCo
static bool executeMoveDown(Frame* frame, Event*, EditorCommandSource, const String&)
{
- frame->selection()->modify(SelectionController::AlterationMove, SelectionController::DirectionForward, LineGranularity, true);
- return true;
+ return frame->selection()->modify(SelectionController::AlterationMove, SelectionController::DirectionForward, LineGranularity, true);
}
static bool executeMoveDownAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&)
@@ -642,8 +641,7 @@ static bool executeMoveForwardAndModifySelection(Frame* frame, Event*, EditorCom
static bool executeMoveLeft(Frame* frame, Event*, EditorCommandSource, const String&)
{
- frame->selection()->modify(SelectionController::AlterationMove, SelectionController::DirectionLeft, CharacterGranularity, true);
- return true;
+ return frame->selection()->modify(SelectionController::AlterationMove, SelectionController::DirectionLeft, CharacterGranularity, true);
}
static bool executeMoveLeftAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&)
@@ -686,8 +684,7 @@ static bool executeMovePageUpAndModifySelection(Frame* frame, Event*, EditorComm
static bool executeMoveRight(Frame* frame, Event*, EditorCommandSource, const String&)
{
- frame->selection()->modify(SelectionController::AlterationMove, SelectionController::DirectionRight, CharacterGranularity, true);
- return true;
+ return frame->selection()->modify(SelectionController::AlterationMove, SelectionController::DirectionRight, CharacterGranularity, true);
}
static bool executeMoveRightAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&)
@@ -806,8 +803,7 @@ static bool executeMoveParagraphForwardAndModifySelection(Frame* frame, Event*,
static bool executeMoveUp(Frame* frame, Event*, EditorCommandSource, const String&)
{
- frame->selection()->modify(SelectionController::AlterationMove, SelectionController::DirectionBackward, LineGranularity, true);
- return true;
+ return frame->selection()->modify(SelectionController::AlterationMove, SelectionController::DirectionBackward, LineGranularity, true);
}
static bool executeMoveUpAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&)
@@ -1074,7 +1070,7 @@ static bool executeYankAndSelect(Frame* frame, Event*, EditorCommandSource, cons
return true;
}
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
static bool executeCancelOperation(Frame* frame, Event*, EditorCommandSource, const String&)
{
frame->editor()->handleCancelOperation();
@@ -1124,7 +1120,7 @@ static bool supportedPaste(Frame* frame, EditorCommandSource source)
return false;
}
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
static bool supportedDismissCorrectionPanel(Frame* frame, EditorCommandSource source)
{
return supportedFromMenuOrKeyBinding(frame, source) && frame->editor()->isShowingCorrectionPanel();
@@ -1506,7 +1502,7 @@ static const CommandMap& createCommandMap()
{ "Unselect", { executeUnselect, supported, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "Yank", { executeYank, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "YankAndSelect", { executeYankAndSelect, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
{ "CancelOperation", { executeCancelOperation, supportedDismissCorrectionPanel, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
#endif
};
diff --git a/WebCore/editing/InsertLineBreakCommand.cpp b/WebCore/editing/InsertLineBreakCommand.cpp
index 8ac1167..5588326 100644
--- a/WebCore/editing/InsertLineBreakCommand.cpp
+++ b/WebCore/editing/InsertLineBreakCommand.cpp
@@ -165,14 +165,14 @@ void InsertLineBreakCommand::doApply()
// Handle the case where there is a typing style.
- CSSMutableStyleDeclaration* typingStyle = document()->frame()->selection()->typingStyle();
+ RefPtr<CSSMutableStyleDeclaration> typingStyle = document()->frame()->selection()->typingStyle();
if (typingStyle && typingStyle->length() > 0) {
// Apply the typing style to the inserted line break, so that if the selection
// leaves and then comes back, new input will have the right style.
// FIXME: We shouldn't always apply the typing style to the line break here,
// see <rdar://problem/5794462>.
- applyStyle(typingStyle, firstDeepEditingPositionForNode(nodeToInsert.get()), lastDeepEditingPositionForNode(nodeToInsert.get()));
+ applyStyle(typingStyle.get(), firstDeepEditingPositionForNode(nodeToInsert.get()), lastDeepEditingPositionForNode(nodeToInsert.get()));
// Even though this applyStyle operates on a Range, it still sets an endingSelection().
// It tries to set a VisibleSelection around the content it operated on. So, that VisibleSelection
// will either (a) select the line break we inserted, or it will (b) be a caret just
diff --git a/WebCore/editing/InsertParagraphSeparatorCommand.cpp b/WebCore/editing/InsertParagraphSeparatorCommand.cpp
index 1804bf4..abd744c 100644
--- a/WebCore/editing/InsertParagraphSeparatorCommand.cpp
+++ b/WebCore/editing/InsertParagraphSeparatorCommand.cpp
@@ -26,19 +26,16 @@
#include "config.h"
#include "InsertParagraphSeparatorCommand.h"
-#include "CSSComputedStyleDeclaration.h"
-#include "CSSMutableStyleDeclaration.h"
#include "CSSPropertyNames.h"
#include "Document.h"
+#include "EditingStyle.h"
#include "HTMLElement.h"
#include "HTMLNames.h"
#include "InsertLineBreakCommand.h"
-#include "Logging.h"
#include "RenderObject.h"
#include "Text.h"
#include "htmlediting.h"
#include "visible_units.h"
-#include "ApplyStyleCommand.h"
namespace WebCore {
@@ -82,7 +79,7 @@ void InsertParagraphSeparatorCommand::calculateStyleBeforeInsertion(const Positi
if (!isStartOfParagraph(visiblePos) && !isEndOfParagraph(visiblePos))
return;
- m_style = ApplyStyleCommand::editingStyleAtPosition(pos, IncludeTypingStyle);
+ m_style = editingStyleIncludingTypingStyle(pos);
}
void InsertParagraphSeparatorCommand::applyStyleAfterInsertion(Node* originalEnclosingBlock)
@@ -95,14 +92,13 @@ void InsertParagraphSeparatorCommand::applyStyleAfterInsertion(Node* originalEnc
originalEnclosingBlock->hasTagName(h4Tag) ||
originalEnclosingBlock->hasTagName(h5Tag))
return;
-
+
if (!m_style)
return;
-
- prepareEditingStyleToApplyAt(m_style.get(), endingSelection().start());
- if (m_style->length() > 0)
- applyStyle(m_style.get());
+ m_style->prepareToApplyAt(endingSelection().start());
+ if (!m_style->isEmpty())
+ applyStyle(m_style->style());
}
bool InsertParagraphSeparatorCommand::shouldUseDefaultParagraphElement(Node* enclosingBlock) const
diff --git a/WebCore/editing/InsertParagraphSeparatorCommand.h b/WebCore/editing/InsertParagraphSeparatorCommand.h
index 23ee51c..2eae77d 100644
--- a/WebCore/editing/InsertParagraphSeparatorCommand.h
+++ b/WebCore/editing/InsertParagraphSeparatorCommand.h
@@ -30,6 +30,8 @@
namespace WebCore {
+class EditingStyle;
+
class InsertParagraphSeparatorCommand : public CompositeEditCommand {
public:
static PassRefPtr<InsertParagraphSeparatorCommand> create(Document* document, bool useDefaultParagraphElement = false)
@@ -46,13 +48,13 @@ private:
void applyStyleAfterInsertion(Node* originalEnclosingBlock);
void getAncestorsInsideBlock(const Node* insertionNode, Element* outerBlock, Vector<Element*>& ancestors);
PassRefPtr<Element> cloneHierarchyUnderNewBlock(const Vector<Element*>& ancestors, PassRefPtr<Element> blockToInsert);
-
+
bool shouldUseDefaultParagraphElement(Node*) const;
virtual bool preservesTypingStyle() const;
- RefPtr<CSSMutableStyleDeclaration> m_style;
-
+ RefPtr<EditingStyle> m_style;
+
bool m_mustUseDefaultParagraphElement;
};
diff --git a/WebCore/editing/InsertTextCommand.cpp b/WebCore/editing/InsertTextCommand.cpp
index b6c8236..87a695d 100644
--- a/WebCore/editing/InsertTextCommand.cpp
+++ b/WebCore/editing/InsertTextCommand.cpp
@@ -190,7 +190,7 @@ void InsertTextCommand::input(const String& text, bool selectInsertedText)
setEndingSelection(forcedEndingSelection);
// Handle the case where there is a typing style.
- CSSMutableStyleDeclaration* typingStyle = document()->frame()->selection()->typingStyle();
+ RefPtr<CSSMutableStyleDeclaration> typingStyle = document()->frame()->selection()->typingStyle();
RefPtr<CSSComputedStyleDeclaration> endingStyle = endPosition.computedStyle();
RefPtr<CSSValue> unicodeBidi;
RefPtr<CSSValue> direction;
@@ -198,7 +198,7 @@ void InsertTextCommand::input(const String& text, bool selectInsertedText)
unicodeBidi = typingStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
direction = typingStyle->getPropertyCSSValue(CSSPropertyDirection);
}
- endingStyle->diff(typingStyle);
+ endingStyle->diff(typingStyle.get());
if (typingStyle && unicodeBidi) {
ASSERT(unicodeBidi->isPrimitiveValue());
typingStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
@@ -209,7 +209,7 @@ void InsertTextCommand::input(const String& text, bool selectInsertedText)
}
if (typingStyle && typingStyle->length())
- applyStyle(typingStyle);
+ applyStyle(typingStyle.get());
if (!selectInsertedText)
setEndingSelection(VisibleSelection(endingSelection().end(), endingSelection().affinity()));
diff --git a/WebCore/editing/RemoveFormatCommand.cpp b/WebCore/editing/RemoveFormatCommand.cpp
index f8807e2..42833cb 100644
--- a/WebCore/editing/RemoveFormatCommand.cpp
+++ b/WebCore/editing/RemoveFormatCommand.cpp
@@ -28,18 +28,11 @@
#include "RemoveFormatCommand.h"
#include "ApplyStyleCommand.h"
-#include "CSSComputedStyleDeclaration.h"
-#include "CSSMutableStyleDeclaration.h"
-#include "CSSValueKeywords.h"
-#include "Editor.h"
+#include "EditingStyle.h"
+#include "Element.h"
#include "Frame.h"
-#include "HTMLElement.h"
#include "HTMLNames.h"
-#include "VisibleSelection.h"
#include "SelectionController.h"
-#include "TextIterator.h"
-#include "TypingCommand.h"
-#include "htmlediting.h"
namespace WebCore {
@@ -92,9 +85,9 @@ void RemoveFormatCommand::doApply()
// Get the default style for this editable root, it's the style that we'll give the
// content that we're operating on.
Node* root = frame->selection()->rootEditableElement();
- RefPtr<CSSMutableStyleDeclaration> defaultStyle = ApplyStyleCommand::editingStyleAtPosition(Position(root, 0));
+ RefPtr<EditingStyle> defaultStyle = EditingStyle::create(root);
- applyCommandToComposite(ApplyStyleCommand::create(document(), defaultStyle.get(), isElementForRemoveFormatCommand, editingAction()));
+ applyCommandToComposite(ApplyStyleCommand::create(document(), defaultStyle->style(), isElementForRemoveFormatCommand, editingAction()));
}
}
diff --git a/WebCore/editing/ReplaceSelectionCommand.cpp b/WebCore/editing/ReplaceSelectionCommand.cpp
index 056ab70..fb52e56 100644
--- a/WebCore/editing/ReplaceSelectionCommand.cpp
+++ b/WebCore/editing/ReplaceSelectionCommand.cpp
@@ -31,7 +31,6 @@
#include "BreakBlockquoteCommand.h"
#include "CSSComputedStyleDeclaration.h"
#include "CSSMutableStyleDeclaration.h"
-#include "CSSProperty.h"
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
#include "Document.h"
@@ -484,17 +483,6 @@ void ReplaceSelectionCommand::negateStyleRulesThatAffectAppearance()
e->getInlineStyleDecl()->setProperty(CSSPropertyDisplay, CSSValueInline);
if (e->renderer() && e->renderer()->style()->floating() != FNONE)
e->getInlineStyleDecl()->setProperty(CSSPropertyFloat, CSSValueNone);
-
- // Undo the effects of page zoom if we have an absolute font size. When we copy, we
- // compute the new font size as an absolute size so pasting will cause the zoom to be
- // applied twice.
- if (e->renderer() && e->renderer()->style() && e->renderer()->style()->effectiveZoom() != 1.0
- && e->renderer()->style()->fontDescription().isAbsoluteSize()) {
- float newSize = e->renderer()->style()->fontDescription().specifiedSize() / e->renderer()->style()->effectiveZoom();
- ExceptionCode ec = 0;
- e->style()->setProperty(CSSPropertyFontSize, String::number(newSize), false, ec);
- ASSERT(!ec);
- }
}
if (node == m_lastLeafInserted)
break;
@@ -563,35 +551,36 @@ VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent()
static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos)
{
Node* topNode = fragment.firstChild();
-
+
// Handling the case where we are doing Paste as Quotation or pasting into quoted content is more complicated (see handleStyleSpans)
// and doesn't receive the optimization.
if (isMailPasteAsQuotationNode(topNode) || nearestMailBlockquote(topNode))
return false;
-
+
// Either there are no style spans in the fragment or a WebKit client has added content to the fragment
// before inserting it. Look for and handle style spans after insertion.
if (!isStyleSpan(topNode))
return false;
-
+
Node* sourceDocumentStyleSpan = topNode;
RefPtr<Node> copiedRangeStyleSpan = sourceDocumentStyleSpan->firstChild();
- RefPtr<CSSMutableStyleDeclaration> styleAtInsertionPos = ApplyStyleCommand::editingStyleAtPosition(rangeCompliantEquivalent(insertionPos));
+ RefPtr<EditingStyle> styleAtInsertionPos = EditingStyle::create(rangeCompliantEquivalent(insertionPos));
+ String styleText = styleAtInsertionPos->style()->cssText();
- String styleText = styleAtInsertionPos->cssText();
-
+ // FIXME: This string comparison is a naive way of comparing two styles.
+ // We should be taking the diff and check that the diff is empty.
if (styleText == static_cast<Element*>(sourceDocumentStyleSpan)->getAttribute(styleAttr)) {
fragment.removeNodePreservingChildren(sourceDocumentStyleSpan);
if (!isStyleSpan(copiedRangeStyleSpan.get()))
return true;
}
-
+
if (isStyleSpan(copiedRangeStyleSpan.get()) && styleText == static_cast<Element*>(copiedRangeStyleSpan.get())->getAttribute(styleAttr)) {
fragment.removeNodePreservingChildren(copiedRangeStyleSpan.get());
return true;
}
-
+
return false;
}
@@ -626,29 +615,20 @@ void ReplaceSelectionCommand::handleStyleSpans()
// we are here because of a document.execCommand("InsertHTML", ...) call.
if (!sourceDocumentStyleSpan)
return;
-
- RefPtr<CSSMutableStyleDeclaration> sourceDocumentStyle = static_cast<HTMLElement*>(sourceDocumentStyleSpan)->getInlineStyleDecl()->copy();
+
+ RefPtr<EditingStyle> sourceDocumentStyle = EditingStyle::create(static_cast<HTMLElement*>(sourceDocumentStyleSpan)->getInlineStyleDecl());
ContainerNode* context = sourceDocumentStyleSpan->parentNode();
-
+
// If Mail wraps the fragment with a Paste as Quotation blockquote, or if you're pasting into a quoted region,
// styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>.
Node* blockquoteNode = isMailPasteAsQuotationNode(context) ? context : nearestMailBlockquote(context);
if (blockquoteNode) {
- RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = ApplyStyleCommand::editingStyleAtPosition(Position(blockquoteNode, 0));
- RefPtr<CSSMutableStyleDeclaration> parentStyle = ApplyStyleCommand::editingStyleAtPosition(Position(blockquoteNode->parentNode(), 0));
- parentStyle->diff(blockquoteStyle.get());
-
- CSSMutableStyleDeclaration::const_iterator end = blockquoteStyle->end();
- for (CSSMutableStyleDeclaration::const_iterator it = blockquoteStyle->begin(); it != end; ++it) {
- const CSSProperty& property = *it;
- sourceDocumentStyle->removeProperty(property.id());
- }
-
+ sourceDocumentStyle->removeStyleConflictingWithStyleOfNode(blockquoteNode);
context = blockquoteNode->parentNode();
}
// This operation requires that only editing styles to be removed from sourceDocumentStyle.
- prepareEditingStyleToApplyAt(sourceDocumentStyle.get(), Position(context, 0));
+ sourceDocumentStyle->prepareToApplyAt(firstPositionInNode(context));
// Remove block properties in the span's style. This prevents properties that probably have no effect
// currently from affecting blocks later if the style is cloned for a new block element during a future
@@ -656,49 +636,44 @@ void ReplaceSelectionCommand::handleStyleSpans()
// FIXME: They *can* have an effect currently if blocks beneath the style span aren't individually marked
// with block styles by the editing engine used to style them. WebKit doesn't do this, but others might.
sourceDocumentStyle->removeBlockProperties();
-
+
// The styles on sourceDocumentStyleSpan are all redundant, and there is no copiedRangeStyleSpan
// to consider. We're finished.
- if (sourceDocumentStyle->length() == 0 && !copiedRangeStyleSpan) {
+ if (sourceDocumentStyle->isEmpty() && !copiedRangeStyleSpan) {
removeNodePreservingChildren(sourceDocumentStyleSpan);
return;
}
-
+
// There are non-redundant styles on sourceDocumentStyleSpan, but there is no
// copiedRangeStyleSpan. Remove the span, because it could be surrounding block elements,
// and apply the styles to its children.
- if (sourceDocumentStyle->length() > 0 && !copiedRangeStyleSpan) {
- copyStyleToChildren(sourceDocumentStyleSpan, sourceDocumentStyle.get());
+ if (!sourceDocumentStyle->isEmpty() && !copiedRangeStyleSpan) {
+ copyStyleToChildren(sourceDocumentStyleSpan, sourceDocumentStyle->style());
removeNodePreservingChildren(sourceDocumentStyleSpan);
return;
}
- RefPtr<CSSMutableStyleDeclaration> copiedRangeStyle = static_cast<HTMLElement*>(copiedRangeStyleSpan)->getInlineStyleDecl()->copy();
-
+ RefPtr<EditingStyle> copiedRangeStyle = EditingStyle::create(static_cast<HTMLElement*>(copiedRangeStyleSpan)->getInlineStyleDecl());
+
// We're going to put sourceDocumentStyleSpan's non-redundant styles onto copiedRangeStyleSpan,
// as long as they aren't overridden by ones on copiedRangeStyleSpan.
- sourceDocumentStyle->merge(copiedRangeStyle.get(), true);
- copiedRangeStyle = sourceDocumentStyle;
-
+ copiedRangeStyle->style()->merge(sourceDocumentStyle->style(), false);
+
removeNodePreservingChildren(sourceDocumentStyleSpan);
-
+
// Remove redundant styles.
context = copiedRangeStyleSpan->parentNode();
- prepareEditingStyleToApplyAt(copiedRangeStyle.get(), Position(context, 0));
-
- // See the comments above about removing block properties.
+ copiedRangeStyle->prepareToApplyAt(firstPositionInNode(context));
copiedRangeStyle->removeBlockProperties();
-
- // All the styles on copiedRangeStyleSpan are redundant, remove it.
- if (copiedRangeStyle->length() == 0) {
+ if (copiedRangeStyle->isEmpty()) {
removeNodePreservingChildren(copiedRangeStyleSpan);
return;
}
-
+
// Clear the redundant styles from the span's style attribute.
// FIXME: If font-family:-webkit-monospace is non-redundant, then the font-size should stay, even if it
// appears redundant.
- setNodeAttribute(static_cast<Element*>(copiedRangeStyleSpan), styleAttr, copiedRangeStyle->cssText());
+ setNodeAttribute(static_cast<Element*>(copiedRangeStyleSpan), styleAttr, copiedRangeStyle->style()->cssText());
}
// Take the style attribute of a span and apply it to it's children instead. This allows us to
@@ -810,9 +785,14 @@ void ReplaceSelectionCommand::doApply()
if (performTrivialReplace(fragment))
return;
- if (m_matchStyle)
- m_insertionStyle = ApplyStyleCommand::editingStyleAtPosition(selection.start(), IncludeTypingStyle);
+ // We can skip matching the style if the selection is plain text.
+ if ((selection.start().node()->renderer() && selection.start().node()->renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY) &&
+ (selection.end().node()->renderer() && selection.end().node()->renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY))
+ m_matchStyle = false;
+ if (m_matchStyle)
+ m_insertionStyle = editingStyleIncludingTypingStyle(selection.start());
+
VisiblePosition visibleStart = selection.visibleStart();
VisiblePosition visibleEnd = selection.visibleEnd();
@@ -1163,7 +1143,7 @@ void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositi
if (m_matchStyle) {
ASSERT(m_insertionStyle);
- applyStyle(m_insertionStyle.get(), start, end);
+ applyStyle(m_insertionStyle->style(), start, end);
}
if (lastPositionToSelect.isNotNull())
diff --git a/WebCore/editing/ReplaceSelectionCommand.h b/WebCore/editing/ReplaceSelectionCommand.h
index e995e79..9fc4a49 100644
--- a/WebCore/editing/ReplaceSelectionCommand.h
+++ b/WebCore/editing/ReplaceSelectionCommand.h
@@ -31,6 +31,7 @@
namespace WebCore {
class DocumentFragment;
+class EditingStyle;
class ReplacementFragment;
class ReplaceSelectionCommand : public CompositeEditCommand {
@@ -82,7 +83,7 @@ private:
RefPtr<Node> m_firstNodeInserted;
RefPtr<Node> m_lastLeafInserted;
- RefPtr<CSSMutableStyleDeclaration> m_insertionStyle;
+ RefPtr<EditingStyle> m_insertionStyle;
bool m_selectReplacement;
bool m_smartReplace;
bool m_matchStyle;
diff --git a/WebCore/editing/SelectionController.cpp b/WebCore/editing/SelectionController.cpp
index 6f25c86..bb42662 100644
--- a/WebCore/editing/SelectionController.cpp
+++ b/WebCore/editing/SelectionController.cpp
@@ -642,6 +642,8 @@ bool SelectionController::modify(EAlteration alter, EDirection direction, TextGr
willBeModified(alter, direction);
+ bool wasRange = m_selection.isRange();
+ Position originalStartPosition = m_selection.start();
VisiblePosition position;
switch (direction) {
case DirectionRight:
@@ -673,6 +675,10 @@ bool SelectionController::modify(EAlteration alter, EDirection direction, TextGr
if (position.isNull())
return false;
+ if (isSpatialNavigationEnabled(m_frame))
+ if (!wasRange && alter == AlterationMove && position == originalStartPosition)
+ return false;
+
// Some of the above operations set an xPosForVerticalArrowNavigation.
// Setting a selection will clear it, so save it to possibly restore later.
// Note: the START position type is arbitrary because it is unused, it would be
diff --git a/WebCore/editing/SelectionController.h b/WebCore/editing/SelectionController.h
index e5fe71b..2edad0a 100644
--- a/WebCore/editing/SelectionController.h
+++ b/WebCore/editing/SelectionController.h
@@ -26,7 +26,7 @@
#ifndef SelectionController_h
#define SelectionController_h
-#include "CSSMutableStyleDeclaration.h"
+#include "EditingStyle.h"
#include "IntRect.h"
#include "Range.h"
#include "ScrollBehavior.h"
@@ -37,6 +37,7 @@
namespace WebCore {
class Frame;
+class CSSMutableStyleDeclaration;
class GraphicsContext;
class HTMLFormElement;
class RenderObject;
@@ -160,7 +161,7 @@ public:
void paintDragCaret(GraphicsContext*, int tx, int ty, const IntRect& clipRect) const;
CSSMutableStyleDeclaration* typingStyle() const;
- void setTypingStyle(PassRefPtr<CSSMutableStyleDeclaration>);
+ void setTypingStyle(PassRefPtr<EditingStyle>);
void clearTypingStyle();
FloatRect bounds(bool clipToVisibleContent = true) const;
@@ -214,7 +215,7 @@ private:
VisibleSelection m_selection;
TextGranularity m_granularity;
- RefPtr<CSSMutableStyleDeclaration> m_typingStyle;
+ RefPtr<EditingStyle> m_typingStyle;
Timer<SelectionController> m_caretBlinkTimer;
@@ -234,7 +235,7 @@ private:
inline CSSMutableStyleDeclaration* SelectionController::typingStyle() const
{
- return m_typingStyle.get();
+ return m_typingStyle ? m_typingStyle->style() : 0;
}
inline void SelectionController::clearTypingStyle()
@@ -242,7 +243,7 @@ inline void SelectionController::clearTypingStyle()
m_typingStyle.clear();
}
-inline void SelectionController::setTypingStyle(PassRefPtr<CSSMutableStyleDeclaration> style)
+inline void SelectionController::setTypingStyle(PassRefPtr<EditingStyle> style)
{
m_typingStyle = style;
}
diff --git a/WebCore/editing/TypingCommand.cpp b/WebCore/editing/TypingCommand.cpp
index d78708b..9165a3e 100644
--- a/WebCore/editing/TypingCommand.cpp
+++ b/WebCore/editing/TypingCommand.cpp
@@ -311,9 +311,9 @@ void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
if (p1 != p2)
document()->frame()->editor()->markMisspellingsAfterTypingToPosition(p1);
-#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
+#if SUPPORT_AUTOCORRECTION_PANEL
else if (commandType == TypingCommand::InsertText)
- document()->frame()->editor()->startCorrectionPanelTimer();
+ document()->frame()->editor()->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection);
#else
UNUSED_PARAM(commandType);
#endif
diff --git a/WebCore/editing/markup.cpp b/WebCore/editing/markup.cpp
index 75d567e..417de71 100644
--- a/WebCore/editing/markup.cpp
+++ b/WebCore/editing/markup.cpp
@@ -26,10 +26,8 @@
#include "config.h"
#include "markup.h"
-#include "ApplyStyleCommand.h"
#include "CDATASection.h"
#include "CharacterNames.h"
-#include "Comment.h"
#include "CSSComputedStyleDeclaration.h"
#include "CSSMutableStyleDeclaration.h"
#include "CSSPrimitiveValue.h"
@@ -49,11 +47,8 @@
#include "HTMLBodyElement.h"
#include "HTMLElement.h"
#include "HTMLNames.h"
-#include "InlineTextBox.h"
#include "KURL.h"
-#include "Logging.h"
#include "MarkupAccumulator.h"
-#include "ProcessingInstruction.h"
#include "Range.h"
#include "TextIterator.h"
#include "VisibleSelection.h"
@@ -525,23 +520,6 @@ static Node* highestAncestorToWrapMarkup(const Range* range, Node* fullySelected
return specialCommonAncestor;
}
-static void removeEnclosingMailBlockquoteStyle(CSSMutableStyleDeclaration* style, Node* node)
-{
- Node* blockquote = nearestMailBlockquote(node);
- if (!blockquote || !blockquote->parentNode())
- return;
-
- removeStylesAddedByNode(style, blockquote);
-}
-
-static void removeDefaultStyles(CSSMutableStyleDeclaration* style, Document* document)
-{
- if (!document || !document->documentElement())
- return;
-
- prepareEditingStyleToApplyAt(style, Position(document->documentElement(), 0));
-}
-
// 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 shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs)
@@ -650,34 +628,34 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc
// Add a wrapper span with the styles that all of the nodes in the markup inherit.
ContainerNode* parentOfLastClosed = lastClosed ? lastClosed->parentNode() : 0;
if (parentOfLastClosed && parentOfLastClosed->renderer()) {
- RefPtr<CSSMutableStyleDeclaration> style = ApplyStyleCommand::editingStyleAtPosition(Position(parentOfLastClosed, 0));
+ RefPtr<EditingStyle> style = EditingStyle::create(parentOfLastClosed);
// Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, to help
// us differentiate those styles from ones that the user has applied. This helps us
// get the color of content pasted into blockquotes right.
- removeEnclosingMailBlockquoteStyle(style.get(), parentOfLastClosed);
-
+ style->removeStyleAddedByNode(nearestMailBlockquote(parentOfLastClosed));
+
// Document default styles will be added on another wrapper span.
- removeDefaultStyles(style.get(), document);
-
+ if (document && document->documentElement())
+ style->prepareToApplyAt(firstPositionInNode(document->documentElement()));
+
// Since we are converting blocks to inlines, remove any inherited block properties that are in the style.
// This cuts out meaningless properties and prevents properties from magically affecting blocks later
// if the style is cloned for a new block element during a future editing operation.
if (convertBlocksToInlines)
style->removeBlockProperties();
- if (style->length() > 0)
- accumulator.wrapWithStyleNode(style.get(), document);
+ if (!style->isEmpty())
+ accumulator.wrapWithStyleNode(style->style(), document);
}
if (lastClosed && lastClosed != document->documentElement()) {
// Add a style span with the document's default styles. We add these in a separate
// span so that at paste time we can differentiate between document defaults and user
// applied styles.
- RefPtr<CSSMutableStyleDeclaration> defaultStyle = ApplyStyleCommand::editingStyleAtPosition(Position(document->documentElement(), 0));
-
- if (defaultStyle->length() > 0)
- accumulator.wrapWithStyleNode(defaultStyle.get(), document);
+ RefPtr<EditingStyle> defaultStyle = EditingStyle::create(document->documentElement());
+ if (!defaultStyle->isEmpty())
+ accumulator.wrapWithStyleNode(defaultStyle->style(), document);
}
// FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally.