diff options
author | Ben Murdoch <benm@google.com> | 2011-05-24 11:24:40 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-06-02 09:53:15 +0100 |
commit | 81bc750723a18f21cd17d1b173cd2a4dda9cea6e (patch) | |
tree | 7a9e5ed86ff429fd347a25153107221543909b19 /Source/WebCore/editing | |
parent | 94088a6d336c1dd80a1e734af51e96abcbb689a7 (diff) | |
download | external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.zip external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.gz external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.bz2 |
Merge WebKit at r80534: Intial merge by Git
Change-Id: Ia7a83357124c9e1cdb1debf55d9661ec0bd09a61
Diffstat (limited to 'Source/WebCore/editing')
53 files changed, 1972 insertions, 1209 deletions
diff --git a/Source/WebCore/editing/ApplyBlockElementCommand.cpp b/Source/WebCore/editing/ApplyBlockElementCommand.cpp index e700875..7e20acc 100644 --- a/Source/WebCore/editing/ApplyBlockElementCommand.cpp +++ b/Source/WebCore/editing/ApplyBlockElementCommand.cpp @@ -134,11 +134,11 @@ void ApplyBlockElementCommand::formatSelection(const VisiblePosition& startOfSel // indentIntoBlockquote could move more than one paragraph if the paragraph // is in a list item or a table. As a result, endAfterSelection could refer to a position // no longer in the document. - if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().node()->inDocument()) + if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().anchorNode()->inDocument()) break; - // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextParagraph.deepEquivalent().node() + // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextParagraph.deepEquivalent().deprecatedNode() // If somehow we did, return to prevent crashes. - if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().node()->inDocument()) { + if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().anchorNode()->inDocument()) { ASSERT_NOT_REACHED(); return; } @@ -182,8 +182,8 @@ void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const RenderStyle* startStyle = renderStyleOfEnclosingTextNode(start); bool isStartAndEndOnSameNode = false; if (startStyle) { - isStartAndEndOnSameNode = renderStyleOfEnclosingTextNode(end) && start.node() == end.node(); - bool isStartAndEndOfLastParagraphOnSameNode = renderStyleOfEnclosingTextNode(m_endOfLastParagraph) && start.node() == m_endOfLastParagraph.node(); + isStartAndEndOnSameNode = renderStyleOfEnclosingTextNode(end) && start.deprecatedNode() == end.deprecatedNode(); + bool isStartAndEndOfLastParagraphOnSameNode = renderStyleOfEnclosingTextNode(m_endOfLastParagraph) && start.deprecatedNode() == m_endOfLastParagraph.deprecatedNode(); // Avoid obtanining the start of next paragraph for start if (startStyle->preserveNewline() && isNewLineAtPosition(start) && !isNewLineAtPosition(start.previous()) && start.offsetInContainerNode() > 0) @@ -192,15 +192,15 @@ void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const // If start is in the middle of a text node, split. if (!startStyle->collapseWhiteSpace() && start.offsetInContainerNode() > 0) { int startOffset = start.offsetInContainerNode(); - splitTextNode(static_cast<Text*>(start.node()), startOffset); - start = positionBeforeNode(start.node()); + splitTextNode(static_cast<Text*>(start.deprecatedNode()), startOffset); + start = positionBeforeNode(start.deprecatedNode()); if (isStartAndEndOnSameNode) { ASSERT(end.offsetInContainerNode() >= startOffset); - end = Position(end.node(), end.offsetInContainerNode() - startOffset, Position::PositionIsOffsetInAnchor); + end = Position(end.deprecatedNode(), end.offsetInContainerNode() - startOffset, Position::PositionIsOffsetInAnchor); } if (isStartAndEndOfLastParagraphOnSameNode) { ASSERT(m_endOfLastParagraph.offsetInContainerNode() >= startOffset); - m_endOfLastParagraph = Position(m_endOfLastParagraph.node(), m_endOfLastParagraph.offsetInContainerNode() - startOffset, + m_endOfLastParagraph = Position(m_endOfLastParagraph.deprecatedNode(), m_endOfLastParagraph.offsetInContainerNode() - startOffset, Position::PositionIsOffsetInAnchor); } } @@ -208,13 +208,13 @@ void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const RenderStyle* endStyle = renderStyleOfEnclosingTextNode(end); if (endStyle) { - bool isEndAndEndOfLastParagraphOnSameNode = renderStyleOfEnclosingTextNode(m_endOfLastParagraph) && end.node() == m_endOfLastParagraph.node(); + bool isEndAndEndOfLastParagraphOnSameNode = renderStyleOfEnclosingTextNode(m_endOfLastParagraph) && end.deprecatedNode() == m_endOfLastParagraph.deprecatedNode(); // Include \n at the end of line if we're at an empty paragraph if (endStyle->preserveNewline() && start == end && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) { int endOffset = end.offsetInContainerNode(); if (!isNewLineAtPosition(end.previous()) && isNewLineAtPosition(end)) - end = Position(end.node(), endOffset + 1, Position::PositionIsOffsetInAnchor); + end = Position(end.deprecatedNode(), endOffset + 1, Position::PositionIsOffsetInAnchor); if (isEndAndEndOfLastParagraphOnSameNode && end.offsetInContainerNode() >= m_endOfLastParagraph.offsetInContainerNode()) m_endOfLastParagraph = end; } @@ -222,17 +222,17 @@ void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const // If end is in the middle of a text node, split. if (!endStyle->collapseWhiteSpace() && end.offsetInContainerNode() && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) { - splitTextNode(static_cast<Text*>(end.node()), end.offsetInContainerNode()); + splitTextNode(static_cast<Text*>(end.deprecatedNode()), end.offsetInContainerNode()); if (isStartAndEndOnSameNode) - start = positionBeforeNode(end.node()->previousSibling()); + start = positionBeforeNode(end.deprecatedNode()->previousSibling()); if (isEndAndEndOfLastParagraphOnSameNode) { if (m_endOfLastParagraph.offsetInContainerNode() == end.offsetInContainerNode()) - m_endOfLastParagraph = lastPositionInNode(end.node()->previousSibling()); + m_endOfLastParagraph = lastPositionInNode(end.deprecatedNode()->previousSibling()); else - m_endOfLastParagraph = Position(end.node(), m_endOfLastParagraph.offsetInContainerNode() - end.offsetInContainerNode(), + m_endOfLastParagraph = Position(end.deprecatedNode(), m_endOfLastParagraph.offsetInContainerNode() - end.offsetInContainerNode(), Position::PositionIsOffsetInAnchor); } - end = lastPositionInNode(end.node()->previousSibling()); + end = lastPositionInNode(end.deprecatedNode()->previousSibling()); } } } diff --git a/Source/WebCore/editing/ApplyStyleCommand.cpp b/Source/WebCore/editing/ApplyStyleCommand.cpp index ccade74..f9ed18e 100644 --- a/Source/WebCore/editing/ApplyStyleCommand.cpp +++ b/Source/WebCore/editing/ApplyStyleCommand.cpp @@ -141,8 +141,8 @@ StyleChange::StyleChange(CSSStyleDeclaration* style, const Position& position) void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position& position) { - Document* document = position.node() ? position.node()->document() : 0; - if (!document || !document->frame()) + Document* document = position.anchorNode() ? position.anchorNode()->document() : 0; + if (!style || !document || !document->frame()) return; RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle(); @@ -153,7 +153,7 @@ void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position& po extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize()); // Changing the whitespace style in a tab span would collapse the tab into a space. - if (isTabSpanTextNode(position.node()) || isTabSpanNode((position.node()))) + if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode()))) mutableStyle->removeProperty(CSSPropertyWhiteSpace); // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle. @@ -274,7 +274,7 @@ void StyleChange::extractTextStyles(Document* document, CSSMutableStyleDeclarati } if (style->getPropertyCSSValue(CSSPropertyColor)) { - m_applyFontColor = Color(getRGBAFontColor(style)).name(); + m_applyFontColor = Color(getRGBAFontColor(style)).serialized(); style->removeProperty(CSSPropertyColor); } @@ -581,7 +581,7 @@ void ApplyStyleCommand::applyBlockStyle(EditingStyle *style) // Save and restore the selection endpoints using their indices in the document, since // addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints. // Calculate start and end indices from the start of the tree that they're in. - Node* scope = highestAncestor(visibleStart.deepEquivalent().node()); + Node* scope = highestAncestor(visibleStart.deepEquivalent().deprecatedNode()); RefPtr<Range> startRange = Range::create(document(), firstPositionInNode(scope), visibleStart.deepEquivalent().parentAnchoredEquivalent()); RefPtr<Range> endRange = Range::create(document(), firstPositionInNode(scope), visibleEnd.deepEquivalent().parentAnchoredEquivalent()); int startIndex = TextIterator::rangeLength(startRange.get(), true); @@ -593,7 +593,7 @@ void ApplyStyleCommand::applyBlockStyle(EditingStyle *style) while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) { StyleChange styleChange(style->style(), paragraphStart.deepEquivalent()); if (styleChange.cssStyle().length() || m_removeOnly) { - RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().node()); + RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().deprecatedNode()); if (!m_removeOnly) { RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent()); if (newBlock) @@ -601,7 +601,7 @@ void ApplyStyleCommand::applyBlockStyle(EditingStyle *style) } ASSERT(block->isHTMLElement()); if (block->isHTMLElement()) { - removeCSSStyle(style->style(), toHTMLElement(block.get())); + removeCSSStyle(style, toHTMLElement(block.get())); if (!m_removeOnly) addBlockStyle(styleChange, toHTMLElement(block.get())); } @@ -636,13 +636,13 @@ void ApplyStyleCommand::applyRelativeFontStyleChange(EditingStyle* style) } // Join up any adjacent text nodes. - if (start.node()->isTextNode()) { - joinChildTextNodes(start.node()->parentNode(), start, end); + if (start.deprecatedNode()->isTextNode()) { + joinChildTextNodes(start.deprecatedNode()->parentNode(), start, end); start = startPosition(); end = endPosition(); } - if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) { - joinChildTextNodes(end.node()->parentNode(), start, end); + if (end.deprecatedNode()->isTextNode() && start.deprecatedNode()->parentNode() != end.deprecatedNode()->parentNode()) { + joinChildTextNodes(end.deprecatedNode()->parentNode(), start, end); start = startPosition(); end = endPosition(); } @@ -664,13 +664,13 @@ void ApplyStyleCommand::applyRelativeFontStyleChange(EditingStyle* style) // If the end node is before the start node (can only happen if the end node is // an ancestor of the start node), we gather nodes up to the next sibling of the end node Node *beyondEnd; - if (start.node()->isDescendantOf(end.node())) - beyondEnd = end.node()->traverseNextSibling(); + if (start.deprecatedNode()->isDescendantOf(end.deprecatedNode())) + beyondEnd = end.deprecatedNode()->traverseNextSibling(); else - beyondEnd = end.node()->traverseNextNode(); + beyondEnd = end.deprecatedNode()->traverseNextNode(); start = start.upstream(); // Move upstream to ensure we do not add redundant spans. - Node *startNode = start.node(); + Node* startNode = start.deprecatedNode(); if (startNode->isTextNode() && start.deprecatedEditingOffset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters. startNode = startNode->traverseNextNode(); @@ -856,7 +856,9 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style) { Node* startDummySpanAncestor = 0; Node* endDummySpanAncestor = 0; - + + style->collapseTextDecorationProperties(); + // update document layout once before removing styles // so that we avoid the expense of updating before each and every call // to check a computed style @@ -874,25 +876,25 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style) // split the start node and containing element if the selection starts inside of it bool splitStart = isValidCaretPositionInTextNode(start); if (splitStart) { - if (shouldSplitTextElement(start.node()->parentElement(), style->style())) + if (shouldSplitTextElement(start.deprecatedNode()->parentElement(), style)) splitTextElementAtStart(start, end); else splitTextAtStart(start, end); start = startPosition(); end = endPosition(); - startDummySpanAncestor = dummySpanAncestorForNode(start.node()); + startDummySpanAncestor = dummySpanAncestorForNode(start.deprecatedNode()); } // split the end node and containing element if the selection ends inside of it bool splitEnd = isValidCaretPositionInTextNode(end); if (splitEnd) { - if (shouldSplitTextElement(end.node()->parentElement(), style->style())) + if (shouldSplitTextElement(end.deprecatedNode()->parentElement(), style)) splitTextElementAtEnd(start, end); else splitTextAtEnd(start, end); start = startPosition(); end = endPosition(); - endDummySpanAncestor = dummySpanAncestorForNode(end.node()); + endDummySpanAncestor = dummySpanAncestorForNode(end.deprecatedNode()); } // Remove style from the selection. @@ -901,18 +903,16 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style) // and prevent us from adding redundant ones, as described in: // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags Position removeStart = start.upstream(); - int unicodeBidi = getIdentifierValue(style->style(), CSSPropertyUnicodeBidi); + WritingDirection textDirection = NaturalWritingDirection; + bool hasTextDirection = style->textDirection(textDirection); RefPtr<EditingStyle> styleWithoutEmbedding; RefPtr<EditingStyle> embeddingStyle; - if (unicodeBidi) { - WritingDirection textDirection = NaturalWritingDirection; - style->textDirection(textDirection); - + if (hasTextDirection) { // Leave alone an ancestor that provides the desired single level embedding, if there is one. - HTMLElement* startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.node(), true, textDirection); - HTMLElement* endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.node(), false, textDirection); - removeEmbeddingUpToEnclosingBlock(start.node(), startUnsplitAncestor); - removeEmbeddingUpToEnclosingBlock(end.node(), endUnsplitAncestor); + HTMLElement* startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.deprecatedNode(), true, textDirection); + HTMLElement* endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.deprecatedNode(), false, textDirection); + removeEmbeddingUpToEnclosingBlock(start.deprecatedNode(), startUnsplitAncestor); + removeEmbeddingUpToEnclosingBlock(end.deprecatedNode(), endUnsplitAncestor); // Avoid removing the dir attribute and the unicode-bidi and direction properties from the unsplit ancestors. Position embeddingRemoveStart = removeStart; @@ -928,21 +928,19 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style) embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirection(); if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0) - removeInlineStyle(embeddingStyle->style(), embeddingRemoveStart, embeddingRemoveEnd); + removeInlineStyle(embeddingStyle.get(), embeddingRemoveStart, embeddingRemoveEnd); } } - removeInlineStyle(styleWithoutEmbedding ? styleWithoutEmbedding->style() : style->style(), removeStart, end); + 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)) { - start = startPosition(); - end = endPosition(); - } + if (splitStart && mergeStartWithPreviousIfIdentical(start, end)) { + start = startPosition(); + end = endPosition(); } if (splitEnd) { @@ -956,11 +954,11 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style) // to check a computed style updateLayout(); - RefPtr<CSSMutableStyleDeclaration> styleToApply = style->isEmpty() ? CSSMutableStyleDeclaration::create() : style->style(); - if (unicodeBidi) { + RefPtr<EditingStyle> styleToApply = style; + if (hasTextDirection) { // Avoid applying the unicode-bidi and direction properties beneath ancestors that already have them. - Node* embeddingStartNode = highestEmbeddingAncestor(start.node(), enclosingBlock(start.node())); - Node* embeddingEndNode = highestEmbeddingAncestor(end.node(), enclosingBlock(end.node())); + Node* embeddingStartNode = highestEmbeddingAncestor(start.deprecatedNode(), enclosingBlock(start.deprecatedNode())); + Node* embeddingEndNode = highestEmbeddingAncestor(end.deprecatedNode(), enclosingBlock(end.deprecatedNode())); if (embeddingStartNode || embeddingEndNode) { Position embeddingApplyStart = embeddingStartNode ? positionInParentAfterNode(embeddingStartNode) : start; @@ -971,9 +969,9 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style) styleWithoutEmbedding = style->copy(); embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirection(); } - fixRangeAndApplyInlineStyle(embeddingStyle->style(), embeddingApplyStart, embeddingApplyEnd); + fixRangeAndApplyInlineStyle(embeddingStyle.get(), embeddingApplyStart, embeddingApplyEnd); - styleToApply = styleWithoutEmbedding->style(); + styleToApply = styleWithoutEmbedding; } } @@ -985,24 +983,24 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style) cleanupUnstyledAppleStyleSpans(endDummySpanAncestor); } -void ApplyStyleCommand::fixRangeAndApplyInlineStyle(CSSMutableStyleDeclaration* style, const Position& start, const Position& end) +void ApplyStyleCommand::fixRangeAndApplyInlineStyle(EditingStyle* style, const Position& start, const Position& end) { - Node* startNode = start.node(); + Node* startNode = start.deprecatedNode(); - if (start.deprecatedEditingOffset() >= caretMaxOffset(start.node())) { + if (start.deprecatedEditingOffset() >= caretMaxOffset(start.deprecatedNode())) { startNode = startNode->traverseNextNode(); if (!startNode || comparePositions(end, firstPositionInOrBeforeNode(startNode)) < 0) return; } - Node* pastEndNode = end.node(); - if (end.deprecatedEditingOffset() >= caretMaxOffset(end.node())) - pastEndNode = end.node()->traverseNextSibling(); + Node* pastEndNode = end.deprecatedNode(); + if (end.deprecatedEditingOffset() >= caretMaxOffset(end.deprecatedNode())) + pastEndNode = end.deprecatedNode()->traverseNextSibling(); // FIXME: Callers should perform this operation on a Range that includes the br // if they want style applied to the empty line. - if (start == end && start.node()->hasTagName(brTag)) - pastEndNode = start.node()->traverseNextNode(); + if (start == end && start.deprecatedNode()->hasTagName(brTag)) + pastEndNode = start.deprecatedNode()->traverseNextNode(); // Start from the highest fully selected ancestor so that we can modify the fully selected node. // e.g. When applying font-size: large on <font color="blue">hello</font>, we need to include the font element in our run @@ -1031,7 +1029,7 @@ static bool containsNonEditableRegion(Node* node) return false; } -void ApplyStyleCommand::applyInlineStyleToNodeRange(CSSMutableStyleDeclaration* style, Node* node, Node* pastEndNode) +void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, Node* node, Node* pastEndNode) { if (m_removeOnly) return; @@ -1051,7 +1049,7 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(CSSMutableStyleDeclaration* // Add to this element's inline style and skip over its contents. HTMLElement* element = toHTMLElement(node); RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy(); - inlineStyle->merge(style); + inlineStyle->merge(style->style()); setNodeAttribute(element, styleAttr, inlineStyle->cssText()); next = node->traverseNextSibling(); continue; @@ -1082,7 +1080,7 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(CSSMutableStyleDeclaration* if (!removeStyleFromRunBeforeApplyingStyle(style, runStart, runEnd)) continue; - addInlineStyleIfNeeded(style, runStart.get(), runEnd.get(), AddStyledElement); + addInlineStyleIfNeeded(style->style(), runStart.get(), runEnd.get(), AddStyledElement); } } @@ -1092,7 +1090,7 @@ bool ApplyStyleCommand::isStyledInlineElementToRemove(Element* element) const || (m_isInlineElementToRemoveFunction && m_isInlineElementToRemoveFunction(element)); } -bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDeclaration* style, RefPtr<Node>& runStart, RefPtr<Node>& runEnd) +bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(EditingStyle* style, RefPtr<Node>& runStart, RefPtr<Node>& runEnd) { ASSERT(runStart && runEnd && runStart->parentNode() == runEnd->parentNode()); RefPtr<Node> pastEndNode = runEnd->traverseNextSibling(); @@ -1101,7 +1099,7 @@ bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDec if (node->childNodeCount()) continue; // We don't consider m_isInlineElementToRemoveFunction here because we never apply style when m_isInlineElementToRemoveFunction is specified - if (getPropertiesNotIn(style, computedStyle(node).get())->length() + if ((!style->isEmpty() && getPropertiesNotIn(style->style(), computedStyle(node).get())->length()) || (m_styledInlineElement && !enclosingNodeWithTag(positionBeforeNode(node), m_styledInlineElement->tagQName()))) { needToApplyStyle = true; break; @@ -1132,7 +1130,7 @@ bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(CSSMutableStyleDec return true; } -bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration* style, PassRefPtr<HTMLElement> element, InlineStyleRemovalMode mode, CSSMutableStyleDeclaration* extractedStyle) +bool ApplyStyleCommand::removeInlineStyleFromElement(EditingStyle* style, PassRefPtr<HTMLElement> element, InlineStyleRemovalMode mode, EditingStyle* extractedStyle) { ASSERT(element); @@ -1143,13 +1141,17 @@ bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration* if (mode == RemoveNone) return true; ASSERT(extractedStyle); - if (element->inlineStyleDecl()) - extractedStyle->merge(element->inlineStyleDecl()); + if (element->inlineStyleDecl()) { + if (extractedStyle->style()) + extractedStyle->style()->merge(element->inlineStyleDecl()); + else + extractedStyle->setStyle(element->inlineStyleDecl()->copy()); + } removeNodePreservingChildren(element); return true; } - if (!style) + if (!style->style()) return false; bool removed = false; @@ -1167,109 +1169,6 @@ bool ApplyStyleCommand::removeInlineStyleFromElement(CSSMutableStyleDeclaration* return removed; } -enum EPushDownType { ShouldBePushedDown, ShouldNotBePushedDown }; -struct HTMLEquivalent { - int propertyID; - bool isValueList; - int primitiveId; - const QualifiedName* element; - const QualifiedName* attribute; - PassRefPtr<CSSValue> (*attributeToCSSValue)(int propertyID, const String&); - EPushDownType pushDownType; -}; - -static PassRefPtr<CSSValue> stringToCSSValue(int propertyID, const String& value) -{ - RefPtr<CSSMutableStyleDeclaration> dummyStyle; - dummyStyle = CSSMutableStyleDeclaration::create(); - dummyStyle->setProperty(propertyID, value); - return dummyStyle->getPropertyCSSValue(propertyID); -} - -static PassRefPtr<CSSValue> fontSizeToCSSValue(int propertyID, const String& value) -{ - UNUSED_PARAM(propertyID); - ASSERT(propertyID == CSSPropertyFontSize); - int size; - if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size)) - return 0; - return CSSPrimitiveValue::createIdentifier(size); -} - -static const HTMLEquivalent HTMLEquivalents[] = { - { CSSPropertyFontWeight, false, CSSValueBold, &bTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyFontWeight, false, CSSValueBold, &strongTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyVerticalAlign, false, CSSValueSub, &subTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyVerticalAlign, false, CSSValueSuper, &supTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyFontStyle, false, CSSValueItalic, &iTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyFontStyle, false, CSSValueItalic, &emTag, 0, 0, ShouldBePushedDown }, - - // text-decorations should be CSSValueList - { CSSPropertyTextDecoration, true, CSSValueUnderline, &uTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyTextDecoration, true, CSSValueLineThrough, &sTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyTextDecoration, true, CSSValueLineThrough, &strikeTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyWebkitTextDecorationsInEffect, true, CSSValueUnderline, &uTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyWebkitTextDecorationsInEffect, true, CSSValueLineThrough, &sTag, 0, 0, ShouldBePushedDown }, - { CSSPropertyWebkitTextDecorationsInEffect, true, CSSValueLineThrough, &strikeTag, 0, 0, ShouldBePushedDown }, - - // FIXME: font attributes should only be removed if values were different - { CSSPropertyColor, false, CSSValueInvalid, &fontTag, &colorAttr, stringToCSSValue, ShouldBePushedDown }, - { CSSPropertyFontFamily, false, CSSValueInvalid, &fontTag, &faceAttr, stringToCSSValue, ShouldBePushedDown }, - { CSSPropertyFontSize, false, CSSValueInvalid, &fontTag, &sizeAttr, fontSizeToCSSValue, ShouldBePushedDown }, - - // unicode-bidi and direction are pushed down separately so don't push down with other styles. - { CSSPropertyDirection, false, CSSValueInvalid, 0, &dirAttr, stringToCSSValue, ShouldNotBePushedDown }, - { CSSPropertyUnicodeBidi, false, CSSValueInvalid, 0, &dirAttr, stringToCSSValue, ShouldNotBePushedDown }, -}; - -bool ApplyStyleCommand::removeImplicitlyStyledElement(CSSMutableStyleDeclaration* style, HTMLElement* element, InlineStyleRemovalMode mode, CSSMutableStyleDeclaration* extractedStyle) -{ - // Current implementation does not support stylePushedDown when mode == RemoveNone because of early exit. - ASSERT(style); - ASSERT(!extractedStyle || mode != RemoveNone); - bool removed = false; - for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) { - const HTMLEquivalent& equivalent = HTMLEquivalents[i]; - ASSERT(equivalent.element || equivalent.attribute); - if ((extractedStyle && equivalent.pushDownType == ShouldNotBePushedDown) - || (equivalent.element && !element->hasTagName(*equivalent.element)) - || (equivalent.attribute && !element->hasAttribute(*equivalent.attribute))) - continue; - - RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(equivalent.propertyID); - if (!styleValue) - continue; - RefPtr<CSSValue> mapValue; - if (equivalent.attribute) - mapValue = equivalent.attributeToCSSValue(equivalent.propertyID, element->getAttribute(*equivalent.attribute)); - else - mapValue = CSSPrimitiveValue::createIdentifier(equivalent.primitiveId).get(); - - if (mode != RemoveAlways) { - if (equivalent.isValueList && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(mapValue.get())) - continue; // If CSS value assumes CSSValueList, then only skip if the value was present in style to apply. - else if (mapValue && styleValue->cssText() == mapValue->cssText()) - continue; // If CSS value is primitive, then skip if they are equal. - } - - if (extractedStyle && mapValue) - extractedStyle->setProperty(equivalent.propertyID, mapValue->cssText()); - - if (mode == RemoveNone) - return true; - - removed = true; - if (!equivalent.attribute) { - replaceWithSpanOrRemoveIfWithoutAttributes(element); - break; - } - removeNodeAttribute(element, *equivalent.attribute); - if (isEmptyFontTag(element) || isSpanWithoutAttributesOrUnstyleStyleSpan(element)) - removeNodePreservingChildren(element); - } - return removed; -} - void ApplyStyleCommand::replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*& elem) { bool removeNode = false; @@ -1293,53 +1192,65 @@ void ApplyStyleCommand::replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*& elem = newSpanElement; } } - -bool ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclaration* style, HTMLElement* element, InlineStyleRemovalMode mode, CSSMutableStyleDeclaration* extractedStyle) + +bool ApplyStyleCommand::removeImplicitlyStyledElement(EditingStyle* style, HTMLElement* element, InlineStyleRemovalMode mode, EditingStyle* extractedStyle) { ASSERT(style); - ASSERT(element); + if (mode == RemoveNone) { + ASSERT(!extractedStyle); + return style->conflictsWithImplicitStyleOfElement(element) || style->conflictsWithImplicitStyleOfAttributes(element); + } + + ASSERT(mode == RemoveIfNeeded || mode == RemoveAlways); + if (style->conflictsWithImplicitStyleOfElement(element, extractedStyle, mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle : EditingStyle::DoNotExtractMatchingStyle)) { + replaceWithSpanOrRemoveIfWithoutAttributes(element); + return true; + } - CSSMutableStyleDeclaration* decl = element->inlineStyleDecl(); - if (!decl) + // unicode-bidi and direction are pushed down separately so don't push down with other styles + Vector<QualifiedName> attributes; + if (!style->extractConflictingImplicitStyleOfAttributes(element, extractedStyle ? EditingStyle::PreserveWritingDirection : EditingStyle::DoNotPreserveWritingDirection, + extractedStyle, attributes, mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle : EditingStyle::DoNotExtractMatchingStyle)) return false; - bool removed = false; - CSSMutableStyleDeclaration::const_iterator end = style->end(); - for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) { - CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id()); - RefPtr<CSSValue> value = decl->getPropertyCSSValue(propertyID); - if (value && (propertyID != CSSPropertyWhiteSpace || !isTabSpanNode(element))) { - removed = true; - if (mode == RemoveNone) - return true; - - ExceptionCode ec = 0; - if (extractedStyle) - extractedStyle->setProperty(propertyID, value->cssText(), decl->getPropertyPriority(propertyID), ec); - removeCSSProperty(element, propertyID); - - if (propertyID == CSSPropertyUnicodeBidi && !decl->getPropertyValue(CSSPropertyDirection).isEmpty()) { - if (extractedStyle) - extractedStyle->setProperty(CSSPropertyDirection, decl->getPropertyValue(CSSPropertyDirection), decl->getPropertyPriority(CSSPropertyDirection), ec); - removeCSSProperty(element, CSSPropertyDirection); - } - } - } + for (size_t i = 0; i < attributes.size(); i++) + removeNodeAttribute(element, attributes[i]); + + if (isEmptyFontTag(element) || isSpanWithoutAttributesOrUnstyleStyleSpan(element)) + removeNodePreservingChildren(element); + + return true; +} + +bool ApplyStyleCommand::removeCSSStyle(EditingStyle* style, HTMLElement* element, InlineStyleRemovalMode mode, EditingStyle* extractedStyle) +{ + ASSERT(style); + ASSERT(element); if (mode == RemoveNone) - return removed; + return style->conflictsWithInlineStyleOfElement(element); + + Vector<CSSPropertyID> properties; + if (!style->conflictsWithInlineStyleOfElement(element, extractedStyle, properties)) + return false; + + CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl(); + ASSERT(inlineStyle); + // FIXME: We should use a mass-removal function here but we don't have an undoable one yet. + for (size_t i = 0; i < properties.size(); i++) + removeCSSProperty(element, properties[i]); // No need to serialize <foo style=""> if we just removed the last css property - if (decl->isEmpty()) + if (inlineStyle->isEmpty()) removeNodeAttribute(element, styleAttr); if (isSpanWithoutAttributesOrUnstyleStyleSpan(element)) removeNodePreservingChildren(element); - return removed; + return true; } -HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(CSSMutableStyleDeclaration* style, Node* node) +HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(EditingStyle* style, Node* node) { if (!node) return 0; @@ -1359,52 +1270,23 @@ HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(CSSMut return result; } -void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, CSSMutableStyleDeclaration* style) +void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, EditingStyle* style) { ASSERT(node); - if (!style || !style->length() || !node->renderer()) + if (!style || style->isEmpty() || !node->renderer()) return; - RefPtr<CSSMutableStyleDeclaration> newInlineStyle = style; - if (node->isHTMLElement()) { - HTMLElement* element = toHTMLElement(node); - CSSMutableStyleDeclaration* existingInlineStyle = element->inlineStyleDecl(); - - // Avoid overriding existing styles of node - if (existingInlineStyle) { - newInlineStyle = existingInlineStyle->copy(); - CSSMutableStyleDeclaration::const_iterator end = style->end(); - for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) { - ExceptionCode ec; - if (!existingInlineStyle->getPropertyCSSValue(it->id())) - newInlineStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec); - - // text-decorations adds up - if (it->id() == CSSPropertyTextDecoration && it->value()->isValueList()) { - RefPtr<CSSValue> textDecoration = newInlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration); - if (textDecoration && textDecoration->isValueList()) { - CSSValueList* textDecorationOfInlineStyle = static_cast<CSSValueList*>(textDecoration.get()); - CSSValueList* textDecorationOfStyleApplied = static_cast<CSSValueList*>(it->value()); - - DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); - DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); - - if (textDecorationOfStyleApplied->hasValue(underline.get()) && !textDecorationOfInlineStyle->hasValue(underline.get())) - textDecorationOfInlineStyle->append(underline.get()); - - if (textDecorationOfStyleApplied->hasValue(lineThrough.get()) && !textDecorationOfInlineStyle->hasValue(lineThrough.get())) - textDecorationOfInlineStyle->append(lineThrough.get()); - } - } - } - } + RefPtr<EditingStyle> newInlineStyle = style; + if (node->isHTMLElement() && static_cast<HTMLElement*>(node)->inlineStyleDecl()) { + newInlineStyle = style->copy(); + newInlineStyle->mergeInlineStyleOfElement(static_cast<HTMLElement*>(node)); } // Since addInlineStyleIfNeeded can't add styles to block-flow render objects, add style attribute instead. // FIXME: applyInlineStyleToRange should be used here instead. if ((node->renderer()->isBlockFlow() || node->childNodeCount()) && node->isHTMLElement()) { - setNodeAttribute(toHTMLElement(node), styleAttr, newInlineStyle->cssText()); + setNodeAttribute(toHTMLElement(node), styleAttr, newInlineStyle->style()->cssText()); return; } @@ -1414,10 +1296,10 @@ void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, CSSMutableStyleDe // We can't wrap node with the styled element here because new styled element will never be removed if we did. // If we modified the child pointer in pushDownInlineStyleAroundNode to point to new style element // then we fall into an infinite loop where we keep removing and adding styled element wrapping node. - addInlineStyleIfNeeded(newInlineStyle.get(), node, node, DoNotAddStyledElement); + addInlineStyleIfNeeded(newInlineStyle->style(), node, node, DoNotAddStyledElement); } -void ApplyStyleCommand::pushDownInlineStyleAroundNode(CSSMutableStyleDeclaration* style, Node* targetNode) +void ApplyStyleCommand::pushDownInlineStyleAroundNode(EditingStyle* style, Node* targetNode) { HTMLElement* highestAncestor = highestAncestorWithConflictingInlineStyle(style, targetNode); if (!highestAncestor) @@ -1439,7 +1321,7 @@ void ApplyStyleCommand::pushDownInlineStyleAroundNode(CSSMutableStyleDeclaration styledElement = static_cast<StyledElement*>(current); elementsToPushDown.append(styledElement); } - RefPtr<CSSMutableStyleDeclaration> styleToPushDown = CSSMutableStyleDeclaration::create(); + RefPtr<EditingStyle> styleToPushDown = EditingStyle::create(); removeInlineStyleFromElement(style, toHTMLElement(current), RemoveIfNeeded, styleToPushDown.get()); // The inner loop will go through children on each level @@ -1474,20 +1356,14 @@ void ApplyStyleCommand::pushDownInlineStyleAroundNode(CSSMutableStyleDeclaration } } -void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration> style, const Position &start, const Position &end) +void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &start, const Position &end) { ASSERT(start.isNotNull()); ASSERT(end.isNotNull()); - ASSERT(start.node()->inDocument()); - ASSERT(end.node()->inDocument()); + ASSERT(start.anchorNode()->inDocument()); + ASSERT(end.anchorNode()->inDocument()); ASSERT(comparePositions(start, end) <= 0); - RefPtr<CSSValue> textDecorationSpecialProperty = style ? style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect) : 0; - if (textDecorationSpecialProperty) { - style = style->copy(); - style->setProperty(CSSPropertyTextDecoration, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSSPropertyWebkitTextDecorationsInEffect)); - } - Position pushDownStart = start.downstream(); // If the pushDownStart is at the end of a text node, then this node is not fully selected. // Move it to the next deep quivalent position to avoid removing the style from this node. @@ -1497,40 +1373,41 @@ void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration> && pushDownStart.computeOffsetInContainerNode() == pushDownStartContainer->maxCharacterOffset()) pushDownStart = nextVisuallyDistinctCandidate(pushDownStart); Position pushDownEnd = end.upstream(); - pushDownInlineStyleAroundNode(style.get(), pushDownStart.node()); - pushDownInlineStyleAroundNode(style.get(), pushDownEnd.node()); + + pushDownInlineStyleAroundNode(style, pushDownStart.deprecatedNode()); + pushDownInlineStyleAroundNode(style, pushDownEnd.deprecatedNode()); // The s and e variables store the positions used to set the ending selection after style removal // takes place. This will help callers to recognize when either the start node or the end node // are removed from the document during the work of this function. - // If pushDownInlineStyleAroundNode has pruned start.node() or end.node(), + // If pushDownInlineStyleAroundNode has pruned start.deprecatedNode() or end.deprecatedNode(), // use pushDownStart or pushDownEnd instead, which pushDownInlineStyleAroundNode won't prune. Position s = start.isNull() || start.isOrphan() ? pushDownStart : start; Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end; - Node* node = start.node(); + Node* node = start.deprecatedNode(); while (node) { RefPtr<Node> next = node->traverseNextNode(); if (node->isHTMLElement() && nodeFullySelected(node, start, end)) { RefPtr<HTMLElement> elem = toHTMLElement(node); RefPtr<Node> prev = elem->traversePreviousNodePostOrder(); RefPtr<Node> next = elem->traverseNextNode(); - RefPtr<CSSMutableStyleDeclaration> styleToPushDown; + RefPtr<EditingStyle> styleToPushDown; PassRefPtr<Node> childNode = 0; if (isStyledInlineElementToRemove(elem.get())) { - styleToPushDown = CSSMutableStyleDeclaration::create(); + styleToPushDown = EditingStyle::create(); childNode = elem->firstChild(); } - removeInlineStyleFromElement(style.get(), elem.get(), RemoveIfNeeded, styleToPushDown.get()); + removeInlineStyleFromElement(style, elem.get(), RemoveIfNeeded, styleToPushDown.get()); if (!elem->inDocument()) { - if (s.node() == elem) { + if (s.deprecatedNode() == elem) { // Since elem must have been fully selected, and it is at the start // of the selection, it is clear we can set the new s offset to 0. ASSERT(s.anchorType() == Position::PositionIsBeforeAnchor || s.offsetInContainerNode() <= 0); s = firstPositionInOrBeforeNode(next.get()); } - if (e.node() == elem) { + if (e.deprecatedNode() == elem) { // Since elem must have been fully selected, and it is at the end // of the selection, it is clear we can set the new e offset to // the max range offset of prev. @@ -1545,7 +1422,7 @@ void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration> applyInlineStyleToPushDown(childNode.get(), styleToPushDown.get()); } } - if (node == end.node()) + if (node == end.deprecatedNode()) break; node = next.get(); } @@ -1583,9 +1460,9 @@ void ApplyStyleCommand::splitTextAtStart(const Position& start, const Position& else newEnd = end; - Text* text = static_cast<Text*>(start.node()); + Text* text = static_cast<Text*>(start.deprecatedNode()); splitTextNode(text, start.offsetInContainerNode()); - updateStartEnd(firstPositionInNode(start.node()), newEnd); + updateStartEnd(firstPositionInNode(start.deprecatedNode()), newEnd); } void ApplyStyleCommand::splitTextAtEnd(const Position& start, const Position& end) @@ -1593,7 +1470,7 @@ void ApplyStyleCommand::splitTextAtEnd(const Position& start, const Position& en ASSERT(end.anchorType() == Position::PositionIsOffsetInAnchor); bool shouldUpdateStart = start.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode(); - Text* text = static_cast<Text *>(end.node()); + Text* text = static_cast<Text *>(end.deprecatedNode()); splitTextNode(text, end.offsetInContainerNode()); Node* prevNode = text->previousSibling(); @@ -1612,9 +1489,9 @@ void ApplyStyleCommand::splitTextElementAtStart(const Position& start, const Pos else newEnd = end; - Text* text = static_cast<Text*>(start.node()); + Text* text = static_cast<Text*>(start.deprecatedNode()); splitTextNodeContainingElement(text, start.deprecatedEditingOffset()); - updateStartEnd(Position(start.node()->parentNode(), start.node()->nodeIndex(), Position::PositionIsOffsetInAnchor), newEnd); + updateStartEnd(Position(start.deprecatedNode()->parentNode(), start.deprecatedNode()->nodeIndex(), Position::PositionIsOffsetInAnchor), newEnd); } void ApplyStyleCommand::splitTextElementAtEnd(const Position& start, const Position& end) @@ -1622,7 +1499,7 @@ void ApplyStyleCommand::splitTextElementAtEnd(const Position& start, const Posit ASSERT(end.anchorType() == Position::PositionIsOffsetInAnchor); bool shouldUpdateStart = start.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode(); - Text* text = static_cast<Text*>(end.node()); + Text* text = static_cast<Text*>(end.deprecatedNode()); splitTextNodeContainingElement(text, end.deprecatedEditingOffset()); Node* prevNode = text->parentNode()->previousSibling()->lastChild(); @@ -1631,7 +1508,7 @@ void ApplyStyleCommand::splitTextElementAtEnd(const Position& start, const Posit updateStartEnd(newStart, Position(prevNode->parentNode(), prevNode->nodeIndex() + 1, Position::PositionIsOffsetInAnchor)); } -bool ApplyStyleCommand::shouldSplitTextElement(Element* element, CSSMutableStyleDeclaration* style) +bool ApplyStyleCommand::shouldSplitTextElement(Element* element, EditingStyle* style) { if (!element || !element->isHTMLElement()) return false; @@ -1713,9 +1590,9 @@ bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position& start, mergeIdenticalElements(previousElement, element); int startOffsetAdjustment = startChild->nodeIndex(); - int endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0; + int endOffsetAdjustment = startNode == end.deprecatedNode() ? startOffsetAdjustment : 0; updateStartEnd(Position(startNode, startOffsetAdjustment, Position::PositionIsOffsetInAnchor), - Position(end.node(), end.deprecatedEditingOffset() + endOffsetAdjustment, Position::PositionIsOffsetInAnchor)); + Position(end.deprecatedNode(), end.deprecatedEditingOffset() + endOffsetAdjustment, Position::PositionIsOffsetInAnchor)); return true; } @@ -1731,11 +1608,11 @@ bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position& start, const if (endOffset < lastOffsetInNode(endNode)) return false; - unsigned parentLastOffset = end.node()->parentNode()->childNodes()->length() - 1; - if (end.node()->nextSibling()) + unsigned parentLastOffset = end.deprecatedNode()->parentNode()->childNodes()->length() - 1; + if (end.deprecatedNode()->nextSibling()) return false; - endNode = end.node()->parentNode(); + endNode = end.deprecatedNode()->parentNode(); endOffset = parentLastOffset; } @@ -1894,7 +1771,7 @@ void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), uTag)); if (styleChange.applyLineThrough()) - surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), sTag)); + surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), strikeTag)); if (styleChange.applySubscript()) surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), subTag)); diff --git a/Source/WebCore/editing/ApplyStyleCommand.h b/Source/WebCore/editing/ApplyStyleCommand.h index 1b2c2ef..d4444e4 100644 --- a/Source/WebCore/editing/ApplyStyleCommand.h +++ b/Source/WebCore/editing/ApplyStyleCommand.h @@ -77,16 +77,16 @@ private: // style-removal helpers bool isStyledInlineElementToRemove(Element*) const; - 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); + bool removeStyleFromRunBeforeApplyingStyle(EditingStyle*, RefPtr<Node>& runStart, RefPtr<Node>& runEnd); + bool removeInlineStyleFromElement(EditingStyle*, PassRefPtr<HTMLElement>, InlineStyleRemovalMode = RemoveIfNeeded, EditingStyle* extractedStyle = 0); + inline bool shouldRemoveInlineStyleFromElement(EditingStyle* style, HTMLElement* element) {return removeInlineStyleFromElement(style, element, RemoveNone);} void replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*&); - bool removeCSSStyle(CSSMutableStyleDeclaration*, HTMLElement*, InlineStyleRemovalMode = RemoveIfNeeded, CSSMutableStyleDeclaration* extractedStyle = 0); - HTMLElement* highestAncestorWithConflictingInlineStyle(CSSMutableStyleDeclaration*, Node*); - void applyInlineStyleToPushDown(Node*, CSSMutableStyleDeclaration *style); - void pushDownInlineStyleAroundNode(CSSMutableStyleDeclaration*, Node*); - void removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration>, const Position& start, const Position& end); + bool removeImplicitlyStyledElement(EditingStyle*, HTMLElement*, InlineStyleRemovalMode, EditingStyle* extractedStyle); + bool removeCSSStyle(EditingStyle*, HTMLElement*, InlineStyleRemovalMode = RemoveIfNeeded, EditingStyle* extractedStyle = 0); + HTMLElement* highestAncestorWithConflictingInlineStyle(EditingStyle*, Node*); + void applyInlineStyleToPushDown(Node*, EditingStyle*); + void pushDownInlineStyleAroundNode(EditingStyle*, Node*); + void removeInlineStyle(EditingStyle* , const Position& start, const Position& end); bool nodeFullySelected(Node*, const Position& start, const Position& end) const; bool nodeFullyUnselected(Node*, const Position& start, const Position& end) const; @@ -94,15 +94,15 @@ private: void applyBlockStyle(EditingStyle*); void applyRelativeFontStyleChange(EditingStyle*); void applyInlineStyle(EditingStyle*); - void fixRangeAndApplyInlineStyle(CSSMutableStyleDeclaration*, const Position& start, const Position& end); - void applyInlineStyleToNodeRange(CSSMutableStyleDeclaration*, Node* startNode, Node* pastEndNode); + void fixRangeAndApplyInlineStyle(EditingStyle*, const Position& start, const Position& end); + void applyInlineStyleToNodeRange(EditingStyle*, Node* startNode, Node* pastEndNode); void addBlockStyle(const StyleChange&, HTMLElement*); 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); void splitTextElementAtEnd(const Position& start, const Position& end); - bool shouldSplitTextElement(Element* elem, CSSMutableStyleDeclaration*); + bool shouldSplitTextElement(Element*, EditingStyle*); bool isValidCaretPositionInTextNode(const Position& position); bool mergeStartWithPreviousIfIdentical(const Position& start, const Position& end); bool mergeEndWithNextIfIdentical(const Position& start, const Position& end); diff --git a/Source/WebCore/editing/BreakBlockquoteCommand.cpp b/Source/WebCore/editing/BreakBlockquoteCommand.cpp index ae409c6..011a787 100644 --- a/Source/WebCore/editing/BreakBlockquoteCommand.cpp +++ b/Source/WebCore/editing/BreakBlockquoteCommand.cpp @@ -67,7 +67,7 @@ void BreakBlockquoteCommand::doApply() // Find the top-most blockquote from the start. Element* topBlockquote = 0; - for (ContainerNode* node = pos.node()->parentNode(); node; node = node->parentNode()) { + for (ContainerNode* node = pos.deprecatedNode()->parentNode(); node; node = node->parentNode()) { if (isMailBlockquote(node)) topBlockquote = static_cast<Element*>(node); } @@ -103,11 +103,11 @@ void BreakBlockquoteCommand::doApply() pos = pos.next(); // Adjust the position so we don't split at the beginning of a quote. - while (isFirstVisiblePositionInNode(VisiblePosition(pos), nearestMailBlockquote(pos.node()))) + while (isFirstVisiblePositionInNode(VisiblePosition(pos), nearestMailBlockquote(pos.deprecatedNode()))) pos = pos.previous(); // startNode is the first node that we need to move to the new blockquote. - Node* startNode = pos.node(); + Node* startNode = pos.deprecatedNode(); // Split at pos if in the middle of a text node. if (startNode->isTextNode()) { diff --git a/Source/WebCore/editing/CompositeEditCommand.cpp b/Source/WebCore/editing/CompositeEditCommand.cpp index ac3d761..b7672ee 100644 --- a/Source/WebCore/editing/CompositeEditCommand.cpp +++ b/Source/WebCore/editing/CompositeEditCommand.cpp @@ -160,7 +160,7 @@ void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Posi // For editing positions like [table, 0], insert before the table, // likewise for replaced elements, brs, etc. Position p = editingPosition.parentAnchoredEquivalent(); - Node* refChild = p.node(); + Node* refChild = p.deprecatedNode(); int offset = p.deprecatedEditingOffset(); if (canHaveChildrenForEditing(refChild)) { @@ -342,18 +342,23 @@ void CompositeEditCommand::replaceTextInNode(PassRefPtr<Text> node, unsigned off Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos) { - if (!isTabSpanTextNode(pos.node())) + if (!isTabSpanTextNode(pos.anchorNode())) return pos; - - Node* tabSpan = tabSpanNode(pos.node()); - - if (pos.deprecatedEditingOffset() <= caretMinOffset(pos.node())) + + if (pos.anchorType() == Position::PositionIsAfterAnchor) + return positionInParentAfterNode(pos.anchorNode()); + if (pos.anchorType() == Position::PositionIsBeforeAnchor) + return positionInParentBeforeNode(pos.anchorNode()); + + Node* tabSpan = tabSpanNode(pos.containerNode()); + + if (pos.offsetInContainerNode() <= caretMinOffset(pos.containerNode())) return positionInParentBeforeNode(tabSpan); - - if (pos.deprecatedEditingOffset() >= caretMaxOffset(pos.node())) + + if (pos.offsetInContainerNode() >= caretMaxOffset(pos.containerNode())) return positionInParentAfterNode(tabSpan); - splitTextNodeContainingElement(static_cast<Text *>(pos.node()), pos.deprecatedEditingOffset()); + splitTextNodeContainingElement(static_cast<Text *>(pos.containerNode()), pos.offsetInContainerNode()); return positionInParentBeforeNode(tabSpan); } @@ -475,7 +480,7 @@ void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(RefPtr<Text> textN void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position) { - Node* node = position.node(); + Node* node = position.deprecatedNode(); if (!node || !node->isTextNode()) return; Text* textNode = static_cast<Text*>(node); @@ -495,10 +500,10 @@ void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& positio VisiblePosition previousVisiblePos(visiblePos.previous()); Position previous(previousVisiblePos.deepEquivalent()); - if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous.node()->isTextNode() && !previous.node()->hasTagName(brTag)) - replaceTextInNode(static_cast<Text*>(previous.node()), previous.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); - if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.node()->isTextNode() && !position.node()->hasTagName(brTag)) - replaceTextInNode(static_cast<Text*>(position.node()), position.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); + if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous.deprecatedNode()->isTextNode() && !previous.deprecatedNode()->hasTagName(brTag)) + replaceTextInNode(static_cast<Text*>(previous.deprecatedNode()), previous.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); + if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.deprecatedNode()->isTextNode() && !position.deprecatedNode()->hasTagName(brTag)) + replaceTextInNode(static_cast<Text*>(position.deprecatedNode()), position.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); } void CompositeEditCommand::rebalanceWhitespace() @@ -600,15 +605,15 @@ void CompositeEditCommand::deleteInsignificantText(const Position& start, const return; Node* next; - for (Node* node = start.node(); node; node = next) { + for (Node* node = start.deprecatedNode(); node; node = next) { next = node->traverseNextNode(); if (node->isTextNode()) { Text* textNode = static_cast<Text*>(node); - int startOffset = node == start.node() ? start.deprecatedEditingOffset() : 0; - int endOffset = node == end.node() ? end.deprecatedEditingOffset() : static_cast<int>(textNode->length()); + int startOffset = node == start.deprecatedNode() ? start.deprecatedEditingOffset() : 0; + int endOffset = node == end.deprecatedNode() ? end.deprecatedEditingOffset() : static_cast<int>(textNode->length()); deleteInsignificantText(textNode, startOffset, endOffset); } - if (node == end.node()) + if (node == end.deprecatedNode()) break; } } @@ -638,7 +643,7 @@ PassRefPtr<Node> CompositeEditCommand::insertBlockPlaceholder(const Position& po return 0; // Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964. - ASSERT(pos.node()->renderer()); + ASSERT(pos.deprecatedNode()->renderer()); RefPtr<Node> placeholder = createBlockPlaceholderElement(document()); insertNodeAt(placeholder, pos); @@ -714,28 +719,26 @@ PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessar return 0; // Perform some checks to see if we need to perform work in this function. - if (isBlock(upstreamStart.node())) { + if (isBlock(upstreamStart.deprecatedNode())) { // If the block is the root editable element, always move content to a new block, // since it is illegal to modify attributes on the root editable element for editing. - if (upstreamStart.node() == editableRootForPosition(upstreamStart)) { + if (upstreamStart.deprecatedNode() == editableRootForPosition(upstreamStart)) { // If the block is the root editable element and it contains no visible content, create a new // block but don't try and move content into it, since there's nothing for moveParagraphs to move. - if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(upstreamStart.node()->renderer())) + if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(upstreamStart.deprecatedNode()->renderer())) return insertNewDefaultParagraphElementAt(upstreamStart); - } else if (isBlock(upstreamEnd.node())) { - if (!upstreamEnd.node()->isDescendantOf(upstreamStart.node())) { + } else if (isBlock(upstreamEnd.deprecatedNode())) { + if (!upstreamEnd.deprecatedNode()->isDescendantOf(upstreamStart.deprecatedNode())) { // If the paragraph end is a descendant of paragraph start, then we need to run // the rest of this function. If not, we can bail here. return 0; } - } - else if (enclosingBlock(upstreamEnd.node()) != upstreamStart.node()) { + } else if (enclosingBlock(upstreamEnd.deprecatedNode()) != upstreamStart.deprecatedNode()) { // The visibleEnd. It must be an ancestor of the paragraph start. // We can bail as we have a full block to work with. - ASSERT(upstreamStart.node()->isDescendantOf(enclosingBlock(upstreamEnd.node()))); + ASSERT(upstreamStart.deprecatedNode()->isDescendantOf(enclosingBlock(upstreamEnd.deprecatedNode()))); return 0; - } - else if (isEndOfDocument(visibleEnd)) { + } else if (isEndOfDocument(visibleEnd)) { // At the end of the document. We can bail here as well. return 0; } @@ -743,7 +746,7 @@ PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessar RefPtr<Node> newBlock = insertNewDefaultParagraphElementAt(upstreamStart); - bool endWasBr = visibleParagraphEnd.deepEquivalent().node()->hasTagName(brTag); + bool endWasBr = visibleParagraphEnd.deepEquivalent().deprecatedNode()->hasTagName(brTag); moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(firstPositionInNode(newBlock.get()))); @@ -778,14 +781,14 @@ void CompositeEditCommand::cloneParagraphUnderNewElement(Position& start, Positi appendNode(topNode, blockElement); RefPtr<Node> lastNode = topNode; - if (start.node() != outerNode) { + if (start.deprecatedNode() != outerNode) { Vector<RefPtr<Node> > ancestors; // Insert each node from innerNode to outerNode (excluded) in a list. - for (Node* n = start.node(); n && n != outerNode; n = n->parentNode()) + for (Node* n = start.deprecatedNode(); n && n != outerNode; n = n->parentNode()) ancestors.append(n); - // Clone every node between start.node() and outerBlock. + // Clone every node between start.deprecatedNode() and outerBlock. for (size_t i = ancestors.size(); i != 0; --i) { Node* item = ancestors[i - 1].get(); @@ -796,25 +799,25 @@ void CompositeEditCommand::cloneParagraphUnderNewElement(Position& start, Positi } // Handle the case of paragraphs with more than one node, - // cloning all the siblings until end.node() is reached. + // cloning all the siblings until end.deprecatedNode() is reached. - if (start.node() != end.node() && !start.node()->isDescendantOf(end.node())) { + if (start.deprecatedNode() != end.deprecatedNode() && !start.deprecatedNode()->isDescendantOf(end.deprecatedNode())) { // If end is not a descendant of outerNode we need to // find the first common ancestor and adjust the insertion // point accordingly. - while (!end.node()->isDescendantOf(outerNode)) { + while (!end.deprecatedNode()->isDescendantOf(outerNode)) { outerNode = outerNode->parentNode(); topNode = topNode->parentNode(); } - for (Node* n = start.node()->traverseNextSibling(outerNode); n; n = n->traverseNextSibling(outerNode)) { - if (n->parentNode() != start.node()->parentNode()) + for (Node* n = start.deprecatedNode()->traverseNextSibling(outerNode); n; n = n->traverseNextSibling(outerNode)) { + if (n->parentNode() != start.deprecatedNode()->parentNode()) lastNode = topNode->lastChild(); RefPtr<Node> clonedNode = n->cloneNode(true); insertNodeAfter(clonedNode, lastNode); lastNode = clonedNode.release(); - if (n == end.node() || end.node()->isDescendantOf(n)) + if (n == end.deprecatedNode() || end.deprecatedNode()->isDescendantOf(n)) break; } } @@ -833,7 +836,7 @@ void CompositeEditCommand::cleanupAfterDeletion() if (isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) { // Note: We want the rightmost candidate. Position position = caretAfterDelete.deepEquivalent().downstream(); - Node* node = position.node(); + Node* node = position.deprecatedNode(); // Normally deletion will leave a br as a placeholder. if (node->hasTagName(brTag)) removeNodeAndPruneAncestors(node); @@ -895,7 +898,7 @@ void CompositeEditCommand::moveParagraphWithClones(const VisiblePosition& startO beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent()); afterParagraph = VisiblePosition(afterParagraph.deepEquivalent()); - if (beforeParagraph.isNotNull() && !isTableElement(beforeParagraph.deepEquivalent().node()) + if (beforeParagraph.isNotNull() && !isTableElement(beforeParagraph.deepEquivalent().deprecatedNode()) && ((!isEndOfParagraph(beforeParagraph) && !isStartOfParagraph(beforeParagraph)) || beforeParagraph == afterParagraph)) { // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal. insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent()); @@ -955,7 +958,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap // start and end can't be used directly to create a Range; they are "editing positions" Position startRangeCompliant = start.parentAnchoredEquivalent(); Position endRangeCompliant = end.parentAnchoredEquivalent(); - RefPtr<Range> range = Range::create(document(), startRangeCompliant.node(), startRangeCompliant.deprecatedEditingOffset(), endRangeCompliant.node(), endRangeCompliant.deprecatedEditingOffset()); + RefPtr<Range> range = Range::create(document(), startRangeCompliant.deprecatedNode(), startRangeCompliant.deprecatedEditingOffset(), endRangeCompliant.deprecatedNode(), endRangeCompliant.deprecatedEditingOffset()); // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move. It // shouldn't matter though, since moved paragraphs will usually be quite small. @@ -969,7 +972,8 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap // too, <div><b><br></b></div> for example. Save it so that we can preserve it later. RefPtr<EditingStyle> styleInEmptyParagraph; if (startOfParagraphToMove == endOfParagraphToMove && preserveStyle) { - styleInEmptyParagraph = editingStyleIncludingTypingStyle(startOfParagraphToMove.deepEquivalent()); + styleInEmptyParagraph = EditingStyle::create(startOfParagraphToMove.deepEquivalent()); + styleInEmptyParagraph->mergeTypingStyle(document()); // The moved paragraph should assume the block style of the destination. styleInEmptyParagraph->removeBlockProperties(); } @@ -980,10 +984,10 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap document()->frame()->editor()->clearMisspellingsAndBadGrammar(endingSelection()); deleteSelection(false, false, false, false); - ASSERT(destination.deepEquivalent().node()->inDocument()); + ASSERT(destination.deepEquivalent().anchorNode()->inDocument()); cleanupAfterDeletion(); - ASSERT(destination.deepEquivalent().node()->inDocument()); + ASSERT(destination.deepEquivalent().anchorNode()->inDocument()); // Add a br if pruning an empty block level element caused a collapse. For example: // foo^ @@ -1006,7 +1010,10 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap setEndingSelection(destination); ASSERT(endingSelection().isCaretOrRange()); - applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment, true, false, !preserveStyle, false, true)); + ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MovingParagraph; + if (!preserveStyle) + options |= ReplaceSelectionCommand::MatchStyle; + applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment, options)); document()->frame()->editor()->markMisspellingsAndBadGrammar(endingSelection()); @@ -1035,7 +1042,8 @@ bool CompositeEditCommand::breakOutOfEmptyListItem() if (!emptyListItem) return false; - RefPtr<EditingStyle> style = editingStyleIncludingTypingStyle(endingSelection().start()); + RefPtr<EditingStyle> style = EditingStyle::create(endingSelection().start()); + style->mergeTypingStyle(document()); ContainerNode* listNode = emptyListItem->parentNode(); // FIXME: Can't we do something better when the immediate parent wasn't a list node? @@ -1125,25 +1133,25 @@ bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph() // If this is an empty paragraph there must be a line break here. if (!lineBreakExistsAtVisiblePosition(caret)) return false; - - Position caretPos(caret.deepEquivalent()); + + Position caretPos(caret.deepEquivalent().downstream()); // A line break is either a br or a preserved newline. - ASSERT(caretPos.node()->hasTagName(brTag) || (caretPos.node()->isTextNode() && caretPos.node()->renderer()->style()->preserveNewline())); + ASSERT(caretPos.deprecatedNode()->hasTagName(brTag) || (caretPos.deprecatedNode()->isTextNode() && caretPos.deprecatedNode()->renderer()->style()->preserveNewline())); - if (caretPos.node()->hasTagName(brTag)) { - Position beforeBR(positionInParentBeforeNode(caretPos.node())); - removeNode(caretPos.node()); - prune(beforeBR.node()); - } else { + if (caretPos.deprecatedNode()->hasTagName(brTag)) { + Position beforeBR(positionInParentBeforeNode(caretPos.deprecatedNode())); + removeNode(caretPos.deprecatedNode()); + prune(beforeBR.deprecatedNode()); + } else if (caretPos.deprecatedNode()->isTextNode()) { ASSERT(caretPos.deprecatedEditingOffset() == 0); - Text* textNode = static_cast<Text*>(caretPos.node()); + Text* textNode = static_cast<Text*>(caretPos.deprecatedNode()); ContainerNode* parentNode = textNode->parentNode(); // The preserved newline must be the first thing in the node, since otherwise the previous // paragraph would be quoted, and we verified that it wasn't above. deleteTextFromNode(textNode, 0, 1); prune(parentNode); } - + return true; } @@ -1172,7 +1180,7 @@ Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Posi if (visiblePos == lastInAnchor) { // Make sure anchors are pushed down before avoiding them so that we don't // also avoid structural elements like lists and blocks (5142012). - if (original.node() != enclosingAnchor && original.node()->parentNode() != enclosingAnchor) { + if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) { pushAnchorElementDown(enclosingAnchor); enclosingAnchor = enclosingAnchorElement(original); if (!enclosingAnchor) @@ -1181,7 +1189,7 @@ Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Posi // Don't insert outside an anchor if doing so would skip over a line break. It would // probably be safe to move the line break so that we could still avoid the anchor here. Position downstream(visiblePos.deepEquivalent().downstream()); - if (lineBreakExistsAtVisiblePosition(visiblePos) && downstream.node()->isDescendantOf(enclosingAnchor)) + if (lineBreakExistsAtVisiblePosition(visiblePos) && downstream.deprecatedNode()->isDescendantOf(enclosingAnchor)) return original; result = positionInParentAfterNode(enclosingAnchor); @@ -1191,7 +1199,7 @@ Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Posi if (visiblePos == firstInAnchor) { // Make sure anchors are pushed down before avoiding them so that we don't // also avoid structural elements like lists and blocks (5142012). - if (original.node() != enclosingAnchor && original.node()->parentNode() != enclosingAnchor) { + if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) { pushAnchorElementDown(enclosingAnchor); enclosingAnchor = enclosingAnchorElement(original); } diff --git a/Source/WebCore/editing/DeleteSelectionCommand.cpp b/Source/WebCore/editing/DeleteSelectionCommand.cpp index 3ba5ae9..897c305 100644 --- a/Source/WebCore/editing/DeleteSelectionCommand.cpp +++ b/Source/WebCore/editing/DeleteSelectionCommand.cpp @@ -84,7 +84,7 @@ DeleteSelectionCommand::DeleteSelectionCommand(Document *document, bool smartDel } DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements) - : CompositeEditCommand(selection.start().node()->document()), + : CompositeEditCommand(selection.start().anchorNode()->document()), m_hasSelectionToDelete(true), m_smartDelete(smartDelete), m_mergeBlocksAfterDelete(mergeBlocksAfterDelete), @@ -110,10 +110,10 @@ void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end) // For HRs, we'll get a position at (HR,1) when hitting delete from the beginning of the previous line, or (HR,0) when forward deleting, // but in these cases, we want to delete it, so manually expand the selection - if (start.node()->hasTagName(hrTag)) - start = positionBeforeNode(start.node()); - else if (end.node()->hasTagName(hrTag)) - end = positionAfterNode(end.node()); + if (start.deprecatedNode()->hasTagName(hrTag)) + start = positionBeforeNode(start.deprecatedNode()); + else if (end.deprecatedNode()->hasTagName(hrTag)) + end = positionAfterNode(end.deprecatedNode()); // FIXME: This is only used so that moveParagraphs can avoid the bugs in special element expansion. if (!m_expandForSpecialElements) @@ -275,7 +275,7 @@ void DeleteSelectionCommand::saveTypingStyleState() // typing style at the start of the selection, nor is there a reason to // compute the style at the start of the selection after deletion (see the // early return in calculateTypingStyleAfterDelete). - if (m_upstreamStart.node() == m_downstreamEnd.node() && m_upstreamStart.node()->isTextNode()) + if (m_upstreamStart.deprecatedNode() == m_downstreamEnd.deprecatedNode() && m_upstreamStart.deprecatedNode()->isTextNode()) return; // Figure out the typing style in effect before the delete is done. @@ -284,7 +284,7 @@ void DeleteSelectionCommand::saveTypingStyleState() // 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())) + if (nearestMailBlockquote(m_selectionToDelete.start().deprecatedNode())) m_deleteIntoBlockquoteStyle = EditingStyle::create(m_selectionToDelete.end()); else m_deleteIntoBlockquoteStyle = 0; @@ -293,11 +293,11 @@ void DeleteSelectionCommand::saveTypingStyleState() bool DeleteSelectionCommand::handleSpecialCaseBRDelete() { // Check for special-case where the selection contains only a BR on a line by itself after another BR. - bool upstreamStartIsBR = m_upstreamStart.node()->hasTagName(brTag); - bool downstreamStartIsBR = m_downstreamStart.node()->hasTagName(brTag); - bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.node() == m_upstreamEnd.node(); + bool upstreamStartIsBR = m_upstreamStart.deprecatedNode()->hasTagName(brTag); + bool downstreamStartIsBR = m_downstreamStart.deprecatedNode()->hasTagName(brTag); + bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.deprecatedNode() == m_upstreamEnd.deprecatedNode(); if (isBROnLineByItself) { - removeNode(m_downstreamStart.node()); + removeNode(m_downstreamStart.deprecatedNode()); return true; } @@ -416,7 +416,7 @@ void DeleteSelectionCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned void DeleteSelectionCommand::handleGeneralDelete() { int startOffset = m_upstreamStart.deprecatedEditingOffset(); - Node* startNode = m_upstreamStart.node(); + Node* startNode = m_upstreamStart.deprecatedNode(); // Never remove the start block unless it's a table, in which case we won't merge content in. if (startNode == m_startBlock && startOffset == 0 && canHaveChildrenForEditing(startNode) && !startNode->hasTagName(tableTag)) { @@ -439,7 +439,7 @@ void DeleteSelectionCommand::handleGeneralDelete() if (!startNode) return; - if (startNode == m_downstreamEnd.node()) { + if (startNode == m_downstreamEnd.deprecatedNode()) { if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) { if (startNode->isTextNode()) { // in a text node that needs to be trimmed @@ -456,7 +456,7 @@ void DeleteSelectionCommand::handleGeneralDelete() removeNode(startNode); } else { - bool startNodeWasDescendantOfEndNode = m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()); + bool startNodeWasDescendantOfEndNode = m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode()); // The selection to delete spans more than one node. RefPtr<Node> node(startNode); @@ -469,21 +469,21 @@ void DeleteSelectionCommand::handleGeneralDelete() } else { node = startNode->childNode(startOffset); } - } else if (startNode == m_upstreamEnd.node() && startNode->isTextNode()) { - Text* text = static_cast<Text*>(m_upstreamEnd.node()); + } else if (startNode == m_upstreamEnd.deprecatedNode() && startNode->isTextNode()) { + Text* text = static_cast<Text*>(m_upstreamEnd.deprecatedNode()); deleteTextFromNode(text, 0, m_upstreamEnd.deprecatedEditingOffset()); } // handle deleting all nodes that are completely selected - while (node && node != m_downstreamEnd.node()) { + while (node && node != m_downstreamEnd.deprecatedNode()) { if (comparePositions(firstPositionInOrBeforeNode(node.get()), m_downstreamEnd) >= 0) { // traverseNextSibling just blew past the end position, so stop deleting node = 0; - } else if (!m_downstreamEnd.node()->isDescendantOf(node.get())) { + } else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(node.get())) { RefPtr<Node> nextNode = node->traverseNextSibling(); // if we just removed a node from the end container, update end position so the // check above will work - if (node->parentNode() == m_downstreamEnd.node()) { + if (node->parentNode() == m_downstreamEnd.deprecatedNode()) { ASSERT(m_downstreamEnd.deprecatedEditingOffset()); ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.deprecatedEditingOffset()); m_downstreamEnd.moveToOffset(m_downstreamEnd.deprecatedEditingOffset() - 1); @@ -492,7 +492,7 @@ void DeleteSelectionCommand::handleGeneralDelete() node = nextNode.get(); } else { Node* n = node->lastDescendant(); - if (m_downstreamEnd.node() == n && m_downstreamEnd.deprecatedEditingOffset() >= caretMaxOffset(n)) { + if (m_downstreamEnd.deprecatedNode() == n && m_downstreamEnd.deprecatedEditingOffset() >= caretMaxOffset(n)) { removeNode(node.get()); node = 0; } else @@ -500,33 +500,33 @@ void DeleteSelectionCommand::handleGeneralDelete() } } - if (m_downstreamEnd.node() != startNode && !m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()) && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.deprecatedEditingOffset() >= caretMinOffset(m_downstreamEnd.node())) { - if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(m_downstreamEnd.node())) { + if (m_downstreamEnd.deprecatedNode() != startNode && !m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode()) && m_downstreamEnd.anchorNode()->inDocument() && m_downstreamEnd.deprecatedEditingOffset() >= caretMinOffset(m_downstreamEnd.deprecatedNode())) { + if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(m_downstreamEnd.deprecatedNode())) { // The node itself is fully selected, not just its contents. Delete it. - removeNode(m_downstreamEnd.node()); + removeNode(m_downstreamEnd.deprecatedNode()); } else { - if (m_downstreamEnd.node()->isTextNode()) { + if (m_downstreamEnd.deprecatedNode()->isTextNode()) { // in a text node that needs to be trimmed - Text *text = static_cast<Text *>(m_downstreamEnd.node()); + Text* text = static_cast<Text*>(m_downstreamEnd.deprecatedNode()); if (m_downstreamEnd.deprecatedEditingOffset() > 0) { deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEditingOffset()); } - // Remove children of m_downstreamEnd.node() that come after m_upstreamStart. - // Don't try to remove children if m_upstreamStart was inside m_downstreamEnd.node() + // Remove children of m_downstreamEnd.deprecatedNode() that come after m_upstreamStart. + // Don't try to remove children if m_upstreamStart was inside m_downstreamEnd.deprecatedNode() // and m_upstreamStart has been removed from the document, because then we don't // know how many children to remove. // FIXME: Make m_upstreamStart a position we update as we remove content, then we can // always know which children to remove. - } else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.node()->inDocument())) { + } else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.anchorNode()->inDocument())) { int offset = 0; - if (m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node())) { - Node *n = m_upstreamStart.node(); - while (n && n->parentNode() != m_downstreamEnd.node()) + if (m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode())) { + Node* n = m_upstreamStart.deprecatedNode(); + while (n && n->parentNode() != m_downstreamEnd.deprecatedNode()) n = n->parentNode(); if (n) offset = n->nodeIndex() + 1; } - removeChildrenInRange(m_downstreamEnd.node(), offset, m_downstreamEnd.deprecatedEditingOffset()); + removeChildrenInRange(m_downstreamEnd.deprecatedNode(), offset, m_downstreamEnd.deprecatedEditingOffset()); m_downstreamEnd.moveToOffset(offset); } } @@ -538,13 +538,13 @@ void DeleteSelectionCommand::fixupWhitespace() { updateLayout(); // FIXME: isRenderedCharacter should be removed, and we should use VisiblePosition::characterAfter and VisiblePosition::characterBefore - if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && m_leadingWhitespace.node()->isTextNode()) { - Text* textNode = static_cast<Text*>(m_leadingWhitespace.node()); + if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && m_leadingWhitespace.deprecatedNode()->isTextNode()) { + Text* textNode = static_cast<Text*>(m_leadingWhitespace.deprecatedNode()); ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); replaceTextInNode(textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); } - if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && m_trailingWhitespace.node()->isTextNode()) { - Text* textNode = static_cast<Text*>(m_trailingWhitespace.node()); + if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && m_trailingWhitespace.deprecatedNode()->isTextNode()) { + Text* textNode = static_cast<Text*>(m_trailingWhitespace.deprecatedNode()); ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace()); replaceTextInNode(textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); } @@ -569,7 +569,7 @@ void DeleteSelectionCommand::mergeParagraphs() ASSERT(!m_pruneStartBlockIfNecessary); // FIXME: Deletion should adjust selection endpoints as it removes nodes so that we never get into this state (4099839). - if (!m_downstreamEnd.node()->inDocument() || !m_upstreamStart.node()->inDocument()) + if (!m_downstreamEnd.anchorNode()->inDocument() || !m_upstreamStart.anchorNode()->inDocument()) return; // FIXME: The deletion algorithm shouldn't let this happen. @@ -585,14 +585,14 @@ void DeleteSelectionCommand::mergeParagraphs() // m_downstreamEnd's block has been emptied out by deletion. There is no content inside of it to // move, so just remove it. - Element* endBlock = static_cast<Element*>(enclosingBlock(m_downstreamEnd.node())); - if (!startOfParagraphToMove.deepEquivalent().node() || !endBlock->contains(startOfParagraphToMove.deepEquivalent().node())) { - removeNode(enclosingBlock(m_downstreamEnd.node())); + Element* endBlock = static_cast<Element*>(enclosingBlock(m_downstreamEnd.deprecatedNode())); + if (!startOfParagraphToMove.deepEquivalent().deprecatedNode() || !endBlock->contains(startOfParagraphToMove.deepEquivalent().deprecatedNode())) { + removeNode(enclosingBlock(m_downstreamEnd.deprecatedNode())); return; } // We need to merge into m_upstreamStart's block, but it's been emptied out and collapsed by deletion. - if (!mergeDestination.deepEquivalent().node() || !mergeDestination.deepEquivalent().node()->isDescendantOf(m_upstreamStart.node()->enclosingBlockFlowElement()) || m_startsAtEmptyLine) { + if (!mergeDestination.deepEquivalent().deprecatedNode() || !mergeDestination.deepEquivalent().deprecatedNode()->isDescendantOf(m_upstreamStart.deprecatedNode()->enclosingBlockFlowElement()) || m_startsAtEmptyLine) { insertNodeAt(createBreakElement(document()).get(), m_upstreamStart); mergeDestination = VisiblePosition(m_upstreamStart); } @@ -608,8 +608,8 @@ void DeleteSelectionCommand::mergeParagraphs() // The rule for merging into an empty block is: only do so if its farther to the right. // FIXME: Consider RTL. if (!m_startsAtEmptyLine && isStartOfParagraph(mergeDestination) && startOfParagraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds().x()) { - if (mergeDestination.deepEquivalent().downstream().node()->hasTagName(brTag)) { - removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downstream().node()); + if (mergeDestination.deepEquivalent().downstream().deprecatedNode()->hasTagName(brTag)) { + removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downstream().deprecatedNode()); m_endingPosition = startOfParagraphToMove.deepEquivalent(); return; } @@ -618,7 +618,7 @@ void DeleteSelectionCommand::mergeParagraphs() // Block images, tables and horizontal rules cannot be made inline with content at mergeDestination. If there is // any (!isStartOfParagraph(mergeDestination)), don't merge, just move the caret to just before the selection we deleted. // See https://bugs.webkit.org/show_bug.cgi?id=25439 - if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalent().node()) && !isStartOfParagraph(mergeDestination)) { + if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalent().deprecatedNode()) && !isStartOfParagraph(mergeDestination)) { m_endingPosition = m_upstreamStart; return; } @@ -666,7 +666,7 @@ void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows() if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_startTableRow) if (isTableRowEmpty(m_endTableRow.get())) { // Don't remove m_endTableRow if it's where we're putting the ending selection. - if (!m_endingPosition.node()->isDescendantOf(m_endTableRow.get())) { + if (!m_endingPosition.deprecatedNode()->isDescendantOf(m_endTableRow.get())) { // FIXME: We probably shouldn't remove m_endTableRow unless it's fully selected, even if it is empty. // We'll need to start adjusting the selection endpoints during deletion to know whether or not m_endTableRow // was fully selected here. @@ -687,7 +687,7 @@ void DeleteSelectionCommand::calculateTypingStyleAfterDelete() // has completed. // If we deleted into a blockquote, but are now no longer in a blockquote, use the alternate typing style - if (m_deleteIntoBlockquoteStyle && !nearestMailBlockquote(m_endingPosition.node())) + if (m_deleteIntoBlockquoteStyle && !nearestMailBlockquote(m_endingPosition.deprecatedNode())) m_typingStyle = m_deleteIntoBlockquoteStyle; m_deleteIntoBlockquoteStyle = 0; @@ -742,7 +742,7 @@ void DeleteSelectionCommand::doApply() // If the deletion is occurring in a text field, and we're not deleting to replace the selection, then let the frame call across the bridge to notify the form delegate. if (!m_replace) { - Node* startNode = m_selectionToDelete.start().node(); + Node* startNode = m_selectionToDelete.start().deprecatedNode(); Node* ancestorNode = startNode ? startNode->shadowAncestorNode() : 0; if (ancestorNode && ancestorNode->hasTagName(inputTag) && static_cast<HTMLInputElement*>(ancestorNode)->isTextField() @@ -762,7 +762,7 @@ void DeleteSelectionCommand::doApply() // and ends inside it (we do need placeholders to hold open empty cells, but that's // handled elsewhere). if (Node* table = isLastPositionBeforeTable(m_selectionToDelete.visibleStart())) - if (m_selectionToDelete.end().node()->isDescendantOf(table)) + if (m_selectionToDelete.end().deprecatedNode()->isDescendantOf(table)) m_needPlaceholder = false; } diff --git a/Source/WebCore/editing/EditCommand.cpp b/Source/WebCore/editing/EditCommand.cpp index 1b4451d..7b0eab4 100644 --- a/Source/WebCore/editing/EditCommand.cpp +++ b/Source/WebCore/editing/EditCommand.cpp @@ -99,6 +99,8 @@ void EditCommand::apply() if (!isTypingCommand()) frame->editor()->appliedEditing(this); } + + setShouldRetainAutocorrectionIndicator(false); } void EditCommand::unapply() @@ -192,6 +194,14 @@ bool EditCommand::isTypingCommand() const return false; } +bool EditCommand::shouldRetainAutocorrectionIndicator() const +{ + return false; +} + +void EditCommand::setShouldRetainAutocorrectionIndicator(bool) +{ +} void EditCommand::updateLayout() const { diff --git a/Source/WebCore/editing/EditCommand.h b/Source/WebCore/editing/EditCommand.h index 4826ec0..8ee37fe 100644 --- a/Source/WebCore/editing/EditCommand.h +++ b/Source/WebCore/editing/EditCommand.h @@ -56,9 +56,12 @@ public: virtual bool isTypingCommand() const; virtual bool preservesTypingStyle() const; - + bool isTopLevelCommand() const { return !m_parent; } + virtual bool shouldRetainAutocorrectionIndicator() const; + virtual void setShouldRetainAutocorrectionIndicator(bool); + protected: EditCommand(Document*); diff --git a/Source/WebCore/editing/EditingBoundary.h b/Source/WebCore/editing/EditingBoundary.h index 1cb0849..f8ac1cb 100644 --- a/Source/WebCore/editing/EditingBoundary.h +++ b/Source/WebCore/editing/EditingBoundary.h @@ -30,7 +30,8 @@ namespace WebCore { enum EditingBoundaryCrossingRule { CanCrossEditingBoundary, - CannotCrossEditingBoundary + CannotCrossEditingBoundary, + CanSkipOverEditingBoundary }; } diff --git a/Source/WebCore/editing/EditingStyle.cpp b/Source/WebCore/editing/EditingStyle.cpp index a3f66be..de71fb7 100644 --- a/Source/WebCore/editing/EditingStyle.cpp +++ b/Source/WebCore/editing/EditingStyle.cpp @@ -31,11 +31,16 @@ #include "CSSComputedStyleDeclaration.h" #include "CSSMutableStyleDeclaration.h" #include "CSSValueKeywords.h" +#include "CSSValueList.h" #include "Frame.h" +#include "HTMLFontElement.h" +#include "HTMLNames.h" #include "Node.h" #include "Position.h" #include "RenderStyle.h" #include "SelectionController.h" +#include "StyledElement.h" +#include "htmlediting.h" namespace WebCore { @@ -82,6 +87,175 @@ static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(Pass return copyEditingProperties(style.get()); } +class HTMLElementEquivalent { +public: + static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName) + { + return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName)); + } + + virtual ~HTMLElementEquivalent() { } + virtual bool matches(Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); } + virtual bool hasAttribute() const { return false; } + bool propertyExistsInStyle(CSSStyleDeclaration* style) const { return style->getPropertyCSSValue(m_propertyID); } + virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; + virtual void addToStyle(Element*, EditingStyle*) const; + +protected: + HTMLElementEquivalent(CSSPropertyID); + HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName); + HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName); + const int m_propertyID; + const RefPtr<CSSPrimitiveValue> m_primitiveValue; + const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global. +}; + +HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id) + : m_propertyID(id) + , m_tagName(0) +{ +} + +HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName) + : m_propertyID(id) + , m_tagName(&tagName) +{ +} + +HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName) + : m_propertyID(id) + , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue)) + , m_tagName(&tagName) +{ + ASSERT(primitiveValue != CSSValueInvalid); +} + +bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const +{ + RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID); + return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent(); +} + +void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const +{ + style->setProperty(m_propertyID, m_primitiveValue->cssText()); +} + +class HTMLTextDecorationEquivalent : public HTMLElementEquivalent { +public: + static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName) + { + return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName)); + } + virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; + +private: + HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName); +}; + +HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName) + : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName) +{ +} + +bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const +{ + RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID); + return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get()); +} + +class HTMLAttributeEquivalent : public HTMLElementEquivalent { +public: + static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName) + { + return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName)); + } + static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName) + { + return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName)); + } + + bool matches(Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); } + virtual bool hasAttribute() const { return true; } + virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; + virtual void addToStyle(Element*, EditingStyle*) const; + virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const; + inline const QualifiedName& attributeName() const { return m_attrName; } + +protected: + HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName); + HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName); + const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global. +}; + +HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName) + : HTMLElementEquivalent(id, tagName) + , m_attrName(attrName) +{ +} + +HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName) + : HTMLElementEquivalent(id) + , m_attrName(attrName) +{ +} + +bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const +{ + RefPtr<CSSValue> value = attributeValueAsCSSValue(element); + RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID); + + // FIXME: This is very inefficient way of comparing values + // but we can't string compare attribute value and CSS property value. + return value && styleValue && value->cssText() == styleValue->cssText(); +} + +void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const +{ + if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element)) + style->setProperty(m_propertyID, value->cssText()); +} + +PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const +{ + ASSERT(element); + if (!element->hasAttribute(m_attrName)) + return 0; + + RefPtr<CSSMutableStyleDeclaration> dummyStyle; + dummyStyle = CSSMutableStyleDeclaration::create(); + dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName)); + return dummyStyle->getPropertyCSSValue(m_propertyID); +} + +class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent { +public: + static PassOwnPtr<HTMLFontSizeEquivalent> create() + { + return adoptPtr(new HTMLFontSizeEquivalent()); + } + virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const; + +private: + HTMLFontSizeEquivalent(); +}; + +HTMLFontSizeEquivalent::HTMLFontSizeEquivalent() + : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr) +{ +} + +PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const +{ + ASSERT(element); + if (!element->hasAttribute(m_attrName)) + return 0; + int size; + if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size)) + return 0; + return CSSPrimitiveValue::createIdentifier(size); +} + float EditingStyle::NoFontDelta = 0.0f; EditingStyle::EditingStyle() @@ -101,7 +275,7 @@ EditingStyle::EditingStyle(const Position& position) : m_shouldUseFixedDefaultFontSize(false) , m_fontSizeDelta(NoFontDelta) { - init(position.node(), OnlyInheritableProperties); + init(position.deprecatedNode(), OnlyInheritableProperties); } EditingStyle::EditingStyle(const CSSStyleDeclaration* style) @@ -112,6 +286,14 @@ EditingStyle::EditingStyle(const CSSStyleDeclaration* style) extractFontSizeDelta(); } +EditingStyle::EditingStyle(int propertyID, const String& value) + : m_mutableStyle(0) + , m_shouldUseFixedDefaultFontSize(false) + , m_fontSizeDelta(NoFontDelta) +{ + setProperty(propertyID, value); +} + EditingStyle::~EditingStyle() { } @@ -144,6 +326,15 @@ void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyl ASSERT(!ec); } +void EditingStyle::setProperty(int propertyID, const String& value, bool important) +{ + if (!m_mutableStyle) + m_mutableStyle = CSSMutableStyleDeclaration::create(); + + ExceptionCode ec; + m_mutableStyle->setProperty(propertyID, value, important, ec); +} + void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle) { ASSERT(renderStyle); @@ -182,6 +373,9 @@ bool EditingStyle::isEmpty() const bool EditingStyle::textDirection(WritingDirection& writingDirection) const { + if (!m_mutableStyle) + return false; + RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); if (!unicodeBidi) return false; @@ -306,6 +500,181 @@ void EditingStyle::removeNonEditingProperties() m_mutableStyle = copyEditingProperties(m_mutableStyle.get()); } +void EditingStyle::collapseTextDecorationProperties() +{ + if (!m_mutableStyle) + return; + + RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); + if (!textDecorationsInEffect) + return; + + m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration)); + m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); +} + +// CSS properties that create a visual difference only when applied to text. +static const int textOnlyProperties[] = { + CSSPropertyTextDecoration, + CSSPropertyWebkitTextDecorationsInEffect, + CSSPropertyFontStyle, + CSSPropertyFontWeight, + CSSPropertyColor, +}; + +TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const +{ + RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare); + + if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties) + difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties)); + + if (!difference->length()) + return TrueTriState; + if (difference->length() == m_mutableStyle->length()) + return FalseTriState; + + return MixedTriState; +} + +bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const +{ + ASSERT(element); + ASSERT(!conflictingProperties || conflictingProperties->isEmpty()); + + CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl(); + if (!m_mutableStyle || !inlineStyle) + return false; + + if (!conflictingProperties) { + CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end(); + for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) { + CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id()); + + // We don't override whitespace property of a tab span because that would collapse the tab into a space. + if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) + continue; + + if (inlineStyle->getPropertyCSSValue(propertyID)) + return true; + } + + return false; + } + + CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end(); + for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) { + CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id()); + if ((propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) || !inlineStyle->getPropertyCSSValue(propertyID)) + continue; + + if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) { + if (extractedStyle) + extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID)); + conflictingProperties->append(CSSPropertyDirection); + } + + conflictingProperties->append(propertyID); + if (extractedStyle) + extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID)); + } + + return !conflictingProperties->isEmpty(); +} + +bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const +{ + if (!m_mutableStyle) + return false; + + static const HTMLElementEquivalent* HTMLEquivalents[] = { + HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag).leakPtr(), + HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag).leakPtr(), + HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag).leakPtr(), + HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag).leakPtr(), + HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag).leakPtr(), + HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag).leakPtr(), + + HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag).leakPtr(), + HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag).leakPtr(), + HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag).leakPtr(), + }; + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) { + const HTMLElementEquivalent* equivalent = HTMLEquivalents[i]; + if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get()) + && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) { + if (extractedStyle) + equivalent->addToStyle(element, extractedStyle); + return true; + } + } + return false; +} + +static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents() +{ + DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ()); + + if (!HTMLAttributeEquivalents.size()) { + HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr)); + HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr)); + HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create()); + + HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr)); + HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr)); + } + + return HTMLAttributeEquivalents; +} + +bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const +{ + ASSERT(element); + if (!m_mutableStyle) + return false; + + const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); + for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { + if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get()) + && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get())) + return true; + } + + return false; +} + +bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection, + EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const +{ + ASSERT(element); + // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties + ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection); + if (!m_mutableStyle) + return false; + + const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); + bool removed = false; + for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { + const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get(); + + // unicode-bidi and direction are pushed down separately so don't push down with other styles. + if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr) + continue; + + if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get()) + || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) + continue; + + if (extractedStyle) + equivalent->addToStyle(element, extractedStyle); + conflictingAttributes.append(equivalent->attributeName()); + removed = true; + } + + return removed; +} + void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection) { if (!m_mutableStyle) @@ -343,13 +712,60 @@ void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWrit } } -PassRefPtr<EditingStyle> editingStyleIncludingTypingStyle(const Position& position) +void EditingStyle::mergeTypingStyle(Document* document) { - RefPtr<EditingStyle> editingStyle = EditingStyle::create(position); - RefPtr<EditingStyle> typingStyle = position.node()->document()->frame()->selection()->typingStyle(); - if (typingStyle && typingStyle->style()) - editingStyle->style()->merge(copyEditingProperties(typingStyle->style()).get()); - return editingStyle; + ASSERT(document); + + RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle(); + if (!typingStyle || typingStyle == this) + return; + + mergeStyle(typingStyle->style()); } - + +void EditingStyle::mergeInlineStyleOfElement(StyledElement* element) +{ + ASSERT(element); + mergeStyle(element->inlineStyleDecl()); +} + +void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style) +{ + if (!style) + return; + + if (!m_mutableStyle) { + m_mutableStyle = style->copy(); + return; + } + + CSSMutableStyleDeclaration::const_iterator end = style->end(); + for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) { + RefPtr<CSSValue> value; + if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) { + value = m_mutableStyle->getPropertyCSSValue(it->id()); + if (value && !value->isValueList()) + value = 0; + } + + if (!value) { + ExceptionCode ec; + m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec); + continue; + } + + CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value()); + CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get()); + + DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); + DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); + + if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get())) + textDecorations->append(underline.get()); + + if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get())) + textDecorations->append(lineThrough.get()); + } +} + } diff --git a/Source/WebCore/editing/EditingStyle.h b/Source/WebCore/editing/EditingStyle.h index 129ade5..aa310ac 100644 --- a/Source/WebCore/editing/EditingStyle.h +++ b/Source/WebCore/editing/EditingStyle.h @@ -31,24 +31,34 @@ #ifndef EditingStyle_h #define EditingStyle_h +#include "CSSPropertyNames.h" #include "WritingDirection.h" +#include <wtf/Forward.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> +#include <wtf/Vector.h> namespace WebCore { class CSSStyleDeclaration; class CSSComputedStyleDeclaration; class CSSMutableStyleDeclaration; +class Document; +class HTMLElement; class Node; class Position; +class QualifiedName; class RenderStyle; +class StyledElement; + +enum TriState { FalseTriState, TrueTriState, MixedTriState }; class EditingStyle : public RefCounted<EditingStyle> { public: enum PropertiesToInclude { AllProperties, OnlyInheritableProperties }; enum ShouldPreserveWritingDirection { PreserveWritingDirection, DoNotPreserveWritingDirection }; + enum ShouldExtractMatchingStyle { ExtractMatchingStyle, DoNotExtractMatchingStyle }; static float NoFontDelta; static PassRefPtr<EditingStyle> create() @@ -71,6 +81,11 @@ public: return adoptRef(new EditingStyle(style)); } + static PassRefPtr<EditingStyle> create(int propertyID, const String& value) + { + return adoptRef(new EditingStyle(propertyID, value)); + } + ~EditingStyle(); CSSMutableStyleDeclaration* style() { return m_mutableStyle.get(); } @@ -86,28 +101,48 @@ public: void removeStyleAddedByNode(Node*); void removeStyleConflictingWithStyleOfNode(Node*); void removeNonEditingProperties(); + void collapseTextDecorationProperties(); + enum ShouldIgnoreTextOnlyProperties { IgnoreTextOnlyProperties, DoNotIgnoreTextOnlyProperties }; + TriState triStateOfStyle(CSSStyleDeclaration*, ShouldIgnoreTextOnlyProperties = DoNotIgnoreTextOnlyProperties) const; + bool conflictsWithInlineStyleOfElement(StyledElement* element) const { return conflictsWithInlineStyleOfElement(element, 0, 0); } + bool conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>& conflictingProperties) const + { + return conflictsWithInlineStyleOfElement(element, extractedStyle, &conflictingProperties); + } + bool conflictsWithImplicitStyleOfElement(HTMLElement*, EditingStyle* extractedStyle = 0, ShouldExtractMatchingStyle = DoNotExtractMatchingStyle) const; + bool conflictsWithImplicitStyleOfAttributes(HTMLElement*) const; + bool extractConflictingImplicitStyleOfAttributes(HTMLElement*, ShouldPreserveWritingDirection, EditingStyle* extractedStyle, + Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle) const; void prepareToApplyAt(const Position&, ShouldPreserveWritingDirection = DoNotPreserveWritingDirection); + void mergeTypingStyle(Document*); + void mergeInlineStyleOfElement(StyledElement*); float fontSizeDelta() const { return m_fontSizeDelta; } bool hasFontSizeDelta() const { return m_fontSizeDelta != NoFontDelta; } + bool shouldUseFixedDefaultFontSize() const { return m_shouldUseFixedDefaultFontSize; } private: EditingStyle(); EditingStyle(Node*, PropertiesToInclude); EditingStyle(const Position&); EditingStyle(const CSSStyleDeclaration*); + EditingStyle(int propertyID, const String& value); void init(Node*, PropertiesToInclude); void removeTextFillAndStrokeColorsIfNeeded(RenderStyle*); + void setProperty(int propertyID, const String& value, bool important = false); void replaceFontSizeByKeywordIfPossible(RenderStyle*, CSSComputedStyleDeclaration*); void extractFontSizeDelta(); + bool conflictsWithInlineStyleOfElement(StyledElement*, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const; + void mergeStyle(CSSMutableStyleDeclaration*); RefPtr<CSSMutableStyleDeclaration> m_mutableStyle; bool m_shouldUseFixedDefaultFontSize; float m_fontSizeDelta; + + friend class HTMLElementEquivalent; + friend class HTMLAttributeEquivalent; }; -PassRefPtr<EditingStyle> editingStyleIncludingTypingStyle(const Position&); - } // namespace WebCore #endif // EditingStyle_h diff --git a/Source/WebCore/editing/Editor.cpp b/Source/WebCore/editing/Editor.cpp index 99624b3..8807965 100644 --- a/Source/WebCore/editing/Editor.cpp +++ b/Source/WebCore/editing/Editor.cpp @@ -64,6 +64,7 @@ #include "NodeList.h" #include "Page.h" #include "Pasteboard.h" +#include "TextCheckerClient.h" #include "TextCheckingHelper.h" #include "RemoveFormatCommand.h" #include "RenderBlock.h" @@ -73,6 +74,7 @@ #include "Settings.h" #include "Sound.h" #include "SpellChecker.h" +#include "SpellingCorrectionCommand.h" #include "Text.h" #include "TextEvent.h" #include "TextIterator.h" @@ -83,11 +85,14 @@ #include "visible_units.h" #include <wtf/UnusedParam.h> #include <wtf/unicode/CharacterNames.h> +#include <wtf/unicode/Unicode.h> namespace WebCore { using namespace std; using namespace HTMLNames; +using namespace WTF; +using namespace Unicode; static inline bool isAmbiguousBoundaryCharacter(UChar character) { @@ -97,19 +102,6 @@ static inline bool isAmbiguousBoundaryCharacter(UChar character) return character == '\'' || character == rightSingleQuotationMark || character == hebrewPunctuationGershayim; } -#if SUPPORT_AUTOCORRECTION_PANEL -static FloatRect boundingBoxForRange(Range* range) -{ - Vector<FloatQuad> textQuads; - range->getBorderAndTextQuads(textQuads); - FloatRect totalBoundingBox; - size_t size = textQuads.size(); - for (size_t i = 0; i< size; ++i) - totalBoundingBox.unite(textQuads[i].boundingBox()); - return totalBoundingBox; -} -#endif // SUPPORT_AUTOCORRECTION_PANEL - static const Vector<DocumentMarker::MarkerType>& markerTypesForAutocorrection() { DEFINE_STATIC_LOCAL(Vector<DocumentMarker::MarkerType>, markerTypesForAutoCorrection, ()); @@ -141,7 +133,7 @@ VisibleSelection Editor::selectionForCommand(Event* event) // If the target is a text control, and the current selection is outside of its shadow tree, // then use the saved selection for that text control. Node* target = event->target()->toNode(); - Node* selectionStart = selection.start().node(); + Node* selectionStart = selection.start().deprecatedNode(); if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) { RefPtr<Range> range; if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField()) @@ -171,6 +163,14 @@ EditorClient* Editor::client() const return 0; } + +TextCheckerClient* Editor::textChecker() const +{ + if (EditorClient* owner = client()) + return owner->textChecker(); + return 0; +} + void Editor::handleKeyboardEvent(KeyboardEvent* event) { if (EditorClient* c = client()) @@ -293,10 +293,10 @@ bool Editor::canDeleteRange(Range* range) const return false; if (range->collapsed(ec)) { - VisiblePosition start(startContainer, range->startOffset(ec), DOWNSTREAM); + VisiblePosition start(Position(startContainer, range->startOffset(ec), Position::PositionIsOffsetInAnchor), DOWNSTREAM); VisiblePosition previous = start.previous(); // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item. - if (previous.isNull() || previous.deepEquivalent().node()->rootEditableElement() != startContainer->rootEditableElement()) + if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer->rootEditableElement()) return false; } return true; @@ -427,8 +427,15 @@ void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, { if (m_frame->selection()->isNone() || !fragment) return; - - applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, selectReplacement, smartReplace, matchStyle)); + + ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting; + if (selectReplacement) + options |= ReplaceSelectionCommand::SelectReplacement; + if (smartReplace) + options |= ReplaceSelectionCommand::SmartReplace; + if (matchStyle) + options |= ReplaceSelectionCommand::MatchStyle; + applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, options, EditActionPaste)); revealSelectionAfterEditingOperation(); Node* nodeToCheck = m_frame->selection()->rootEditableElement(); @@ -536,7 +543,7 @@ void Editor::respondToChangedSelection(const VisibleSelection& oldSelection) size_t markerCount = markers.size(); for (size_t i = 0; i < markerCount; ++i) { const DocumentMarker& marker = markers[i]; - if (((marker.type == DocumentMarker::CorrectionIndicator && marker.description.length()) || marker.type == DocumentMarker::Spelling) && static_cast<int>(marker.endOffset) == endOffset) { + if (((marker.type == DocumentMarker::Replacement && !marker.description.isNull()) || marker.type == DocumentMarker::Spelling) && 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()) { @@ -558,7 +565,7 @@ void Editor::respondToChangedSelection(const VisibleSelection& oldSelection) void Editor::respondToChangedContents(const VisibleSelection& endingSelection) { if (AXObjectCache::accessibilityEnabled()) { - Node* node = endingSelection.start().node(); + Node* node = endingSelection.start().deprecatedNode(); if (node) m_frame->document()->axObjectCache()->postNotification(node->renderer(), AXObjectCache::AXValueChanged, false); } @@ -596,7 +603,7 @@ const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const const SimpleFontData* font = 0; RefPtr<Range> range = m_frame->selection()->toNormalizedRange(); - Node* startNode = range->editingStartPosition().node(); + Node* startNode = range->editingStartPosition().deprecatedNode(); if (startNode) { Node* pastEnd = range->pastLastNode(); // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one @@ -631,7 +638,7 @@ WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbe Position position = m_frame->selection()->selection().start().downstream(); - Node* node = position.node(); + Node* node = position.deprecatedNode(); if (!node) return NaturalWritingDirection; @@ -663,7 +670,7 @@ WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbe hasNestedOrMultipleEmbeddings = false; return direction; } - node = m_frame->selection()->selection().visibleStart().deepEquivalent().node(); + node = m_frame->selection()->selection().visibleStart().deepEquivalent().deprecatedNode(); } // The selection is either a caret with no typing attributes or a range in which no embedding is added, so just use the start position @@ -702,7 +709,7 @@ WritingDirection Editor::textDirectionForSelection(bool& hasNestedOrMultipleEmbe return NaturalWritingDirection; // In the range case, make sure that the embedding element persists until the end of the range. - if (m_frame->selection()->isRange() && !end.node()->isDescendantOf(node)) + if (m_frame->selection()->isRange() && !end.deprecatedNode()->isDescendantOf(node)) return NaturalWritingDirection; foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection; @@ -718,12 +725,12 @@ bool Editor::hasBidiSelection() const Node* startNode; if (m_frame->selection()->isRange()) { - startNode = m_frame->selection()->selection().start().downstream().node(); - Node* endNode = m_frame->selection()->selection().end().upstream().node(); + startNode = m_frame->selection()->selection().start().downstream().deprecatedNode(); + Node* endNode = m_frame->selection()->selection().end().upstream().deprecatedNode(); if (enclosingBlock(startNode) != enclosingBlock(endNode)) return false; } else - startNode = m_frame->selection()->selection().visibleStart().deepEquivalent().node(); + startNode = m_frame->selection()->selection().visibleStart().deepEquivalent().deprecatedNode(); RenderObject* renderer = startNode->renderer(); while (renderer && !renderer->isRenderBlock()) @@ -938,68 +945,42 @@ void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditActi applyParagraphStyle(style, editingAction); } -bool Editor::clientIsEditable() const +bool Editor::selectionStartHasStyle(int propertyID, const String& value) const { - return client() && client()->isEditable(); + RefPtr<EditingStyle> style = EditingStyle::create(propertyID, value); + RefPtr<EditingStyle> selectionStyle = selectionStartStyle(); + if (!selectionStyle || !selectionStyle->style()) + return false; + return style->triStateOfStyle(selectionStyle->style()) == TrueTriState; } -// CSS properties that only has a visual difference when applied to text. -static const int textOnlyProperties[] = { - CSSPropertyTextDecoration, - CSSPropertyWebkitTextDecorationsInEffect, - CSSPropertyFontStyle, - CSSPropertyFontWeight, - CSSPropertyColor, -}; - -static TriState triStateOfStyle(CSSStyleDeclaration* desiredStyle, CSSStyleDeclaration* styleToCompare, bool ignoreTextOnlyProperties = false) +TriState Editor::selectionHasStyle(int propertyID, const String& value) const { - RefPtr<CSSMutableStyleDeclaration> diff = getPropertiesNotIn(desiredStyle, styleToCompare); - - if (ignoreTextOnlyProperties) - diff->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties)); - - if (!diff->length()) - return TrueTriState; - if (diff->length() == desiredStyle->length()) + RefPtr<EditingStyle> style = EditingStyle::create(propertyID, value); + if (!m_frame->selection()->isCaretOrRange()) return FalseTriState; - return MixedTriState; -} -bool Editor::selectionStartHasStyle(CSSStyleDeclaration* style) const -{ - bool shouldUseFixedFontDefaultSize; - RefPtr<CSSMutableStyleDeclaration> selectionStyle = selectionComputedStyle(shouldUseFixedFontDefaultSize); - if (!selectionStyle) - return false; - return triStateOfStyle(style, selectionStyle.get()) == TrueTriState; -} + if (m_frame->selection()->isCaret()) { + RefPtr<EditingStyle> selectionStyle = selectionStartStyle(); + if (!selectionStyle || !selectionStyle->style()) + return FalseTriState; + return style->triStateOfStyle(selectionStyle->style()); + } -TriState Editor::selectionHasStyle(CSSStyleDeclaration* style) const -{ TriState state = FalseTriState; - - if (!m_frame->selection()->isRange()) { - bool shouldUseFixedFontDefaultSize; - RefPtr<CSSMutableStyleDeclaration> selectionStyle = selectionComputedStyle(shouldUseFixedFontDefaultSize); - if (!selectionStyle) - return FalseTriState; - state = triStateOfStyle(style, selectionStyle.get()); - } else { - for (Node* node = m_frame->selection()->start().node(); node; node = node->traverseNextNode()) { - RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node); - if (nodeStyle) { - TriState nodeState = triStateOfStyle(style, nodeStyle.get(), !node->isTextNode()); - if (node == m_frame->selection()->start().node()) - state = nodeState; - else if (state != nodeState && node->isTextNode()) { - state = MixedTriState; - break; - } - } - if (node == m_frame->selection()->end().node()) + for (Node* node = m_frame->selection()->start().deprecatedNode(); node; node = node->traverseNextNode()) { + RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node); + if (nodeStyle) { + TriState nodeState = style->triStateOfStyle(nodeStyle.get(), node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties); + if (node == m_frame->selection()->start().deprecatedNode()) + state = nodeState; + else if (state != nodeState && node->isTextNode()) { + state = MixedTriState; break; + } } + if (node == m_frame->selection()->end().deprecatedNode()) + break; } return state; @@ -1023,33 +1004,30 @@ static bool hasTransparentBackgroundColor(CSSStyleDeclaration* style) String Editor::selectionStartCSSPropertyValue(int propertyID) { - bool shouldUseFixedFontDefaultSize = false; - RefPtr<CSSMutableStyleDeclaration> selectionStyle = selectionComputedStyle(shouldUseFixedFontDefaultSize); - if (!selectionStyle) + RefPtr<EditingStyle> selectionStyle = selectionStartStyle(); + if (!selectionStyle || !selectionStyle->style()) return String(); - String value = selectionStyle->getPropertyValue(propertyID); + String value = selectionStyle->style()->getPropertyValue(propertyID); // If background color is transparent, traverse parent nodes until we hit a different value or document root // Also, if the selection is a range, ignore the background color at the start of selection, // and find the background color of the common ancestor. - if (propertyID == CSSPropertyBackgroundColor && (m_frame->selection()->isRange() || hasTransparentBackgroundColor(selectionStyle.get()))) { + if (propertyID == CSSPropertyBackgroundColor && (m_frame->selection()->isRange() || hasTransparentBackgroundColor(selectionStyle->style()))) { RefPtr<Range> range(m_frame->selection()->toNormalizedRange()); ExceptionCode ec = 0; for (Node* ancestor = range->commonAncestorContainer(ec); ancestor; ancestor = ancestor->parentNode()) { - selectionStyle = computedStyle(ancestor)->copy(); - if (!hasTransparentBackgroundColor(selectionStyle.get())) { - value = selectionStyle->getPropertyValue(CSSPropertyBackgroundColor); - break; - } + RefPtr<CSSComputedStyleDeclaration> ancestorStyle = computedStyle(ancestor); + if (!hasTransparentBackgroundColor(ancestorStyle.get())) + return ancestorStyle->getPropertyValue(CSSPropertyBackgroundColor); } } if (propertyID == CSSPropertyFontSize) { - RefPtr<CSSValue> cssValue = selectionStyle->getPropertyCSSValue(CSSPropertyFontSize); + RefPtr<CSSValue> cssValue = selectionStyle->style()->getPropertyCSSValue(CSSPropertyFontSize); if (cssValue->isPrimitiveValue()) { value = String::number(legacyFontSizeFromCSSValue(m_frame->document(), static_cast<CSSPrimitiveValue*>(cssValue.get()), - shouldUseFixedFontDefaultSize, AlwaysUseLegacyFontSize)); + selectionStyle->shouldUseFixedDefaultFontSize(), AlwaysUseLegacyFontSize)); } } @@ -1079,17 +1057,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()); #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 (cmd->isTopLevelCommand() && !inSameParagraph(cmd->startingSelection().start(), newSelection.end())) + if (cmd->isTopLevelCommand() && !cmd->shouldRetainAutocorrectionIndicator()) m_frame->document()->markers()->removeMarkers(DocumentMarker::CorrectionIndicator); #endif @@ -1124,7 +1098,7 @@ void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd) m_lastEditCommand = 0; if (client()) client()->registerCommandForRedo(cmd); - respondToChangedContents(newSelection); + respondToChangedContents(newSelection); } void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd) @@ -1139,7 +1113,7 @@ void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd) m_lastEditCommand = 0; if (client()) client()->registerCommandForUndo(cmd); - respondToChangedContents(newSelection); + respondToChangedContents(newSelection); } Editor::Editor(Frame* frame) @@ -1150,7 +1124,7 @@ Editor::Editor(Frame* frame) // This is off by default, since most editors want this behavior (this matches IE but not FF). , m_shouldStyleWithCSS(false) , m_killRing(adoptPtr(new KillRing)) - , m_spellChecker(new SpellChecker(frame, frame->page() ? frame->page()->editorClient() : 0)) + , m_spellChecker(new SpellChecker(frame, frame->page() ? frame->page()->editorClient()->textChecker() : 0)) , m_correctionPanelTimer(this, &Editor::correctionPanelTimerFired) , m_areMarkedTextMatchesHighlighted(false) { @@ -1193,22 +1167,37 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn if (!shouldInsertText(text, range.get(), EditorInsertActionTyped)) return true; +#if REMOVE_MARKERS_UPON_EDITING + if (!text.isEmpty()) + removeSpellAndCorrectionMarkersFromWordsToBeEdited(isSpaceOrNewline(text[0])); +#endif + + bool shouldConsiderApplyingAutocorrection = false; if (text == " " || text == "\t") - applyAutocorrectionBeforeTypingIfAppropriate(); + shouldConsiderApplyingAutocorrection = true; + + if (text.length() == 1 && isPunct(text[0]) && !isAmbiguousBoundaryCharacter(text[0])) + shouldConsiderApplyingAutocorrection = true; + + bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && applyAutocorrectionBeforeTypingIfAppropriate(); // Get the selection to use for the event that triggered this insertText. // If the event handler changed the selection, we may want to use a different selection // that is contained in the event target. selection = selectionForCommand(triggeringEvent); if (selection.isContentEditable()) { - if (Node* selectionStart = selection.start().node()) { + if (Node* selectionStart = selection.start().deprecatedNode()) { RefPtr<Document> document = selectionStart->document(); - - // Insert the text - TypingCommand::insertText(document.get(), text, selection, selectInsertedText, - triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone); - // Reveal the current selection + // Insert the text + TypingCommand::TypingCommandOptions options = 0; + if (selectInsertedText) + options |= TypingCommand::SelectInsertedText; + if (autocorrectionWasApplied) + options |= TypingCommand::RetainAutocorrectionIndicator; + TypingCommand::insertText(document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone); + + // Reveal the current selection if (Frame* editedFrame = document->frame()) if (Page* page = editedFrame->page()) page->focusController()->focusedOrMainFrame()->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); @@ -1226,10 +1215,10 @@ bool Editor::insertLineBreak() if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped)) return true; - applyAutocorrectionBeforeTypingIfAppropriate(); - - TypingCommand::insertLineBreak(m_frame->document()); + bool autocorrectionIsApplied = applyAutocorrectionBeforeTypingIfAppropriate(); + TypingCommand::insertLineBreak(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0); revealSelectionAfterEditingOperation(); + return true; } @@ -1244,10 +1233,10 @@ bool Editor::insertParagraphSeparator() if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped)) return true; - applyAutocorrectionBeforeTypingIfAppropriate(); - - TypingCommand::insertParagraphSeparator(m_frame->document()); + bool autocorrectionIsApplied = applyAutocorrectionBeforeTypingIfAppropriate(); + TypingCommand::insertParagraphSeparator(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0); revealSelectionAfterEditingOperation(); + return true; } @@ -1264,7 +1253,7 @@ void Editor::cut() #if REMOVE_MARKERS_UPON_EDITING removeSpellAndCorrectionMarkersFromWordsToBeEdited(true); #endif - if (isNodeInTextFormControl(m_frame->selection()->start().node())) + if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode())) Pasteboard::generalPasteboard()->writePlainText(selectedText()); else Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame); @@ -1282,7 +1271,7 @@ void Editor::copy() return; } - if (isNodeInTextFormControl(m_frame->selection()->start().node())) + if (isNodeInTextFormControl(m_frame->selection()->start().deprecatedNode())) Pasteboard::generalPasteboard()->writePlainText(selectedText()); else { Document* document = m_frame->document(); @@ -1578,7 +1567,7 @@ void Editor::selectComposition() // See <http://bugs.webkit.org/show_bug.cgi?id=15781> VisibleSelection selection; selection.setWithoutValidation(range->startPosition(), range->endPosition()); - m_frame->selection()->setSelection(selection, false, false); + m_frame->selection()->setSelection(selection, 0); } void Editor::confirmComposition() @@ -1636,7 +1625,7 @@ void Editor::confirmComposition(const String& text, bool preserveSelection) insertTextForConfirmedComposition(text); if (preserveSelection) { - m_frame->selection()->setSelection(oldSelection, false, false); + m_frame->selection()->setSelection(oldSelection, 0); // An open typing command that disagrees about current selection would cause issues with typing later on. TypingCommand::closeTyping(m_lastEditCommand.get()); } @@ -1707,9 +1696,9 @@ void Editor::setComposition(const String& text, const Vector<CompositionUnderlin // Find out what node has the composition now. Position base = m_frame->selection()->base().downstream(); Position extent = m_frame->selection()->extent(); - Node* baseNode = base.node(); + Node* baseNode = base.deprecatedNode(); unsigned baseOffset = base.deprecatedEditingOffset(); - Node* extentNode = extent.node(); + Node* extentNode = extent.deprecatedNode(); unsigned extentOffset = extent.deprecatedEditingOffset(); if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) { @@ -1746,7 +1735,7 @@ void Editor::ignoreSpelling() String text = selectedText(); ASSERT(text.length()); - client()->ignoreWordInSpellDocument(text); + textChecker()->ignoreWordInSpellDocument(text); } void Editor::learnSpelling() @@ -1754,12 +1743,15 @@ void Editor::learnSpelling() if (!client()) return; - // FIXME: We don't call this on the Mac, and it should remove misspelling markers around the - // learned word, see <rdar://problem/5396072>. + // FIXME: On Mac OS X, when use "learn" button on "Spelling and Grammar" panel, we don't call this function. It should remove misspelling markers around the learned word, see <rdar://problem/5396072>. + + RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange(); + if (selectedRange) + frame()->document()->markers()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling); String text = selectedText(); ASSERT(text.length()); - client()->learnWord(text); + textChecker()->learnWord(text); } void Editor::advanceToNextMisspelling(bool startBeforeSelection) @@ -1775,7 +1767,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document())); bool startedWithSelection = false; - if (selection.start().node()) { + if (selection.start().deprecatedNode()) { startedWithSelection = true; if (startBeforeSelection) { VisiblePosition start(selection.visibleStart()); @@ -1799,7 +1791,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection) return; Position rangeCompliantPosition = position.parentAnchoredEquivalent(); - spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.deprecatedEditingOffset(), ec); + spellingSearchRange->setStart(rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), ec); startedWithSelection = false; // won't need to wrap } @@ -1949,7 +1941,7 @@ bool Editor::isSelectionMisspelled() int misspellingLocation = -1; int misspellingLength = 0; - client()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength); + textChecker()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength); // The selection only counts as misspelled if the selected text is exactly one misspelled word if (misspellingLength != length) @@ -1993,7 +1985,7 @@ Vector<String> Editor::guessesForMisspelledSelection() Vector<String> guesses; if (client()) - client()->getGuessesForWord(selectedString, String(), guesses); + textChecker()->getGuessesForWord(selectedString, String(), guesses); return guesses; } @@ -2110,6 +2102,7 @@ void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, } else { markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get()); } + #else UNUSED_PARAM(selectionAfterTyping); if (!isContinuousSpellCheckingEnabled()) @@ -2125,7 +2118,7 @@ void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, // Get the misspelled word. const String misspelledWord = plainText(misspellingRange.get()); - String autocorrectedString = client()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord); + String autocorrectedString = textChecker()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord); // If autocorrected word is non empty, replace the misspelled word by this word. if (!autocorrectedString.isEmpty()) { @@ -2204,7 +2197,7 @@ bool Editor::isSpellCheckingEnabledFor(Node* node) const bool Editor::isSpellCheckingEnabledInFocusedNode() const { - return isSpellCheckingEnabledFor(m_frame->selection()->start().node()); + return isSpellCheckingEnabledFor(m_frame->selection()->start().deprecatedNode()); } void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange) @@ -2260,7 +2253,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh if (shouldMarkGrammar ? (spellingParagraph.isRangeEmpty() && grammarParagraph.isEmpty()) : spellingParagraph.isEmpty()) return; - if (shouldPerformReplacement) { + if (shouldPerformReplacement || shouldMarkSpelling) { if (m_frame->selection()->selectionType() == VisibleSelection::CaretSelection) { // Attempt to save the caret position so we can restore it later if needed Position caretPosition = m_frame->selection()->end(); @@ -2296,7 +2289,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh if (shouldMarkSpelling && isAutomaticSpellingCorrectionEnabled()) checkingTypes |= TextCheckingTypeCorrection; } - client()->checkTextOfParagraph(paragraph.textCharacters(), paragraph.textLength(), checkingTypes, results); + textChecker()->checkTextOfParagraph(paragraph.textCharacters(), paragraph.textLength(), checkingTypes, results); #if SUPPORT_AUTOCORRECTION_PANEL // If this checking is only for showing correction panel, we shouldn't bother to mark misspellings. @@ -2345,7 +2338,7 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation. ASSERT(resultLength > 0 && resultLocation >= 0); - if (shouldShowCorrectionPanel && resultLocation + resultLength < spellingRangeEndOffset) + if (shouldShowCorrectionPanel && (resultLocation + resultLength < spellingRangeEndOffset || result->type != TextCheckingTypeCorrection)) continue; int replacementLength = result->replacement.length(); @@ -2360,10 +2353,10 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh // adding links should be done only immediately after they are typed if (result->type == TextCheckingTypeLink && selectionOffset > resultLocation + resultLength + 1) - doReplacement = false; + continue; // Don't correct spelling in an already-corrected word. - if (doReplacement && result->type == TextCheckingTypeCorrection) { + if (result->type == TextCheckingTypeCorrection) { Node* node = rangeToReplace->startContainer(); int startOffset = rangeToReplace->startOffset(); int endOffset = startOffset + replacementLength; @@ -2379,58 +2372,71 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingOptions textCh break; } } - if (doReplacement && !shouldShowCorrectionPanel && selectionToReplace != m_frame->selection()->selection()) { - if (m_frame->selection()->shouldChangeSelection(selectionToReplace)) { - m_frame->selection()->setSelection(selectionToReplace); - selectionChanged = true; - } else { - doReplacement = false; + + if (!doReplacement) + continue; + +#if SUPPORT_AUTOCORRECTION_PANEL + if (shouldShowCorrectionPanel) { + if (resultLocation + resultLength == spellingRangeEndOffset) { + // We only show the correction panel on the last word. + FloatRect boundingBox = windowRectForRange(rangeToReplace.get()); + if (boundingBox.isEmpty()) + break; + m_correctionPanelInfo.rangeToBeReplaced = rangeToReplace; + m_correctionPanelInfo.replacedString = plainText(rangeToReplace.get()); + m_correctionPanelInfo.replacementString = result->replacement; + m_correctionPanelInfo.isActive = true; + client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, result->replacement, Vector<String>(), this); + break; } + // If this function is called for showing correction panel, we ignore other correction or replacement. + continue; } +#endif - String replacedString; - if (doReplacement) { - if (result->type == TextCheckingTypeLink) { - restoreSelectionAfterChange = false; - if (canEditRichly()) - applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement)); - } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) { - if (result->type == TextCheckingTypeCorrection) - replacedString = plainText(rangeToReplace.get()); -#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; - rangeToReplace->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.rangeToBeReplaced = rangeToReplace; - m_correctionPanelInfo.replacedString = replacedString; - m_correctionPanelInfo.replacementString = result->replacement; - m_correctionPanelInfo.isActive = true; - client()->showCorrectionPanel(m_correctionPanelInfo.panelType, totalBoundingBox, m_correctionPanelInfo.replacedString, result->replacement, Vector<String>(), this); - doReplacement = false; - } + if (selectionToReplace != m_frame->selection()->selection()) { + if (!m_frame->selection()->shouldChangeSelection(selectionToReplace)) + continue; + } + + if (result->type == TextCheckingTypeLink) { + m_frame->selection()->setSelection(selectionToReplace); + selectionChanged = true; + restoreSelectionAfterChange = false; + if (canEditRichly()) + applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement)); + } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) { + String replacedString; + if (result->type == TextCheckingTypeCorrection) + replacedString = plainText(rangeToReplace.get()); + + bool useSpellingCorrectionCommand = false; +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + if (result->type == TextCheckingTypeCorrection) + useSpellingCorrectionCommand = true; #endif - if (doReplacement) { - replaceSelectionWithText(result->replacement, false, false); - offsetDueToReplacement += replacementLength - resultLength; - if (resultLocation < selectionOffset) { - selectionOffset += replacementLength - resultLength; - if (ambiguousBoundaryOffset >= 0) - ambiguousBoundaryOffset = selectionOffset - 1; - } - - if (result->type == TextCheckingTypeCorrection) { - // Add a marker so that corrections can easily be undone and won't be re-corrected. - RefPtr<Range> replacedRange = paragraph.subrange(resultLocation, replacementLength); - replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::Replacement, replacedString); - replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::CorrectionIndicator, replacedString); - replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::SpellCheckingExemption); - } - } + if (useSpellingCorrectionCommand) + applyCommand(SpellingCorrectionCommand::create(rangeToReplace, result->replacement)); + else { + m_frame->selection()->setSelection(selectionToReplace); + replaceSelectionWithText(result->replacement, false, false); + } + + selectionChanged = true; + offsetDueToReplacement += replacementLength - resultLength; + if (resultLocation < selectionOffset) { + selectionOffset += replacementLength - resultLength; + if (ambiguousBoundaryOffset >= 0) + ambiguousBoundaryOffset = selectionOffset - 1; + } + + if (result->type == TextCheckingTypeCorrection) { + // Add a marker so that corrections can easily be undone and won't be re-corrected. + RefPtr<Range> replacedRange = paragraph.subrange(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::SpellCheckingExemption); } } } @@ -2461,10 +2467,17 @@ void Editor::changeBackToReplacedString(const String& replacedString) if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted)) return; +#if SUPPORT_AUTOCORRECTION_PANEL + String replacement = plainText(selection.get()); + client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, replacedString, replacement); +#endif TextCheckingParagraph paragraph(selection); replaceSelectionWithText(replacedString, false, false); RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length()); changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::Replacement, String()); +#if SUPPORT_AUTOCORRECTION_PANEL + changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::SpellCheckingExemption); +#endif } #endif @@ -2502,7 +2515,9 @@ void Editor::correctionPanelTimerFired(Timer<Editor>*) case CorrectionPanelInfo::PanelTypeReversion: { m_correctionPanelInfo.isActive = true; m_correctionPanelInfo.replacedString = plainText(m_correctionPanelInfo.rangeToBeReplaced.get()); - client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBoxForRange(m_correctionPanelInfo.rangeToBeReplaced.get()), m_correctionPanelInfo.replacedString, m_correctionPanelInfo.replacementString, Vector<String>(), this); + FloatRect boundingBox = windowRectForRange(m_correctionPanelInfo.rangeToBeReplaced.get()); + if (!boundingBox.isEmpty()) + client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, m_correctionPanelInfo.replacementString, Vector<String>(), this); } break; case CorrectionPanelInfo::PanelTypeSpellingSuggestions: { @@ -2510,7 +2525,7 @@ void Editor::correctionPanelTimerFired(Timer<Editor>*) break; String paragraphText = plainText(TextCheckingParagraph(m_correctionPanelInfo.rangeToBeReplaced).paragraphRange().get()); Vector<String> suggestions; - client()->getGuessesForWord(m_correctionPanelInfo.replacedString, paragraphText, suggestions); + textChecker()->getGuessesForWord(m_correctionPanelInfo.replacedString, paragraphText, suggestions); if (suggestions.isEmpty()) { m_correctionPanelInfo.rangeToBeReplaced.clear(); break; @@ -2518,7 +2533,9 @@ void Editor::correctionPanelTimerFired(Timer<Editor>*) String topSuggestion = suggestions.first(); suggestions.remove(0); m_correctionPanelInfo.isActive = true; - client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBoxForRange(m_correctionPanelInfo.rangeToBeReplaced.get()), m_correctionPanelInfo.replacedString, topSuggestion, suggestions, this); + FloatRect boundingBox = windowRectForRange(m_correctionPanelInfo.rangeToBeReplaced.get()); + if (!boundingBox.isEmpty()) + client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, topSuggestion, suggestions, this); } break; } @@ -2640,20 +2657,22 @@ void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemove VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary); VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary); - // This can be the case if the end of selection is at the end of document. - if (endOfLastWord.deepEquivalent().anchorType() != Position::PositionIsOffsetInAnchor) { - startOfLastWord = startOfWord(frame()->selection()->selection().start(), LeftWordIfOnBoundary); - endOfLastWord = endOfWord(frame()->selection()->selection().start(), LeftWordIfOnBoundary); + if (startOfFirstWord.isNull()) { + startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary); + endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary); + } + + if (endOfLastWord.isNull()) { + startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary); + endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary); } // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection, // we choose next word as the first word. if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) { startOfFirstWord = nextWordPosition(startOfFirstWord); - if (startOfFirstWord == endOfSelection) - return; endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary); - if (endOfFirstWord.deepEquivalent().anchorType() != Position::PositionIsOffsetInAnchor) + if (startOfFirstWord == endOfSelection) return; } @@ -2662,10 +2681,13 @@ void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemove if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) { startOfLastWord = previousWordPosition(startOfLastWord); endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary); - if (endOfLastWord == startOfFirstWord) + if (endOfLastWord == startOfSelection) return; } + if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull()) + return; + // Now we remove markers on everything between startOfFirstWord and endOfLastWord. // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant @@ -2676,10 +2698,12 @@ void Editor::removeSpellAndCorrectionMarkersFromWordsToBeEdited(bool doNotRemove RefPtr<Range> wordRange = Range::create(document, startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent()); document->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption, DocumentMarkerController::RemovePartiallyOverlappingMarker); + document->markers()->clearDescriptionOnMarkersIntersectingRange(wordRange.get(), DocumentMarker::Replacement); } void Editor::applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>& markerTypesToAdd) { +#if SUPPORT_AUTOCORRECTION_PANEL if (!m_correctionPanelInfo.rangeToBeReplaced) return; @@ -2711,36 +2735,66 @@ void Editor::applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>& // Clone the range, since the caller of this method may want to keep the original range around. RefPtr<Range> rangeToBeReplaced = m_correctionPanelInfo.rangeToBeReplaced->cloneRange(ec); - VisibleSelection selectionToReplace(rangeToBeReplaced.get(), DOWNSTREAM); - if (m_frame->selection()->shouldChangeSelection(selectionToReplace)) { - m_frame->selection()->setSelection(selectionToReplace); - replaceSelectionWithText(m_correctionPanelInfo.replacementString, false, false); - setEnd(paragraphRangeContainingCorrection.get(), m_frame->selection()->selection().start()); - RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionPanelInfo.replacementString.length()); - DocumentMarkerController* markers = replacementRange->startContainer()->document()->markers(); - size_t size = markerTypesToAdd.size(); - for (size_t i = 0; i < size; ++i) { - if (m_correctionPanelInfo.panelType == CorrectionPanelInfo::PanelTypeReversion) - markers->addMarker(replacementRange.get(), markerTypesToAdd[i]); - else - markers->addMarker(replacementRange.get(), markerTypesToAdd[i], m_correctionPanelInfo.replacedString); - } + applyCommand(SpellingCorrectionCommand::create(rangeToBeReplaced, m_correctionPanelInfo.replacementString)); + setEnd(paragraphRangeContainingCorrection.get(), m_frame->selection()->selection().start()); + RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionPanelInfo.replacementString.length()); + String newText = plainText(replacementRange.get()); + + // Check to see if replacement succeeded. + if (newText != m_correctionPanelInfo.replacementString) + return; + + DocumentMarkerController* markers = replacementRange->startContainer()->document()->markers(); + size_t size = markerTypesToAdd.size(); + for (size_t i = 0; i < size; ++i) { + if (m_correctionPanelInfo.panelType == CorrectionPanelInfo::PanelTypeReversion) + markers->addMarker(replacementRange.get(), markerTypesToAdd[i]); + else + markers->addMarker(replacementRange.get(), markerTypesToAdd[i], m_correctionPanelInfo.replacedString); } +#else // SUPPORT_AUTOCORRECTION_PANEL + UNUSED_PARAM(markerTypesToAdd); +#endif // SUPPORT_AUTOCORRECTION_PANEL } -void Editor::applyAutocorrectionBeforeTypingIfAppropriate() +bool Editor::applyAutocorrectionBeforeTypingIfAppropriate() { if (!m_correctionPanelInfo.rangeToBeReplaced || !m_correctionPanelInfo.isActive) - return; + return false; if (m_correctionPanelInfo.panelType != CorrectionPanelInfo::PanelTypeCorrection) - return; + return false; Position caretPosition = m_frame->selection()->selection().start(); + if (m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition) { + dismissCorrectionPanel(ReasonForDismissingCorrectionPanelAccepted); + return true; + } + // Pending correction should always be where caret is. But in case this is not always true, we still want to dismiss the panel without accepting the correction. ASSERT(m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition); - dismissCorrectionPanel(m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition ? ReasonForDismissingCorrectionPanelAccepted : ReasonForDismissingCorrectionPanelIgnored); + dismissCorrectionPanel(ReasonForDismissingCorrectionPanelIgnored); + return false; +} + +void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction) +{ +#if SUPPORT_AUTOCORRECTION_PANEL + client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, corrected, correction); + m_frame->document()->updateLayout(); + m_frame->selection()->setSelection(selectionOfCorrected, SelectionController::CloseTyping | SelectionController::ClearTypingStyle | SelectionController::SpellCorrectionTriggered); + RefPtr<Range> range = Range::create(m_frame->document(), m_frame->selection()->selection().start(), m_frame->selection()->selection().end()); + + DocumentMarkerController* markers = m_frame->document()->markers(); + markers->removeMarkers(range.get(), DocumentMarker::Spelling); + markers->addMarker(range.get(), DocumentMarker::Replacement); + markers->addMarker(range.get(), DocumentMarker::SpellCheckingExemption); +#else // SUPPORT_AUTOCORRECTION_PANEL + UNUSED_PARAM(selectionOfCorrected); + UNUSED_PARAM(corrected); + UNUSED_PARAM(correction); +#endif // SUPPORT_AUTOCORRECTION_PANEL } PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint) @@ -2794,10 +2848,10 @@ bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selecti if (!m_compositionNode) return false; Position start = m_frame->selection()->start(); - if (start.node() != m_compositionNode) + if (start.deprecatedNode() != m_compositionNode) return false; Position end = m_frame->selection()->end(); - if (end.node() != m_compositionNode) + if (end.deprecatedNode() != m_compositionNode) return false; if (static_cast<unsigned>(start.deprecatedEditingOffset()) < m_compositionStart) @@ -3014,8 +3068,14 @@ void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, b // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls. // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid bool selectionDidNotChangeDOMPosition = newSelection == m_frame->selection()->selection(); - if (selectionDidNotChangeDOMPosition || m_frame->selection()->shouldChangeSelection(newSelection)) - m_frame->selection()->setSelection(newSelection, closeTyping, clearTypingStyle); + if (selectionDidNotChangeDOMPosition || m_frame->selection()->shouldChangeSelection(newSelection)) { + SelectionController::SetSelectionOptions options = 0; + if (closeTyping) + options |= SelectionController::CloseTyping; + if (clearTypingStyle) + options |= SelectionController::ClearTypingStyle; + m_frame->selection()->setSelection(newSelection, options); + } // Some editing operations change the selection visually without affecting its position within the DOM. // For example when you press return in the following (the caret is marked by ^): @@ -3047,7 +3107,7 @@ IntRect Editor::firstRectForRange(Range* range) const return IntRect(); startPosition.getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset); - RenderObject* startRenderer = startPosition.node()->renderer(); + RenderObject* startRenderer = startPosition.deprecatedNode()->renderer(); ASSERT(startRenderer); IntRect startCaretRect = startRenderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine); if (startCaretRect != IntRect()) @@ -3060,7 +3120,7 @@ IntRect Editor::firstRectForRange(Range* range) const return IntRect(); endPosition.getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset); - RenderObject* endRenderer = endPosition.node()->renderer(); + RenderObject* endRenderer = endPosition.deprecatedNode()->renderer(); ASSERT(endRenderer); IntRect endCaretRect = endRenderer->localCaretRect(endInlineBox, endCaretOffset); if (endCaretRect != IntRect()) @@ -3112,7 +3172,7 @@ void Editor::computeAndSetTypingStyle(CSSStyleDeclaration* style, EditAction edi m_frame->selection()->setTypingStyle(typingStyle); } -PassRefPtr<CSSMutableStyleDeclaration> Editor::selectionComputedStyle(bool& shouldUseFixedFontDefaultSize) const +PassRefPtr<EditingStyle> Editor::selectionStartStyle() const { if (m_frame->selection()->isNone()) return 0; @@ -3132,19 +3192,9 @@ PassRefPtr<CSSMutableStyleDeclaration> Editor::selectionComputedStyle(bool& shou if (!element) return 0; - RefPtr<Element> styleElement = element; - RefPtr<CSSComputedStyleDeclaration> style = computedStyle(styleElement.release()); - RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->copy(); - shouldUseFixedFontDefaultSize = style->useFixedFontDefaultSize(); - - if (!m_frame->selection()->typingStyle()) - return mutableStyle; - - RefPtr<EditingStyle> typingStyle = m_frame->selection()->typingStyle()->copy(); - typingStyle->prepareToApplyAt(position); - mutableStyle->merge(typingStyle->style()); - - return mutableStyle; + RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties); + style->mergeTypingStyle(m_frame->document()); + return style; } void Editor::textFieldDidBeginEditing(Element* e) @@ -3220,12 +3270,12 @@ RenderStyle* Editor::styleForSelectionStart(Node *&nodeToRemove) const Position position = m_frame->selection()->selection().visibleStart().deepEquivalent(); if (!position.isCandidate()) return 0; - if (!position.node()) + if (!position.deprecatedNode()) return 0; RefPtr<EditingStyle> typingStyle = m_frame->selection()->typingStyle(); if (!typingStyle || !typingStyle->style()) - return position.node()->renderer()->style(); + return position.deprecatedNode()->renderer()->style(); RefPtr<Element> styleElement = m_frame->document()->createElement(spanTag, false); @@ -3237,7 +3287,7 @@ RenderStyle* Editor::styleForSelectionStart(Node *&nodeToRemove) const styleElement->appendChild(m_frame->document()->createEditingTextNode(""), ec); ASSERT(!ec); - position.node()->parentNode()->appendChild(styleElement, ec); + position.deprecatedNode()->parentNode()->appendChild(styleElement, ec); ASSERT(!ec); nodeToRemove = styleElement.get(); @@ -3439,7 +3489,7 @@ void Editor::setMarkedTextMatchesAreHighlighted(bool flag) m_frame->document()->markers()->repaintMarkers(DocumentMarker::TextMatch); } -void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, bool closeTyping) +void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, SelectionController::SetSelectionOptions options) { #if SUPPORT_AUTOCORRECTION_PANEL // Make sure there's no pending autocorrection before we call markMisspellingsAndBadGrammar() below. @@ -3450,6 +3500,7 @@ void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, boo } #endif // SUPPORT_AUTOCORRECTION_PANEL + bool closeTyping = options & SelectionController::CloseTyping; bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled(); bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled(); if (isContinuousSpellCheckingEnabled) { @@ -3463,10 +3514,16 @@ void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, boo newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart)); } + bool shouldCheckSpellingAndGrammar = true; +#if SUPPORT_AUTOCORRECTION_PANEL + // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself. + shouldCheckSpellingAndGrammar = !(options & SelectionController::SpellCorrectionTriggered); +#endif + // When typing we check spelling elsewhere, so don't redo it here. // If this is a change in selection resulting from a delete operation, // oldSelection may no longer be in the document. - if (closeTyping && oldSelection.isContentEditable() && oldSelection.start().node() && oldSelection.start().node()->inDocument()) { + if (shouldCheckSpellingAndGrammar && closeTyping && oldSelection.isContentEditable() && oldSelection.start().deprecatedNode() && oldSelection.start().anchorNode()->inDocument()) { VisiblePosition oldStart(oldSelection.visibleStart()); VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary)); if (oldAdjacentWords != newAdjacentWords) { @@ -3505,7 +3562,7 @@ static Node* findFirstMarkable(Node* node) if (node->renderer()->isText()) return node; if (node->renderer()->isTextControl()) - node = toRenderTextControl(node->renderer())->visiblePositionForIndex(1).deepEquivalent().node(); + node = toRenderTextControl(node->renderer())->visiblePositionForIndex(1).deepEquivalent().deprecatedNode(); else if (node->firstChild()) node = node->firstChild(); else @@ -3517,7 +3574,7 @@ static Node* findFirstMarkable(Node* node) bool Editor::selectionStartHasSpellingMarkerFor(int from, int length) const { - Node* node = findFirstMarkable(m_frame->selection()->start().node()); + Node* node = findFirstMarkable(m_frame->selection()->start().deprecatedNode()); if (!node) return false; @@ -3533,5 +3590,10 @@ bool Editor::selectionStartHasSpellingMarkerFor(int from, int length) const return false; } +FloatRect Editor::windowRectForRange(const Range* range) const +{ + FrameView* view = frame()->view(); + return view ? view->contentsToWindow(IntRect(range->boundingRect())) : FloatRect(); +} } // namespace WebCore diff --git a/Source/WebCore/editing/Editor.h b/Source/WebCore/editing/Editor.h index 0a06a4a..e1a7119 100644 --- a/Source/WebCore/editing/Editor.h +++ b/Source/WebCore/editing/Editor.h @@ -35,6 +35,7 @@ #include "EditorDeleteAction.h" #include "EditorInsertAction.h" #include "FindOptions.h" +#include "SelectionController.h" #include "Timer.h" #include "VisibleSelection.h" #include "WritingDirection.h" @@ -61,6 +62,7 @@ class Pasteboard; class SimpleFontData; class SpellChecker; class Text; +class TextCheckerClient; class TextEvent; struct CompositionUnderline { @@ -74,7 +76,6 @@ struct CompositionUnderline { bool thick; }; -enum TriState { FalseTriState, TrueTriState, MixedTriState }; enum EditorCommandSource { CommandFromMenuOrKeyBinding, CommandFromDOM, CommandFromDOMWithUserInterface }; class Editor { @@ -83,6 +84,8 @@ public: ~Editor(); EditorClient* client() const; + TextCheckerClient* textChecker() const; + Frame* frame() const { return m_frame; } DeleteButtonController* deleteButtonController() const { return m_deleteButtonController.get(); } EditCommand* lastEditCommand() { return m_lastEditCommand.get(); } @@ -129,7 +132,8 @@ public: void respondToChangedSelection(const VisibleSelection& oldSelection); void respondToChangedContents(const VisibleSelection& endingSelection); - TriState selectionHasStyle(CSSStyleDeclaration*) const; + bool selectionStartHasStyle(int propertyID, const String& value) const; + TriState selectionHasStyle(int propertyID, const String& value) const; String selectionStartCSSPropertyValue(int propertyID); const SimpleFontData* fontForSelection(bool&) const; WritingDirection textDirectionForSelection(bool&) const; @@ -164,11 +168,8 @@ public: void appliedEditing(PassRefPtr<EditCommand>); void unappliedEditing(PassRefPtr<EditCommand>); void reappliedEditing(PassRefPtr<EditCommand>); - - bool selectionStartHasStyle(CSSStyleDeclaration*) const; + void unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction); - bool clientIsEditable() const; - void setShouldStyleWithCSS(bool flag) { m_shouldStyleWithCSS = flag; } bool shouldStyleWithCSS() const { return m_shouldStyleWithCSS; } @@ -350,7 +351,7 @@ public: IntRect firstRectForRange(Range*) const; - void respondToChangedSelection(const VisibleSelection& oldSelection, bool closeTyping); + void respondToChangedSelection(const VisibleSelection& oldSelection, SelectionController::SetSelectionOptions); bool shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity, bool stillSelecting) const; RenderStyle* styleForSelectionStart(Node*& nodeToRemove) const; @@ -360,7 +361,7 @@ public: bool markedTextMatchesAreHighlighted() const; void setMarkedTextMatchesAreHighlighted(bool); - PassRefPtr<CSSMutableStyleDeclaration> selectionComputedStyle(bool& shouldUseFixedFontDefaultSize) const; + PassRefPtr<EditingStyle> selectionStartStyle() const; void textFieldDidBeginEditing(Element*); void textFieldDidEndEditing(Element*); @@ -374,6 +375,7 @@ public: NSWritingDirection baseWritingDirectionForSelectionStart() const; bool canCopyExcludingStandaloneImages(); void takeFindStringFromSelection(); + void writeSelectionToPasteboard(const String& pasteboardName, const Vector<String>& pasteboardTypes); #endif bool selectionStartHasSpellingMarkerFor(int from, int length) const; @@ -424,7 +426,9 @@ private: void stopCorrectionPanelTimer(); void dismissCorrectionPanel(ReasonForDismissingCorrectionPanel); void applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>& markerTypesToAdd); - void applyAutocorrectionBeforeTypingIfAppropriate(); + // Return true if correction was applied, false otherwise. + bool applyAutocorrectionBeforeTypingIfAppropriate(); + FloatRect windowRectForRange(const Range*) const; }; inline void Editor::setStartNewKillRingSequence(bool flag) diff --git a/Source/WebCore/editing/EditorCommand.cpp b/Source/WebCore/editing/EditorCommand.cpp index 451d855..6ea9954 100644 --- a/Source/WebCore/editing/EditorCommand.cpp +++ b/Source/WebCore/editing/EditorCommand.cpp @@ -132,12 +132,11 @@ static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditActi static bool executeToggleStyleInList(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, CSSValue* value) { ExceptionCode ec = 0; - bool shouldUseFixedFontDefaultSize; - RefPtr<CSSMutableStyleDeclaration> selectionStyle = frame->editor()->selectionComputedStyle(shouldUseFixedFontDefaultSize); - if (!selectionStyle) + RefPtr<EditingStyle> selectionStyle = frame->editor()->selectionStartStyle(); + if (!selectionStyle || !selectionStyle->style()) return false; - RefPtr<CSSValue> selectedCSSValue = selectionStyle->getPropertyCSSValue(propertyID); + RefPtr<CSSValue> selectedCSSValue = selectionStyle->style()->getPropertyCSSValue(propertyID); String newStyle = "none"; if (selectedCSSValue->isValueList()) { RefPtr<CSSValueList> selectedCSSValueList = static_cast<CSSValueList*>(selectedCSSValue.get()); @@ -157,21 +156,18 @@ static bool executeToggleStyleInList(Frame* frame, EditorCommandSource source, E static bool executeToggleStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, const char* offValue, const char* onValue) { - RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); - style->setProperty(propertyID, onValue); // We need to add this style to pass it to selectionStartHasStyle / selectionHasStyle - // Style is considered present when // Mac: present at the beginning of selection // other: present throughout the selection bool styleIsPresent; if (frame->editor()->behavior().shouldToggleStyleBasedOnStartOfSelection()) - styleIsPresent = frame->editor()->selectionStartHasStyle(style.get()); + styleIsPresent = frame->editor()->selectionStartHasStyle(propertyID, onValue); else - styleIsPresent = frame->editor()->selectionHasStyle(style.get()) == TrueTriState; + styleIsPresent = frame->editor()->selectionHasStyle(propertyID, onValue) == TrueTriState; - style->setProperty(propertyID, styleIsPresent ? offValue : onValue); - return applyCommandToFrame(frame, source, action, style.get()); + RefPtr<EditingStyle> style = EditingStyle::create(propertyID, styleIsPresent ? offValue : onValue); + return applyCommandToFrame(frame, source, action, style->style()); } static bool executeApplyParagraphStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, const String& propertyValue) @@ -194,8 +190,7 @@ static bool executeApplyParagraphStyle(Frame* frame, EditorCommandSource source, static bool executeInsertFragment(Frame* frame, PassRefPtr<DocumentFragment> fragment) { - applyCommand(ReplaceSelectionCommand::create(frame->document(), fragment, - false, false, false, true, false, EditActionUnspecified)); + applyCommand(ReplaceSelectionCommand::create(frame->document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionUnspecified)); return true; } @@ -229,12 +224,9 @@ static bool expandSelectionToGranularity(Frame* frame, TextGranularity granulari static TriState stateStyle(Frame* frame, int propertyID, const char* desiredValue) { - RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); - style->setProperty(propertyID, desiredValue); - if (frame->editor()->behavior().shouldToggleStyleBasedOnStartOfSelection()) - return frame->editor()->selectionStartHasStyle(style.get()) ? TrueTriState : FalseTriState; - return frame->editor()->selectionHasStyle(style.get()); + return frame->editor()->selectionStartHasStyle(propertyID, desiredValue) ? TrueTriState : FalseTriState; + return frame->editor()->selectionHasStyle(propertyID, desiredValue); } static String valueStyle(Frame* frame, int propertyID) @@ -511,7 +503,7 @@ static bool executeInsertLineBreak(Frame* frame, Event* event, EditorCommandSour // Doesn't scroll to make the selection visible, or modify the kill ring. // InsertLineBreak is not implemented in IE or Firefox, so this behavior is only needed for // backward compatibility with ourselves, and for consistency with other commands. - TypingCommand::insertLineBreak(frame->document()); + TypingCommand::insertLineBreak(frame->document(), 0); return true; } ASSERT_NOT_REACHED(); @@ -538,7 +530,7 @@ static bool executeInsertOrderedList(Frame* frame, Event*, EditorCommandSource, static bool executeInsertParagraph(Frame* frame, Event*, EditorCommandSource, const String&) { - TypingCommand::insertParagraphSeparator(frame->document()); + TypingCommand::insertParagraphSeparator(frame->document(), 0); return true; } @@ -549,7 +541,7 @@ static bool executeInsertTab(Frame* frame, Event* event, EditorCommandSource, co static bool executeInsertText(Frame* frame, Event*, EditorCommandSource, const String& value) { - TypingCommand::insertText(frame->document(), value); + TypingCommand::insertText(frame->document(), value, 0); return true; } @@ -1121,14 +1113,26 @@ static bool supportedFromMenuOrKeyBinding(Frame*) static bool supportedCopyCut(Frame* frame) { - Settings* settings = frame ? frame->settings() : 0; - return settings && settings->javaScriptCanAccessClipboard(); + if (!frame) + return false; + + Settings* settings = frame->settings(); + bool defaultValue = settings && settings->javaScriptCanAccessClipboard(); + + EditorClient* client = frame->editor()->client(); + return client ? client->canCopyCut(defaultValue) : defaultValue; } static bool supportedPaste(Frame* frame) { - Settings* settings = frame ? frame->settings() : 0; - return settings && (settings->javaScriptCanAccessClipboard() ? settings->isDOMPasteAllowed() : 0); + if (!frame) + return false; + + Settings* settings = frame->settings(); + bool defaultValue = settings && settings->javaScriptCanAccessClipboard() && settings->isDOMPasteAllowed(); + + EditorClient* client = frame->editor()->client(); + return client ? client->canPaste(defaultValue) : defaultValue; } // Enabled functions diff --git a/Source/WebCore/editing/FormatBlockCommand.cpp b/Source/WebCore/editing/FormatBlockCommand.cpp index 58157af..9d90a1e 100644 --- a/Source/WebCore/editing/FormatBlockCommand.cpp +++ b/Source/WebCore/editing/FormatBlockCommand.cpp @@ -60,8 +60,8 @@ void FormatBlockCommand::formatSelection(const VisiblePosition& startOfSelection void FormatBlockCommand::formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>& blockNode) { - Node* nodeToSplitTo = enclosingBlockToSplitTreeTo(start.node()); - RefPtr<Node> outerBlock = (start.node() == nodeToSplitTo) ? start.node() : splitTreeToNode(start.node(), nodeToSplitTo); + Node* nodeToSplitTo = enclosingBlockToSplitTreeTo(start.deprecatedNode()); + RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTreeToNode(start.deprecatedNode(), nodeToSplitTo); RefPtr<Node> nodeAfterInsertionPosition = outerBlock; RefPtr<Range> range = Range::create(document(), start, endOfSelection); diff --git a/Source/WebCore/editing/IndentOutdentCommand.cpp b/Source/WebCore/editing/IndentOutdentCommand.cpp index 9d1adc1..82bec06 100644 --- a/Source/WebCore/editing/IndentOutdentCommand.cpp +++ b/Source/WebCore/editing/IndentOutdentCommand.cpp @@ -59,7 +59,7 @@ IndentOutdentCommand::IndentOutdentCommand(Document* document, EIndentType typeO bool IndentOutdentCommand::tryIndentingAsListItem(const Position& start, const Position& end) { // If our selection is not inside a list, bail out. - Node* lastNodeInSelectedParagraph = start.node(); + Node* lastNodeInSelectedParagraph = start.deprecatedNode(); RefPtr<Element> listNode = enclosingList(lastNodeInSelectedParagraph); if (!listNode) return false; @@ -94,15 +94,15 @@ void IndentOutdentCommand::indentIntoBlockquote(const Position& start, const Pos Node* nodeToSplitTo; if (enclosingCell) nodeToSplitTo = enclosingCell; - else if (enclosingList(start.node())) - nodeToSplitTo = enclosingBlock(start.node()); + else if (enclosingList(start.deprecatedNode())) + nodeToSplitTo = enclosingBlock(start.deprecatedNode()); else nodeToSplitTo = editableRootForPosition(start); if (!nodeToSplitTo) return; - RefPtr<Node> outerBlock = (start.node() == nodeToSplitTo) ? start.node() : splitTreeToNode(start.node(), nodeToSplitTo); + RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTreeToNode(start.deprecatedNode(), nodeToSplitTo); if (!targetBlockquote) { // Create a new blockquote and insert it as a child of the root editable element. We accomplish @@ -166,13 +166,13 @@ void IndentOutdentCommand::outdentParagraph() return; } - Node* enclosingBlockFlow = enclosingBlock(visibleStartOfParagraph.deepEquivalent().node()); + Node* enclosingBlockFlow = enclosingBlock(visibleStartOfParagraph.deepEquivalent().deprecatedNode()); RefPtr<Node> splitBlockquoteNode = enclosingNode; if (enclosingBlockFlow != enclosingNode) splitBlockquoteNode = splitTreeToNode(enclosingBlockFlow, enclosingNode, true); else { // We split the blockquote at where we start outdenting. - splitElement(static_cast<Element*>(enclosingNode), visibleStartOfParagraph.deepEquivalent().node()); + splitElement(static_cast<Element*>(enclosingNode), visibleStartOfParagraph.deepEquivalent().deprecatedNode()); } RefPtr<Node> placeholder = createBreakElement(document()); insertNodeBefore(placeholder, splitBlockquoteNode); @@ -205,10 +205,10 @@ void IndentOutdentCommand::outdentRegion(const VisiblePosition& startOfSelection // outdentParagraph could move more than one paragraph if the paragraph // is in a list item. As a result, endAfterSelection and endOfNextParagraph // could refer to positions no longer in the document. - if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().node()->inDocument()) + if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().anchorNode()->inDocument()) break; - if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().node()->inDocument()) { + if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().anchorNode()->inDocument()) { endOfCurrentParagraph = endingSelection().end(); endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next()); } diff --git a/Source/WebCore/editing/InsertLineBreakCommand.cpp b/Source/WebCore/editing/InsertLineBreakCommand.cpp index af8f2fc..2260a00 100644 --- a/Source/WebCore/editing/InsertLineBreakCommand.cpp +++ b/Source/WebCore/editing/InsertLineBreakCommand.cpp @@ -26,7 +26,6 @@ #include "config.h" #include "InsertLineBreakCommand.h" -#include "CSSMutableStyleDeclaration.h" #include "Document.h" #include "Frame.h" #include "HTMLElement.h" @@ -57,11 +56,11 @@ void InsertLineBreakCommand::insertNodeAfterPosition(Node* node, const Position& // Insert the BR after the caret position. In the case the // position is a block, do an append. We don't want to insert // the BR *after* the block. - Element* cb = pos.node()->enclosingBlockFlowElement(); - if (cb == pos.node()) + Element* cb = pos.deprecatedNode()->enclosingBlockFlowElement(); + if (cb == pos.deprecatedNode()) appendNode(node, cb); else - insertNodeAfter(node, pos.node()); + insertNodeAfter(node, pos.deprecatedNode()); } void InsertLineBreakCommand::insertNodeBeforePosition(Node* node, const Position& pos) @@ -69,11 +68,11 @@ void InsertLineBreakCommand::insertNodeBeforePosition(Node* node, const Position // Insert the BR after the caret position. In the case the // position is a block, do an append. We don't want to insert // the BR *before* the block. - Element* cb = pos.node()->enclosingBlockFlowElement(); - if (cb == pos.node()) + Element* cb = pos.deprecatedNode()->enclosingBlockFlowElement(); + if (cb == pos.deprecatedNode()) appendNode(node, cb); else - insertNodeBefore(node, pos.node()); + insertNodeBefore(node, pos.deprecatedNode()); } // Whether we should insert a break element or a '\n'. @@ -83,7 +82,7 @@ bool InsertLineBreakCommand::shouldUseBreakElement(const Position& insertionPos) // the input element, and in that case we need to check the input element's // parent's renderer. Position p(insertionPos.parentAnchoredEquivalent()); - return p.node()->renderer() && !p.node()->renderer()->style()->preserveNewline(); + return p.deprecatedNode()->renderer() && !p.deprecatedNode()->renderer()->style()->preserveNewline(); } void InsertLineBreakCommand::doApply() @@ -114,7 +113,7 @@ void InsertLineBreakCommand::doApply() // FIXME: Need to merge text nodes when inserting just after or before text. if (isEndOfParagraph(caret) && !lineBreakExistsAtVisiblePosition(caret)) { - bool needExtraLineBreak = !pos.node()->hasTagName(hrTag) && !pos.node()->hasTagName(tableTag); + bool needExtraLineBreak = !pos.deprecatedNode()->hasTagName(hrTag) && !pos.deprecatedNode()->hasTagName(tableTag); insertNodeAt(nodeToInsert.get(), pos); @@ -123,7 +122,7 @@ void InsertLineBreakCommand::doApply() VisiblePosition endingPosition(positionBeforeNode(nodeToInsert.get())); setEndingSelection(VisibleSelection(endingPosition)); - } else if (pos.deprecatedEditingOffset() <= caretMinOffset(pos.node())) { + } else if (pos.deprecatedEditingOffset() <= caretMinOffset(pos.deprecatedNode())) { insertNodeAt(nodeToInsert.get(), pos); // Insert an extra br or '\n' if the just inserted one collapsed. @@ -133,12 +132,12 @@ void InsertLineBreakCommand::doApply() setEndingSelection(VisibleSelection(positionInParentAfterNode(nodeToInsert.get()), DOWNSTREAM)); // If we're inserting after all of the rendered text in a text node, or into a non-text node, // a simple insertion is sufficient. - } else if (pos.deprecatedEditingOffset() >= caretMaxOffset(pos.node()) || !pos.node()->isTextNode()) { + } else if (pos.deprecatedEditingOffset() >= caretMaxOffset(pos.deprecatedNode()) || !pos.deprecatedNode()->isTextNode()) { insertNodeAt(nodeToInsert.get(), pos); setEndingSelection(VisibleSelection(positionInParentAfterNode(nodeToInsert.get()), DOWNSTREAM)); - } else if (pos.node()->isTextNode()) { + } else if (pos.deprecatedNode()->isTextNode()) { // Split a text node - Text* textNode = static_cast<Text*>(pos.node()); + Text* textNode = static_cast<Text*>(pos.deprecatedNode()); splitTextNode(textNode, pos.deprecatedEditingOffset()); insertNodeBefore(nodeToInsert, textNode); Position endingPosition = firstPositionInNode(textNode); diff --git a/Source/WebCore/editing/InsertListCommand.cpp b/Source/WebCore/editing/InsertListCommand.cpp index c24c683..68661b4 100644 --- a/Source/WebCore/editing/InsertListCommand.cpp +++ b/Source/WebCore/editing/InsertListCommand.cpp @@ -84,12 +84,12 @@ bool InsertListCommand::selectionHasListOfType(const VisibleSelection& selection { VisiblePosition start = selection.visibleStart(); - if (!enclosingList(start.deepEquivalent().node())) + if (!enclosingList(start.deepEquivalent().deprecatedNode())) return false; VisiblePosition end = selection.visibleEnd(); while (start.isNotNull() && start != end) { - Element* listNode = enclosingList(start.deepEquivalent().node()); + Element* listNode = enclosingList(start.deepEquivalent().deprecatedNode()); if (!listNode || !listNode->hasTagName(listTag)) return false; start = startOfNextParagraph(start); @@ -143,7 +143,7 @@ void InsertListCommand::doApply() // infinite loop and because there is no more work to be done. // FIXME(<rdar://problem/5983974>): The endingSelection() may be incorrect here. Compute // the new location of endOfSelection and use it as the end of the new selection. - if (!startOfLastParagraph.deepEquivalent().node()->inDocument()) + if (!startOfLastParagraph.deepEquivalent().anchorNode()->inDocument()) return; setEndingSelection(startOfCurrentParagraph); @@ -191,7 +191,7 @@ void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const Qu // FIXME: This will produce unexpected results for a selection that starts just before a // table and ends inside the first cell, selectionForParagraphIteration should probably // be renamed and deployed inside setEndingSelection(). - Node* selectionNode = endingSelection().start().node(); + Node* selectionNode = endingSelection().start().deprecatedNode(); Node* listChildNode = enclosingListChild(selectionNode); bool switchListType = false; if (listChildNode) { @@ -217,7 +217,7 @@ void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const Qu RefPtr<HTMLElement> newList = createHTMLElement(document(), listTag); insertNodeBefore(newList, listNode); - Node* firstChildInList = enclosingListChild(VisiblePosition(firstPositionInNode(listNode.get())).deepEquivalent().node(), listNode.get()); + Node* firstChildInList = enclosingListChild(VisiblePosition(firstPositionInNode(listNode.get())).deepEquivalent().deprecatedNode(), listNode.get()); Node* outerBlock = firstChildInList->isBlockFlow() ? firstChildInList : listNode.get(); moveParagraphWithClones(firstPositionInNode(listNode.get()), lastPositionInNode(listNode.get()), newList.get(), outerBlock); @@ -265,9 +265,9 @@ void InsertListCommand::unlistifyParagraph(const VisiblePosition& originalStart, // A paragraph is visually a list item minus a list marker. The paragraph will be moved. start = startOfParagraph(originalStart); end = endOfParagraph(start); - nextListChild = enclosingListChild(end.next().deepEquivalent().node(), listNode); + nextListChild = enclosingListChild(end.next().deepEquivalent().deprecatedNode(), listNode); ASSERT(nextListChild != listChildNode); - previousListChild = enclosingListChild(start.previous().deepEquivalent().node(), listNode); + previousListChild = enclosingListChild(start.previous().deepEquivalent().deprecatedNode(), listNode); ASSERT(previousListChild != listChildNode); } // When removing a list, we must always create a placeholder to act as a point of insertion @@ -308,7 +308,7 @@ void InsertListCommand::unlistifyParagraph(const VisiblePosition& originalStart, static Element* adjacentEnclosingList(const VisiblePosition& pos, const VisiblePosition& adjacentPos, const QualifiedName& listTag) { - Element* listNode = outermostEnclosingList(adjacentPos.deepEquivalent().node()); + Element* listNode = outermostEnclosingList(adjacentPos.deepEquivalent().deprecatedNode()); if (!listNode) return 0; @@ -317,9 +317,9 @@ static Element* adjacentEnclosingList(const VisiblePosition& pos, const VisibleP Node* currentCell = enclosingTableCell(adjacentPos.deepEquivalent()); if (!listNode->hasTagName(listTag) - || listNode->contains(pos.deepEquivalent().node()) + || listNode->contains(pos.deepEquivalent().deprecatedNode()) || previousCell != currentCell - || enclosingList(listNode) != enclosingList(pos.deepEquivalent().node())) + || enclosingList(listNode) != enclosingList(pos.deepEquivalent().deprecatedNode())) return 0; return listNode; @@ -351,7 +351,7 @@ PassRefPtr<HTMLElement> InsertListCommand::listifyParagraph(const VisiblePositio listElement = createHTMLElement(document(), listTag); appendNode(listItemElement, listElement); - if (start == end && isBlock(start.deepEquivalent().node())) { + if (start == end && isBlock(start.deepEquivalent().deprecatedNode())) { // Inserting the list into an empty paragraph that isn't held open // by a br or a '\n', will invalidate start and end. Insert // a placeholder and then recompute start and end. @@ -367,7 +367,7 @@ PassRefPtr<HTMLElement> InsertListCommand::listifyParagraph(const VisiblePositio // clean markup when inline elements are pushed down as far as possible. Position insertionPos(start.deepEquivalent().upstream()); // Also avoid the containing list item. - Node* listChild = enclosingListChild(insertionPos.node()); + Node* listChild = enclosingListChild(insertionPos.deprecatedNode()); if (listChild && listChild->hasTagName(liTag)) insertionPos = positionInParentBeforeNode(listChild); diff --git a/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp b/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp index 1d50851..771c56a 100644 --- a/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp +++ b/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp @@ -78,8 +78,10 @@ void InsertParagraphSeparatorCommand::calculateStyleBeforeInsertion(const Positi VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY); if (!isStartOfParagraph(visiblePos) && !isEndOfParagraph(visiblePos)) return; - - m_style = editingStyleIncludingTypingStyle(pos); + + ASSERT(pos.isNotNull()); + m_style = EditingStyle::create(pos); + m_style->mergeTypingStyle(pos.anchorNode()->document()); } void InsertParagraphSeparatorCommand::applyStyleAfterInsertion(Node* originalEnclosingBlock) @@ -169,8 +171,8 @@ void InsertParagraphSeparatorCommand::doApply() || isTableCell(startBlock) || startBlock->hasTagName(formTag) // FIXME: If the node is hidden, we don't have a canonical position so we will do the wrong thing for tables and <hr>. https://bugs.webkit.org/show_bug.cgi?id=40342 - || (!canonicalPos.isNull() && canonicalPos.node()->renderer() && canonicalPos.node()->renderer()->isTable()) - || (!canonicalPos.isNull() && canonicalPos.node()->hasTagName(hrTag))) { + || (!canonicalPos.isNull() && canonicalPos.deprecatedNode()->renderer() && canonicalPos.deprecatedNode()->renderer()->isTable()) + || (!canonicalPos.isNull() && canonicalPos.deprecatedNode()->hasTagName(hrTag))) { applyCommandToComposite(InsertLineBreakCommand::create(document())); return; } @@ -237,7 +239,7 @@ void InsertParagraphSeparatorCommand::doApply() // Recreate the same structure in the new paragraph. Vector<Element*> ancestors; - getAncestorsInsideBlock(insertionPosition.node(), startBlock, ancestors); + getAncestorsInsideBlock(insertionPosition.deprecatedNode(), startBlock, ancestors); RefPtr<Element> parent = cloneHierarchyUnderNewBlock(ancestors, blockToInsert); appendBlockPlaceholder(parent); @@ -254,11 +256,11 @@ void InsertParagraphSeparatorCommand::doApply() Node *refNode; if (isFirstInBlock && !nestNewBlock) refNode = startBlock; - else if (insertionPosition.node() == startBlock && nestNewBlock) { + else if (insertionPosition.deprecatedNode() == startBlock && nestNewBlock) { refNode = startBlock->childNode(insertionPosition.deprecatedEditingOffset()); ASSERT(refNode); // must be true or we'd be in the end of block case } else - refNode = insertionPosition.node(); + refNode = insertionPosition.deprecatedNode(); // find ending selection position easily before inserting the paragraph insertionPosition = insertionPosition.downstream(); @@ -268,7 +270,7 @@ void InsertParagraphSeparatorCommand::doApply() // Recreate the same structure in the new paragraph. Vector<Element*> ancestors; - getAncestorsInsideBlock(positionAvoidingSpecialElementBoundary(insertionPosition).node(), startBlock, ancestors); + getAncestorsInsideBlock(positionAvoidingSpecialElementBoundary(insertionPosition).deprecatedNode(), startBlock, ancestors); appendBlockPlaceholder(cloneHierarchyUnderNewBlock(ancestors, blockToInsert)); @@ -301,22 +303,22 @@ void InsertParagraphSeparatorCommand::doApply() // Build up list of ancestors in between the start node and the start block. Vector<Element*> ancestors; - getAncestorsInsideBlock(insertionPosition.node(), startBlock, ancestors); + getAncestorsInsideBlock(insertionPosition.deprecatedNode(), startBlock, ancestors); // Make sure we do not cause a rendered space to become unrendered. // FIXME: We need the affinity for pos, but pos.downstream() does not give it Position leadingWhitespace = insertionPosition.leadingWhitespacePosition(VP_DEFAULT_AFFINITY); // FIXME: leadingWhitespacePosition is returning the position before preserved newlines for positions // after the preserved newline, causing the newline to be turned into a nbsp. - if (leadingWhitespace.isNotNull() && leadingWhitespace.node()->isTextNode()) { - Text* textNode = static_cast<Text*>(leadingWhitespace.node()); + if (leadingWhitespace.isNotNull() && leadingWhitespace.deprecatedNode()->isTextNode()) { + Text* textNode = static_cast<Text*>(leadingWhitespace.deprecatedNode()); ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); replaceTextInNode(textNode, leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); } // Split at pos if in the middle of a text node. - if (insertionPosition.node()->isTextNode()) { - Text* textNode = static_cast<Text*>(insertionPosition.node()); + if (insertionPosition.deprecatedNode()->isTextNode()) { + Text* textNode = static_cast<Text*>(insertionPosition.deprecatedNode()); bool atEnd = (unsigned)insertionPosition.deprecatedEditingOffset() >= textNode->length(); if (insertionPosition.deprecatedEditingOffset() > 0 && !atEnd) { splitTextNode(textNode, insertionPosition.deprecatedEditingOffset()); @@ -344,8 +346,8 @@ void InsertParagraphSeparatorCommand::doApply() appendNode(createBreakElement(document()).get(), blockToInsert.get()); // Move the start node and the siblings of the start node. - if (insertionPosition.node() != startBlock) { - Node* n = insertionPosition.node(); + if (insertionPosition.deprecatedNode() != startBlock) { + Node* n = insertionPosition.deprecatedNode(); if (insertionPosition.deprecatedEditingOffset() >= caretMaxOffset(n)) n = n->nextSibling(); @@ -382,10 +384,10 @@ void InsertParagraphSeparatorCommand::doApply() insertionPosition.moveToOffset(0); if (!insertionPosition.isRenderedCharacter()) { // Clear out all whitespace and insert one non-breaking space - ASSERT(!insertionPosition.node()->renderer() || insertionPosition.node()->renderer()->style()->collapseWhiteSpace()); + ASSERT(!insertionPosition.deprecatedNode()->renderer() || insertionPosition.deprecatedNode()->renderer()->style()->collapseWhiteSpace()); deleteInsignificantTextDownstream(insertionPosition); - if (insertionPosition.node()->isTextNode()) - insertTextIntoNode(static_cast<Text*>(insertionPosition.node()), 0, nonBreakingSpaceString()); + if (insertionPosition.deprecatedNode()->isTextNode()) + insertTextIntoNode(static_cast<Text*>(insertionPosition.deprecatedNode()), 0, nonBreakingSpaceString()); } } diff --git a/Source/WebCore/editing/InsertTextCommand.cpp b/Source/WebCore/editing/InsertTextCommand.cpp index 1ec11ad..33ebe3a 100644 --- a/Source/WebCore/editing/InsertTextCommand.cpp +++ b/Source/WebCore/editing/InsertTextCommand.cpp @@ -26,19 +26,13 @@ #include "config.h" #include "InsertTextCommand.h" -#include "CSSComputedStyleDeclaration.h" -#include "CSSMutableStyleDeclaration.h" -#include "CSSPropertyNames.h" #include "Document.h" #include "Element.h" #include "EditingText.h" #include "Editor.h" #include "Frame.h" -#include "Logging.h" #include "HTMLInterchange.h" #include "htmlediting.h" -#include "TextIterator.h" -#include "TypingCommand.h" #include "visible_units.h" #include <wtf/unicode/CharacterNames.h> @@ -53,20 +47,20 @@ void InsertTextCommand::doApply() { } -Position InsertTextCommand::prepareForTextInsertion(const Position& p) +Position InsertTextCommand::positionInsideTextNode(const Position& p) { Position pos = p; - // Prepare for text input by looking at the specified position. - // It may be necessary to insert a text node to receive characters. - if (!pos.node()->isTextNode()) { + if (isTabSpanTextNode(pos.anchorNode())) { RefPtr<Node> textNode = document()->createEditingTextNode(""); - insertNodeAt(textNode.get(), pos); + insertNodeAtTabSpanPosition(textNode.get(), pos); return firstPositionInNode(textNode.get()); } - if (isTabSpanTextNode(pos.node())) { + // Prepare for text input by looking at the specified position. + // It may be necessary to insert a text node to receive characters. + if (!pos.containerNode()->isTextNode()) { RefPtr<Node> textNode = document()->createEditingTextNode(""); - insertNodeAtTabSpanPosition(textNode.get(), pos); + insertNodeAt(textNode.get(), pos); return firstPositionInNode(textNode.get()); } @@ -147,9 +141,9 @@ void InsertTextCommand::input(const String& text, bool selectInsertedText, Rebal // It is possible for the node that contains startPosition to contain only unrendered whitespace, // and so deleteInsignificantText could remove it. Save the position before the node in case that happens. - Position positionBeforeStartNode(positionInParentBeforeNode(startPosition.node())); + Position positionBeforeStartNode(positionInParentBeforeNode(startPosition.containerNode())); deleteInsignificantText(startPosition.upstream(), startPosition.downstream()); - if (!startPosition.node()->inDocument()) + if (!startPosition.anchorNode()->inDocument()) startPosition = positionBeforeStartNode; if (!startPosition.isCandidate()) startPosition = startPosition.downstream(); @@ -165,11 +159,14 @@ void InsertTextCommand::input(const String& text, bool selectInsertedText, Rebal removePlaceholderAt(placeholder); } else { // Make sure the document is set up to receive text - startPosition = prepareForTextInsertion(startPosition); + startPosition = positionInsideTextNode(startPosition); + ASSERT(startPosition.anchorType() == Position::PositionIsOffsetInAnchor); + ASSERT(startPosition.containerNode()); + ASSERT(startPosition.containerNode()->isTextNode()); if (placeholder.isNotNull()) removePlaceholderAt(placeholder); - Text *textNode = static_cast<Text *>(startPosition.node()); - int offset = startPosition.deprecatedEditingOffset(); + Text* textNode = static_cast<Text*>(startPosition.containerNode()); + const unsigned offset = startPosition.offsetInContainerNode(); insertTextIntoNode(textNode, offset, text); endPosition = Position(textNode, offset + text.length(), Position::PositionIsOffsetInAnchor); @@ -183,7 +180,7 @@ void InsertTextCommand::input(const String& text, bool selectInsertedText, Rebal } else { ASSERT(whitespaceRebalance == RebalanceAllWhitespaces); if (canRebalance(startPosition) && canRebalance(endPosition)) - rebalanceWhitespaceOnTextSubstring(textNode, startPosition.deprecatedEditingOffset(), endPosition.deprecatedEditingOffset()); + rebalanceWhitespaceOnTextSubstring(textNode, startPosition.offsetInContainerNode(), endPosition.offsetInContainerNode()); } } @@ -209,7 +206,7 @@ Position InsertTextCommand::insertTab(const Position& pos) { Position insertPos = VisiblePosition(pos, DOWNSTREAM).deepEquivalent(); - Node *node = insertPos.node(); + Node* node = insertPos.deprecatedNode(); unsigned int offset = insertPos.deprecatedEditingOffset(); // keep tabs coalesced in tab span diff --git a/Source/WebCore/editing/InsertTextCommand.h b/Source/WebCore/editing/InsertTextCommand.h index 672e576..da86a7b 100644 --- a/Source/WebCore/editing/InsertTextCommand.h +++ b/Source/WebCore/editing/InsertTextCommand.h @@ -53,7 +53,7 @@ private: virtual void doApply(); virtual bool isInsertTextCommand() const; - Position prepareForTextInsertion(const Position&); + Position positionInsideTextNode(const Position&); Position insertTab(const Position&); bool performTrivialReplace(const String&, bool selectInsertedText); diff --git a/Source/WebCore/editing/ModifySelectionListLevel.cpp b/Source/WebCore/editing/ModifySelectionListLevel.cpp index 3e6754e..cbcd488 100644 --- a/Source/WebCore/editing/ModifySelectionListLevel.cpp +++ b/Source/WebCore/editing/ModifySelectionListLevel.cpp @@ -52,12 +52,12 @@ static bool getStartEndListChildren(const VisibleSelection& selection, Node*& st return false; // start must be in a list child - Node* startListChild = enclosingListChild(selection.start().node()); + Node* startListChild = enclosingListChild(selection.start().anchorNode()); if (!startListChild) return false; - + // end must be in a list child - Node* endListChild = selection.isRange() ? enclosingListChild(selection.end().node()) : startListChild; + Node* endListChild = selection.isRange() ? enclosingListChild(selection.end().anchorNode()) : startListChild; if (!endListChild) return false; diff --git a/Source/WebCore/editing/MoveSelectionCommand.cpp b/Source/WebCore/editing/MoveSelectionCommand.cpp index 0f23b29..cdf05ee 100644 --- a/Source/WebCore/editing/MoveSelectionCommand.cpp +++ b/Source/WebCore/editing/MoveSelectionCommand.cpp @@ -32,7 +32,7 @@ namespace WebCore { MoveSelectionCommand::MoveSelectionCommand(PassRefPtr<DocumentFragment> fragment, const Position& position, bool smartInsert, bool smartDelete) - : CompositeEditCommand(position.node()->document()), m_fragment(fragment), m_position(position), m_smartInsert(smartInsert), m_smartDelete(smartDelete) + : CompositeEditCommand(position.anchorNode()->document()), m_fragment(fragment), m_position(position), m_smartInsert(smartInsert), m_smartDelete(smartDelete) { ASSERT(m_fragment); } @@ -62,7 +62,7 @@ void MoveSelectionCommand::doApply() // set the destination to the ending point after the deletion. // Fixes: <rdar://problem/3910425> REGRESSION (Mail): Crash in ReplaceSelectionCommand; // selection is empty, leading to null deref - if (!pos.node()->inDocument()) + if (!pos.anchorNode()->inDocument()) pos = endingSelection().start(); setEndingSelection(VisibleSelection(pos, endingSelection().affinity())); @@ -70,7 +70,10 @@ void MoveSelectionCommand::doApply() // Document was modified out from under us. return; } - applyCommandToComposite(ReplaceSelectionCommand::create(document(), m_fragment, true, m_smartInsert)); + ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::PreventNesting; + if (m_smartInsert) + options |= ReplaceSelectionCommand::SmartReplace; + applyCommandToComposite(ReplaceSelectionCommand::create(document(), m_fragment, options)); } EditAction MoveSelectionCommand::editingAction() const diff --git a/Source/WebCore/editing/RemoveCSSPropertyCommand.h b/Source/WebCore/editing/RemoveCSSPropertyCommand.h index 46e0498..00ed3bc 100644 --- a/Source/WebCore/editing/RemoveCSSPropertyCommand.h +++ b/Source/WebCore/editing/RemoveCSSPropertyCommand.h @@ -27,7 +27,6 @@ #define RemoveCSSPropertyCommand_h #include "EditCommand.h" -#include "CSSMutableStyleDeclaration.h" #include "CSSPropertyNames.h" #include "StyledElement.h" diff --git a/Source/WebCore/editing/ReplaceSelectionCommand.cpp b/Source/WebCore/editing/ReplaceSelectionCommand.cpp index 846b932..b0a2d68 100644 --- a/Source/WebCore/editing/ReplaceSelectionCommand.cpp +++ b/Source/WebCore/editing/ReplaceSelectionCommand.cpp @@ -110,15 +110,15 @@ static bool isInterchangeConvertedSpaceSpan(const Node *node) static Position positionAvoidingPrecedingNodes(Position pos) { // If we're already on a break, it's probably a placeholder and we shouldn't change our position. - if (pos.node()->hasTagName(brTag)) + if (pos.deprecatedNode()->hasTagName(brTag)) return pos; // We also stop when changing block flow elements because even though the visual position is the // same. E.g., // <div>foo^</div>^ // The two positions above are the same visual position, but we want to stay in the same block. - Node* stopNode = pos.node()->enclosingBlockFlowElement(); - while (stopNode != pos.node() && VisiblePosition(pos) == VisiblePosition(pos.next())) + Node* stopNode = pos.deprecatedNode()->enclosingBlockFlowElement(); + while (stopNode != pos.deprecatedNode() && VisiblePosition(pos) == VisiblePosition(pos.next())) pos = pos.next(); return pos; } @@ -152,7 +152,7 @@ ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* f return; } - Node* styleNode = selection.base().node(); + Node* styleNode = selection.base().deprecatedNode(); RefPtr<StyledElement> holder = insertFragmentForTestRendering(styleNode); RefPtr<Range> range = VisibleSelection::selectionFromContentsOfNode(holder.get()).toNormalizedRange(); @@ -340,18 +340,16 @@ void ReplacementFragment::removeInterchangeNodes(Node* container) } } -ReplaceSelectionCommand::ReplaceSelectionCommand(Document* document, PassRefPtr<DocumentFragment> fragment, - bool selectReplacement, bool smartReplace, bool matchStyle, bool preventNesting, bool movingParagraph, - EditAction editAction) - : CompositeEditCommand(document), - m_selectReplacement(selectReplacement), - m_smartReplace(smartReplace), - m_matchStyle(matchStyle), - m_documentFragment(fragment), - m_preventNesting(preventNesting), - m_movingParagraph(movingParagraph), - m_editAction(editAction), - m_shouldMergeEnd(false) +ReplaceSelectionCommand::ReplaceSelectionCommand(Document* document, PassRefPtr<DocumentFragment> fragment, CommandOptions options, EditAction editAction) + : CompositeEditCommand(document) + , m_selectReplacement(options & SelectReplacement) + , m_smartReplace(options & SmartReplace) + , m_matchStyle(options & MatchStyle) + , m_documentFragment(fragment) + , m_preventNesting(options & PreventNesting) + , m_movingParagraph(options & MovingParagraph) + , m_editAction(editAction) + , m_shouldMergeEnd(false) { } @@ -359,7 +357,7 @@ static bool hasMatchingQuoteLevel(VisiblePosition endOfExistingContent, VisibleP { Position existing = endOfExistingContent.deepEquivalent(); Position inserted = endOfInsertedContent.deepEquivalent(); - bool isInsideMailBlockquote = nearestMailBlockquote(inserted.node()); + bool isInsideMailBlockquote = nearestMailBlockquote(inserted.deprecatedNode()); return isInsideMailBlockquote && (numEnclosingMailBlockquotes(existing) == numEnclosingMailBlockquotes(inserted)); } @@ -381,11 +379,11 @@ bool ReplaceSelectionCommand::shouldMergeStart(bool selectionStartWasStartOfPara if (isStartOfParagraph(startOfInsertedContent) && selectionStartWasInsideMailBlockquote && hasMatchingQuoteLevel(prev, positionAtEndOfInsertedContent())) return true; - return !selectionStartWasStartOfParagraph && - !fragmentHasInterchangeNewlineAtStart && - isStartOfParagraph(startOfInsertedContent) && - !startOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) && - shouldMerge(startOfInsertedContent, prev); + return !selectionStartWasStartOfParagraph + && !fragmentHasInterchangeNewlineAtStart + && isStartOfParagraph(startOfInsertedContent) + && !startOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName(brTag) + && shouldMerge(startOfInsertedContent, prev); } bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph) @@ -395,10 +393,10 @@ bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph) if (next.isNull()) return false; - return !selectionEndWasEndOfParagraph && - isEndOfParagraph(endOfInsertedContent) && - !endOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) && - shouldMerge(endOfInsertedContent, next); + return !selectionEndWasEndOfParagraph + && isEndOfParagraph(endOfInsertedContent) + && !endOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName(brTag) + && shouldMerge(endOfInsertedContent, next); } static bool isMailPasteAsQuotationNode(const Node* node) @@ -455,8 +453,8 @@ bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const V if (source.isNull() || destination.isNull()) return false; - Node* sourceNode = source.deepEquivalent().node(); - Node* destinationNode = destination.deepEquivalent().node(); + Node* sourceNode = source.deepEquivalent().deprecatedNode(); + Node* destinationNode = destination.deepEquivalent().deprecatedNode(); Node* sourceBlock = enclosingBlock(sourceNode); Node* destinationBlock = enclosingBlock(destinationNode); return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotationNode) && @@ -742,7 +740,7 @@ void ReplaceSelectionCommand::mergeEndIfNeeded() // To avoid this, we add a placeholder node before the start of the paragraph. if (endOfParagraph(startOfParagraphToMove) == destination) { RefPtr<Node> placeholder = createBreakElement(document()); - insertNodeBefore(placeholder, startOfParagraphToMove.deepEquivalent().node()); + insertNodeBefore(placeholder, startOfParagraphToMove.deepEquivalent().deprecatedNode()); destination = VisiblePosition(positionBeforeNode(placeholder.get())); } @@ -753,9 +751,9 @@ void ReplaceSelectionCommand::mergeEndIfNeeded() // only ever used to create positions where inserted content starts/ends. Also, we sometimes insert content // directly into text nodes already in the document, in which case tracking inserted nodes is inadequate. if (mergeForward) { - m_lastLeafInserted = destination.previous().deepEquivalent().node(); + m_lastLeafInserted = destination.previous().deepEquivalent().deprecatedNode(); if (!m_firstNodeInserted->inDocument()) - m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().node(); + m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().deprecatedNode(); // If we merged text nodes, m_lastLeafInserted could be null. If this is the case, // we use m_firstNodeInserted. if (!m_lastLeafInserted) @@ -782,8 +780,8 @@ void ReplaceSelectionCommand::doApply() { VisibleSelection selection = endingSelection(); ASSERT(selection.isCaretOrRange()); - ASSERT(selection.start().node()); - if (!selection.isNonOrphanedCaretOrRange() || !selection.start().node()) + ASSERT(selection.start().deprecatedNode()); + if (!selection.isNonOrphanedCaretOrRange() || !selection.start().deprecatedNode()) return; bool selectionIsPlainText = !selection.isContentRichlyEditable(); @@ -795,12 +793,14 @@ void ReplaceSelectionCommand::doApply() return; // 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)) + if ((selection.start().deprecatedNode()->renderer() && selection.start().deprecatedNode()->renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY) + && (selection.end().deprecatedNode()->renderer() && selection.end().deprecatedNode()->renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY)) m_matchStyle = false; - if (m_matchStyle) - m_insertionStyle = editingStyleIncludingTypingStyle(selection.start()); + if (m_matchStyle) { + m_insertionStyle = EditingStyle::create(selection.start()); + m_insertionStyle->mergeTypingStyle(document()); + } VisiblePosition visibleStart = selection.visibleStart(); VisiblePosition visibleEnd = selection.visibleEnd(); @@ -808,10 +808,10 @@ void ReplaceSelectionCommand::doApply() bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd); bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart); - Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().node()); + Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().deprecatedNode()); Position insertionPos = selection.start(); - bool startIsInsideMailBlockquote = nearestMailBlockquote(insertionPos.node()); + bool startIsInsideMailBlockquote = nearestMailBlockquote(insertionPos.deprecatedNode()); if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !startIsInsideMailBlockquote) || startBlock == currentRoot || isListItem(startBlock) || selectionIsPlainText) @@ -864,7 +864,7 @@ void ReplaceSelectionCommand::doApply() if (startIsInsideMailBlockquote && m_preventNesting && !(enclosingNodeOfType(insertionPos, &isTableStructureNode))) { applyCommandToComposite(BreakBlockquoteCommand::create(document())); // This will leave a br between the split. - Node* br = endingSelection().start().node(); + Node* br = endingSelection().start().deprecatedNode(); ASSERT(br->hasTagName(brTag)); // Insert content between the two blockquotes, but remove the br (since it was just a placeholder). insertionPos = positionInParentBeforeNode(br); @@ -875,18 +875,18 @@ void ReplaceSelectionCommand::doApply() prepareWhitespaceAtPositionForSplit(insertionPos); // If the downstream node has been removed there's no point in continuing. - if (!insertionPos.downstream().node()) + if (!insertionPos.downstream().deprecatedNode()) return; // NOTE: This would be an incorrect usage of downstream() if downstream() were changed to mean the last position after // p that maps to the same visible position as p (since in the case where a br is at the end of a block and collapsed // away, there are positions after the br which map to the same visible position as [br, 0]). - Node* endBR = insertionPos.downstream().node()->hasTagName(brTag) ? insertionPos.downstream().node() : 0; + Node* endBR = insertionPos.downstream().deprecatedNode()->hasTagName(brTag) ? insertionPos.downstream().deprecatedNode() : 0; VisiblePosition originalVisPosBeforeEndBR; if (endBR) - originalVisPosBeforeEndBR = VisiblePosition(endBR, 0, DOWNSTREAM).previous(); + originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), DOWNSTREAM).previous(); - startBlock = enclosingBlock(insertionPos.node()); + startBlock = enclosingBlock(insertionPos.deprecatedNode()); // Adjust insertionPos to prevent nesting. // If the start was in a Mail blockquote, we will have already handled adjusting insertionPos above. @@ -941,7 +941,7 @@ void ReplaceSelectionCommand::doApply() fragment.removeNode(refNode); - Node* blockStart = enclosingBlock(insertionPos.node()); + Node* blockStart = enclosingBlock(insertionPos.deprecatedNode()); if ((isListElement(refNode.get()) || (isStyleSpan(refNode.get()) && isListElement(refNode->firstChild()))) && blockStart->renderer()->isListItem()) refNode = insertAsListItems(refNode, blockStart, insertionPos); @@ -985,7 +985,7 @@ void ReplaceSelectionCommand::doApply() // We inserted before the startBlock to prevent nesting, and the content before the startBlock wasn't in its own block and // didn't have a br after it, so the inserted content ended up in the same paragraph. - if (startBlock && insertionPos.node() == startBlock->parentNode() && (unsigned)insertionPos.deprecatedEditingOffset() < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent)) + if (startBlock && insertionPos.deprecatedNode() == startBlock->parentNode() && (unsigned)insertionPos.deprecatedEditingOffset() < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent)) insertNodeAt(createBreakElement(document()).get(), startOfInsertedContent.deepEquivalent()); Position lastPositionToSelect; @@ -1005,7 +1005,7 @@ void ReplaceSelectionCommand::doApply() // We need to handle the case where we need to merge the end // but our destination node is inside an inline that is the last in the block. // We insert a placeholder before the newly inserted content to avoid being merged into the inline. - Node* destinationNode = destination.deepEquivalent().node(); + Node* destinationNode = destination.deepEquivalent().deprecatedNode(); if (m_shouldMergeEnd && destinationNode != enclosingInline(destinationNode) && enclosingInline(destinationNode)->nextSibling()) insertNodeBefore(createBreakElement(document()), refNode.get()); @@ -1020,16 +1020,16 @@ void ReplaceSelectionCommand::doApply() if (startOfParagraph(endOfInsertedContent) == startOfParagraphToMove) { insertNodeAt(createBreakElement(document()).get(), endOfInsertedContent.deepEquivalent()); // Mutation events (bug 22634) triggered by inserting the <br> might have removed the content we're about to move - if (!startOfParagraphToMove.deepEquivalent().node()->inDocument()) + if (!startOfParagraphToMove.deepEquivalent().anchorNode()->inDocument()) return; } // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes. The nodes are // only ever used to create positions where inserted content starts/ends. moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination); - m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().downstream().node(); + m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().downstream().deprecatedNode(); if (!m_lastLeafInserted->inDocument()) - m_lastLeafInserted = endingSelection().visibleEnd().deepEquivalent().upstream().node(); + m_lastLeafInserted = endingSelection().visibleEnd().deepEquivalent().upstream().deprecatedNode(); } endOfInsertedContent = positionAtEndOfInsertedContent(); @@ -1041,7 +1041,7 @@ void ReplaceSelectionCommand::doApply() if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedContent) || next.isNull()) { if (!isStartOfParagraph(endOfInsertedContent)) { setEndingSelection(endOfInsertedContent); - Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEquivalent().node()); + Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEquivalent().deprecatedNode()); if (isListItem(enclosingNode)) { RefPtr<Node> newListItem = createListItemElement(document()); insertNodeAfter(newListItem, enclosingNode); @@ -1053,7 +1053,7 @@ void ReplaceSelectionCommand::doApply() // Select up to the paragraph separator that was added. lastPositionToSelect = endingSelection().visibleStart().deepEquivalent(); - updateNodesInserted(lastPositionToSelect.node()); + updateNodesInserted(lastPositionToSelect.deprecatedNode()); } } else { // Select up to the beginning of the next paragraph. @@ -1081,7 +1081,7 @@ void ReplaceSelectionCommand::doApply() if (needsTrailingSpace) { RenderObject* renderer = m_lastLeafInserted->renderer(); bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace(); - Node* endNode = positionAtEndOfInsertedContent().deepEquivalent().upstream().node(); + Node* endNode = positionAtEndOfInsertedContent().deepEquivalent().upstream().deprecatedNode(); if (endNode->isTextNode()) { Text* text = static_cast<Text*>(endNode); insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " "); @@ -1096,7 +1096,7 @@ void ReplaceSelectionCommand::doApply() if (needsLeadingSpace) { RenderObject* renderer = m_lastLeafInserted->renderer(); bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace(); - Node* startNode = positionAtStartOfInsertedContent().deepEquivalent().downstream().node(); + Node* startNode = positionAtStartOfInsertedContent().deepEquivalent().downstream().deprecatedNode(); if (startNode->isTextNode()) { Text* text = static_cast<Text*>(startNode); insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " "); @@ -1214,9 +1214,9 @@ Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtr<Node> listElement, N // list items and insert these nodes between them. if (isMiddle) { int textNodeOffset = insertPos.offsetInContainerNode(); - if (insertPos.node()->isTextNode() && textNodeOffset > 0) - splitTextNode(static_cast<Text*>(insertPos.node()), textNodeOffset); - splitTreeToNode(insertPos.node(), lastNode, true); + if (insertPos.deprecatedNode()->isTextNode() && textNodeOffset > 0) + splitTextNode(static_cast<Text*>(insertPos.deprecatedNode()), textNodeOffset); + splitTreeToNode(insertPos.deprecatedNode(), lastNode, true); } while (RefPtr<Node> listItem = listElement->firstChild()) { diff --git a/Source/WebCore/editing/ReplaceSelectionCommand.h b/Source/WebCore/editing/ReplaceSelectionCommand.h index 9fc4a49..3d49af1 100644 --- a/Source/WebCore/editing/ReplaceSelectionCommand.h +++ b/Source/WebCore/editing/ReplaceSelectionCommand.h @@ -36,16 +36,23 @@ class ReplacementFragment; class ReplaceSelectionCommand : public CompositeEditCommand { public: - static PassRefPtr<ReplaceSelectionCommand> create(Document* document, PassRefPtr<DocumentFragment> fragment, - bool selectReplacement = true, bool smartReplace = false, bool matchStyle = false, bool preventNesting = true, bool movingParagraph = false, - EditAction action = EditActionPaste) + enum CommandOption { + SelectReplacement = 1 << 0, + SmartReplace = 1 << 1, + MatchStyle = 1 << 2, + PreventNesting = 1 << 3, + MovingParagraph = 1 << 4 + }; + + typedef unsigned CommandOptions; + + static PassRefPtr<ReplaceSelectionCommand> create(Document* document, PassRefPtr<DocumentFragment> fragment, CommandOptions options, EditAction action = EditActionPaste) { - return adoptRef(new ReplaceSelectionCommand(document, fragment, selectReplacement, smartReplace, matchStyle, preventNesting, movingParagraph, action)); + return adoptRef(new ReplaceSelectionCommand(document, fragment, options, action)); } private: - ReplaceSelectionCommand(Document*, PassRefPtr<DocumentFragment>, - bool selectReplacement, bool smartReplace, bool matchStyle, bool preventNesting, bool movingParagraph, EditAction); + ReplaceSelectionCommand(Document*, PassRefPtr<DocumentFragment>, CommandOptions, EditAction); virtual void doApply(); virtual EditAction editingAction() const; diff --git a/Source/WebCore/editing/SelectionController.cpp b/Source/WebCore/editing/SelectionController.cpp index a574b5a..65f9062 100644 --- a/Source/WebCore/editing/SelectionController.cpp +++ b/Source/WebCore/editing/SelectionController.cpp @@ -91,34 +91,53 @@ SelectionController::SelectionController(Frame* frame, bool isDragCaretControlle void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered, CursorAlignOnScroll align) { - setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered, align); + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; + setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), options, align); } void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered) { - setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), true, true, userTriggered); + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; + setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), options); } void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered) { - setSelection(VisibleSelection(pos, affinity), true, true, userTriggered); + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; + setSelection(VisibleSelection(pos, affinity), options); } void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered) { + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity); - setSelection(selection, true, true, userTriggered); + setSelection(selection, options); } void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered) { - setSelection(VisibleSelection(base, extent, affinity), true, true, userTriggered); + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; + setSelection(VisibleSelection(base, extent, affinity), options); } -void SelectionController::setSelection(const VisibleSelection& s, bool closeTyping, bool shouldClearTypingStyle, bool userTriggered, CursorAlignOnScroll align, TextGranularity granularity, DirectionalityPolicy directionalityPolicy) +void SelectionController::setSelection(const VisibleSelection& s, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity, DirectionalityPolicy directionalityPolicy) { m_granularity = granularity; + bool closeTyping = options & CloseTyping; + bool shouldClearTypingStyle = options & ClearTypingStyle; + bool userTriggered = options & UserTriggered; + setIsDirectional(directionalityPolicy == MakeDirectionalSelection); if (m_isDragCaretController) { @@ -134,7 +153,7 @@ void SelectionController::setSelection(const VisibleSelection& s, bool closeTypi return; } - Node* baseNode = s.base().node(); + Node* baseNode = s.base().deprecatedNode(); Document* document = 0; if (baseNode) document = baseNode->document(); @@ -142,10 +161,10 @@ void SelectionController::setSelection(const VisibleSelection& s, bool closeTypi // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at SelectionController::setSelection // if document->frame() == m_frame we can get into an infinite loop if (document && document->frame() && document->frame() != m_frame && document != m_frame->document()) { - document->frame()->selection()->setSelection(s, closeTyping, shouldClearTypingStyle, userTriggered); + document->frame()->selection()->setSelection(s, options); return; } - + if (closeTyping) TypingCommand::closeTyping(m_frame->editor()->lastEditCommand()); @@ -171,7 +190,7 @@ void SelectionController::setSelection(const VisibleSelection& s, bool closeTypi m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation; selectFrameElementInParentIfFullySelected(); notifyRendererOfSelectionChange(userTriggered); - m_frame->editor()->respondToChangedSelection(oldSelection, closeTyping); + m_frame->editor()->respondToChangedSelection(oldSelection, options); if (userTriggered) { ScrollAlignment alignment; @@ -184,21 +203,22 @@ void SelectionController::setSelection(const VisibleSelection& s, bool closeTypi } notifyAccessibilityForSelectionChange(); + m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false)); } static bool removingNodeRemovesPosition(Node* node, const Position& position) { - if (!position.node()) + if (!position.deprecatedNode()) return false; - if (position.node() == node) + if (position.deprecatedNode() == node) return true; if (!node->isElementNode()) return false; Element* element = static_cast<Element*>(node); - return element->contains(position.node()) || element->contains(position.node()->shadowAncestorNode()); + return element->contains(position.deprecatedNode()) || element->contains(position.deprecatedNode()->shadowAncestorNode()); } void SelectionController::nodeWillBeRemoved(Node *node) @@ -246,14 +266,14 @@ void SelectionController::respondToNodeModification(Node* node, bool baseRemoved } if (clearRenderTreeSelection) { - RefPtr<Document> document = m_selection.start().node()->document(); + RefPtr<Document> document = m_selection.start().anchorNode()->document(); document->updateStyleIfNeeded(); if (RenderView* view = toRenderView(document->renderer())) view->clearSelection(); } if (clearDOMTreeSelection) - setSelection(VisibleSelection(), false, false); + setSelection(VisibleSelection(), 0); } enum EndPointType { EndPointIsStart, EndPointIsEnd }; @@ -263,12 +283,16 @@ static bool shouldRemovePositionAfterAdoptingTextReplacement(Position& position, if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor) return false; - if (static_cast<unsigned>(position.offsetInContainerNode()) > offset && static_cast<unsigned>(position.offsetInContainerNode()) < offset + oldLength) + ASSERT(position.offsetInContainerNode() >= 0); + unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode()); + if (positionOffset > offset && positionOffset < offset + oldLength) return true; - if ((type == EndPointIsStart && static_cast<unsigned>(position.offsetInContainerNode()) >= offset + oldLength) - || (type == EndPointIsEnd && static_cast<unsigned>(position.offsetInContainerNode()) > offset + oldLength)) - position.moveToOffset(position.offsetInContainerNode() - oldLength + newLength); + // Adjust the offset if the position is after or at the end of the deleted contents (positionOffset >= offset + oldLength) + // to avoid having a stale offset except when the position is the end of selection and nothing is deleted, in which case, + // adjusting offset results in incorrectly extending the selection until the end of newly inserted contents. + if ((positionOffset > offset + oldLength) || (positionOffset == offset + oldLength && (type == EndPointIsStart || oldLength))) + position.moveToOffset(positionOffset - oldLength + newLength); return false; } @@ -290,14 +314,18 @@ void SelectionController::textWillBeReplaced(CharacterData* node, unsigned offse if ((base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end()) && !shouldRemoveStart && !shouldRemoveEnd) { + VisibleSelection newSelection; if (!shouldRemoveBase && !shouldRemoveExtent) - m_selection.setWithoutValidation(base, extent); + newSelection.setWithoutValidation(base, extent); else { - if (m_selection.isBaseFirst()) - m_selection.setWithoutValidation(m_selection.start(), m_selection.end()); + if (newSelection.isBaseFirst()) + newSelection.setWithoutValidation(start, end); else - m_selection.setWithoutValidation(m_selection.end(), m_selection.start()); + newSelection.setWithoutValidation(end, start); } + m_frame->document()->updateLayout(); + setSelection(newSelection, 0); + return; } respondToNodeModification(node, shouldRemoveBase, shouldRemoveExtent, shouldRemoveStart, shouldRemoveEnd); @@ -359,7 +387,7 @@ void SelectionController::willBeModified(EAlteration alter, SelectionDirection d TextDirection SelectionController::directionOfEnclosingBlock() { - Node* enclosingBlockNode = enclosingBlock(m_selection.extent().node()); + Node* enclosingBlockNode = enclosingBlock(m_selection.extent().deprecatedNode()); if (!enclosingBlockNode) return LTR; RenderObject* renderer = enclosingBlockNode->renderer(); @@ -413,11 +441,16 @@ VisiblePosition SelectionController::modifyExtendingRight(TextGranularity granul else pos = previousWordPosition(pos); break; + case LineBoundary: + if (directionOfEnclosingBlock() == LTR) + pos = modifyExtendingForward(granularity); + else + pos = modifyExtendingBackward(granularity); + break; case SentenceGranularity: case LineGranularity: case ParagraphGranularity: case SentenceBoundary: - case LineBoundary: case ParagraphBoundary: case DocumentBoundary: // FIXME: implement all of the above? @@ -425,7 +458,7 @@ VisiblePosition SelectionController::modifyExtendingRight(TextGranularity granul } return pos; } - + VisiblePosition SelectionController::modifyExtendingForward(TextGranularity granularity) { VisiblePosition pos(m_selection.extent(), m_selection.affinity()); @@ -566,11 +599,16 @@ VisiblePosition SelectionController::modifyExtendingLeft(TextGranularity granula else pos = nextWordPosition(pos); break; + case LineBoundary: + if (directionOfEnclosingBlock() == LTR) + pos = modifyExtendingBackward(granularity); + else + pos = modifyExtendingForward(granularity); + break; case SentenceGranularity: case LineGranularity: case ParagraphGranularity: case SentenceBoundary: - case LineBoundary: case ParagraphBoundary: case DocumentBoundary: pos = modifyExtendingBackward(granularity); @@ -767,7 +805,8 @@ bool SelectionController::modify(EAlteration alter, SelectionDirection direction if (!m_frame || !m_frame->editor()->behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity)) setExtent(position, userTriggered); else { - if (direction == DirectionForward || direction == DirectionRight) + TextDirection textDirection = directionOfEnclosingBlock(); + if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft)) setEnd(position, userTriggered); else setStart(position, userTriggered); @@ -905,7 +944,7 @@ int SelectionController::xPosForVerticalArrowNavigation(EPositionType type) break; } - Frame* frame = pos.node()->document()->frame(); + Frame* frame = pos.anchorNode()->document()->frame(); if (!frame) return x; @@ -945,22 +984,34 @@ void SelectionController::setEnd(const VisiblePosition &pos, bool userTriggered) void SelectionController::setBase(const VisiblePosition &pos, bool userTriggered) { - setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity()), true, true, userTriggered); + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; + setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity()), options); } void SelectionController::setExtent(const VisiblePosition &pos, bool userTriggered) { - setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered); + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; + setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity()), options); } void SelectionController::setBase(const Position &pos, EAffinity affinity, bool userTriggered) { - setSelection(VisibleSelection(pos, m_selection.extent(), affinity), true, true, userTriggered); + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; + setSelection(VisibleSelection(pos, m_selection.extent(), affinity), options); } void SelectionController::setExtent(const Position &pos, EAffinity affinity, bool userTriggered) { - setSelection(VisibleSelection(m_selection.base(), pos, affinity), true, true, userTriggered); + SetSelectionOptions options = CloseTyping | ClearTypingStyle; + if (userTriggered) + options |= UserTriggered; + setSelection(VisibleSelection(m_selection.base(), pos, affinity), options); } void SelectionController::setCaretRectNeedsUpdate(bool flag) @@ -970,19 +1021,19 @@ void SelectionController::setCaretRectNeedsUpdate(bool flag) void SelectionController::updateCaretRect() { - if (isNone() || !m_selection.start().node()->inDocument() || !m_selection.end().node()->inDocument()) { + if (isNone() || !m_selection.start().anchorNode()->inDocument() || !m_selection.end().anchorNode()->inDocument()) { m_caretRect = IntRect(); return; } - m_selection.start().node()->document()->updateStyleIfNeeded(); + m_selection.start().anchorNode()->document()->updateStyleIfNeeded(); m_caretRect = IntRect(); if (isCaret()) { VisiblePosition pos(m_selection.start(), m_selection.affinity()); if (pos.isNotNull()) { - ASSERT(pos.deepEquivalent().node()->renderer()); + ASSERT(pos.deepEquivalent().deprecatedNode()->renderer()); // First compute a rect local to the renderer at the selection start RenderObject* renderer; @@ -1016,7 +1067,7 @@ void SelectionController::updateCaretRect() RenderObject* SelectionController::caretRenderer() const { - Node* node = m_selection.start().node(); + Node* node = m_selection.start().deprecatedNode(); if (!node) return 0; @@ -1123,7 +1174,7 @@ void SelectionController::invalidateCaretRect() if (!isCaret()) return; - Document* d = m_selection.start().node()->document(); + Document* d = m_selection.start().anchorNode()->document(); // recomputeCaretRect will always return false for the drag caret, // because its m_frame is always 0. @@ -1205,9 +1256,9 @@ void SelectionController::debugRenderer(RenderObject *r, bool selected) const int textLength = text.length(); if (selected) { int offset = 0; - if (r->node() == m_selection.start().node()) + if (r->node() == m_selection.start().deprecatedNode()) offset = m_selection.start().deprecatedEditingOffset(); - else if (r->node() == m_selection.end().node()) + else if (r->node() == m_selection.end().deprecatedNode()) offset = m_selection.end().deprecatedEditingOffset(); int pos; @@ -1309,7 +1360,7 @@ void SelectionController::selectFrameElementInParentIfFullySelected() return; // Get to the <iframe> or <frame> (or even <object>) element in the parent frame. - Element* ownerElement = m_frame->document()->ownerElement(); + Element* ownerElement = m_frame->ownerElement(); if (!ownerElement) return; ContainerNode* ownerElementParent = ownerElement->parentNode(); @@ -1322,8 +1373,8 @@ void SelectionController::selectFrameElementInParentIfFullySelected() // Create compute positions before and after the element. unsigned ownerElementNodeIndex = ownerElement->nodeIndex(); - VisiblePosition beforeOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex, SEL_DEFAULT_AFFINITY)); - VisiblePosition afterOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex + 1, VP_UPSTREAM_IF_POSSIBLE)); + VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor))); + VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE)); // Focus on the parent frame, and then select from before this element to after. VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement); @@ -1394,15 +1445,18 @@ bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, boo return false; // FIXME: Can we provide extentAffinity? - VisiblePosition visibleStart(startContainer, startOffset, collapsed ? affinity : DOWNSTREAM); - VisiblePosition visibleEnd(endContainer, endOffset, SEL_DEFAULT_AFFINITY); - setSelection(VisibleSelection(visibleStart, visibleEnd), closeTyping); + VisiblePosition visibleStart(Position(startContainer, startOffset, Position::PositionIsOffsetInAnchor), collapsed ? affinity : DOWNSTREAM); + VisiblePosition visibleEnd(Position(endContainer, endOffset, Position::PositionIsOffsetInAnchor), SEL_DEFAULT_AFFINITY); + SetSelectionOptions options = ClearTypingStyle; + if (closeTyping) + options |= CloseTyping; + setSelection(VisibleSelection(visibleStart, visibleEnd), options); return true; } bool SelectionController::isInPasswordField() const { - Node* startNode = start().node(); + Node* startNode = start().deprecatedNode(); if (!startNode) return false; @@ -1548,8 +1602,8 @@ void SelectionController::updateAppearance() // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted // because we don't yet notify the SelectionController of text removal. if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) { - RenderObject* startRenderer = startPos.node()->renderer(); - RenderObject* endRenderer = endPos.node()->renderer(); + RenderObject* startRenderer = startPos.deprecatedNode()->renderer(); + RenderObject* endRenderer = endPos.deprecatedNode()->renderer(); view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset()); } } @@ -1650,7 +1704,7 @@ void SelectionController::paintDragCaret(GraphicsContext* p, int tx, int ty, con #if ENABLE(TEXT_CARET) SelectionController* dragCaretController = m_frame->page()->dragCaretController(); ASSERT(dragCaretController->selection().isCaret()); - if (dragCaretController->selection().start().node()->document()->frame() == m_frame) + if (dragCaretController->selection().start().anchorNode()->document()->frame() == m_frame) dragCaretController->paintCaret(p, tx, ty, clipRect); #else UNUSED_PARAM(p); @@ -1724,7 +1778,7 @@ HTMLFormElement* SelectionController::currentForm() const // Start looking either at the active (first responder) node, or where the selection is. Node* start = m_frame->document()->focusedNode(); if (!start) - start = this->start().node(); + start = this->start().deprecatedNode(); // Try walking up the node tree to find a form element. Node* node; @@ -1755,12 +1809,12 @@ void SelectionController::revealSelection(const ScrollAlignment& alignment, bool } Position start = this->start(); - ASSERT(start.node()); - if (start.node() && start.node()->renderer()) { + ASSERT(start.deprecatedNode()); + if (start.deprecatedNode() && start.deprecatedNode()->renderer()) { // FIXME: This code only handles scrolling the startContainer's layer, but // the selection rect could intersect more than just that. // See <rdar://problem/4799899>. - if (RenderLayer* layer = start.node()->renderer()->enclosingLayer()) { + if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) { layer->scrollRectToVisible(rect, false, alignment, alignment); updateAppearance(); } @@ -1774,7 +1828,7 @@ void SelectionController::setSelectionFromNone() Document* document = m_frame->document(); bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled(); - if (!isNone() || !(m_frame->isContentEditable() || caretBrowsing)) + if (!isNone() || !(document->inDesignMode() || caretBrowsing)) return; Node* node = document->documentElement(); diff --git a/Source/WebCore/editing/SelectionController.h b/Source/WebCore/editing/SelectionController.h index 3f805d3..d6a9dff 100644 --- a/Source/WebCore/editing/SelectionController.h +++ b/Source/WebCore/editing/SelectionController.h @@ -54,6 +54,13 @@ public: enum EAlteration { AlterationMove, AlterationExtend }; enum CursorAlignOnScroll { AlignCursorOnScrollIfNeeded, AlignCursorOnScrollAlways }; + enum SetSelectionOption { + CloseTyping = 1 << 0, + ClearTypingStyle = 1 << 1, + UserTriggered = 1 << 2, + SpellCorrectionTriggered = 1 << 3, + }; + typedef unsigned SetSelectionOptions; SelectionController(Frame* = 0, bool isDragCaretController = false); @@ -69,8 +76,8 @@ public: void moveTo(const Position&, const Position&, EAffinity, bool userTriggered = false); const VisibleSelection& selection() const { return m_selection; } - void setSelection(const VisibleSelection&, bool closeTyping = true, bool clearTypingStyle = true, bool userTriggered = false, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity, DirectionalityPolicy = MakeDirectionalSelection); - void setSelection(const VisibleSelection& selection, TextGranularity granularity, DirectionalityPolicy directionality = MakeDirectionalSelection) { setSelection(selection, true, true, false, AlignCursorOnScrollIfNeeded, granularity, directionality); } + void setSelection(const VisibleSelection&, SetSelectionOptions = CloseTyping | ClearTypingStyle, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity, DirectionalityPolicy = MakeDirectionalSelection); + void setSelection(const VisibleSelection& selection, TextGranularity granularity, DirectionalityPolicy directionality = MakeDirectionalSelection) { setSelection(selection, CloseTyping | ClearTypingStyle, AlignCursorOnScrollIfNeeded, granularity, directionality); } bool setSelectedRange(Range*, EAffinity, bool closeTyping); void selectAll(); void clear(); diff --git a/Source/WebCore/editing/SetSelectionCommand.cpp b/Source/WebCore/editing/SetSelectionCommand.cpp new file mode 100644 index 0000000..a96e0ad --- /dev/null +++ b/Source/WebCore/editing/SetSelectionCommand.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 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. + */ + +#include "config.h" +#include "SetSelectionCommand.h" + +#include "Frame.h" + +namespace WebCore { + +SetSelectionCommand::SetSelectionCommand(const VisibleSelection& selection, SelectionController::SetSelectionOptions options) + : SimpleEditCommand(selection.base().anchorNode()->document()) + , m_options(options) + , m_selectionToSet(selection) +{ +} + +void SetSelectionCommand::doApply() +{ + SelectionController* selectionController = document()->frame()->selection(); + ASSERT(selectionController); + + if (selectionController->shouldChangeSelection(m_selectionToSet) && m_selectionToSet.isNonOrphanedCaretOrRange()) { + selectionController->setSelection(m_selectionToSet, m_options); + setEndingSelection(m_selectionToSet); + } +} + +void SetSelectionCommand::doUnapply() +{ + SelectionController* selectionController = document()->frame()->selection(); + ASSERT(selectionController); + + if (selectionController->shouldChangeSelection(startingSelection()) && startingSelection().isNonOrphanedCaretOrRange()) + selectionController->setSelection(startingSelection(), m_options); +} + +} // namespace WebCore diff --git a/Source/WebCore/editing/SetSelectionCommand.h b/Source/WebCore/editing/SetSelectionCommand.h new file mode 100644 index 0000000..3d6609c --- /dev/null +++ b/Source/WebCore/editing/SetSelectionCommand.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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 SetSelectionCommand_h +#define SetSelectionCommand_h + +#include "EditCommand.h" +#include "SelectionController.h" + +namespace WebCore { + +class SetSelectionCommand : public SimpleEditCommand { +public: + static PassRefPtr<SetSelectionCommand> create(const VisibleSelection& selection, SelectionController::SetSelectionOptions options) + { + return adoptRef(new SetSelectionCommand(selection, options)); + } + +private: + SetSelectionCommand(const VisibleSelection&, SelectionController::SetSelectionOptions); + virtual void doApply(); + virtual void doUnapply(); + + SelectionController::SetSelectionOptions m_options; + VisibleSelection m_selectionToSet; +}; + +} // namespace WebCore + +#endif // SetSelectionCommand_h diff --git a/Source/WebCore/editing/SmartReplace.cpp b/Source/WebCore/editing/SmartReplace.cpp index c5f5240..7b8ca45 100644 --- a/Source/WebCore/editing/SmartReplace.cpp +++ b/Source/WebCore/editing/SmartReplace.cpp @@ -29,7 +29,7 @@ #include "config.h" #include "SmartReplace.h" -#if !PLATFORM(CF) && !USE(ICU_UNICODE) +#if !USE(CF) && !USE(ICU_UNICODE) namespace WebCore { @@ -40,4 +40,4 @@ bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter) } -#endif // !PLATFORM(CF) +#endif // !USE(CF) diff --git a/Source/WebCore/editing/SmartReplaceICU.cpp b/Source/WebCore/editing/SmartReplaceICU.cpp index 9acd350..c3507ad 100644 --- a/Source/WebCore/editing/SmartReplaceICU.cpp +++ b/Source/WebCore/editing/SmartReplaceICU.cpp @@ -30,7 +30,7 @@ #include "config.h" #include "SmartReplace.h" -#if !PLATFORM(CF) && USE(ICU_UNICODE) +#if !USE(CF) && USE(ICU_UNICODE) #include "PlatformString.h" #include <unicode/uset.h> #include <wtf/Assertions.h> @@ -97,4 +97,4 @@ bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter) } -#endif // !PLATFORM(CF) && USE(ICU_UNICODE) +#endif // !USE(CF) && USE(ICU_UNICODE) diff --git a/Source/WebCore/editing/SpellChecker.cpp b/Source/WebCore/editing/SpellChecker.cpp index f14a74d..7988e41 100644 --- a/Source/WebCore/editing/SpellChecker.cpp +++ b/Source/WebCore/editing/SpellChecker.cpp @@ -37,12 +37,13 @@ #include "Range.h" #include "RenderObject.h" #include "Settings.h" +#include "TextCheckerClient.h" #include "TextIterator.h" #include "htmlediting.h" namespace WebCore { -SpellChecker::SpellChecker(Frame* frame, EditorClient* client) +SpellChecker::SpellChecker(Frame* frame, TextCheckerClient* client) : m_frame(frame) , m_client(client) , m_requestSequence(0) diff --git a/Source/WebCore/editing/SpellChecker.h b/Source/WebCore/editing/SpellChecker.h index 81bb519..d3940e5 100644 --- a/Source/WebCore/editing/SpellChecker.h +++ b/Source/WebCore/editing/SpellChecker.h @@ -31,7 +31,7 @@ namespace WebCore { -class EditorClient; +class TextCheckerClient; class Frame; class Node; @@ -57,7 +57,7 @@ private: class SpellChecker { WTF_MAKE_NONCOPYABLE(SpellChecker); public: - explicit SpellChecker(Frame*, EditorClient*); + explicit SpellChecker(Frame*, TextCheckerClient*); ~SpellChecker(); bool isAsynchronousEnabled() const; @@ -73,7 +73,7 @@ private: void clearRequest(); Frame* m_frame; - EditorClient* m_client; + TextCheckerClient* m_client; RefPtr<Node> m_requestNode; String m_requestText; diff --git a/Source/WebCore/editing/SpellingCorrectionCommand.cpp b/Source/WebCore/editing/SpellingCorrectionCommand.cpp new file mode 100644 index 0000000..bad4a99 --- /dev/null +++ b/Source/WebCore/editing/SpellingCorrectionCommand.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2011 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. + */ + +#include "config.h" +#include "SpellingCorrectionCommand.h" + +#include "CorrectionPanelInfo.h" +#include "DocumentFragment.h" +#include "Frame.h" +#include "ReplaceSelectionCommand.h" +#include "SetSelectionCommand.h" +#include "TextIterator.h" +#include "markup.h" + +namespace WebCore { + +#if SUPPORT_AUTOCORRECTION_PANEL +// On Mac OS X, we use this command to keep track of user undoing a correction for the first time. +// This information is needed by spell checking service to update user specific data. +class SpellingCorrectionRecordUndoCommand : public SimpleEditCommand { +public: + static PassRefPtr<SpellingCorrectionRecordUndoCommand> create(Document* document, const String& corrected, const String& correction) + { + return adoptRef(new SpellingCorrectionRecordUndoCommand(document, corrected, correction)); + } +private: + SpellingCorrectionRecordUndoCommand(Document* document, const String& corrected, const String& correction) + : SimpleEditCommand(document) + , m_corrected(corrected) + , m_correction(correction) + , m_hasBeenUndone(false) + { + } + + virtual void doApply() + { + } + + virtual void doUnapply() + { + if (!m_hasBeenUndone) { + document()->frame()->editor()->unappliedSpellCorrection(startingSelection(), m_corrected, m_correction); + m_hasBeenUndone = true; + } + + } + + String m_corrected; + String m_correction; + bool m_hasBeenUndone; +}; +#endif + +SpellingCorrectionCommand::SpellingCorrectionCommand(PassRefPtr<Range> rangeToBeCorrected, const String& correction) + : CompositeEditCommand(rangeToBeCorrected->startContainer()->document()) + , m_rangeToBeCorrected(rangeToBeCorrected) + , m_selectionToBeCorrected(m_rangeToBeCorrected.get()) + , m_correction(correction) +{ +} + +void SpellingCorrectionCommand::doApply() +{ + m_corrected = plainText(m_rangeToBeCorrected.get()); + if (!m_corrected.length()) + return; + + if (!document()->frame()->selection()->shouldChangeSelection(m_selectionToBeCorrected)) + return; + + RefPtr<DocumentFragment> fragment = createFragmentFromText(m_rangeToBeCorrected.get(), m_correction); + if (!fragment) + return; + + applyCommandToComposite(SetSelectionCommand::create(m_selectionToBeCorrected, SelectionController::CloseTyping | SelectionController::ClearTypingStyle)); +#if SUPPORT_AUTOCORRECTION_PANEL + applyCommandToComposite(SpellingCorrectionRecordUndoCommand::create(document(), m_corrected, m_correction)); +#endif + applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment, ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting, EditActionPaste)); +} + +bool SpellingCorrectionCommand::shouldRetainAutocorrectionIndicator() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/editing/SpellingCorrectionCommand.h b/Source/WebCore/editing/SpellingCorrectionCommand.h new file mode 100644 index 0000000..6136366 --- /dev/null +++ b/Source/WebCore/editing/SpellingCorrectionCommand.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 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 SpellingCorrectionCommand_h +#define SpellingCorrectionCommand_h + +#include "CompositeEditCommand.h" +#include "Range.h" + +namespace WebCore { + +class SpellingCorrectionCommand : public CompositeEditCommand { +public: + static PassRefPtr<SpellingCorrectionCommand> create(PassRefPtr<Range> rangeToBeCorrected, const String& correction) + { + return adoptRef(new SpellingCorrectionCommand(rangeToBeCorrected, correction)); + } +private: + SpellingCorrectionCommand(PassRefPtr<Range> rangeToBeCorrected, const String& correction); + virtual void doApply(); + virtual bool shouldRetainAutocorrectionIndicator() const; + + RefPtr<Range> m_rangeToBeCorrected; + VisibleSelection m_selectionToBeCorrected; + String m_corrected; + String m_correction; +}; + +} // namespace WebCore + +#endif // SpellingCorrectionCommand_h diff --git a/Source/WebCore/editing/TextCheckingHelper.cpp b/Source/WebCore/editing/TextCheckingHelper.cpp index b4429a6..e5553fd 100644 --- a/Source/WebCore/editing/TextCheckingHelper.cpp +++ b/Source/WebCore/editing/TextCheckingHelper.cpp @@ -29,6 +29,7 @@ #include "DocumentMarkerController.h" #include "Range.h" +#include "TextCheckerClient.h" #include "TextIterator.h" #include "VisiblePosition.h" #include "visible_units.h" @@ -179,7 +180,7 @@ String TextCheckingHelper::findFirstMisspelling(int& firstMisspellingOffset, boo int misspellingLocation = -1; int misspellingLength = 0; - m_client->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength); + m_client->textChecker()->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength); // 5490627 shows that there was some code path here where the String constructor below crashes. // We don't know exactly what combination of bad input caused this, so we're making this much @@ -274,7 +275,7 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b Vector<TextCheckingResult> results; uint64_t checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling; - m_client->checkTextOfParagraph(paragraphString.characters(), paragraphString.length(), checkingTypes, results); + m_client->textChecker()->checkTextOfParagraph(paragraphString.characters(), paragraphString.length(), checkingTypes, results); for (unsigned i = 0; i < results.size(); i++) { const TextCheckingResult* result = &results[i]; @@ -422,7 +423,7 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail, Vector<GrammarDetail> grammarDetails; int badGrammarPhraseLocation = -1; int badGrammarPhraseLength = 0; - m_client->checkGrammarOfString(paragraph.textCharacters() + startOffset, paragraph.textLength() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength); + m_client->textChecker()->checkGrammarOfString(paragraph.textCharacters() + startOffset, paragraph.textLength() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength); if (!badGrammarPhraseLength) { ASSERT(badGrammarPhraseLocation == -1); @@ -535,14 +536,14 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool Vector<TextCheckingResult> results; uint64_t checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling; - m_client->checkTextOfParagraph(paragraph.textCharacters(), paragraph.textLength(), checkingTypes, results); + m_client->textChecker()->checkTextOfParagraph(paragraph.textCharacters(), paragraph.textLength(), checkingTypes, results); for (unsigned i = 0; i < results.size(); i++) { const TextCheckingResult* result = &results[i]; if (result->type == TextCheckingTypeSpelling && paragraph.checkingRangeMatches(result->location, result->length)) { String misspelledWord = paragraph.checkingSubstring(); ASSERT(misspelledWord.length()); - m_client->getGuessesForWord(misspelledWord, String(), guesses); + m_client->textChecker()->getGuessesForWord(misspelledWord, String(), guesses); m_client->updateSpellingUIWithMisspelledWord(misspelledWord); misspelled = true; return guesses; diff --git a/Source/WebCore/editing/TextIterator.cpp b/Source/WebCore/editing/TextIterator.cpp index b621dc2..a9546b8 100644 --- a/Source/WebCore/editing/TextIterator.cpp +++ b/Source/WebCore/editing/TextIterator.cpp @@ -194,21 +194,6 @@ static Node* nextInPreOrderCrossingShadowBoundaries(Node* rangeEndContainer, int return 0; } -static Node* previousInPostOrderCrossingShadowBoundaries(Node* rangeStartContainer, int rangeStartOffset) -{ - if (!rangeStartContainer) - return 0; - if (rangeStartOffset > 0 && !rangeStartContainer->offsetInCharacters()) { - if (Node* previous = rangeStartContainer->childNode(rangeStartOffset - 1)) - return previous; - } - for (Node* node = rangeStartContainer; node; node = node->parentOrHostNode()) { - if (Node* previous = node->previousSibling()) - return previous; - } - return 0; -} - // -------- static inline bool fullyClipsContents(Node* node) @@ -290,9 +275,6 @@ TextIterator::TextIterator(const Range* r, TextIteratorBehavior behavior) , m_handledFirstLetter(false) , m_ignoresStyleVisibility(behavior & TextIteratorIgnoresStyleVisibility) { - // FIXME: should support TextIteratorEndsAtEditingBoundary http://webkit.org/b/43609 - ASSERT(behavior != TextIteratorEndsAtEditingBoundary); - if (!r) return; @@ -878,8 +860,8 @@ bool TextIterator::shouldRepresentNodeOffsetZero() // and in that case we'll get null. We don't want to put in newlines at the start in that case. // The currPos.isNotNull() check is needed because positions in non-HTML content // (like SVG) do not have visible positions, and we don't want to emit for them either. - VisiblePosition startPos = VisiblePosition(m_startContainer, m_startOffset, DOWNSTREAM); - VisiblePosition currPos = VisiblePosition(m_node, 0, DOWNSTREAM); + VisiblePosition startPos = VisiblePosition(Position(m_startContainer, m_startOffset, Position::PositionIsOffsetInAnchor), DOWNSTREAM); + VisiblePosition currPos = VisiblePosition(positionBeforeNode(m_node), DOWNSTREAM); return startPos.isNotNull() && currPos.isNotNull() && !inSameLine(startPos, currPos); } @@ -1052,7 +1034,7 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r, , m_node(0) , m_positionNode(0) { - ASSERT(m_behavior == TextIteratorDefaultBehavior || m_behavior == TextIteratorEndsAtEditingBoundary); + ASSERT(m_behavior == TextIteratorDefaultBehavior); if (!r) return; @@ -1077,7 +1059,7 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r, } } - setCurrentNode(endNode); + m_node = endNode; setUpFullyClippedStack(m_fullyClippedStack, m_node); m_offset = endOffset; m_handledNode = false; @@ -1096,7 +1078,7 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r, m_lastTextNode = 0; m_lastCharacter = '\n'; - m_pastStartNode = previousInPostOrderCrossingShadowBoundaries(startNode, startOffset); + m_havePassedStartNode = false; advance(); } @@ -1108,7 +1090,7 @@ void SimplifiedBackwardsTextIterator::advance() m_positionNode = 0; m_textLength = 0; - while (m_node && m_node != m_pastStartNode) { + while (m_node && !m_havePassedStartNode) { // Don't handle node if we start iterating at [node, 0]. if (!m_handledNode && !(m_node == m_endNode && m_endOffset == 0)) { RenderObject* renderer = m_node->renderer(); @@ -1125,8 +1107,10 @@ void SimplifiedBackwardsTextIterator::advance() return; } - Node* next = m_handledChildren ? 0 : m_node->lastChild(); - if (!next) { + if (!m_handledChildren && m_node->hasChildNodes()) { + m_node = m_node->lastChild(); + pushFullyClippedState(m_fullyClippedStack, m_node); + } else { // Exit empty containers as we pass over them or containers // where [container, 0] is where we started iterating. if (!m_handledNode @@ -1138,11 +1122,12 @@ void SimplifiedBackwardsTextIterator::advance() m_handledNode = true; m_handledChildren = true; return; - } + } } + // Exit all other containers. while (!m_node->previousSibling()) { - if (!setCurrentNode(m_node->parentOrHostNode())) + if (!advanceRespectingRange(m_node->parentOrHostNode())) break; m_fullyClippedStack.pop(); exitNode(); @@ -1153,14 +1138,12 @@ void SimplifiedBackwardsTextIterator::advance() } } - next = m_node->previousSibling(); m_fullyClippedStack.pop(); + if (advanceRespectingRange(m_node->previousSibling())) + pushFullyClippedState(m_fullyClippedStack, m_node); + else + m_node = 0; } - - if (m_node && setCurrentNode(next)) - pushFullyClippedState(m_fullyClippedStack, m_node); - else - clearCurrentNode(); // For the purpose of word boundary detection, // we should iterate all visible text and trailing (collapsed) whitespaces. @@ -1240,26 +1223,17 @@ void SimplifiedBackwardsTextIterator::emitCharacter(UChar c, Node* node, int sta m_lastCharacter = c; } -bool SimplifiedBackwardsTextIterator::crossesEditingBoundary(Node* node) const +bool SimplifiedBackwardsTextIterator::advanceRespectingRange(Node* next) { - return m_node && m_node->isContentEditable() != node->isContentEditable(); -} - -bool SimplifiedBackwardsTextIterator::setCurrentNode(Node* node) -{ - if (!node) + if (!next) return false; - if (m_behavior == TextIteratorEndsAtEditingBoundary && crossesEditingBoundary(node)) + m_havePassedStartNode |= m_node == m_startNode; + if (m_havePassedStartNode) return false; - m_node = node; + m_node = next; return true; } -void SimplifiedBackwardsTextIterator::clearCurrentNode() -{ - m_node = 0; -} - PassRefPtr<Range> SimplifiedBackwardsTextIterator::range() const { if (m_positionNode) @@ -2346,7 +2320,7 @@ PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(Element* scope, int r Position runEnd = VisiblePosition(runStart).next().deepEquivalent(); if (runEnd.isNotNull()) { ExceptionCode ec = 0; - textRunRange->setEnd(runEnd.node(), runEnd.deprecatedEditingOffset(), ec); + textRunRange->setEnd(runEnd.deprecatedNode(), runEnd.deprecatedEditingOffset(), ec); ASSERT(!ec); } } diff --git a/Source/WebCore/editing/TextIterator.h b/Source/WebCore/editing/TextIterator.h index 8b61afe..b0c310a 100644 --- a/Source/WebCore/editing/TextIterator.h +++ b/Source/WebCore/editing/TextIterator.h @@ -41,8 +41,7 @@ enum TextIteratorBehavior { TextIteratorEmitsCharactersBetweenAllVisiblePositions = 1 << 0, TextIteratorEntersTextControls = 1 << 1, TextIteratorEmitsTextsWithoutTranscoding = 1 << 2, - TextIteratorEndsAtEditingBoundary = 1 << 3, - TextIteratorIgnoresStyleVisibility = 1 << 4 + TextIteratorIgnoresStyleVisibility = 1 << 3 }; // FIXME: Can't really answer this question correctly without knowing the white-space mode. @@ -205,9 +204,7 @@ private: bool handleReplacedElement(); bool handleNonTextNode(); void emitCharacter(UChar, Node*, int startOffset, int endOffset); - bool crossesEditingBoundary(Node*) const; - bool setCurrentNode(Node*); - void clearCurrentNode(); + bool advanceRespectingRange(Node*); TextIteratorBehavior m_behavior; // Current position, not necessarily of the text being returned, but position @@ -238,9 +235,9 @@ private: // Used for whitespace characters that aren't in the DOM, so we can point at them. UChar m_singleCharacterBuffer; - - // The node after the last node this iterator should process. - Node* m_pastStartNode; + + // Whether m_node has advanced beyond the iteration range (i.e. m_startNode). + bool m_havePassedStartNode; }; // Builds on the text iterator, adding a character position so we can walk one diff --git a/Source/WebCore/editing/TypingCommand.cpp b/Source/WebCore/editing/TypingCommand.cpp index 9723c2d..3b42915 100644 --- a/Source/WebCore/editing/TypingCommand.cpp +++ b/Source/WebCore/editing/TypingCommand.cpp @@ -47,18 +47,18 @@ namespace WebCore { using namespace HTMLNames; -TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, bool selectInsertedText, TextGranularity granularity, TextCompositionType compositionType, - bool killRing) - : CompositeEditCommand(document), - m_commandType(commandType), - m_textToInsert(textToInsert), - m_openForMoreTyping(true), - m_selectInsertedText(selectInsertedText), - m_smartDelete(false), - m_granularity(granularity), - m_compositionType(compositionType), - m_killRing(killRing), - m_openedByBackwardDelete(false) +TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, TypingCommandOptions options, TextGranularity granularity, TextCompositionType compositionType) + : CompositeEditCommand(document) + , m_commandType(commandType) + , m_textToInsert(textToInsert) + , m_openForMoreTyping(true) + , m_selectInsertedText(options & SelectInsertedText) + , m_smartDelete(false) + , m_granularity(granularity) + , m_compositionType(compositionType) + , m_killRing(options & KillRing) + , m_openedByBackwardDelete(false) + , m_shouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator) { updatePreservesTypingStyle(m_commandType); } @@ -78,8 +78,8 @@ void TypingCommand::deleteSelection(Document* document, bool smartDelete) static_cast<TypingCommand*>(lastEditCommand)->deleteSelection(smartDelete); return; } - - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteSelection, "", false); + + RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteSelection, "", 0); typingCommand->setSmartDelete(smartDelete); typingCommand->apply(); } @@ -97,8 +97,9 @@ void TypingCommand::deleteKeyPressed(Document *document, bool smartDelete, TextG static_cast<TypingCommand*>(lastEditCommand)->deleteKeyPressed(granularity, killRing); return; } - - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteKey, "", false, granularity, killRing); + + TypingCommandOptions options = killRing ? KillRing : 0; + RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteKey, "", options, granularity); typingCommand->setSmartDelete(smartDelete); typingCommand->apply(); } @@ -118,7 +119,8 @@ void TypingCommand::forwardDeleteKeyPressed(Document *document, bool smartDelete return; } - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, ForwardDeleteKey, "", false, granularity, killRing); + TypingCommandOptions options = killRing ? KillRing : 0; + RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, ForwardDeleteKey, "", options, granularity); typingCommand->setSmartDelete(smartDelete); typingCommand->apply(); } @@ -129,30 +131,29 @@ void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand VisibleSelection currentSelection = frame->selection()->selection(); if (currentSelection == typingCommand->endingSelection()) return; - + typingCommand->setStartingSelection(currentSelection); typingCommand->setEndingSelection(currentSelection); } - -void TypingCommand::insertText(Document* document, const String& text, bool selectInsertedText, TextCompositionType composition) +void TypingCommand::insertText(Document* document, const String& text, TypingCommandOptions options, TextCompositionType composition) { ASSERT(document); - + Frame* frame = document->frame(); ASSERT(frame); - insertText(document, text, frame->selection()->selection(), selectInsertedText, composition); -} - -// FIXME: We shouldn't need to take selectionForInsertion. It should be identical to SelectionController's current selection. -void TypingCommand::insertText(Document* document, const String& text, const VisibleSelection& selectionForInsertion, bool selectInsertedText, TextCompositionType compositionType) -{ #if REMOVE_MARKERS_UPON_EDITING if (!text.isEmpty()) document->frame()->editor()->removeSpellAndCorrectionMarkersFromWordsToBeEdited(isSpaceOrNewline(text.characters()[0])); #endif + insertText(document, text, frame->selection()->selection(), options, composition); +} + +// FIXME: We shouldn't need to take selectionForInsertion. It should be identical to SelectionController's current selection. +void TypingCommand::insertText(Document* document, const String& text, const VisibleSelection& selectionForInsertion, TypingCommandOptions options, TextCompositionType compositionType) +{ ASSERT(document); RefPtr<Frame> frame = document->frame(); @@ -161,7 +162,7 @@ void TypingCommand::insertText(Document* document, const String& text, const Vis VisibleSelection currentSelection = frame->selection()->selection(); bool changeSelection = currentSelection != selectionForInsertion; String newText = text; - Node* startNode = selectionForInsertion.start().node(); + Node* startNode = selectionForInsertion.start().deprecatedNode(); if (startNode && startNode->rootEditableElement() && compositionType != TextCompositionUpdate) { // Send BeforeTextInsertedEvent. The event handler will update text if necessary. @@ -184,13 +185,14 @@ void TypingCommand::insertText(Document* document, const String& text, const Vis lastTypingCommand->setStartingSelection(selectionForInsertion); lastTypingCommand->setEndingSelection(selectionForInsertion); } - + lastTypingCommand->setCompositionType(compositionType); - lastTypingCommand->insertText(newText, selectInsertedText); + lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator); + lastTypingCommand->insertText(newText, options & SelectInsertedText); return; } - RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, selectInsertedText, compositionType); + RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, options, compositionType); if (changeSelection) { cmd->setStartingSelection(selectionForInsertion); cmd->setEndingSelection(selectionForInsertion); @@ -202,29 +204,31 @@ void TypingCommand::insertText(Document* document, const String& text, const Vis } } -void TypingCommand::insertLineBreak(Document *document) +void TypingCommand::insertLineBreak(Document *document, TypingCommandOptions options) { ASSERT(document); - + Frame* frame = document->frame(); ASSERT(frame); - + EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->insertLineBreak(); + TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand); + lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator); + lastTypingCommand->insertLineBreak(); return; } - applyCommand(TypingCommand::create(document, InsertLineBreak)); + applyCommand(TypingCommand::create(document, InsertLineBreak, "", options)); } void TypingCommand::insertParagraphSeparatorInQuotedContent(Document *document) { ASSERT(document); - + Frame* frame = document->frame(); ASSERT(frame); - + EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); if (isOpenForMoreTypingCommand(lastEditCommand)) { static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparatorInQuotedContent(); @@ -234,20 +238,22 @@ void TypingCommand::insertParagraphSeparatorInQuotedContent(Document *document) applyCommand(TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent)); } -void TypingCommand::insertParagraphSeparator(Document *document) +void TypingCommand::insertParagraphSeparator(Document *document, TypingCommandOptions options) { ASSERT(document); - + Frame* frame = document->frame(); ASSERT(frame); - + EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparator(); + TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand); + lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator); + lastTypingCommand->insertParagraphSeparator(); return; } - applyCommand(TypingCommand::create(document, InsertParagraphSeparator)); + applyCommand(TypingCommand::create(document, InsertParagraphSeparator, "", options)); } bool TypingCommand::isOpenForMoreTypingCommand(const EditCommand* cmd) @@ -483,7 +489,7 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing) VisiblePosition visibleStart(endingSelection().visibleStart()); // If we have a caret selection on an empty cell, we have nothing to do. - if (isEmptyTableCell(visibleStart.deepEquivalent().node())) + if (isEmptyTableCell(visibleStart.deepEquivalent().deprecatedNode())) return; // If the caret is at the start of a paragraph after a table, move content into the last table cell. @@ -495,14 +501,14 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing) selection.modify(SelectionController::AlterationExtend, DirectionBackward, granularity); // If the caret is just after a table, select the table and don't delete anything. } else if (Node* table = isFirstPositionAfterTable(visibleStart)) { - setEndingSelection(VisibleSelection(positionAfterNode(table), endingSelection().start(), DOWNSTREAM)); + setEndingSelection(VisibleSelection(positionBeforeNode(table), endingSelection().start(), DOWNSTREAM)); typingAddedToOpenCommand(DeleteKey); return; } selectionToDelete = selection.selection(); - if (granularity == CharacterGranularity && selectionToDelete.end().node() == selectionToDelete.start().node() && selectionToDelete.end().deprecatedEditingOffset() - selectionToDelete.start().deprecatedEditingOffset() > 1) { + if (granularity == CharacterGranularity && selectionToDelete.end().deprecatedNode() == selectionToDelete.start().deprecatedNode() && selectionToDelete.end().deprecatedEditingOffset() - selectionToDelete.start().deprecatedEditingOffset() > 1) { // If there are multiple Unicode code points to be deleted, adjust the range to match platform conventions. selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(BackwardDeletion)); } @@ -570,8 +576,8 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki if (visibleEnd == endOfParagraph(visibleEnd)) downstreamEnd = visibleEnd.next(true).deepEquivalent().downstream(); // When deleting tables: Select the table first, then perform the deletion - if (downstreamEnd.node() && downstreamEnd.node()->renderer() && downstreamEnd.node()->renderer()->isTable() && !downstreamEnd.deprecatedEditingOffset()) { - setEndingSelection(VisibleSelection(endingSelection().end(), lastDeepEditingPositionForNode(downstreamEnd.node()), DOWNSTREAM)); + if (downstreamEnd.deprecatedNode() && downstreamEnd.deprecatedNode()->renderer() && downstreamEnd.deprecatedNode()->renderer()->isTable() && !downstreamEnd.deprecatedEditingOffset()) { + setEndingSelection(VisibleSelection(endingSelection().end(), lastDeepEditingPositionForNode(downstreamEnd.deprecatedNode()), DOWNSTREAM)); typingAddedToOpenCommand(ForwardDeleteKey); return; } @@ -588,15 +594,15 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on // the current state of the document and we'll get the wrong result. Position extent = startingSelection().end(); - if (extent.node() != selectionToDelete.end().node()) + if (extent.deprecatedNode() != selectionToDelete.end().deprecatedNode()) extent = selectionToDelete.extent(); else { int extraCharacters; - if (selectionToDelete.start().node() == selectionToDelete.end().node()) + if (selectionToDelete.start().deprecatedNode() == selectionToDelete.end().deprecatedNode()) extraCharacters = selectionToDelete.end().deprecatedEditingOffset() - selectionToDelete.start().deprecatedEditingOffset(); else extraCharacters = selectionToDelete.end().deprecatedEditingOffset(); - extent = Position(extent.node(), extent.deprecatedEditingOffset() + extraCharacters, Position::PositionIsOffsetInAnchor); + extent = Position(extent.deprecatedNode(), extent.deprecatedEditingOffset() + extraCharacters, Position::PositionIsOffsetInAnchor); } selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent); } diff --git a/Source/WebCore/editing/TypingCommand.h b/Source/WebCore/editing/TypingCommand.h index b34bdc1..beb4d02 100644 --- a/Source/WebCore/editing/TypingCommand.h +++ b/Source/WebCore/editing/TypingCommand.h @@ -48,13 +48,20 @@ public: TextCompositionConfirm }; + enum TypingCommandOption { + SelectInsertedText = 1 << 0, + KillRing = 1 << 1, + RetainAutocorrectionIndicator = 1 << 2 + }; + typedef unsigned TypingCommandOptions; + static void deleteSelection(Document*, bool smartDelete = false); static void deleteKeyPressed(Document*, bool smartDelete = false, TextGranularity = CharacterGranularity, bool killRing = false); static void forwardDeleteKeyPressed(Document*, bool smartDelete = false, TextGranularity = CharacterGranularity, bool killRing = false); - static void insertText(Document*, const String&, bool selectInsertedText = false, TextCompositionType = TextCompositionNone); - static void insertText(Document*, const String&, const VisibleSelection&, bool selectInsertedText = false, TextCompositionType = TextCompositionNone); - static void insertLineBreak(Document*); - static void insertParagraphSeparator(Document*); + static void insertText(Document*, const String&, TypingCommandOptions, TextCompositionType = TextCompositionNone); + static void insertText(Document*, const String&, const VisibleSelection&, TypingCommandOptions, TextCompositionType = TextCompositionNone); + static void insertLineBreak(Document*, TypingCommandOptions); + static void insertParagraphSeparator(Document*, TypingCommandOptions); static void insertParagraphSeparatorInQuotedContent(Document*); static bool isOpenForMoreTypingCommand(const EditCommand*); static void closeTyping(EditCommand*); @@ -73,17 +80,17 @@ public: void setCompositionType(TextCompositionType type) { m_compositionType = type; } private: - static PassRefPtr<TypingCommand> create(Document* document, ETypingCommand command, const String& text = "", bool selectInsertedText = false, TextGranularity granularity = CharacterGranularity, bool killRing = false) + static PassRefPtr<TypingCommand> create(Document* document, ETypingCommand command, const String& text = "", TypingCommandOptions options = 0, TextGranularity granularity = CharacterGranularity) { - return adoptRef(new TypingCommand(document, command, text, selectInsertedText, granularity, TextCompositionNone, killRing)); + return adoptRef(new TypingCommand(document, command, text, options, granularity, TextCompositionNone)); } - static PassRefPtr<TypingCommand> create(Document* document, ETypingCommand command, const String& text, bool selectInsertedText, TextCompositionType compositionType) + static PassRefPtr<TypingCommand> create(Document* document, ETypingCommand command, const String& text, TypingCommandOptions options, TextCompositionType compositionType) { - return adoptRef(new TypingCommand(document, command, text, selectInsertedText, CharacterGranularity, compositionType, false)); + return adoptRef(new TypingCommand(document, command, text, options, CharacterGranularity, compositionType)); } - TypingCommand(Document*, ETypingCommand, const String& text, bool selectInsertedText, TextGranularity, TextCompositionType, bool killRing); + TypingCommand(Document*, ETypingCommand, const String& text, TypingCommandOptions, TextGranularity, TextCompositionType); bool smartDelete() const { return m_smartDelete; } void setSmartDelete(bool smartDelete) { m_smartDelete = smartDelete; } @@ -92,6 +99,8 @@ private: virtual EditAction editingAction() const; virtual bool isTypingCommand() const; virtual bool preservesTypingStyle() const { return m_preservesTypingStyle; } + virtual bool shouldRetainAutocorrectionIndicator() const { return m_shouldRetainAutocorrectionIndicator; } + virtual void setShouldRetainAutocorrectionIndicator(bool retain) { m_shouldRetainAutocorrectionIndicator = retain; } static void updateSelectionIfDifferentFromCurrentSelection(TypingCommand*, Frame*); @@ -114,6 +123,8 @@ private: // characters that were deleted, but only if the typing command being undone // was opened with a backward delete. bool m_openedByBackwardDelete; + + bool m_shouldRetainAutocorrectionIndicator; }; } // namespace WebCore diff --git a/Source/WebCore/editing/VisiblePosition.cpp b/Source/WebCore/editing/VisiblePosition.cpp index 5999fa6..10798ad 100644 --- a/Source/WebCore/editing/VisiblePosition.cpp +++ b/Source/WebCore/editing/VisiblePosition.cpp @@ -48,12 +48,6 @@ VisiblePosition::VisiblePosition(const Position &pos, EAffinity affinity) init(pos, affinity); } -VisiblePosition::VisiblePosition(Node *node, int offset, EAffinity affinity) -{ - ASSERT(offset >= 0); - init(Position(node, offset), affinity); -} - void VisiblePosition::init(const Position& position, EAffinity affinity) { m_affinity = affinity; @@ -106,7 +100,7 @@ VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const Position VisiblePosition::leftVisuallyDistinctCandidate() const { Position p = m_deepPosition; - if (!p.node()) + if (p.isNull()) return Position(); Position downstreamStart = p.downstream(); @@ -242,7 +236,7 @@ VisiblePosition VisiblePosition::left(bool stayInEditableContent) const Position VisiblePosition::rightVisuallyDistinctCandidate() const { Position p = m_deepPosition; - if (!p.node()) + if (p.isNull()) return Position(); Position downstreamStart = p.downstream(); @@ -384,7 +378,7 @@ VisiblePosition VisiblePosition::honorEditableBoundaryAtOrBefore(const VisiblePo Node* highestRoot = highestEditableRoot(deepEquivalent()); // Return empty position if pos is not somewhere inside the editable region containing this position - if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot)) + if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(highestRoot)) return VisiblePosition(); // Return pos itself if the two are from the very same editable region, or both are non-editable @@ -410,7 +404,7 @@ VisiblePosition VisiblePosition::honorEditableBoundaryAtOrAfter(const VisiblePos Node* highestRoot = highestEditableRoot(deepEquivalent()); // Return empty position if pos is not somewhere inside the editable region containing this position - if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot)) + if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(highestRoot)) return VisiblePosition(); // Return pos itself if the two are from the very same editable region, or both are non-editable @@ -451,12 +445,13 @@ Position VisiblePosition::canonicalPosition(const Position& passedPosition) // To fix this, we need to either a) add code to all paintCarets to pass the responsibility off to // the appropriate renderer for VisiblePosition's like these, or b) canonicalize to the rightmost candidate // unless the affinity is upstream. - Node* node = position.node(); - if (!node) + if (position.isNull()) return Position(); - ASSERT(node->document()); - node->document()->updateLayoutIgnorePendingStylesheets(); + Node* node = position.containerNode(); + + ASSERT(position.document()); + position.document()->updateLayoutIgnorePendingStylesheets(); Position candidate = position.upstream(); if (candidate.isCandidate()) @@ -469,19 +464,19 @@ Position VisiblePosition::canonicalPosition(const Position& passedPosition) // blocks or enter new ones), we search forward and backward until we find one. Position next = canonicalizeCandidate(nextCandidate(position)); Position prev = canonicalizeCandidate(previousCandidate(position)); - Node* nextNode = next.node(); - Node* prevNode = prev.node(); + Node* nextNode = next.deprecatedNode(); + Node* prevNode = prev.deprecatedNode(); // The new position must be in the same editable element. Enforce that first. // Unless the descent is from a non-editable html element to an editable body. - if (node->hasTagName(htmlTag) && !node->isContentEditable() && node->document()->body() && node->document()->body()->isContentEditable()) + if (node && node->hasTagName(htmlTag) && !node->isContentEditable() && node->document()->body() && node->document()->body()->isContentEditable()) return next.isNotNull() ? next : prev; Node* editingRoot = editableRootForPosition(position); // If the html element is editable, descending into its body will look like a descent // from non-editable to editable content since rootEditableElement() always stops at the body. - if ((editingRoot && editingRoot->hasTagName(htmlTag)) || position.node()->isDocumentNode()) + if ((editingRoot && editingRoot->hasTagName(htmlTag)) || position.deprecatedNode()->isDocumentNode()) return next.isNotNull() ? next : prev; bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot; @@ -496,7 +491,7 @@ Position VisiblePosition::canonicalPosition(const Position& passedPosition) return Position(); // The new position should be in the same block flow element. Favor that. - Node *originalBlock = node->enclosingBlockFlowElement(); + Node* originalBlock = node ? node->enclosingBlockFlowElement() : 0; bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) && nextNode != originalBlock; bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) && prevNode != originalBlock; if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock) @@ -510,11 +505,12 @@ UChar32 VisiblePosition::characterAfter() const // We canonicalize to the first of two equivalent candidates, but the second of the two candidates // is the one that will be inside the text node containing the character after this visible position. Position pos = m_deepPosition.downstream(); - Node* node = pos.node(); - if (!node || !node->isTextNode()) + Node* node = pos.containerNode(); + if (!node || !node->isTextNode() || pos.anchorType() == Position::PositionIsAfterAnchor) return 0; - Text* textNode = static_cast<Text*>(pos.node()); - unsigned offset = pos.deprecatedEditingOffset(); + ASSERT(pos.anchorType() == Position::PositionIsBeforeAnchor || pos.anchorType() == Position::PositionIsOffsetInAnchor); + Text* textNode = static_cast<Text*>(pos.containerNode()); + unsigned offset = pos.anchorType() == Position::PositionIsOffsetInAnchor ? pos.offsetInContainerNode() : 0; unsigned length = textNode->length(); if (offset >= length) return 0; @@ -527,11 +523,11 @@ UChar32 VisiblePosition::characterAfter() const IntRect VisiblePosition::localCaretRect(RenderObject*& renderer) const { - Node* node = m_deepPosition.node(); - if (!node) { + if (m_deepPosition.isNull()) { renderer = 0; return IntRect(); } + Node* node = m_deepPosition.anchorNode(); renderer = node->renderer(); if (!renderer) @@ -570,16 +566,18 @@ int VisiblePosition::xOffsetForVerticalNavigation() const return renderer->localToAbsolute(localRect.location()).x(); } +#ifndef NDEBUG + void VisiblePosition::debugPosition(const char* msg) const { if (isNull()) fprintf(stderr, "Position [%s]: null\n", msg); - else - fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, m_deepPosition.node()->nodeName().utf8().data(), m_deepPosition.node(), m_deepPosition.deprecatedEditingOffset()); + else { + fprintf(stderr, "Position [%s]: %s, ", msg, m_deepPosition.deprecatedNode()->nodeName().utf8().data()); + m_deepPosition.showAnchorTypeAndOffset(); + } } -#ifndef NDEBUG - void VisiblePosition::formatForDebugger(char* buffer, unsigned length) const { m_deepPosition.formatForDebugger(buffer, length); @@ -599,19 +597,19 @@ PassRefPtr<Range> makeRange(const VisiblePosition &start, const VisiblePosition Position s = start.deepEquivalent().parentAnchoredEquivalent(); Position e = end.deepEquivalent().parentAnchoredEquivalent(); - return Range::create(s.node()->document(), s.node(), s.deprecatedEditingOffset(), e.node(), e.deprecatedEditingOffset()); + return Range::create(s.containerNode()->document(), s.containerNode(), s.offsetInContainerNode(), e.containerNode(), e.offsetInContainerNode()); } VisiblePosition startVisiblePosition(const Range *r, EAffinity affinity) { int exception = 0; - return VisiblePosition(r->startContainer(exception), r->startOffset(exception), affinity); + return VisiblePosition(Position(r->startContainer(exception), r->startOffset(exception), Position::PositionIsOffsetInAnchor), affinity); } VisiblePosition endVisiblePosition(const Range *r, EAffinity affinity) { int exception = 0; - return VisiblePosition(r->endContainer(exception), r->endOffset(exception), affinity); + return VisiblePosition(Position(r->endContainer(exception), r->endOffset(exception), Position::PositionIsOffsetInAnchor), affinity); } bool setStart(Range *r, const VisiblePosition &visiblePosition) @@ -620,7 +618,7 @@ bool setStart(Range *r, const VisiblePosition &visiblePosition) return false; Position p = visiblePosition.deepEquivalent().parentAnchoredEquivalent(); int code = 0; - r->setStart(p.node(), p.deprecatedEditingOffset(), code); + r->setStart(p.containerNode(), p.offsetInContainerNode(), code); return code == 0; } @@ -630,7 +628,7 @@ bool setEnd(Range *r, const VisiblePosition &visiblePosition) return false; Position p = visiblePosition.deepEquivalent().parentAnchoredEquivalent(); int code = 0; - r->setEnd(p.node(), p.deprecatedEditingOffset(), code); + r->setEnd(p.containerNode(), p.offsetInContainerNode(), code); return code == 0; } @@ -639,31 +637,31 @@ Element* enclosingBlockFlowElement(const VisiblePosition &visiblePosition) if (visiblePosition.isNull()) return NULL; - return visiblePosition.deepEquivalent().node()->enclosingBlockFlowElement(); + return visiblePosition.deepEquivalent().deprecatedNode()->enclosingBlockFlowElement(); } bool isFirstVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) { if (visiblePosition.isNull()) return false; - - if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node)) + + if (!visiblePosition.deepEquivalent().containerNode()->isDescendantOf(node)) return false; - + VisiblePosition previous = visiblePosition.previous(); - return previous.isNull() || !previous.deepEquivalent().node()->isDescendantOf(node); + return previous.isNull() || !previous.deepEquivalent().deprecatedNode()->isDescendantOf(node); } bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) { if (visiblePosition.isNull()) return false; - - if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node)) + + if (!visiblePosition.deepEquivalent().containerNode()->isDescendantOf(node)) return false; - + VisiblePosition next = visiblePosition.next(); - return next.isNull() || !next.deepEquivalent().node()->isDescendantOf(node); + return next.isNull() || !next.deepEquivalent().deprecatedNode()->isDescendantOf(node); } } // namespace WebCore diff --git a/Source/WebCore/editing/VisiblePosition.h b/Source/WebCore/editing/VisiblePosition.h index e649b68..008d676 100644 --- a/Source/WebCore/editing/VisiblePosition.h +++ b/Source/WebCore/editing/VisiblePosition.h @@ -54,7 +54,6 @@ public: // NOTE: UPSTREAM affinity will be used only if pos is at end of a wrapped line, // otherwise it will be converted to DOWNSTREAM VisiblePosition() : m_affinity(VP_DEFAULT_AFFINITY) { } - VisiblePosition(Node*, int offset, EAffinity); VisiblePosition(const Position&, EAffinity = VP_DEFAULT_AFFINITY); void clear() { m_deepPosition.clear(); } @@ -80,10 +79,8 @@ public: UChar32 characterAfter() const; UChar32 characterBefore() const { return previous().characterAfter(); } - - void debugPosition(const char* msg = "") const; - - Element* rootEditableElement() const { return m_deepPosition.isNotNull() ? m_deepPosition.node()->rootEditableElement() : 0; } + + Element* rootEditableElement() const { return m_deepPosition.isNotNull() ? m_deepPosition.deprecatedNode()->rootEditableElement() : 0; } void getInlineBoxAndOffset(InlineBox*& inlineBox, int& caretOffset) const { @@ -104,6 +101,7 @@ public: int xOffsetForVerticalNavigation() const; #ifndef NDEBUG + void debugPosition(const char* msg = "") const; void formatForDebugger(char* buffer, unsigned length) const; void showTreeForThis() const; #endif diff --git a/Source/WebCore/editing/VisibleSelection.cpp b/Source/WebCore/editing/VisibleSelection.cpp index 9096dc5..75531ca 100644 --- a/Source/WebCore/editing/VisibleSelection.cpp +++ b/Source/WebCore/editing/VisibleSelection.cpp @@ -122,7 +122,7 @@ PassRefPtr<Range> VisibleSelection::firstRange() const return 0; Position start = m_start.parentAnchoredEquivalent(); Position end = m_end.parentAnchoredEquivalent(); - return Range::create(start.node()->document(), start, end); + return Range::create(start.anchorNode()->document(), start, end); } PassRefPtr<Range> VisibleSelection::toNormalizedRange() const @@ -134,7 +134,7 @@ PassRefPtr<Range> VisibleSelection::toNormalizedRange() const // in the course of running edit commands which modify the DOM. // Failing to call this can result in equivalentXXXPosition calls returning // incorrect results. - m_start.node()->document()->updateLayout(); + m_start.anchorNode()->document()->updateLayout(); // Check again, because updating layout can clear the selection. if (isNone()) @@ -178,7 +178,7 @@ PassRefPtr<Range> VisibleSelection::toNormalizedRange() const // VisibleSelections are supposed to always be valid. This constructor will ASSERT // if a valid range could not be created, which is fine for this callsite. - return Range::create(s.node()->document(), s, e); + return Range::create(s.anchorNode()->document(), s, e); } bool VisibleSelection::expandUsingGranularity(TextGranularity granularity) @@ -192,7 +192,7 @@ bool VisibleSelection::expandUsingGranularity(TextGranularity granularity) static PassRefPtr<Range> makeSearchRange(const Position& pos) { - Node* n = pos.node(); + Node* n = pos.deprecatedNode(); if (!n) return 0; Document* d = n->document(); @@ -208,7 +208,7 @@ static PassRefPtr<Range> makeSearchRange(const Position& pos) Position start(pos.parentAnchoredEquivalent()); searchRange->selectNodeContents(boundary, ec); - searchRange->setStart(start.node(), start.deprecatedEditingOffset(), ec); + searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), ec); ASSERT(!ec); if (ec) @@ -296,7 +296,7 @@ void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(Text VisiblePosition wordEnd(endOfWord(originalEnd, side)); VisiblePosition end(wordEnd); - if (isEndOfParagraph(originalEnd) && !isEmptyTableCell(m_start.node())) { + if (isEndOfParagraph(originalEnd) && !isEmptyTableCell(m_start.deprecatedNode())) { // Select the paragraph break (the space from the end of a paragraph to the start of // the next one) to match TextEdit. end = wordEnd.next(); @@ -457,7 +457,7 @@ void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries() Node* startRoot = highestEditableRoot(m_start); Node* endRoot = highestEditableRoot(m_end); - Node* baseEditableAncestor = lowestEditableAncestor(m_base.node()); + Node* baseEditableAncestor = lowestEditableAncestor(m_base.deprecatedNode()); // The base, start and end are all in the same region. No adjustment necessary. if (baseRoot == startRoot && baseRoot == endRoot) @@ -492,17 +492,17 @@ void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries() // The selection ends in editable content or non-editable content inside a different editable ancestor, // move backward until non-editable content inside the same lowest editable ancestor is reached. - Node* endEditableAncestor = lowestEditableAncestor(m_end.node()); + Node* endEditableAncestor = lowestEditableAncestor(m_end.deprecatedNode()); if (endRoot || endEditableAncestor != baseEditableAncestor) { Position p = previousVisuallyDistinctCandidate(m_end); Node* shadowAncestor = endRoot ? endRoot->shadowAncestorNode() : 0; if (p.isNull() && endRoot && (shadowAncestor != endRoot)) p = lastDeepEditingPositionForNode(shadowAncestor); - while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) { + while (p.isNotNull() && !(lowestEditableAncestor(p.deprecatedNode()) == baseEditableAncestor && !isEditablePosition(p))) { Node* root = editableRootForPosition(p); shadowAncestor = root ? root->shadowAncestorNode() : 0; - p = isAtomicNode(p.node()) ? positionInParentBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p); + p = isAtomicNode(p.deprecatedNode()) ? positionInParentBeforeNode(p.deprecatedNode()) : previousVisuallyDistinctCandidate(p); if (p.isNull() && (shadowAncestor != root)) p = lastDeepEditingPositionForNode(shadowAncestor); } @@ -522,16 +522,16 @@ void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries() // The selection starts in editable content or non-editable content inside a different editable ancestor, // move forward until non-editable content inside the same lowest editable ancestor is reached. - Node* startEditableAncestor = lowestEditableAncestor(m_start.node()); + Node* startEditableAncestor = lowestEditableAncestor(m_start.deprecatedNode()); if (startRoot || startEditableAncestor != baseEditableAncestor) { Position p = nextVisuallyDistinctCandidate(m_start); Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() : 0; if (p.isNull() && startRoot && (shadowAncestor != startRoot)) p = positionBeforeNode(shadowAncestor); - while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) { + while (p.isNotNull() && !(lowestEditableAncestor(p.deprecatedNode()) == baseEditableAncestor && !isEditablePosition(p))) { Node* root = editableRootForPosition(p); shadowAncestor = root ? root->shadowAncestorNode() : 0; - p = isAtomicNode(p.node()) ? positionInParentAfterNode(p.node()) : nextVisuallyDistinctCandidate(p); + p = isAtomicNode(p.deprecatedNode()) ? positionInParentAfterNode(p.deprecatedNode()) : nextVisuallyDistinctCandidate(p); if (p.isNull() && (shadowAncestor != root)) p = positionBeforeNode(shadowAncestor); } @@ -551,7 +551,7 @@ void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries() } // Correct the extent if necessary. - if (baseEditableAncestor != lowestEditableAncestor(m_extent.node())) + if (baseEditableAncestor != lowestEditableAncestor(m_extent.deprecatedNode())) m_extent = m_baseIsFirst ? m_end : m_start; } @@ -572,33 +572,30 @@ Element* VisibleSelection::rootEditableElement() const Node* VisibleSelection::shadowTreeRootNode() const { - return start().node() ? start().node()->shadowTreeRootNode() : 0; + return start().deprecatedNode() ? start().deprecatedNode()->shadowTreeRootNode() : 0; } +#ifndef NDEBUG + void VisibleSelection::debugPosition() const { - if (!m_start.node()) - return; + fprintf(stderr, "VisibleSelection ===============\n"); - fprintf(stderr, "VisibleSelection =================\n"); - - if (m_start == m_end) { - Position pos = m_start; - fprintf(stderr, "pos: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.deprecatedEditingOffset()); + if (!m_start.anchorNode()) + fputs("pos: null", stderr); + else if (m_start == m_end) { + fprintf(stderr, "pos: %s ", m_start.anchorNode()->nodeName().utf8().data()); + m_start.showAnchorTypeAndOffset(); } else { - Position pos = m_start; - fprintf(stderr, "start: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.deprecatedEditingOffset()); - fprintf(stderr, "-----------------------------------\n"); - pos = m_end; - fprintf(stderr, "end: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.deprecatedEditingOffset()); - fprintf(stderr, "-----------------------------------\n"); + fprintf(stderr, "start: %s ", m_start.anchorNode()->nodeName().utf8().data()); + m_start.showAnchorTypeAndOffset(); + fprintf(stderr, "end: %s ", m_end.anchorNode()->nodeName().utf8().data()); + m_end.showAnchorTypeAndOffset(); } fprintf(stderr, "================================\n"); } -#ifndef NDEBUG - void VisibleSelection::formatForDebugger(char* buffer, unsigned length) const { String result; @@ -622,9 +619,12 @@ void VisibleSelection::formatForDebugger(char* buffer, unsigned length) const void VisibleSelection::showTreeForThis() const { - if (start().node()) { - start().node()->showTreeAndMark(start().node(), "S", end().node(), "E"); - fprintf(stderr, "start offset: %d, end offset: %d\n", start().deprecatedEditingOffset(), end().deprecatedEditingOffset()); + if (start().anchorNode()) { + start().anchorNode()->showTreeAndMark(start().anchorNode(), "S", end().anchorNode(), "E"); + fputs("start: ", stderr); + start().showAnchorTypeAndOffset(); + fputs("end: ", stderr); + end().showAnchorTypeAndOffset(); } } diff --git a/Source/WebCore/editing/VisibleSelection.h b/Source/WebCore/editing/VisibleSelection.h index 10b5c8f..5d52a08 100644 --- a/Source/WebCore/editing/VisibleSelection.h +++ b/Source/WebCore/editing/VisibleSelection.h @@ -97,10 +97,9 @@ public: bool isContentEditable() const; bool isContentRichlyEditable() const; Node* shadowTreeRootNode() const; - - void debugPosition() const; #ifndef NDEBUG + void debugPosition() const; void formatForDebugger(char* buffer, unsigned length) const; void showTreeForThis() const; #endif diff --git a/Source/WebCore/editing/chromium/EditorChromium.cpp b/Source/WebCore/editing/chromium/EditorChromium.cpp index ca7f41a..2bfb5af 100644 --- a/Source/WebCore/editing/chromium/EditorChromium.cpp +++ b/Source/WebCore/editing/chromium/EditorChromium.cpp @@ -39,7 +39,7 @@ namespace WebCore { PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy, Frame* frame) { - return ClipboardChromium::create(Clipboard::CopyAndPaste, policy, frame); + return ClipboardChromium::create(Clipboard::CopyAndPaste, ChromiumDataObject::create(Clipboard::CopyAndPaste), policy, frame); } } // namespace WebCore diff --git a/Source/WebCore/editing/chromium/SelectionControllerChromium.cpp b/Source/WebCore/editing/chromium/SelectionControllerChromium.cpp index d627d9b..f40134b 100644 --- a/Source/WebCore/editing/chromium/SelectionControllerChromium.cpp +++ b/Source/WebCore/editing/chromium/SelectionControllerChromium.cpp @@ -41,7 +41,7 @@ void SelectionController::notifyAccessibilityForSelectionChange() // FIXME: Support editable text in chromium. if (AXObjectCache::accessibilityEnabled() && m_selection.start().isNotNull() && m_selection.end().isNotNull()) { Document* document = m_frame->document(); - document->axObjectCache()->postNotification(m_selection.start().node()->renderer(), AXObjectCache::AXSelectedTextChanged, false); + document->axObjectCache()->postNotification(m_selection.start().deprecatedNode()->renderer(), AXObjectCache::AXSelectedTextChanged, false); } } diff --git a/Source/WebCore/editing/gtk/SelectionControllerGtk.cpp b/Source/WebCore/editing/gtk/SelectionControllerGtk.cpp index 19097b2..264bdc3 100644 --- a/Source/WebCore/editing/gtk/SelectionControllerGtk.cpp +++ b/Source/WebCore/editing/gtk/SelectionControllerGtk.cpp @@ -84,7 +84,7 @@ void SelectionController::notifyAccessibilityForSelectionChange() if (!m_selection.start().isNotNull() || !m_selection.end().isNotNull()) return; - RenderObject* focusedNode = m_selection.end().node()->renderer(); + RenderObject* focusedNode = m_selection.end().deprecatedNode()->renderer(); AccessibilityObject* accessibilityObject = m_frame->document()->axObjectCache()->getOrCreate(focusedNode); // Need to check this as getOrCreate could return 0, diff --git a/Source/WebCore/editing/htmlediting.cpp b/Source/WebCore/editing/htmlediting.cpp index cb157d2..a078d91 100644 --- a/Source/WebCore/editing/htmlediting.cpp +++ b/Source/WebCore/editing/htmlediting.cpp @@ -95,9 +95,9 @@ bool canHaveChildrenForEditing(const Node* node) // could be inside a shadow tree. Only works for non-null values. int comparePositions(const Position& a, const Position& b) { - Node* nodeA = a.node(); + Node* nodeA = a.deprecatedNode(); ASSERT(nodeA); - Node* nodeB = b.node(); + Node* nodeB = b.deprecatedNode(); ASSERT(nodeB); int offsetA = a.deprecatedEditingOffset(); int offsetB = b.deprecatedEditingOffset(); @@ -134,7 +134,7 @@ int comparePositions(const VisiblePosition& a, const VisiblePosition& b) Node* highestEditableRoot(const Position& position) { - Node* node = position.node(); + Node* node = position.deprecatedNode(); if (!node) return 0; @@ -173,7 +173,7 @@ Node* lowestEditableAncestor(Node* node) bool isEditablePosition(const Position& p) { - Node* node = p.node(); + Node* node = p.deprecatedNode(); if (!node) return false; @@ -185,14 +185,14 @@ bool isEditablePosition(const Position& p) bool isAtUnsplittableElement(const Position& pos) { - Node* node = pos.node(); + Node* node = pos.deprecatedNode(); return (node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell)); } bool isRichlyEditablePosition(const Position& p) { - Node* node = p.node(); + Node* node = p.deprecatedNode(); if (!node) return false; @@ -204,7 +204,7 @@ bool isRichlyEditablePosition(const Position& p) Element* editableRootForPosition(const Position& p) { - Node* node = p.node(); + Node* node = p.deprecatedNode(); if (!node) return 0; @@ -282,14 +282,14 @@ VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& positio Position p = position; - if (Node* shadowAncestor = p.node()->shadowAncestorNode()) - if (shadowAncestor != p.node()) + if (Node* shadowAncestor = p.deprecatedNode()->shadowAncestorNode()) + if (shadowAncestor != p.deprecatedNode()) p = lastDeepEditingPositionForNode(shadowAncestor); - while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) - p = isAtomicNode(p.node()) ? positionInParentAfterNode(p.node()) : nextVisuallyDistinctCandidate(p); + while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot)) + p = isAtomicNode(p.deprecatedNode()) ? positionInParentAfterNode(p.deprecatedNode()) : nextVisuallyDistinctCandidate(p); - if (p.node() && p.node() != highestRoot && !p.node()->isDescendantOf(highestRoot)) + if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot)) return VisiblePosition(); return VisiblePosition(p); @@ -303,14 +303,14 @@ VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& positio Position p = position; - if (Node* shadowAncestor = p.node()->shadowAncestorNode()) - if (shadowAncestor != p.node()) + if (Node* shadowAncestor = p.deprecatedNode()->shadowAncestorNode()) + if (shadowAncestor != p.deprecatedNode()) p = firstDeepEditingPositionForNode(shadowAncestor); - while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) - p = isAtomicNode(p.node()) ? positionInParentBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p); + while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot)) + p = isAtomicNode(p.deprecatedNode()) ? positionInParentBeforeNode(p.deprecatedNode()) : previousVisuallyDistinctCandidate(p); - if (p.node() && p.node() != highestRoot && !p.node()->isDescendantOf(highestRoot)) + if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot)) return VisiblePosition(); return VisiblePosition(p); @@ -421,12 +421,12 @@ bool isSpecialElement(const Node *n) static Node* firstInSpecialElement(const Position& pos) { - // FIXME: This begins at pos.node(), which doesn't necessarily contain pos (suppose pos was [img, 0]). See <rdar://problem/5027702>. - Node* rootEditableElement = pos.node()->rootEditableElement(); - for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) + // FIXME: This begins at pos.deprecatedNode(), which doesn't necessarily contain pos (suppose pos was [img, 0]). See <rdar://problem/5027702>. + Node* rootEditableElement = pos.deprecatedNode()->rootEditableElement(); + for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) if (isSpecialElement(n)) { VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); - VisiblePosition firstInElement = VisiblePosition(n, 0, DOWNSTREAM); + VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(n), DOWNSTREAM); if (isTableElement(n) && vPos == firstInElement.next()) return n; if (vPos == firstInElement) @@ -437,12 +437,12 @@ static Node* firstInSpecialElement(const Position& pos) static Node* lastInSpecialElement(const Position& pos) { - // FIXME: This begins at pos.node(), which doesn't necessarily contain pos (suppose pos was [img, 0]). See <rdar://problem/5027702>. - Node* rootEditableElement = pos.node()->rootEditableElement(); - for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) + // FIXME: This begins at pos.deprecatedNode(), which doesn't necessarily contain pos (suppose pos was [img, 0]). See <rdar://problem/5027702>. + Node* rootEditableElement = pos.deprecatedNode()->rootEditableElement(); + for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) if (isSpecialElement(n)) { VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); - VisiblePosition lastInElement = VisiblePosition(n, n->childNodeCount(), DOWNSTREAM); + VisiblePosition lastInElement = VisiblePosition(Position(n, n->childNodeCount(), Position::PositionIsOffsetInAnchor), DOWNSTREAM); if (isTableElement(n) && vPos == lastInElement.previous()) return n; if (vPos == lastInElement) @@ -462,7 +462,7 @@ Position positionBeforeContainingSpecialElement(const Position& pos, Node** cont if (!n) return pos; Position result = positionInParentBeforeNode(n); - if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement()) + if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement()) return pos; if (containingSpecialElement) *containingSpecialElement = n; @@ -480,7 +480,7 @@ Position positionAfterContainingSpecialElement(const Position& pos, Node **conta if (!n) return pos; Position result = positionInParentAfterNode(n); - if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement()) + if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement()) return pos; if (containingSpecialElement) *containingSpecialElement = n; @@ -499,8 +499,8 @@ Position positionOutsideContainingSpecialElement(const Position &pos, Node **con Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition) { Position upstream(visiblePosition.deepEquivalent().upstream()); - if (upstream.node() && upstream.node()->renderer() && upstream.node()->renderer()->isTable() && upstream.atLastEditingPositionForNode()) - return upstream.node(); + if (upstream.deprecatedNode() && upstream.deprecatedNode()->renderer() && upstream.deprecatedNode()->renderer()->isTable() && upstream.atLastEditingPositionForNode()) + return upstream.deprecatedNode(); return 0; } @@ -508,8 +508,8 @@ Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition) Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition) { Position downstream(visiblePosition.deepEquivalent().downstream()); - if (downstream.node() && downstream.node()->renderer() && downstream.node()->renderer()->isTable() && downstream.atFirstEditingPositionForNode()) - return downstream.node(); + if (downstream.deprecatedNode() && downstream.deprecatedNode()->renderer() && downstream.deprecatedNode()->renderer()->isTable() && downstream.atFirstEditingPositionForNode()) + return downstream.deprecatedNode(); return 0; } @@ -519,7 +519,7 @@ VisiblePosition visiblePositionBeforeNode(Node* node) { ASSERT(node); if (node->childNodeCount()) - return VisiblePosition(node, 0, DOWNSTREAM); + return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM); ASSERT(node->parentNode()); return positionInParentBeforeNode(node); } @@ -529,7 +529,7 @@ VisiblePosition visiblePositionAfterNode(Node* node) { ASSERT(node); if (node->childNodeCount()) - return VisiblePosition(node, node->childNodeCount(), DOWNSTREAM); + return VisiblePosition(lastPositionInOrAfterNode(node), DOWNSTREAM); ASSERT(node->parentNode()); return positionInParentAfterNode(node); } @@ -591,7 +591,7 @@ Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName) return 0; Node* root = highestEditableRoot(p); - for (Node* n = p.node(); n; n = n->parentNode()) { + for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) { if (root && !n->isContentEditable()) continue; if (n->hasTagName(tagName)) @@ -609,7 +609,7 @@ Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), return 0; Node* root = highestEditableRoot(p); - for (Node* n = p.node(); n; n = n->parentNode()) { + for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) { // Don't return a non-editable node if the input position was editable, since // the callers from editing will no doubt want to perform editing inside the returned node. if (root && !n->isContentEditable() && onlyReturnEditableNodes) @@ -627,7 +627,7 @@ Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const N { Node* highest = 0; Node* root = highestEditableRoot(p); - for (Node* n = p.node(); n; n = n->parentNode()) { + for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) { if ((*nodeIsOfType)(n)) highest = n; if (n == root) @@ -647,7 +647,7 @@ Node* enclosingAnchorElement(const Position& p) if (p.isNull()) return 0; - Node* node = p.node(); + Node* node = p.deprecatedNode(); while (node && !(node->isElementNode() && node->isLink())) node = node->parentNode(); return node; @@ -717,7 +717,7 @@ static Node* appendedSublist(Node* listItem) Node* enclosingEmptyListItem(const VisiblePosition& visiblePos) { // Check that position is on a line by itself inside a list item - Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().node()); + Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode()); if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos)) return 0; @@ -883,7 +883,7 @@ bool isNodeInTextFormControl(Node* node) Position positionBeforeTabSpan(const Position& pos) { - Node *node = pos.node(); + Node* node = pos.deprecatedNode(); if (isTabSpanTextNode(node)) node = tabSpanNode(node); else if (!isTabSpanNode(node)) @@ -944,7 +944,7 @@ Node *nearestMailBlockquote(const Node *node) unsigned numEnclosingMailBlockquotes(const Position& p) { unsigned num = 0; - for (Node* n = p.node(); n; n = n->parentNode()) + for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) if (isMailBlockquote(n)) num++; @@ -1015,7 +1015,7 @@ VisibleSelection selectionForParagraphIteration(const VisibleSelection& original // that we'll want modify is the last one inside the table, not the table itself // (a table is itself a paragraph). if (Node* table = isFirstPositionAfterTable(endOfSelection)) - if (startOfSelection.deepEquivalent().node()->isDescendantOf(table)) + if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table)) newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(true)); // If the start of the selection to modify is just before a table, @@ -1023,7 +1023,7 @@ VisibleSelection selectionForParagraphIteration(const VisibleSelection& original // we'll want to modify is the first one inside the table, not the paragraph // containing the table itself. if (Node* table = isLastPositionBeforeTable(startOfSelection)) - if (endOfSelection.deepEquivalent().node()->isDescendantOf(table)) + if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table)) newSelection = VisibleSelection(startOfSelection.next(true), endOfSelection); return newSelection; @@ -1035,7 +1035,7 @@ int indexForVisiblePosition(const VisiblePosition& visiblePosition) if (visiblePosition.isNull()) return 0; Position p(visiblePosition.deepEquivalent()); - RefPtr<Range> range = Range::create(p.node()->document(), firstPositionInNode(p.anchorNode()->document()->documentElement()), + RefPtr<Range> range = Range::create(p.anchorNode()->document(), firstPositionInNode(p.anchorNode()->document()->documentElement()), p.parentAnchoredEquivalent()); return TextIterator::rangeLength(range.get(), true); } @@ -1114,8 +1114,8 @@ VisibleSelection avoidIntersectionWithNode(const VisibleSelection& selection, No return VisibleSelection(selection); VisibleSelection updatedSelection(selection); - Node* base = selection.base().node(); - Node* extent = selection.extent().node(); + Node* base = selection.base().deprecatedNode(); + Node* extent = selection.extent().deprecatedNode(); ASSERT(base); ASSERT(extent); diff --git a/Source/WebCore/editing/mac/EditorMac.mm b/Source/WebCore/editing/mac/EditorMac.mm index 56b9f71..4c617c0 100644 --- a/Source/WebCore/editing/mac/EditorMac.mm +++ b/Source/WebCore/editing/mac/EditorMac.mm @@ -158,7 +158,7 @@ NSWritingDirection Editor::baseWritingDirectionForSelectionStart() const NSWritingDirection result = NSWritingDirectionLeftToRight; Position pos = m_frame->selection()->selection().visibleStart().deepEquivalent(); - Node* node = pos.node(); + Node* node = pos.deprecatedNode(); if (!node) return result; @@ -208,4 +208,12 @@ void Editor::takeFindStringFromSelection() [findPasteboard setString:nsSelectedText forType:NSStringPboardType]; } +void Editor::writeSelectionToPasteboard(const String& pasteboardName, const Vector<String>& pasteboardTypes) +{ + RetainPtr<NSMutableArray> types(AdoptNS, [[NSMutableArray alloc] init]); + for (size_t i = 0; i < pasteboardTypes.size(); ++i) + [types.get() addObject:pasteboardTypes[i]]; + Pasteboard::writeSelection([NSPasteboard pasteboardWithName:pasteboardName], types.get(), selectedRange().get(), true, m_frame); +} + } // namespace WebCore diff --git a/Source/WebCore/editing/mac/SelectionControllerMac.mm b/Source/WebCore/editing/mac/SelectionControllerMac.mm index 119d406..94199c4 100644 --- a/Source/WebCore/editing/mac/SelectionControllerMac.mm +++ b/Source/WebCore/editing/mac/SelectionControllerMac.mm @@ -51,7 +51,7 @@ void SelectionController::notifyAccessibilityForSelectionChange() Document* document = m_frame->document(); if (AXObjectCache::accessibilityEnabled() && m_selection.start().isNotNull() && m_selection.end().isNotNull()) - document->axObjectCache()->postNotification(m_selection.start().node()->renderer(), AXObjectCache::AXSelectedTextChanged, false); + document->axObjectCache()->postNotification(m_selection.start().deprecatedNode()->renderer(), AXObjectCache::AXSelectedTextChanged, false); // if zoom feature is enabled, insertion point changes should update the zoom if (!UAZoomEnabled() || !m_selection.isCaret()) diff --git a/Source/WebCore/editing/markup.cpp b/Source/WebCore/editing/markup.cpp index 34c3ec7..9d97b3f 100644 --- a/Source/WebCore/editing/markup.cpp +++ b/Source/WebCore/editing/markup.cpp @@ -437,8 +437,8 @@ static bool propertyMissingOrEqualToNone(CSSStyleDeclaration* style, int propert static bool needInterchangeNewlineAfter(const VisiblePosition& v) { VisiblePosition next = v.next(); - Node* upstreamNode = next.deepEquivalent().upstream().node(); - Node* downstreamNode = v.deepEquivalent().downstream().node(); + Node* upstreamNode = next.deepEquivalent().upstream().deprecatedNode(); + Node* downstreamNode = v.deepEquivalent().downstream().deprecatedNode(); // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it. return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode); } @@ -571,7 +571,7 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc } accumulator.appendString(interchangeNewlineString); - startNode = visibleStart.next().deepEquivalent().node(); + startNode = visibleStart.next().deepEquivalent().deprecatedNode(); if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0) >= 0) { if (deleteButton) @@ -764,7 +764,7 @@ PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String Node* styleNode = context->firstNode(); if (!styleNode) { - styleNode = context->startPosition().node(); + styleNode = context->startPosition().deprecatedNode(); if (!styleNode) return 0; } diff --git a/Source/WebCore/editing/visible_units.cpp b/Source/WebCore/editing/visible_units.cpp index 391d6e6..0baacf2 100644 --- a/Source/WebCore/editing/visible_units.cpp +++ b/Source/WebCore/editing/visible_units.cpp @@ -68,7 +68,7 @@ static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearch if (requiresContextForWordBoundary(c.characterBefore())) { RefPtr<Range> forwardsScanRange(d->createRange()); forwardsScanRange->setEndAfter(boundary, ec); - forwardsScanRange->setStart(end.node(), end.deprecatedEditingOffset(), ec); + forwardsScanRange->setStart(end.deprecatedNode(), end.deprecatedEditingOffset(), ec); TextIterator forwardsIterator(forwardsScanRange.get()); while (!forwardsIterator.atEnd()) { const UChar* characters = forwardsIterator.characters(); @@ -82,16 +82,16 @@ static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearch } } - searchRange->setStart(start.node(), start.deprecatedEditingOffset(), ec); - searchRange->setEnd(end.node(), end.deprecatedEditingOffset(), ec); + searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), ec); + searchRange->setEnd(end.deprecatedNode(), end.deprecatedEditingOffset(), ec); ASSERT(!ec); if (ec) return VisiblePosition(); - SimplifiedBackwardsTextIterator it(searchRange.get(), TextIteratorEndsAtEditingBoundary); + SimplifiedBackwardsTextIterator it(searchRange.get()); unsigned next = 0; - bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; + bool inTextSecurityMode = start.deprecatedNode() && start.deprecatedNode()->renderer() && start.deprecatedNode()->renderer()->style()->textSecurity() != TSNONE; bool needMoreContext = false; while (!it.atEnd()) { // iterate to get chunks until the searchFunction returns a non-zero value. @@ -124,7 +124,7 @@ static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearch return VisiblePosition(Position(node, next), DOWNSTREAM); // Use the character iterator to translate the next value into a DOM position. - BackwardsCharacterIterator charIt(searchRange.get(), TextIteratorEndsAtEditingBoundary); + BackwardsCharacterIterator charIt(searchRange.get()); charIt.advance(string.size() - suffixLength - next); return VisiblePosition(charIt.range()->endPosition(), DOWNSTREAM); } @@ -146,7 +146,7 @@ static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunc ExceptionCode ec = 0; if (requiresContextForWordBoundary(c.characterAfter())) { RefPtr<Range> backwardsScanRange(d->createRange()); - backwardsScanRange->setEnd(start.node(), start.deprecatedEditingOffset(), ec); + backwardsScanRange->setEnd(start.deprecatedNode(), start.deprecatedEditingOffset(), ec); SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get()); while (!backwardsIterator.atEnd()) { const UChar* characters = backwardsIterator.characters(); @@ -161,10 +161,10 @@ static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunc } searchRange->selectNodeContents(boundary, ec); - searchRange->setStart(start.node(), start.deprecatedEditingOffset(), ec); + searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), ec); TextIterator it(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions); unsigned next = 0; - bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; + bool inTextSecurityMode = start.deprecatedNode() && start.deprecatedNode()->renderer() && start.deprecatedNode()->renderer()->style()->textSecurity() != TSNONE; bool needMoreContext = false; while (!it.atEnd()) { // Keep asking the iterator for chunks until the search function @@ -316,7 +316,7 @@ VisiblePosition nextWordPosition(const VisiblePosition &c) static RootInlineBox *rootBoxForLine(const VisiblePosition &c) { Position p = c.deepEquivalent(); - Node *node = p.node(); + Node* node = p.deprecatedNode(); if (!node) return 0; @@ -351,7 +351,7 @@ static VisiblePosition startPositionForLine(const VisiblePosition& c) // There are VisiblePositions at offset 0 in blocks without // RootInlineBoxes, like empty editable blocks and bordered blocks. Position p = c.deepEquivalent(); - if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.deprecatedEditingOffset() == 0) + if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset()) return positionAvoidingFirstPositionInTable(c); return VisiblePosition(); @@ -377,13 +377,8 @@ static VisiblePosition startPositionForLine(const VisiblePosition& c) startBox = startBox->nextLeafChild(); } - int startOffset = 0; - if (startBox->isInlineTextBox()) { - InlineTextBox *startTextBox = static_cast<InlineTextBox *>(startBox); - startOffset = startTextBox->start(); - } - - VisiblePosition visPos = VisiblePosition(startNode, startOffset, DOWNSTREAM); + VisiblePosition visPos = startBox->isInlineTextBox() ? VisiblePosition(Position(startNode, static_cast<InlineTextBox *>(startBox)->start(), Position::PositionIsOffsetInAnchor), DOWNSTREAM) + : VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM); return positionAvoidingFirstPositionInTable(visPos); } @@ -404,7 +399,7 @@ static VisiblePosition endPositionForLine(const VisiblePosition& c) // There are VisiblePositions at offset 0 in blocks without // RootInlineBoxes, like empty editable blocks and bordered blocks. Position p = c.deepEquivalent(); - if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.deprecatedEditingOffset() == 0) + if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset()) return c; return VisiblePosition(); } @@ -429,17 +424,19 @@ static VisiblePosition endPositionForLine(const VisiblePosition& c) endBox = endBox->prevLeafChild(); } - int endOffset = 1; + Position pos; if (endNode->hasTagName(brTag)) { - endOffset = 0; + pos = positionBeforeNode(endNode); } else if (endBox->isInlineTextBox()) { InlineTextBox *endTextBox = static_cast<InlineTextBox *>(endBox); - endOffset = endTextBox->start(); + int endOffset = endTextBox->start(); if (!endTextBox->isLineBreak()) endOffset += endTextBox->len(); - } + pos = Position(endNode, endOffset, Position::PositionIsOffsetInAnchor); + } else + pos = positionAfterNode(endNode); - return VisiblePosition(endNode, endOffset, VP_UPSTREAM_IF_POSSIBLE); + return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); } VisiblePosition endOfLine(const VisiblePosition& c) @@ -501,7 +498,7 @@ static Node* enclosingNodeWithNonInlineRenderer(Node* n) VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int x) { Position p = visiblePosition.deepEquivalent(); - Node *node = p.node(); + Node* node = p.deprecatedNode(); Node* highestRoot = highestEditableRoot(p); if (!node) return VisiblePosition(); @@ -575,7 +572,7 @@ VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int // Move to the start of the content in this block, which effectively moves us // to the start of the line we're on. Element* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement(); - return VisiblePosition(rootElement, 0, DOWNSTREAM); + return VisiblePosition(firstPositionInNode(rootElement), DOWNSTREAM); } static Node* nextLeafWithSameEditability(Node* node, int offset) @@ -610,7 +607,7 @@ static Node* nextLeafWithSameEditability(Node* node) VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x) { Position p = visiblePosition.deepEquivalent(); - Node *node = p.node(); + Node* node = p.deprecatedNode(); Node* highestRoot = highestEditableRoot(p); if (!node) return VisiblePosition(); @@ -680,7 +677,7 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x) // Move to the end of the content in this block, which effectively moves us // to the end of the line we're on. Element* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement(); - return VisiblePosition(rootElement, rootElement ? rootElement->childNodeCount() : 0, DOWNSTREAM); + return VisiblePosition(lastPositionInNode(rootElement), DOWNSTREAM); } // --------- @@ -740,7 +737,7 @@ VisiblePosition nextSentencePosition(const VisiblePosition &c) VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule) { Position p = c.deepEquivalent(); - Node *startNode = p.node(); + Node* startNode = p.deprecatedNode(); if (!startNode) return VisiblePosition(); @@ -752,6 +749,7 @@ VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossi Node *node = startNode; int offset = p.deprecatedEditingOffset(); + Position::AnchorType type = p.anchorType(); Node *n = startNode; while (n) { @@ -772,6 +770,7 @@ VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossi break; if (r->isText() && r->caretMaxRenderedOffset() > 0) { + type = Position::PositionIsOffsetInAnchor; if (style->preserveNewline()) { const UChar* chars = toRenderText(r)->characters(); int i = toRenderText(r)->textLength(); @@ -780,20 +779,23 @@ VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossi i = max(0, o); while (--i >= 0) if (chars[i] == '\n') - return VisiblePosition(n, i + 1, DOWNSTREAM); + return VisiblePosition(Position(n, i + 1, Position::PositionIsOffsetInAnchor), DOWNSTREAM); } node = n; offset = 0; n = n->traversePreviousNodePostOrder(startBlock); } else if (editingIgnoresContent(n) || isTableElement(n)) { node = n; - offset = 0; + type = Position::PositionIsBeforeAnchor; n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock); } else n = n->traversePreviousNodePostOrder(startBlock); } - return VisiblePosition(node, offset, DOWNSTREAM); + if (type == Position::PositionIsOffsetInAnchor) + return VisiblePosition(Position(node, offset, type), DOWNSTREAM); + + return VisiblePosition(Position(node, type), DOWNSTREAM); } VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossingRule boundaryCrossingRule) @@ -802,7 +804,7 @@ VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossing return VisiblePosition(); Position p = c.deepEquivalent(); - Node* startNode = p.node(); + Node* startNode = p.deprecatedNode(); if (isRenderedAsNonInlineTableImageOrHR(startNode)) return lastDeepEditingPositionForNode(startNode); @@ -812,6 +814,7 @@ VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossing Node *node = startNode; int offset = p.deprecatedEditingOffset(); + Position::AnchorType type = p.anchorType(); Node *n = startNode; while (n) { @@ -834,25 +837,29 @@ VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossing // FIXME: We avoid returning a position where the renderer can't accept the caret. if (r->isText() && r->caretMaxRenderedOffset() > 0) { int length = toRenderText(r)->textLength(); + type = Position::PositionIsOffsetInAnchor; if (style->preserveNewline()) { const UChar* chars = toRenderText(r)->characters(); int o = n == startNode ? offset : 0; for (int i = o; i < length; ++i) if (chars[i] == '\n') - return VisiblePosition(n, i, DOWNSTREAM); + return VisiblePosition(Position(n, i, Position::PositionIsOffsetInAnchor), DOWNSTREAM); } node = n; offset = r->caretMaxOffset(); n = n->traverseNextNode(stayInsideBlock); } else if (editingIgnoresContent(n) || isTableElement(n)) { node = n; - offset = lastOffsetForEditing(n); + type = Position::PositionIsAfterAnchor; n = n->traverseNextSibling(stayInsideBlock); } else n = n->traverseNextNode(stayInsideBlock); } - return VisiblePosition(node, offset, DOWNSTREAM); + if (type == Position::PositionIsOffsetInAnchor) + return VisiblePosition(Position(node, offset, type), DOWNSTREAM); + + return VisiblePosition(Position(node, type), DOWNSTREAM); } VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition) @@ -910,7 +917,7 @@ VisiblePosition nextParagraphPosition(const VisiblePosition& p, int x) VisiblePosition startOfBlock(const VisiblePosition &c) { Position p = c.deepEquivalent(); - Node *startNode = p.node(); + Node* startNode = p.deprecatedNode(); if (!startNode) return VisiblePosition(); return VisiblePosition(firstPositionInNode(startNode->enclosingBlockFlowElement()), DOWNSTREAM); @@ -920,13 +927,13 @@ VisiblePosition endOfBlock(const VisiblePosition &c) { Position p = c.deepEquivalent(); - Node *startNode = p.node(); + Node* startNode = p.deprecatedNode(); if (!startNode) return VisiblePosition(); Node *startBlock = startNode->enclosingBlockFlowElement(); - return VisiblePosition(startBlock, startBlock->childNodeCount(), VP_DEFAULT_AFFINITY); + return VisiblePosition(lastPositionInNode(startBlock), VP_DEFAULT_AFFINITY); } bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b) @@ -951,12 +958,12 @@ VisiblePosition startOfDocument(const Node* node) if (!node) return VisiblePosition(); - return VisiblePosition(node->document()->documentElement(), 0, DOWNSTREAM); + return VisiblePosition(firstPositionInNode(node->document()->documentElement()), DOWNSTREAM); } VisiblePosition startOfDocument(const VisiblePosition &c) { - return startOfDocument(c.deepEquivalent().node()); + return startOfDocument(c.deepEquivalent().deprecatedNode()); } VisiblePosition endOfDocument(const Node* node) @@ -965,22 +972,22 @@ VisiblePosition endOfDocument(const Node* node) return VisiblePosition(); Element* doc = node->document()->documentElement(); - return VisiblePosition(doc, doc->childNodeCount(), DOWNSTREAM); + return VisiblePosition(lastPositionInNode(doc), DOWNSTREAM); } VisiblePosition endOfDocument(const VisiblePosition &c) { - return endOfDocument(c.deepEquivalent().node()); + return endOfDocument(c.deepEquivalent().deprecatedNode()); } bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b) { Position ap = a.deepEquivalent(); - Node *an = ap.node(); + Node* an = ap.deprecatedNode(); if (!an) return false; Position bp = b.deepEquivalent(); - Node *bn = bp.node(); + Node* bn = bp.deprecatedNode(); if (an == bn) return true; @@ -1108,7 +1115,7 @@ static VisiblePosition logicalStartPositionForLine(const VisiblePosition& c) // There are VisiblePositions at offset 0 in blocks without // RootInlineBoxes, like empty editable blocks and bordered blocks. Position p = c.deepEquivalent(); - if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset()) + if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset()) return positionAvoidingFirstPositionInTable(c); return VisiblePosition(); @@ -1121,9 +1128,8 @@ static VisiblePosition logicalStartPositionForLine(const VisiblePosition& c) if (!logicalStartNode) return VisiblePosition(); - int startOffset = logicalStartBox->caretMinOffset(); - - VisiblePosition visPos = VisiblePosition(logicalStartNode, startOffset, DOWNSTREAM); + VisiblePosition visPos = logicalStartNode->isTextNode() ? VisiblePosition(Position(logicalStartNode, logicalStartBox->caretMinOffset(), Position::PositionIsOffsetInAnchor), DOWNSTREAM) + : VisiblePosition(positionBeforeNode(logicalStartNode), DOWNSTREAM); return positionAvoidingFirstPositionInTable(visPos); } @@ -1146,7 +1152,7 @@ static VisiblePosition logicalEndPositionForLine(const VisiblePosition& c) // There are VisiblePositions at offset 0 in blocks without // RootInlineBoxes, like empty editable blocks and bordered blocks. Position p = c.deepEquivalent(); - if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset()) + if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset()) return c; return VisiblePosition(); } @@ -1157,17 +1163,19 @@ static VisiblePosition logicalEndPositionForLine(const VisiblePosition& c) if (!logicalEndNode) return VisiblePosition(); - int endOffset = 1; + Position pos; if (logicalEndNode->hasTagName(brTag)) - endOffset = 0; + pos = positionBeforeNode(logicalEndNode); else if (logicalEndBox->isInlineTextBox()) { InlineTextBox* endTextBox = static_cast<InlineTextBox*>(logicalEndBox); - endOffset = endTextBox->start(); + int endOffset = endTextBox->start(); if (!endTextBox->isLineBreak()) endOffset += endTextBox->len(); - } + pos = Position(logicalEndNode, endOffset, Position::PositionIsOffsetInAnchor); + } else + pos = positionAfterNode(logicalEndNode); - return VisiblePosition(logicalEndNode, endOffset, VP_UPSTREAM_IF_POSSIBLE); + return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); } bool inSameLogicalLine(const VisiblePosition& a, const VisiblePosition& b) |